From e69a7f732e3e32976316b6b2d50fa721adfdb6ff Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 21 Sep 2023 19:55:43 +0200 Subject: [PATCH 001/199] Do proper type checking for type handles. (#7065) Instead of relying purely on the assumption that type handles can be compared cheaply by pointer equality, fallback to a more expensive walk of the type tree that recursively compares types structurally. This allows different components to call into each other as long as their types are structurally equivalent. Signed-off-by: Ryan Levick --- crates/wasmtime/src/component/types.rs | 269 +++++++++++++++++++++++-- tests/all/component_model/import.rs | 61 ++++++ 2 files changed, 310 insertions(+), 20 deletions(-) diff --git a/crates/wasmtime/src/component/types.rs b/crates/wasmtime/src/component/types.rs index 43b25a13c388..f751079fa037 100644 --- a/crates/wasmtime/src/component/types.rs +++ b/crates/wasmtime/src/component/types.rs @@ -9,8 +9,8 @@ use std::ops::Deref; use std::sync::Arc; use wasmtime_environ::component::{ CanonicalAbiInfo, ComponentTypes, InterfaceType, ResourceIndex, TypeEnumIndex, TypeFlagsIndex, - TypeListIndex, TypeOptionIndex, TypeRecordIndex, TypeResultIndex, TypeTupleIndex, - TypeVariantIndex, + TypeListIndex, TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, + TypeTupleIndex, TypeVariantIndex, }; use wasmtime_environ::PrimaryMap; @@ -56,6 +56,29 @@ impl Handle { resources: &self.resources, } } + + fn equivalent<'a>( + &'a self, + other: &'a Self, + type_check: fn(&TypeChecker<'a>, T, T) -> bool, + ) -> bool + where + T: PartialEq + Copy, + { + (self.index == other.index + && Arc::ptr_eq(&self.types, &other.types) + && Arc::ptr_eq(&self.resources, &other.resources)) + || type_check( + &TypeChecker { + a_types: &self.types, + b_types: &other.types, + a_resource: &self.resources, + b_resource: &other.resources, + }, + self.index, + other.index, + ) + } } impl fmt::Debug for Handle { @@ -66,23 +89,173 @@ impl fmt::Debug for Handle { } } -impl PartialEq for Handle { - fn eq(&self, other: &Self) -> bool { - // FIXME: This is an overly-restrictive definition of equality in that it doesn't consider types to be - // equal unless they refer to the same declaration in the same component. It's a good shortcut for the - // common case, but we should also do a recursive structural equality test if the shortcut test fails. - self.index == other.index - && Arc::ptr_eq(&self.types, &other.types) - && Arc::ptr_eq(&self.resources, &other.resources) - } +/// Type checker between two `Handle`s +struct TypeChecker<'a> { + a_types: &'a ComponentTypes, + a_resource: &'a PrimaryMap, + b_types: &'a ComponentTypes, + b_resource: &'a PrimaryMap, } -impl Eq for Handle {} +impl TypeChecker<'_> { + fn interface_types_equal(&self, a: InterfaceType, b: InterfaceType) -> bool { + match (a, b) { + (InterfaceType::Own(o1), InterfaceType::Own(o2)) => self.resources_equal(o1, o2), + (InterfaceType::Own(_), _) => false, + (InterfaceType::Borrow(b1), InterfaceType::Borrow(b2)) => self.resources_equal(b1, b2), + (InterfaceType::Borrow(_), _) => false, + (InterfaceType::List(l1), InterfaceType::List(l2)) => self.lists_equal(l1, l2), + (InterfaceType::List(_), _) => false, + (InterfaceType::Record(r1), InterfaceType::Record(r2)) => self.records_equal(r1, r2), + (InterfaceType::Record(_), _) => false, + (InterfaceType::Variant(v1), InterfaceType::Variant(v2)) => self.variants_equal(v1, v2), + (InterfaceType::Variant(_), _) => false, + (InterfaceType::Result(r1), InterfaceType::Result(r2)) => self.results_equal(r1, r2), + (InterfaceType::Result(_), _) => false, + (InterfaceType::Option(o1), InterfaceType::Option(o2)) => self.options_equal(o1, o2), + (InterfaceType::Option(_), _) => false, + (InterfaceType::Enum(e1), InterfaceType::Enum(e2)) => self.enums_equal(e1, e2), + (InterfaceType::Enum(_), _) => false, + (InterfaceType::Tuple(t1), InterfaceType::Tuple(t2)) => self.tuples_equal(t1, t2), + (InterfaceType::Tuple(_), _) => false, + (InterfaceType::Flags(f1), InterfaceType::Flags(f2)) => self.flags_equal(f1, f2), + (InterfaceType::Flags(_), _) => false, + (InterfaceType::Bool, InterfaceType::Bool) => true, + (InterfaceType::Bool, _) => false, + (InterfaceType::U8, InterfaceType::U8) => true, + (InterfaceType::U8, _) => false, + (InterfaceType::U16, InterfaceType::U16) => true, + (InterfaceType::U16, _) => false, + (InterfaceType::U32, InterfaceType::U32) => true, + (InterfaceType::U32, _) => false, + (InterfaceType::U64, InterfaceType::U64) => true, + (InterfaceType::U64, _) => false, + (InterfaceType::S8, InterfaceType::S8) => true, + (InterfaceType::S8, _) => false, + (InterfaceType::S16, InterfaceType::S16) => true, + (InterfaceType::S16, _) => false, + (InterfaceType::S32, InterfaceType::S32) => true, + (InterfaceType::S32, _) => false, + (InterfaceType::S64, InterfaceType::S64) => true, + (InterfaceType::S64, _) => false, + (InterfaceType::Float32, InterfaceType::Float32) => true, + (InterfaceType::Float32, _) => false, + (InterfaceType::Float64, InterfaceType::Float64) => true, + (InterfaceType::Float64, _) => false, + (InterfaceType::String, InterfaceType::String) => true, + (InterfaceType::String, _) => false, + (InterfaceType::Char, InterfaceType::Char) => true, + (InterfaceType::Char, _) => false, + } + } + + fn lists_equal(&self, l1: TypeListIndex, l2: TypeListIndex) -> bool { + let a = &self.a_types[l1]; + let b = &self.b_types[l2]; + self.interface_types_equal(a.element, b.element) + } + + fn resources_equal(&self, o1: TypeResourceTableIndex, o2: TypeResourceTableIndex) -> bool { + let a = &self.a_types[o1]; + let b = &self.b_types[o2]; + self.a_resource[a.ty] == self.b_resource[b.ty] + } + + fn records_equal(&self, r1: TypeRecordIndex, r2: TypeRecordIndex) -> bool { + let a = &self.a_types[r1]; + let b = &self.b_types[r2]; + if a.fields.len() != b.fields.len() { + return false; + } + a.fields + .iter() + .zip(b.fields.iter()) + .all(|(a_field, b_field)| { + a_field.name == b_field.name && self.interface_types_equal(a_field.ty, b_field.ty) + }) + } + + fn variants_equal(&self, v1: TypeVariantIndex, v2: TypeVariantIndex) -> bool { + let a = &self.a_types[v1]; + let b = &self.b_types[v2]; + if a.cases.len() != b.cases.len() { + return false; + } + a.cases.iter().zip(b.cases.iter()).all(|(a_case, b_case)| { + if a_case.name != b_case.name { + return false; + } + match (a_case.ty, b_case.ty) { + (Some(a_case_ty), Some(b_case_ty)) => { + self.interface_types_equal(a_case_ty, b_case_ty) + } + (None, None) => true, + _ => false, + } + }) + } + + fn results_equal(&self, r1: TypeResultIndex, r2: TypeResultIndex) -> bool { + let a = &self.a_types[r1]; + let b = &self.b_types[r2]; + let oks = match (a.ok, b.ok) { + (Some(ok1), Some(ok2)) => self.interface_types_equal(ok1, ok2), + (None, None) => true, + _ => false, + }; + if !oks { + return false; + } + match (a.err, b.err) { + (Some(err1), Some(err2)) => self.interface_types_equal(err1, err2), + (None, None) => true, + _ => false, + } + } + + fn options_equal(&self, o1: TypeOptionIndex, o2: TypeOptionIndex) -> bool { + let a = &self.a_types[o1]; + let b = &self.b_types[o2]; + self.interface_types_equal(a.ty, b.ty) + } + + fn enums_equal(&self, e1: TypeEnumIndex, e2: TypeEnumIndex) -> bool { + let a = &self.a_types[e1]; + let b = &self.b_types[e2]; + a.names == b.names + } + + fn tuples_equal(&self, t1: TypeTupleIndex, t2: TypeTupleIndex) -> bool { + let a = &self.a_types[t1]; + let b = &self.b_types[t2]; + if a.types.len() != b.types.len() { + return false; + } + a.types + .iter() + .zip(b.types.iter()) + .all(|(&a, &b)| self.interface_types_equal(a, b)) + } + + fn flags_equal(&self, f1: TypeFlagsIndex, f2: TypeFlagsIndex) -> bool { + let a = &self.a_types[f1]; + let b = &self.b_types[f2]; + a.names == b.names + } +} /// A `list` interface type -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Debug)] pub struct List(Handle); +impl PartialEq for List { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::lists_equal) + } +} + +impl Eq for List {} + impl List { /// Instantiate this type with the specified `values`. pub fn new_val(&self, values: Box<[Val]>) -> Result { @@ -108,7 +281,7 @@ pub struct Field<'a> { } /// A `record` interface type -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Debug)] pub struct Record(Handle); impl Record { @@ -130,8 +303,16 @@ impl Record { } } +impl PartialEq for Record { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::records_equal) + } +} + +impl Eq for Record {} + /// A `tuple` interface type -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Debug)] pub struct Tuple(Handle); impl Tuple { @@ -153,6 +334,14 @@ impl Tuple { } } +impl PartialEq for Tuple { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::tuples_equal) + } +} + +impl Eq for Tuple {} + /// A case declaration belonging to a `variant` pub struct Case<'a> { /// The name of the case @@ -162,7 +351,7 @@ pub struct Case<'a> { } /// A `variant` interface type -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Debug)] pub struct Variant(Handle); impl Variant { @@ -187,8 +376,16 @@ impl Variant { } } +impl PartialEq for Variant { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::variants_equal) + } +} + +impl Eq for Variant {} + /// An `enum` interface type -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Debug)] pub struct Enum(Handle); impl Enum { @@ -210,8 +407,16 @@ impl Enum { } } +impl PartialEq for Enum { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::enums_equal) + } +} + +impl Eq for Enum {} + /// An `option` interface type -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Debug)] pub struct OptionType(Handle); impl OptionType { @@ -230,8 +435,16 @@ impl OptionType { } } +impl PartialEq for OptionType { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::options_equal) + } +} + +impl Eq for OptionType {} + /// An `expected` interface type -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Debug)] pub struct ResultType(Handle); impl ResultType { @@ -261,8 +474,16 @@ impl ResultType { } } +impl PartialEq for ResultType { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::results_equal) + } +} + +impl Eq for ResultType {} + /// A `flags` interface type -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Debug)] pub struct Flags(Handle); impl Flags { @@ -288,6 +509,14 @@ impl Flags { } } +impl PartialEq for Flags { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::flags_equal) + } +} + +impl Eq for Flags {} + /// Represents a component model interface type #[derive(Clone, PartialEq, Eq, Debug)] #[allow(missing_docs)] diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index 38e23edd4f95..e77b9a73433b 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -924,3 +924,64 @@ fn no_actual_wasm_code() -> Result<()> { Ok(()) } + +#[test] +fn use_types_across_component_boundaries() -> Result<()> { + // Create a component that exports a function that returns a record + let engine = super::engine(); + let component = Component::new( + &engine, + r#"(component + (type (;0;) (record (field "a" u8) (field "b" string))) + (import "my-record" (type $my-record (eq 0))) + (core module $m + (memory $memory 17) + (export "memory" (memory $memory)) + (func (export "my-func") (result i32) + i32.const 4 + return)) + (core instance $instance (instantiate $m)) + (type $func-type (func (result $my-record))) + (alias core export $instance "my-func" (core func $my-func)) + (alias core export $instance "memory" (core memory $memory)) + (func $my-func (type $func-type) (canon lift (core func $my-func) (memory $memory) string-encoding=utf8)) + (export $export "my-func" (func $my-func)) + )"#, + )?; + let mut store = Store::new(&engine, 0); + let linker = Linker::new(&engine); + let instance = linker.instantiate(&mut store, &component)?; + let my_func = instance.get_func(&mut store, "my-func").unwrap(); + let mut results = vec![Val::Bool(false)]; + my_func.call(&mut store, &[], &mut results)?; + + // Create another component that exports a function that takes that record as an argument + let component = Component::new( + &engine, + format!( + r#"(component + (type (;0;) (record (field "a" u8) (field "b" string))) + (import "my-record" (type $my-record (eq 0))) + (core module $m + (memory $memory 17) + (export "memory" (memory $memory)) + {REALLOC_AND_FREE} + (func (export "my-func") (param i32 i32 i32))) + (core instance $instance (instantiate $m)) + (type $func-type (func (param "my-record" $my-record))) + (alias core export $instance "my-func" (core func $my-func)) + (alias core export $instance "memory" (core memory $memory)) + (func $my-func (type $func-type) (canon lift (core func $my-func) (memory $memory) string-encoding=utf8 (realloc (func $instance "realloc")))) + (export $export "my-func" (func $my-func)) + )"# + ), + )?; + let mut store = Store::new(&engine, 0); + let linker = Linker::new(&engine); + let instance = linker.instantiate(&mut store, &component)?; + let my_func = instance.get_func(&mut store, "my-func").unwrap(); + // Call the exported function with the return values of the call to the previous component's exported function + my_func.call(&mut store, &results, &mut [])?; + + Ok(()) +} From 30ee0dc8d990cb6126ede5c9ca7bc6d8000b44e5 Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Thu, 21 Sep 2023 16:04:52 -0700 Subject: [PATCH 002/199] Use wasi-streams in the wasi-http implementation (#7056) * Start refactoring wasi-http * Checkpoint * Initial implementation of response future handling * Lazily initialize response headers and body * make wasmtime-wasi-http compile * wasi-http wit: make a way to reject outgoing-request in outgoing-handler before waiting for the future to resolve * wasi: sync wit from wasi-http * outgoing handler impl: report errors to userland * test-programs: get wasi-http-components kicking over, delete modules and components-sync tests wasi-http-components-sync will come back once we get done with other stuff, but its superfulous for now. wasi-http-modules will not be returning. * Process headers * Add HostIncomingBody::new * Add trailers functions * Add TODO for body task outline * Rework incoming-response-consume to return a future-trailers value as well * Fix the wit * First cut at the worker loop * wasi-http: change how we represent bodies/trailers, and annotate own/borrow/child throughout * Update types_impl.rs for wit changes * Split body management into its own module * Checkpoint * more work on incoming body and future trailers * Fill out some more functions * Implement future-trailers-{subscribe,get} * Implement drop-future-trailers * Rework fields, but make the borrow checker mad * Fix borrow error * wasi-http-tests: fix build * test-runner: report errors with stdout/stderr properly * fix two trivial wasi-http tests the error type here changed from a types::Error to an outbound_handler::Error * Remove unnecessary drops * Convert a `bail!` to a `todo!` * Remove a TODO that documented the body worker structure * fill in a bunch more of OutputBody * Remove the custom FrameFut future in favor of using http_body_util * Move the outgoing body types to body.rs * Rework the handling of outgoing bodies * Fix the `outgoing request get` test * Avoid deadlocking the post tests * future_incoming_request_get shouldn't delete the resource * Fix the invalid_dnsname test * implement drop-future-incoming-response * Fix invalid_port and invalid_dnsname tests * Fix the post test * Passing a too large string to println! caused the large post test to fail * Format * Plumb through `between_bytes_timeout` * Downgrade hyper * Revert "Downgrade hyper" This reverts commit fa0750e0c42823cb785288fcf6c0507c5ae9fd64. * Restore old https connection setup * Sync the wasi and wasi-http http deps * Fix tests * Remove the module and component-sync tests, as they are currently not supported * Fix the reference to the large_post test in the components test * Fix wasi-http integration * sync implementation of wasi-http * Slightly more robust error checking * Ignore the wasi-http cli test prtest:full * Consistent ignore attributes between sync and async tests * Fix doc errors * code motion: introduce intermediate `HostIncomingBodyBuilder` rather than a tuple * explain design * Turn FieldMap into a type synonym * Tidy up some future state (#7073) Co-authored-by: Pat Hickey * body HostInputStream: report runtime errors with StreamRuntimeError HostInputStream is designed wrong to need that in the first place. We will fix it in a follow-up as soon as resources land. --------- Co-authored-by: Pat Hickey Co-authored-by: Alex Crichton --- .../tests/wasi-http-components-sync.rs | 13 +- .../tests/wasi-http-components.rs | 18 +- .../test-programs/tests/wasi-http-modules.rs | 196 ---- .../bin/outbound_request_invalid_dnsname.rs | 4 +- .../bin/outbound_request_invalid_version.rs | 2 +- .../src/bin/outbound_request_large_post.rs | 2 +- .../src/bin/outbound_request_post.rs | 2 +- .../bin/outbound_request_unknown_method.rs | 2 +- .../outbound_request_unsupported_scheme.rs | 2 +- .../test-programs/wasi-http-tests/src/lib.rs | 83 +- crates/wasi-http/src/body.rs | 498 ++++++++++ crates/wasi-http/src/http_impl.rs | 450 ++++----- crates/wasi-http/src/incoming_handler.rs | 21 +- crates/wasi-http/src/lib.rs | 59 +- crates/wasi-http/src/proxy.rs | 38 +- crates/wasi-http/src/types.rs | 593 +++++------- crates/wasi-http/src/types_impl.rs | 881 +++++++----------- .../wit/deps/http/incoming-handler.wit | 6 +- .../wit/deps/http/outgoing-handler.wit | 13 +- crates/wasi-http/wit/deps/http/types.wit | 148 ++- crates/wasi/src/preview2/mod.rs | 5 +- crates/wasi/src/preview2/pipe.rs | 195 +--- crates/wasi/src/preview2/table.rs | 11 +- crates/wasi/src/preview2/write_stream.rs | 196 ++++ .../wasi/wit/deps/http/incoming-handler.wit | 6 +- .../wasi/wit/deps/http/outgoing-handler.wit | 13 +- crates/wasi/wit/deps/http/types.wit | 148 ++- src/commands/run.rs | 24 +- tests/all/cli_tests.rs | 1 + 29 files changed, 1728 insertions(+), 1902 deletions(-) delete mode 100644 crates/test-programs/tests/wasi-http-modules.rs create mode 100644 crates/wasi-http/src/body.rs create mode 100644 crates/wasi/src/preview2/write_stream.rs diff --git a/crates/test-programs/tests/wasi-http-components-sync.rs b/crates/test-programs/tests/wasi-http-components-sync.rs index f525d5b44724..33c638fc6883 100644 --- a/crates/test-programs/tests/wasi-http-components-sync.rs +++ b/crates/test-programs/tests/wasi-http-components-sync.rs @@ -46,12 +46,13 @@ impl WasiView for Ctx { } impl WasiHttpView for Ctx { - fn http_ctx(&self) -> &WasiHttpCtx { - &self.http - } - fn http_ctx_mut(&mut self) -> &mut WasiHttpCtx { + fn ctx(&mut self) -> &mut WasiHttpCtx { &mut self.http } + + fn table(&mut self) -> &mut Table { + &mut self.table + } } fn instantiate_component( @@ -60,7 +61,7 @@ fn instantiate_component( ) -> Result<(Store, Command), anyhow::Error> { let mut linker = Linker::new(&ENGINE); add_to_linker(&mut linker)?; - wasmtime_wasi_http::proxy::sync::add_to_linker(&mut linker)?; + wasmtime_wasi_http::proxy::add_to_linker(&mut linker)?; let mut store = Store::new(&ENGINE, ctx); @@ -84,7 +85,7 @@ fn run(name: &str) -> anyhow::Result<()> { builder.env(var, val); } let wasi = builder.build(&mut table)?; - let http = WasiHttpCtx::new(); + let http = WasiHttpCtx {}; let (mut store, command) = instantiate_component(component, Ctx { table, wasi, http })?; command diff --git a/crates/test-programs/tests/wasi-http-components.rs b/crates/test-programs/tests/wasi-http-components.rs index 1f05411d0178..8d1fa1f96c52 100644 --- a/crates/test-programs/tests/wasi-http-components.rs +++ b/crates/test-programs/tests/wasi-http-components.rs @@ -47,10 +47,10 @@ impl WasiView for Ctx { } impl WasiHttpView for Ctx { - fn http_ctx(&self) -> &WasiHttpCtx { - &self.http + fn table(&mut self) -> &mut Table { + &mut self.table } - fn http_ctx_mut(&mut self) -> &mut WasiHttpCtx { + fn ctx(&mut self) -> &mut WasiHttpCtx { &mut self.http } } @@ -85,16 +85,11 @@ async fn run(name: &str) -> anyhow::Result<()> { builder.env(var, val); } let wasi = builder.build(&mut table)?; - let http = WasiHttpCtx::new(); + let http = WasiHttpCtx; let (mut store, command) = instantiate_component(component, Ctx { table, wasi, http }).await?; - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("run returned a failure"))?; - Ok(()) + command.wasi_cli_run().call_run(&mut store).await }; r.map_err(move |trap: anyhow::Error| { let stdout = stdout.try_into_inner().expect("single ref to stdout"); @@ -109,7 +104,8 @@ async fn run(name: &str) -> anyhow::Result<()> { "error while testing wasi-tests {} with http-components", name )) - })?; + })? + .map_err(|()| anyhow::anyhow!("run returned an error"))?; Ok(()) } diff --git a/crates/test-programs/tests/wasi-http-modules.rs b/crates/test-programs/tests/wasi-http-modules.rs deleted file mode 100644 index e73251d5a1f8..000000000000 --- a/crates/test-programs/tests/wasi-http-modules.rs +++ /dev/null @@ -1,196 +0,0 @@ -#![cfg(all(feature = "test_programs", not(skip_wasi_http_tests)))] -use wasmtime::{Config, Engine, Func, Linker, Module, Store}; -use wasmtime_wasi::preview2::{ - pipe::MemoryOutputPipe, - preview1::{WasiPreview1Adapter, WasiPreview1View}, - IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView, -}; -use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; - -use test_programs::http_server::{setup_http1, setup_http2}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(true); - config.async_support(true); - - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_module(&str) -> Module -include!(concat!(env!("OUT_DIR"), "/wasi_http_tests_modules.rs")); - -struct Ctx { - table: Table, - wasi: WasiCtx, - adapter: WasiPreview1Adapter, - http: WasiHttpCtx, -} - -impl WasiView for Ctx { - fn table(&self) -> &Table { - &self.table - } - fn table_mut(&mut self) -> &mut Table { - &mut self.table - } - fn ctx(&self) -> &WasiCtx { - &self.wasi - } - fn ctx_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } -} -impl WasiPreview1View for Ctx { - fn adapter(&self) -> &WasiPreview1Adapter { - &self.adapter - } - fn adapter_mut(&mut self) -> &mut WasiPreview1Adapter { - &mut self.adapter - } -} -impl WasiHttpView for Ctx { - fn http_ctx(&self) -> &WasiHttpCtx { - &self.http - } - fn http_ctx_mut(&mut self) -> &mut WasiHttpCtx { - &mut self.http - } -} - -async fn instantiate_module(module: Module, ctx: Ctx) -> Result<(Store, Func), anyhow::Error> { - let mut linker = Linker::new(&ENGINE); - wasmtime_wasi_http::add_to_linker(&mut linker)?; - wasmtime_wasi::preview2::preview1::add_to_linker_async(&mut linker)?; - - let mut store = Store::new(&ENGINE, ctx); - - let instance = linker.instantiate_async(&mut store, &module).await?; - let command = instance.get_func(&mut store, "_start").unwrap(); - Ok((store, command)) -} - -async fn run(name: &str) -> anyhow::Result<()> { - let stdout = MemoryOutputPipe::new(4096); - let stderr = MemoryOutputPipe::new(4096); - let r = { - let mut table = Table::new(); - let module = get_module(name); - - // Create our wasi context. - let mut builder = WasiCtxBuilder::new(); - builder.stdout(stdout.clone(), IsATTY::No); - builder.stderr(stderr.clone(), IsATTY::No); - builder.arg(name); - for (var, val) in test_programs::wasi_tests_environment() { - builder.env(var, val); - } - let wasi = builder.build(&mut table)?; - let http = WasiHttpCtx::new(); - - let adapter = WasiPreview1Adapter::new(); - - let (mut store, command) = instantiate_module( - module, - Ctx { - table, - wasi, - http, - adapter, - }, - ) - .await?; - command.call_async(&mut store, &[], &mut []).await - }; - r.map_err(move |trap: anyhow::Error| { - let stdout = stdout.try_into_inner().expect("single ref to stdout"); - if !stdout.is_empty() { - println!("[guest] stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); - } - let stderr = stderr.try_into_inner().expect("single ref to stderr"); - if !stderr.is_empty() { - println!("[guest] stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); - } - trap.context(format!( - "error while testing wasi-tests {} with http-modules", - name - )) - })?; - Ok(()) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_get() { - setup_http1(run("outbound_request_get")).await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_post() { - setup_http1(run("outbound_request_post")).await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_large_post() { - setup_http1(run("outbound_request_large_post")) - .await - .unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_put() { - setup_http1(run("outbound_request_put")).await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_invalid_version() { - setup_http2(run("outbound_request_invalid_version")) - .await - .unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_unknown_method() { - run("outbound_request_unknown_method").await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_unsupported_scheme() { - run("outbound_request_unsupported_scheme").await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_invalid_port() { - run("outbound_request_invalid_port").await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_invalid_dnsname() { - run("outbound_request_invalid_dnsname").await.unwrap(); -} diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs index 7c3ba5909b8f..6cc5ce8bc97b 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs @@ -15,6 +15,6 @@ async fn run() { ) .await; - let error = res.unwrap_err(); - assert_eq!(error.to_string(), "Error::InvalidUrl(\"invalid dnsname\")"); + let error = res.unwrap_err().to_string(); + assert!(error.starts_with("Error::InvalidUrl(\"failed to lookup address information:")); } diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs index cadabda4dec1..53c767edec8f 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs @@ -17,7 +17,7 @@ async fn run() { let error = res.unwrap_err().to_string(); if error.ne("Error::ProtocolError(\"invalid HTTP version parsed\")") - && error.ne("Error::ProtocolError(\"operation was canceled\")") + && !error.starts_with("Error::ProtocolError(\"operation was canceled") { panic!( r#"assertion failed: `(left == right)` diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs index 9f4bacef8f73..80e0688d47fc 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs @@ -23,7 +23,7 @@ async fn run() { .context("localhost:3000 /post large") .unwrap(); - println!("localhost:3000 /post large: {res:?}"); + println!("localhost:3000 /post large: {}", res.status); assert_eq!(res.status, 200); let method = res.header("x-wasmtime-test-method").unwrap(); assert_eq!(std::str::from_utf8(method).unwrap(), "POST"); diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs index 69a03d754ee9..131356fa91bc 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs @@ -22,5 +22,5 @@ async fn run() { assert_eq!(res.status, 200); let method = res.header("x-wasmtime-test-method").unwrap(); assert_eq!(std::str::from_utf8(method).unwrap(), "POST"); - assert_eq!(res.body, b"{\"foo\": \"bar\"}"); + assert_eq!(res.body, b"{\"foo\": \"bar\"}", "invalid body returned"); } diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs index a2ab5e48dc02..727294861b00 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs @@ -18,6 +18,6 @@ async fn run() { let error = res.unwrap_err(); assert_eq!( error.to_string(), - "Error::InvalidUrl(\"unknown method OTHER\")" + "Error::Invalid(\"unknown method OTHER\")" ); } diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs index 482550627e8d..c8a66b7da648 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs @@ -18,6 +18,6 @@ async fn run() { let error = res.unwrap_err(); assert_eq!( error.to_string(), - "Error::InvalidUrl(\"unsupported scheme WS\")" + "Error::Invalid(\"unsupported scheme WS\")" ); } diff --git a/crates/test-programs/wasi-http-tests/src/lib.rs b/crates/test-programs/wasi-http-tests/src/lib.rs index e1d9fb76dd6c..d516af03da16 100644 --- a/crates/test-programs/wasi-http-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-tests/src/lib.rs @@ -42,29 +42,22 @@ impl Response { } } -struct DropPollable { - pollable: poll::Pollable, -} - -impl Drop for DropPollable { - fn drop(&mut self) { - poll::drop_pollable(self.pollable); - } -} - pub async fn request( method: http_types::Method, scheme: http_types::Scheme, authority: &str, path_with_query: &str, body: Option<&[u8]>, - additional_headers: Option<&[(String, String)]>, + additional_headers: Option<&[(String, Vec)]>, ) -> Result { + fn header_val(v: &str) -> Vec { + v.to_string().into_bytes() + } let headers = http_types::new_fields( &[ &[ - ("User-agent".to_string(), "WASI-HTTP/0.0.1".to_string()), - ("Content-type".to_string(), "application/json".to_string()), + ("User-agent".to_string(), header_val("WASI-HTTP/0.0.1")), + ("Content-type".to_string(), header_val("application/json")), ], additional_headers.unwrap_or(&[]), ] @@ -79,15 +72,16 @@ pub async fn request( headers, ); - let request_body = http_types::outgoing_request_write(request) + let outgoing_body = http_types::outgoing_request_write(request) .map_err(|_| anyhow!("outgoing request write failed"))?; if let Some(mut buf) = body { - let sub = DropPollable { - pollable: streams::subscribe_to_output_stream(request_body), - }; + let request_body = http_types::outgoing_body_write(outgoing_body) + .map_err(|_| anyhow!("outgoing request write failed"))?; + + let pollable = streams::subscribe_to_output_stream(request_body); while !buf.is_empty() { - poll::poll_oneoff(&[sub.pollable]); + poll::poll_oneoff(&[pollable]); let permit = match streams::check_write(request_body) { Ok(n) => n, @@ -109,36 +103,39 @@ pub async fn request( _ => {} } - poll::poll_oneoff(&[sub.pollable]); + poll::poll_oneoff(&[pollable]); + poll::drop_pollable(pollable); match streams::check_write(request_body) { Ok(_) => {} Err(_) => anyhow::bail!("output stream error"), }; + + streams::drop_output_stream(request_body); } - let future_response = outgoing_handler::handle(request, None); + let future_response = outgoing_handler::handle(request, None)?; + + // TODO: The current implementation requires this drop after the request is sent. + // The ownership semantics are unclear in wasi-http we should clarify exactly what is + // supposed to happen here. + http_types::drop_outgoing_body(outgoing_body); let incoming_response = match http_types::future_incoming_response_get(future_response) { - Some(result) => result, + Some(result) => result.map_err(|_| anyhow!("incoming response errored"))?, None => { let pollable = http_types::listen_to_future_incoming_response(future_response); let _ = poll::poll_oneoff(&[pollable]); + poll::drop_pollable(pollable); http_types::future_incoming_response_get(future_response) .expect("incoming response available") + .map_err(|_| anyhow!("incoming response errored"))? } } // TODO: maybe anything that appears in the Result<_, E> position should impl // Error? anyway, just use its Debug here: .map_err(|e| anyhow!("{e:?}"))?; - // TODO: The current implementation requires this drop after the request is sent. - // The ownership semantics are unclear in wasi-http we should clarify exactly what is - // supposed to happen here. - streams::drop_output_stream(request_body); - - http_types::drop_outgoing_request(request); - http_types::drop_future_incoming_response(future_response); let status = http_types::incoming_response_status(incoming_response); @@ -147,26 +144,32 @@ pub async fn request( let headers = http_types::fields_entries(headers_handle); http_types::drop_fields(headers_handle); - let body_stream = http_types::incoming_response_consume(incoming_response) + let incoming_body = http_types::incoming_response_consume(incoming_response) .map_err(|()| anyhow!("incoming response has no body stream"))?; - let input_stream_pollable = streams::subscribe_to_input_stream(body_stream); + + http_types::drop_incoming_response(incoming_response); + + let input_stream = http_types::incoming_body_stream(incoming_body).unwrap(); + let input_stream_pollable = streams::subscribe_to_input_stream(input_stream); let mut body = Vec::new(); let mut eof = streams::StreamStatus::Open; while eof != streams::StreamStatus::Ended { - let (mut body_chunk, stream_status) = - streams::read(body_stream, u64::MAX).map_err(|_| anyhow!("body_stream read failed"))?; - eof = if body_chunk.is_empty() { - streams::StreamStatus::Ended - } else { - stream_status - }; - body.append(&mut body_chunk); + poll::poll_oneoff(&[input_stream_pollable]); + + let (mut body_chunk, stream_status) = streams::read(input_stream, 1024 * 1024) + .map_err(|_| anyhow!("input_stream read failed"))?; + + eof = stream_status; + + if !body_chunk.is_empty() { + body.append(&mut body_chunk); + } } poll::drop_pollable(input_stream_pollable); - streams::drop_input_stream(body_stream); - http_types::drop_incoming_response(incoming_response); + streams::drop_input_stream(input_stream); + http_types::drop_incoming_body(incoming_body); Ok(Response { status, diff --git a/crates/wasi-http/src/body.rs b/crates/wasi-http/src/body.rs new file mode 100644 index 000000000000..298fe7b92bfe --- /dev/null +++ b/crates/wasi-http/src/body.rs @@ -0,0 +1,498 @@ +use crate::{bindings::http::types, types::FieldMap}; +use anyhow::anyhow; +use bytes::Bytes; +use http_body_util::combinators::BoxBody; +use std::future::Future; +use std::{ + convert::Infallible, + pin::Pin, + sync::{Arc, Mutex}, + time::Duration, +}; +use tokio::sync::{mpsc, oneshot}; +use wasmtime_wasi::preview2::{ + self, AbortOnDropJoinHandle, HostInputStream, HostOutputStream, OutputStreamError, + StreamRuntimeError, StreamState, +}; + +/// Holds onto the things needed to construct a [`HostIncomingBody`] until we are ready to build +/// one. The HostIncomingBody spawns a task that starts consuming the incoming body, and we don't +/// want to do that unless the user asks to consume the body. +pub struct HostIncomingBodyBuilder { + pub body: hyper::body::Incoming, + pub between_bytes_timeout: Duration, +} + +impl HostIncomingBodyBuilder { + /// Consume the state held in the [`HostIncomingBodyBuilder`] to spawn a task that will drive the + /// streaming body to completion. Data segments will be communicated out over the + /// [`HostIncomingBodyStream`], and a [`HostFutureTrailers`] gives a way to block on/retrieve + /// the trailers. + pub fn build(mut self) -> HostIncomingBody { + let (body_writer, body_receiver) = mpsc::channel(1); + let (trailer_writer, trailers) = oneshot::channel(); + + let worker = preview2::spawn(async move { + loop { + let frame = match tokio::time::timeout( + self.between_bytes_timeout, + http_body_util::BodyExt::frame(&mut self.body), + ) + .await + { + Ok(None) => break, + + Ok(Some(Ok(frame))) => frame, + + Ok(Some(Err(e))) => { + match body_writer.send(Err(anyhow::anyhow!(e))).await { + Ok(_) => {} + // If the body read end has dropped, then we report this error with the + // trailers. unwrap and rewrap Err because the Ok side of these two Results + // are different. + Err(e) => { + let _ = trailer_writer.send(Err(e.0.unwrap_err())); + } + } + break; + } + + Err(_) => { + match body_writer + .send(Err(types::Error::TimeoutError( + "data frame timed out".to_string(), + ) + .into())) + .await + { + Ok(_) => {} + Err(e) => { + let _ = trailer_writer.send(Err(e.0.unwrap_err())); + } + } + break; + } + }; + + if frame.is_trailers() { + // We know we're not going to write any more data frames at this point, so we + // explicitly drop the body_writer so that anything waiting on the read end returns + // immediately. + drop(body_writer); + + let trailers = frame.into_trailers().unwrap(); + + // TODO: this will fail in two cases: + // 1. we've already used the channel once, which should be imposible, + // 2. the read end is closed. + // I'm not sure how to differentiate between these two cases, or really + // if we need to do anything to handle either. + let _ = trailer_writer.send(Ok(trailers)); + + break; + } + + assert!(frame.is_data(), "frame wasn't data"); + + let data = frame.into_data().unwrap(); + + // If the receiver no longer exists, thats ok - in that case we want to keep the + // loop running to relieve backpressure, so we get to the trailers. + let _ = body_writer.send(Ok(data)).await; + } + }); + + HostIncomingBody { + worker, + stream: Some(HostIncomingBodyStream::new(body_receiver)), + trailers, + } + } +} + +pub struct HostIncomingBody { + pub worker: AbortOnDropJoinHandle<()>, + pub stream: Option, + pub trailers: oneshot::Receiver>, +} + +impl HostIncomingBody { + pub fn into_future_trailers(self) -> HostFutureTrailers { + HostFutureTrailers { + _worker: self.worker, + state: HostFutureTrailersState::Waiting(self.trailers), + } + } +} + +pub struct HostIncomingBodyStream { + pub open: bool, + pub receiver: mpsc::Receiver>, + pub buffer: Bytes, + pub error: Option, +} + +impl HostIncomingBodyStream { + fn new(receiver: mpsc::Receiver>) -> Self { + Self { + open: true, + receiver, + buffer: Bytes::new(), + error: None, + } + } +} + +#[async_trait::async_trait] +impl HostInputStream for HostIncomingBodyStream { + fn read(&mut self, size: usize) -> anyhow::Result<(Bytes, StreamState)> { + use mpsc::error::TryRecvError; + + if !self.buffer.is_empty() { + let len = size.min(self.buffer.len()); + let chunk = self.buffer.split_to(len); + return Ok((chunk, StreamState::Open)); + } + + if let Some(e) = self.error.take() { + return Err(StreamRuntimeError::from(e).into()); + } + + if !self.open { + return Ok((Bytes::new(), StreamState::Closed)); + } + + match self.receiver.try_recv() { + Ok(Ok(mut bytes)) => { + let len = bytes.len().min(size); + let chunk = bytes.split_to(len); + if !bytes.is_empty() { + self.buffer = bytes; + } + + return Ok((chunk, StreamState::Open)); + } + + Ok(Err(e)) => { + self.open = false; + return Err(StreamRuntimeError::from(e).into()); + } + + Err(TryRecvError::Empty) => { + return Ok((Bytes::new(), StreamState::Open)); + } + + Err(TryRecvError::Disconnected) => { + self.open = false; + return Ok((Bytes::new(), StreamState::Closed)); + } + } + } + + async fn ready(&mut self) -> anyhow::Result<()> { + if !self.buffer.is_empty() { + return Ok(()); + } + + if !self.open { + return Ok(()); + } + + match self.receiver.recv().await { + Some(Ok(bytes)) => self.buffer = bytes, + + Some(Err(e)) => { + self.error = Some(e); + self.open = false; + } + + None => self.open = false, + } + + Ok(()) + } +} + +pub struct HostFutureTrailers { + _worker: AbortOnDropJoinHandle<()>, + pub state: HostFutureTrailersState, +} + +pub enum HostFutureTrailersState { + Waiting(oneshot::Receiver>), + Done(Result), +} + +impl HostFutureTrailers { + pub async fn ready(&mut self) -> anyhow::Result<()> { + if let HostFutureTrailersState::Waiting(rx) = &mut self.state { + let result = match rx.await { + Ok(Ok(headers)) => Ok(FieldMap::from(headers)), + Ok(Err(e)) => Err(types::Error::ProtocolError(format!("hyper error: {e:?}"))), + Err(_) => Err(types::Error::ProtocolError( + "stream hung up before trailers were received".to_string(), + )), + }; + self.state = HostFutureTrailersState::Done(result); + } + Ok(()) + } +} + +pub type HyperBody = BoxBody; + +pub struct HostOutgoingBody { + pub body_output_stream: Option>, + pub trailers_sender: Option>, +} + +impl HostOutgoingBody { + pub fn new() -> (Self, HyperBody) { + use http_body_util::BodyExt; + use hyper::{ + body::{Body, Frame}, + HeaderMap, + }; + use std::task::{Context, Poll}; + use tokio::sync::oneshot::error::RecvError; + struct BodyImpl { + body_receiver: mpsc::Receiver, + trailers_receiver: Option>, + } + impl Body for BodyImpl { + type Data = Bytes; + type Error = Infallible; + fn poll_frame( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + match self.as_mut().body_receiver.poll_recv(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Some(frame)) => Poll::Ready(Some(Ok(Frame::data(frame)))), + + // This means that the `body_sender` end of the channel has been dropped. + Poll::Ready(None) => { + if let Some(mut trailers_receiver) = self.as_mut().trailers_receiver.take() + { + match Pin::new(&mut trailers_receiver).poll(cx) { + Poll::Pending => { + self.as_mut().trailers_receiver = Some(trailers_receiver); + Poll::Pending + } + Poll::Ready(Ok(trailers)) => { + Poll::Ready(Some(Ok(Frame::trailers(trailers)))) + } + Poll::Ready(Err(RecvError { .. })) => Poll::Ready(None), + } + } else { + Poll::Ready(None) + } + } + } + } + } + + let (body_sender, body_receiver) = mpsc::channel(1); + let (trailers_sender, trailers_receiver) = oneshot::channel(); + let body_impl = BodyImpl { + body_receiver, + trailers_receiver: Some(trailers_receiver), + } + .boxed(); + ( + Self { + // TODO: this capacity constant is arbitrary, and should be configurable + body_output_stream: Some(Box::new(BodyWriteStream::new(1024 * 1024, body_sender))), + trailers_sender: Some(trailers_sender), + }, + body_impl, + ) + } +} + +// copied in from preview2::write_stream + +#[derive(Debug)] +struct WorkerState { + alive: bool, + items: std::collections::VecDeque, + write_budget: usize, + flush_pending: bool, + error: Option, +} + +impl WorkerState { + fn check_error(&mut self) -> Result<(), OutputStreamError> { + if let Some(e) = self.error.take() { + return Err(OutputStreamError::LastOperationFailed(e)); + } + if !self.alive { + return Err(OutputStreamError::Closed); + } + Ok(()) + } +} + +struct Worker { + state: Mutex, + new_work: tokio::sync::Notify, + write_ready_changed: tokio::sync::Notify, +} + +enum Job { + Flush, + Write(Bytes), +} + +enum WriteStatus<'a> { + Done(Result), + Pending(tokio::sync::futures::Notified<'a>), +} + +impl Worker { + fn new(write_budget: usize) -> Self { + Self { + state: Mutex::new(WorkerState { + alive: true, + items: std::collections::VecDeque::new(), + write_budget, + flush_pending: false, + error: None, + }), + new_work: tokio::sync::Notify::new(), + write_ready_changed: tokio::sync::Notify::new(), + } + } + fn check_write(&self) -> WriteStatus<'_> { + let mut state = self.state(); + if let Err(e) = state.check_error() { + return WriteStatus::Done(Err(e)); + } + + if state.flush_pending || state.write_budget == 0 { + return WriteStatus::Pending(self.write_ready_changed.notified()); + } + + WriteStatus::Done(Ok(state.write_budget)) + } + fn state(&self) -> std::sync::MutexGuard { + self.state.lock().unwrap() + } + fn pop(&self) -> Option { + let mut state = self.state(); + if state.items.is_empty() { + if state.flush_pending { + return Some(Job::Flush); + } + } else if let Some(bytes) = state.items.pop_front() { + return Some(Job::Write(bytes)); + } + + None + } + fn report_error(&self, e: std::io::Error) { + { + let mut state = self.state(); + state.alive = false; + state.error = Some(e.into()); + state.flush_pending = false; + } + self.write_ready_changed.notify_waiters(); + } + + async fn work(&self, writer: mpsc::Sender) { + loop { + let notified = self.new_work.notified(); + while let Some(job) = self.pop() { + match job { + Job::Flush => { + self.state().flush_pending = false; + } + + Job::Write(bytes) => { + tracing::debug!("worker writing: {bytes:?}"); + let len = bytes.len(); + match writer.send(bytes).await { + Err(_) => { + self.report_error(std::io::Error::new( + std::io::ErrorKind::BrokenPipe, + "Outgoing stream body reader has dropped", + )); + return; + } + Ok(_) => { + self.state().write_budget += len; + } + } + } + } + + self.write_ready_changed.notify_waiters(); + } + + notified.await; + } + } +} + +/// Provides a [`HostOutputStream`] impl from a [`tokio::sync::mpsc::Sender`]. +pub struct BodyWriteStream { + worker: Arc, + _join_handle: preview2::AbortOnDropJoinHandle<()>, +} + +impl BodyWriteStream { + /// Create a [`BodyWriteStream`]. + pub fn new(write_budget: usize, writer: mpsc::Sender) -> Self { + let worker = Arc::new(Worker::new(write_budget)); + + let w = Arc::clone(&worker); + let join_handle = preview2::spawn(async move { w.work(writer).await }); + + BodyWriteStream { + worker, + _join_handle: join_handle, + } + } +} + +#[async_trait::async_trait] +impl HostOutputStream for BodyWriteStream { + fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError> { + let mut state = self.worker.state(); + state.check_error()?; + if state.flush_pending { + return Err(OutputStreamError::Trap(anyhow!( + "write not permitted while flush pending" + ))); + } + match state.write_budget.checked_sub(bytes.len()) { + Some(remaining_budget) => { + state.write_budget = remaining_budget; + state.items.push_back(bytes); + } + None => return Err(OutputStreamError::Trap(anyhow!("write exceeded budget"))), + } + drop(state); + self.worker.new_work.notify_waiters(); + Ok(()) + } + fn flush(&mut self) -> Result<(), OutputStreamError> { + let mut state = self.worker.state(); + state.check_error()?; + + state.flush_pending = true; + self.worker.new_work.notify_waiters(); + + Ok(()) + } + + async fn write_ready(&mut self) -> Result { + loop { + match self.worker.check_write() { + WriteStatus::Done(r) => return r, + WriteStatus::Pending(notifier) => notifier.await, + } + } + } +} diff --git a/crates/wasi-http/src/http_impl.rs b/crates/wasi-http/src/http_impl.rs index e181ef143e0d..570d229cf7b2 100644 --- a/crates/wasi-http/src/http_impl.rs +++ b/crates/wasi-http/src/http_impl.rs @@ -1,122 +1,45 @@ -use crate::bindings::http::types::{ - FutureIncomingResponse, OutgoingRequest, RequestOptions, Scheme, +use crate::bindings::http::{ + outgoing_handler, + types::{FutureIncomingResponse, OutgoingRequest, RequestOptions, Scheme}, }; -use crate::types::{ActiveFields, ActiveFuture, ActiveResponse, HttpResponse, TableHttpExt}; +use crate::types::{HostFutureIncomingResponse, IncomingResponseInternal, TableHttpExt}; use crate::WasiHttpView; use anyhow::Context; -use bytes::{Bytes, BytesMut}; -use http_body_util::{BodyExt, Empty, Full}; -use hyper::{Method, Request}; -#[cfg(not(any(target_arch = "riscv64", target_arch = "s390x")))] -use std::sync::Arc; +use bytes::Bytes; +use http_body_util::{BodyExt, Empty}; +use hyper::Method; use std::time::Duration; use tokio::net::TcpStream; use tokio::time::timeout; -#[cfg(not(any(target_arch = "riscv64", target_arch = "s390x")))] -use tokio_rustls::rustls::{self, OwnedTrustAnchor}; -use wasmtime_wasi::preview2::{StreamState, TableStreamExt}; +use wasmtime_wasi::preview2; -#[async_trait::async_trait] -impl crate::bindings::http::outgoing_handler::Host for T { - async fn handle( +impl outgoing_handler::Host for T { + fn handle( &mut self, request_id: OutgoingRequest, options: Option, - ) -> wasmtime::Result { - let future = ActiveFuture::new(request_id, options); - let future_id = self - .table_mut() - .push_future(Box::new(future)) - .context("[handle] pushing future")?; - Ok(future_id) - } -} - -#[cfg(feature = "sync")] -pub mod sync { - use crate::bindings::http::outgoing_handler::{ - Host as AsyncHost, RequestOptions as AsyncRequestOptions, - }; - use crate::bindings::sync::http::types::{ - FutureIncomingResponse, OutgoingRequest, RequestOptions, - }; - use crate::WasiHttpView; - use wasmtime_wasi::preview2::in_tokio; - - // same boilerplate everywhere, converting between two identical types with different - // definition sites. one day wasmtime-wit-bindgen will make all this unnecessary - impl From for AsyncRequestOptions { - fn from(other: RequestOptions) -> Self { - Self { - connect_timeout_ms: other.connect_timeout_ms, - first_byte_timeout_ms: other.first_byte_timeout_ms, - between_bytes_timeout_ms: other.between_bytes_timeout_ms, - } - } - } - - impl crate::bindings::sync::http::outgoing_handler::Host for T { - fn handle( - &mut self, - request_id: OutgoingRequest, - options: Option, - ) -> wasmtime::Result { - in_tokio(async { AsyncHost::handle(self, request_id, options.map(|v| v.into())).await }) - } - } -} - -fn port_for_scheme(scheme: &Option) -> &str { - match scheme { - Some(s) => match s { - Scheme::Http => ":80", - Scheme::Https => ":443", - // This should never happen. - _ => panic!("unsupported scheme!"), - }, - None => ":443", - } -} + ) -> wasmtime::Result> { + let connect_timeout = Duration::from_millis( + options + .and_then(|opts| opts.connect_timeout_ms) + .unwrap_or(600 * 1000) as u64, + ); -#[async_trait::async_trait] -pub trait WasiHttpViewExt { - async fn handle_async( - &mut self, - request_id: OutgoingRequest, - options: Option, - ) -> wasmtime::Result; -} + let first_byte_timeout = Duration::from_millis( + options + .and_then(|opts| opts.first_byte_timeout_ms) + .unwrap_or(600 * 1000) as u64, + ); -#[async_trait::async_trait] -impl WasiHttpViewExt for T { - async fn handle_async( - &mut self, - request_id: OutgoingRequest, - options: Option, - ) -> wasmtime::Result { - tracing::debug!("preparing outgoing request"); - let opts = options.unwrap_or( - // TODO: Configurable defaults here? - RequestOptions { - connect_timeout_ms: Some(600 * 1000), - first_byte_timeout_ms: Some(600 * 1000), - between_bytes_timeout_ms: Some(600 * 1000), - }, + let between_bytes_timeout = Duration::from_millis( + options + .and_then(|opts| opts.between_bytes_timeout_ms) + .unwrap_or(600 * 1000) as u64, ); - let connect_timeout = - Duration::from_millis(opts.connect_timeout_ms.unwrap_or(600 * 1000).into()); - let first_bytes_timeout = - Duration::from_millis(opts.first_byte_timeout_ms.unwrap_or(600 * 1000).into()); - let between_bytes_timeout = - Duration::from_millis(opts.between_bytes_timeout_ms.unwrap_or(600 * 1000).into()); - let request = self - .table() - .get_request(request_id) - .context("[handle_async] getting request")?; - tracing::debug!("http request retrieved from table"); + let req = self.table().delete_outgoing_request(request_id)?; - let method = match request.method() { + let method = match req.method { crate::bindings::http::types::Method::Get => Method::GET, crate::bindings::http::types::Method::Head => Method::HEAD, crate::bindings::http::types::Method::Post => Method::POST, @@ -126,214 +49,155 @@ impl WasiHttpViewExt for T { crate::bindings::http::types::Method::Options => Method::OPTIONS, crate::bindings::http::types::Method::Trace => Method::TRACE, crate::bindings::http::types::Method::Patch => Method::PATCH, - crate::bindings::http::types::Method::Other(s) => { - return Err(crate::bindings::http::types::Error::InvalidUrl(format!( - "unknown method {}", - s - )) - .into()); + crate::bindings::http::types::Method::Other(method) => { + return Ok(Err(outgoing_handler::Error::Invalid(format!( + "unknown method {method}" + )))); } }; - let scheme = match request.scheme().as_ref().unwrap_or(&Scheme::Https) { - Scheme::Http => "http://", - Scheme::Https => "https://", - Scheme::Other(s) => { - return Err(crate::bindings::http::types::Error::InvalidUrl(format!( - "unsupported scheme {}", - s - )) - .into()); + let (use_tls, scheme, port) = match req.scheme.unwrap_or(Scheme::Https) { + Scheme::Http => (false, "http://", 80), + Scheme::Https => (true, "https://", 443), + Scheme::Other(scheme) => { + return Ok(Err(outgoing_handler::Error::Invalid(format!( + "unsupported scheme {scheme}" + )))) } }; - // Largely adapted from https://hyper.rs/guides/1/client/basic/ - let authority = match request.authority().find(":") { - Some(_) => request.authority().to_owned(), - None => request.authority().to_owned() + port_for_scheme(request.scheme()), + let authority = if req.authority.find(':').is_some() { + req.authority.clone() + } else { + format!("{}:{port}", req.authority) }; - let tcp_stream = TcpStream::connect(authority.clone()).await?; - let mut sender = if scheme == "https://" { - tracing::debug!("initiating client connection client with TLS"); - #[cfg(not(any(target_arch = "riscv64", target_arch = "s390x")))] - { - //TODO: uncomment this code and make the tls implementation a feature decision. - //let connector = tokio_native_tls::native_tls::TlsConnector::builder().build()?; - //let connector = tokio_native_tls::TlsConnector::from(connector); - //let host = authority.split(":").next().unwrap_or(&authority); - //let stream = connector.connect(&host, stream).await?; - // derived from https://github.com/tokio-rs/tls/blob/master/tokio-rustls/examples/client/src/main.rs - let mut root_cert_store = rustls::RootCertStore::empty(); - root_cert_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map( - |ta| { - OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - }, - )); - let config = rustls::ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates(root_cert_store) - .with_no_client_auth(); - let connector = tokio_rustls::TlsConnector::from(Arc::new(config)); - let mut parts = authority.split(":"); - let host = parts.next().unwrap_or(&authority); - let domain = rustls::ServerName::try_from(host)?; - let stream = connector.connect(domain, tcp_stream).await.map_err(|e| { - crate::bindings::http::types::Error::ProtocolError(e.to_string()) - })?; + let mut builder = hyper::Request::builder() + .method(method) + .uri(format!("{scheme}{authority}{}", req.path_with_query)) + .header(hyper::header::HOST, &authority); - let t = timeout( - connect_timeout, - hyper::client::conn::http1::handshake(stream), - ) - .await?; - let (s, conn) = t?; - tokio::task::spawn(async move { - if let Err(err) = conn.await { - tracing::debug!("[host/client] Connection failed: {:?}", err); - } - }); - s - } - #[cfg(any(target_arch = "riscv64", target_arch = "s390x"))] - return Err(crate::bindings::http::types::Error::UnexpectedError( - "unsupported architecture for SSL".to_string(), - )); - } else { - tracing::debug!("initiating client connection without TLS"); - let t = timeout( - connect_timeout, - hyper::client::conn::http1::handshake(tcp_stream), - ) - .await?; - let (s, conn) = t?; - tokio::task::spawn(async move { - if let Err(err) = conn.await { - tracing::debug!("[host/client] Connection failed: {:?}", err); - } - }); - s - }; + for (k, v) in req.headers.iter() { + builder = builder.header(k, v); + } - let url = scheme.to_owned() + &request.authority() + &request.path_with_query(); + let body = req.body.unwrap_or_else(|| Empty::::new().boxed()); - tracing::debug!("request to url {:?}", &url); - let mut call = Request::builder() - .method(method) - .uri(url) - .header(hyper::header::HOST, request.authority()); + let request = builder.body(body).map_err(http_protocol_error)?; - if let Some(headers) = request.headers() { - for (key, val) in self - .table() - .get_fields(headers) - .context("[handle_async] getting request headers")? - .iter() - { - for item in val { - call = call.header(key, item.clone()); + let handle = preview2::spawn(async move { + let tcp_stream = TcpStream::connect(authority.clone()) + .await + .map_err(invalid_url)?; + + let (mut sender, worker) = if use_tls { + #[cfg(any(target_arch = "riscv64", target_arch = "s390x"))] + { + anyhow::bail!(crate::bindings::http::types::Error::UnexpectedError( + "unsupported architecture for SSL".to_string(), + )); } - } - } - let mut response = ActiveResponse::new(); - let body = match request.body() { - Some(id) => { - let table = self.table_mut(); - let stream = table - .get_stream(id) - .context("[handle_async] getting stream")?; - let input_stream = table - .get_input_stream_mut(stream.incoming()) - .context("[handle_async] getting mutable input stream")?; - let mut bytes = BytesMut::new(); - let mut eof = StreamState::Open; - while eof != StreamState::Closed { - let (chunk, state) = input_stream.read(4096)?; - eof = if chunk.is_empty() { - StreamState::Closed - } else { - state - }; - bytes.extend_from_slice(&chunk[..]); + #[cfg(not(any(target_arch = "riscv64", target_arch = "s390x")))] + { + use tokio_rustls::rustls::OwnedTrustAnchor; + + // derived from https://github.com/tokio-rs/tls/blob/master/tokio-rustls/examples/client/src/main.rs + let mut root_cert_store = rustls::RootCertStore::empty(); + root_cert_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map( + |ta| { + OwnedTrustAnchor::from_subject_spki_name_constraints( + ta.subject, + ta.spki, + ta.name_constraints, + ) + }, + )); + let config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(root_cert_store) + .with_no_client_auth(); + let connector = tokio_rustls::TlsConnector::from(std::sync::Arc::new(config)); + let mut parts = authority.split(":"); + let host = parts.next().unwrap_or(&authority); + let domain = rustls::ServerName::try_from(host)?; + let stream = connector.connect(domain, tcp_stream).await.map_err(|e| { + crate::bindings::http::types::Error::ProtocolError(e.to_string()) + })?; + + let (sender, conn) = timeout( + connect_timeout, + hyper::client::conn::http1::handshake(stream), + ) + .await + .map_err(|_| timeout_error("connection"))??; + + let worker = preview2::spawn(async move { + conn.await.context("hyper connection failed")?; + Ok::<_, anyhow::Error>(()) + }); + + (sender, worker) } - Full::::new(bytes.freeze()).boxed() - } - None => Empty::::new().boxed(), - }; - let request = call.body(body)?; - tracing::trace!("hyper request {:?}", request); - let t = timeout(first_bytes_timeout, sender.send_request(request)).await?; - let mut res = t?; - tracing::trace!("hyper response {:?}", res); - response.status = res.status().as_u16(); + } else { + let (sender, conn) = timeout( + connect_timeout, + // TODO: we should plumb the builder through the http context, and use it here + hyper::client::conn::http1::handshake(tcp_stream), + ) + .await + .map_err(|_| timeout_error("connection"))??; - let mut map = ActiveFields::new(); - for (key, value) in res.headers().iter() { - let mut vec = Vec::new(); - vec.push(value.as_bytes().to_vec()); - map.insert(key.as_str().to_string(), vec); - } - let headers = self - .table_mut() - .push_fields(Box::new(map)) - .context("[handle_async] pushing response headers")?; - response.set_headers(headers); + let worker = preview2::spawn(async move { + conn.await.context("hyper connection failed")?; + Ok::<_, anyhow::Error>(()) + }); - let mut buf: Vec = Vec::new(); - while let Some(next) = timeout(between_bytes_timeout, res.frame()).await? { - let frame = next?; - tracing::debug!("response body next frame"); - if let Some(chunk) = frame.data_ref() { - tracing::trace!("response body chunk size {:?}", chunk.len()); - buf.extend_from_slice(chunk); - } - if let Some(trailers) = frame.trailers_ref() { - tracing::debug!("response trailers present"); - let mut map = ActiveFields::new(); - for (name, value) in trailers.iter() { - let key = name.to_string(); - match map.get_mut(&key) { - Some(vec) => vec.push(value.as_bytes().to_vec()), - None => { - let mut vec = Vec::new(); - vec.push(value.as_bytes().to_vec()); - map.insert(key, vec); - } - }; - } - let trailers = self - .table_mut() - .push_fields(Box::new(map)) - .context("[handle_async] pushing response trailers")?; - response.set_trailers(trailers); - tracing::debug!("http trailers saved to table"); - } - } + (sender, worker) + }; - let response_id = self - .table_mut() - .push_response(Box::new(response)) - .context("[handle_async] pushing response")?; - tracing::trace!("response body {:?}", std::str::from_utf8(&buf[..]).unwrap()); - let (stream_id, stream) = self - .table_mut() - .push_stream(Bytes::from(buf), response_id) - .await - .context("[handle_async] pushing stream")?; - let response = self - .table_mut() - .get_response_mut(response_id) - .context("[handle_async] getting mutable response")?; - response.set_body(stream_id); - tracing::debug!("http response saved to table with id {:?}", response_id); + let resp = timeout(first_byte_timeout, sender.send_request(request)) + .await + .map_err(|_| timeout_error("first byte"))? + .map_err(hyper_protocol_error)?; - self.http_ctx_mut().streams.insert(stream_id, stream); + Ok(IncomingResponseInternal { + resp, + worker, + between_bytes_timeout, + }) + }); - Ok(response_id) + let fut = self + .table() + .push_future_incoming_response(HostFutureIncomingResponse::new(handle))?; + + Ok(Ok(fut)) } } + +fn timeout_error(kind: &str) -> anyhow::Error { + anyhow::anyhow!(crate::bindings::http::types::Error::TimeoutError(format!( + "{kind} timed out" + ))) +} + +fn http_protocol_error(e: http::Error) -> anyhow::Error { + anyhow::anyhow!(crate::bindings::http::types::Error::ProtocolError( + e.to_string() + )) +} + +fn hyper_protocol_error(e: hyper::Error) -> anyhow::Error { + anyhow::anyhow!(crate::bindings::http::types::Error::ProtocolError( + e.to_string() + )) +} + +fn invalid_url(e: std::io::Error) -> anyhow::Error { + // TODO: DNS errors show up as a Custom io error, what subset of errors should we consider for + // InvalidUrl here? + anyhow::anyhow!(crate::bindings::http::types::Error::InvalidUrl( + e.to_string() + )) +} diff --git a/crates/wasi-http/src/incoming_handler.rs b/crates/wasi-http/src/incoming_handler.rs index e65a88e27d53..3fd26bf7d0af 100644 --- a/crates/wasi-http/src/incoming_handler.rs +++ b/crates/wasi-http/src/incoming_handler.rs @@ -1,9 +1,8 @@ use crate::bindings::http::types::{IncomingRequest, ResponseOutparam}; use crate::WasiHttpView; -#[async_trait::async_trait] impl crate::bindings::http::incoming_handler::Host for T { - async fn handle( + fn handle( &mut self, _request: IncomingRequest, _response_out: ResponseOutparam, @@ -11,21 +10,3 @@ impl crate::bindings::http::incoming_handler::Host for T { anyhow::bail!("unimplemented: [incoming_handler] handle") } } - -#[cfg(feature = "sync")] -pub mod sync { - use crate::bindings::http::incoming_handler::Host as AsyncHost; - use crate::bindings::sync::http::types::{IncomingRequest, ResponseOutparam}; - use crate::WasiHttpView; - use wasmtime_wasi::preview2::in_tokio; - - impl crate::bindings::sync::http::incoming_handler::Host for T { - fn handle( - &mut self, - request: IncomingRequest, - response_out: ResponseOutparam, - ) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::handle(self, request, response_out).await }) - } - } -} diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs index 7a99a0b79a8f..c6b072b1e60f 100644 --- a/crates/wasi-http/src/lib.rs +++ b/crates/wasi-http/src/lib.rs @@ -1,9 +1,8 @@ -pub use crate::http_impl::WasiHttpViewExt; pub use crate::types::{WasiHttpCtx, WasiHttpView}; use core::fmt::Formatter; use std::fmt::{self, Display}; -pub mod component_impl; +pub mod body; pub mod http_impl; pub mod incoming_handler; pub mod proxy; @@ -11,56 +10,22 @@ pub mod types; pub mod types_impl; pub mod bindings { - #[cfg(feature = "sync")] - pub mod sync { - pub(crate) mod _internal { - wasmtime::component::bindgen!({ - path: "wit", - interfaces: " - import wasi:http/incoming-handler - import wasi:http/outgoing-handler - import wasi:http/types - ", - tracing: true, - with: { - "wasi:io/streams": wasmtime_wasi::preview2::bindings::sync_io::io::streams, - "wasi:poll/poll": wasmtime_wasi::preview2::bindings::sync_io::poll::poll, - } - }); - } - pub use self::_internal::wasi::http; - } - - pub(crate) mod _internal_rest { - wasmtime::component::bindgen!({ - path: "wit", - interfaces: " + wasmtime::component::bindgen!({ + path: "wit", + interfaces: " import wasi:http/incoming-handler import wasi:http/outgoing-handler import wasi:http/types ", - tracing: true, - async: true, - with: { - "wasi:io/streams": wasmtime_wasi::preview2::bindings::io::streams, - "wasi:poll/poll": wasmtime_wasi::preview2::bindings::poll::poll, - } - }); - } - - pub use self::_internal_rest::wasi::http; -} - -pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> { - crate::component_impl::add_component_to_linker::(linker, |t| t) -} - -pub mod sync { - use crate::types::WasiHttpView; + tracing: true, + async: false, + with: { + "wasi:io/streams": wasmtime_wasi::preview2::bindings::io::streams, + "wasi:poll/poll": wasmtime_wasi::preview2::bindings::poll::poll, + } + }); - pub fn add_to_linker(linker: &mut wasmtime::Linker) -> anyhow::Result<()> { - crate::component_impl::sync::add_component_to_linker::(linker, |t| t) - } + pub use wasi::http; } impl std::error::Error for crate::bindings::http::types::Error {} diff --git a/crates/wasi-http/src/proxy.rs b/crates/wasi-http/src/proxy.rs index 8cd78d700265..9f156fd09a14 100644 --- a/crates/wasi-http/src/proxy.rs +++ b/crates/wasi-http/src/proxy.rs @@ -4,7 +4,7 @@ use wasmtime_wasi::preview2; wasmtime::component::bindgen!({ world: "wasi:http/proxy", tracing: true, - async: true, + async: false, with: { "wasi:cli/stderr": preview2::bindings::cli::stderr, "wasi:cli/stdin": preview2::bindings::cli::stdin, @@ -30,39 +30,3 @@ where bindings::http::types::add_to_linker(l, |t| t)?; Ok(()) } - -#[cfg(feature = "sync")] -pub mod sync { - use crate::{bindings, WasiHttpView}; - use wasmtime_wasi::preview2; - - wasmtime::component::bindgen!({ - world: "wasi:http/proxy", - tracing: true, - async: false, - with: { - "wasi:cli/stderr": preview2::bindings::cli::stderr, - "wasi:cli/stdin": preview2::bindings::cli::stdin, - "wasi:cli/stdout": preview2::bindings::cli::stdout, - "wasi:clocks/monotonic-clock": preview2::bindings::clocks::monotonic_clock, - "wasi:clocks/timezone": preview2::bindings::clocks::timezone, - "wasi:clocks/wall-clock": preview2::bindings::clocks::wall_clock, - "wasi:http/incoming-handler": bindings::sync::http::incoming_handler, - "wasi:http/outgoing-handler": bindings::sync::http::outgoing_handler, - "wasi:http/types": bindings::sync::http::types, - "wasi:io/streams": preview2::bindings::sync_io::io::streams, - "wasi:poll/poll": preview2::bindings::sync_io::poll::poll, - "wasi:random/random": preview2::bindings::random::random, - }, - }); - - pub fn add_to_linker(l: &mut wasmtime::component::Linker) -> anyhow::Result<()> - where - T: WasiHttpView + bindings::sync::http::types::Host, - { - bindings::sync::http::incoming_handler::add_to_linker(l, |t| t)?; - bindings::sync::http::outgoing_handler::add_to_linker(l, |t| t)?; - bindings::sync::http::types::add_to_linker(l, |t| t)?; - Ok(()) - } -} diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs index 050a83470947..3b73f7f3fd49 100644 --- a/crates/wasi-http/src/types.rs +++ b/crates/wasi-http/src/types.rs @@ -1,449 +1,302 @@ //! Implements the base structure (i.e. [WasiHttpCtx]) that will provide the //! implementation of the wasi-http API. -use crate::bindings::http::types::{ - IncomingStream, Method, OutgoingRequest, OutgoingStream, RequestOptions, Scheme, +use crate::{ + bindings::http::types::{FutureTrailers, IncomingBody, Method, OutgoingBody, Scheme}, + body::{ + HostFutureTrailers, HostIncomingBody, HostIncomingBodyBuilder, HostOutgoingBody, HyperBody, + }, }; -use bytes::Bytes; use std::any::Any; -use std::collections::HashMap; -use std::ops::{Deref, DerefMut}; -use wasmtime_wasi::preview2::{ - pipe::{AsyncReadStream, AsyncWriteStream}, - HostInputStream, HostOutputStream, Table, TableError, TableStreamExt, WasiView, -}; - -const MAX_BUF_SIZE: usize = 65_536; +use std::pin::Pin; +use std::task; +use wasmtime_wasi::preview2::{AbortOnDropJoinHandle, Table, TableError}; /// Capture the state necessary for use in the wasi-http API implementation. -pub struct WasiHttpCtx { - pub streams: HashMap, -} - -impl WasiHttpCtx { - /// Make a new context from the default state. - pub fn new() -> Self { - Self { - streams: HashMap::new(), - } - } -} +pub struct WasiHttpCtx; -pub trait WasiHttpView: WasiView { - fn http_ctx(&self) -> &WasiHttpCtx; - fn http_ctx_mut(&mut self) -> &mut WasiHttpCtx; +pub trait WasiHttpView: Send { + fn ctx(&mut self) -> &mut WasiHttpCtx; + fn table(&mut self) -> &mut Table; } -pub type FieldsMap = HashMap>>; - -#[derive(Clone, Debug)] -pub struct ActiveRequest { - pub active: bool, +pub struct HostOutgoingRequest { pub method: Method, pub scheme: Option, pub path_with_query: String, pub authority: String, - pub headers: Option, - pub body: Option, + pub headers: FieldMap, + pub body: Option, } -pub trait HttpRequest: Send + Sync { - fn new() -> Self - where - Self: Sized; - - fn as_any(&self) -> &dyn Any; - - fn method(&self) -> &Method; - fn scheme(&self) -> &Option; - fn path_with_query(&self) -> &str; - fn authority(&self) -> &str; - fn headers(&self) -> Option; - fn set_headers(&mut self, headers: u32); - fn body(&self) -> Option; - fn set_body(&mut self, body: u32); +pub struct HostIncomingResponse { + pub status: u16, + pub headers: FieldMap, + pub body: Option, + pub worker: AbortOnDropJoinHandle>, } -impl HttpRequest for ActiveRequest { - fn new() -> Self { - Self { - active: false, - method: Method::Get, - scheme: Some(Scheme::Http), - path_with_query: "".to_string(), - authority: "".to_string(), - headers: None, - body: None, - } - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn method(&self) -> &Method { - &self.method - } +pub type FieldMap = hyper::HeaderMap; - fn scheme(&self) -> &Option { - &self.scheme - } - - fn path_with_query(&self) -> &str { - &self.path_with_query - } - - fn authority(&self) -> &str { - &self.authority - } - - fn headers(&self) -> Option { - self.headers - } - - fn set_headers(&mut self, headers: u32) { - self.headers = Some(headers); - } - - fn body(&self) -> Option { - self.body - } +pub enum HostFields { + Ref { + parent: u32, - fn set_body(&mut self, body: u32) { - self.body = Some(body); - } + // NOTE: there's not failure in the result here because we assume that HostFields will + // always be registered as a child of the entry with the `parent` id. This ensures that the + // entry will always exist while this `HostFields::Ref` entry exists in the table, thus we + // don't need to account for failure when fetching the fields ref from the parent. + get_fields: for<'a> fn(elem: &'a mut (dyn Any + 'static)) -> &'a mut FieldMap, + }, + Owned { + fields: FieldMap, + }, } -#[derive(Clone, Debug)] -pub struct ActiveResponse { - pub active: bool, - pub status: u16, - pub headers: Option, - pub body: Option, - pub trailers: Option, +pub struct IncomingResponseInternal { + pub resp: hyper::Response, + pub worker: AbortOnDropJoinHandle>, + pub between_bytes_timeout: std::time::Duration, } -pub trait HttpResponse: Send + Sync { - fn new() -> Self - where - Self: Sized; +type FutureIncomingResponseHandle = AbortOnDropJoinHandle>; - fn as_any(&self) -> &dyn Any; - - fn status(&self) -> u16; - fn headers(&self) -> Option; - fn set_headers(&mut self, headers: u32); - fn body(&self) -> Option; - fn set_body(&mut self, body: u32); - fn trailers(&self) -> Option; - fn set_trailers(&mut self, trailers: u32); +pub enum HostFutureIncomingResponse { + Pending(FutureIncomingResponseHandle), + Ready(anyhow::Result), + Consumed, } -impl HttpResponse for ActiveResponse { - fn new() -> Self { - Self { - active: false, - status: 0, - headers: None, - body: None, - trailers: None, - } +impl HostFutureIncomingResponse { + pub fn new(handle: FutureIncomingResponseHandle) -> Self { + Self::Pending(handle) } - fn as_any(&self) -> &dyn Any { - self + pub fn is_ready(&self) -> bool { + matches!(self, Self::Ready(_)) } - fn status(&self) -> u16 { - self.status + pub fn unwrap_ready(self) -> anyhow::Result { + match self { + Self::Ready(res) => res, + Self::Pending(_) | Self::Consumed => { + panic!("unwrap_ready called on a pending HostFutureIncomingResponse") + } + } } +} - fn headers(&self) -> Option { - self.headers +impl std::future::Future for HostFutureIncomingResponse { + type Output = anyhow::Result<()>; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let s = self.get_mut(); + match s { + Self::Pending(ref mut handle) => match Pin::new(handle).poll(cx) { + task::Poll::Pending => task::Poll::Pending, + task::Poll::Ready(r) => { + *s = Self::Ready(r); + task::Poll::Ready(Ok(())) + } + }, + + Self::Consumed | Self::Ready(_) => task::Poll::Ready(Ok(())), + } } +} - fn set_headers(&mut self, headers: u32) { - self.headers = Some(headers); - } +#[async_trait::async_trait] +pub trait TableHttpExt { + fn push_outgoing_response(&mut self, request: HostOutgoingRequest) -> Result; + fn get_outgoing_request(&self, id: u32) -> Result<&HostOutgoingRequest, TableError>; + fn get_outgoing_request_mut(&mut self, id: u32) + -> Result<&mut HostOutgoingRequest, TableError>; + fn delete_outgoing_request(&mut self, id: u32) -> Result; + + fn push_incoming_response(&mut self, response: HostIncomingResponse) + -> Result; + fn get_incoming_response(&self, id: u32) -> Result<&HostIncomingResponse, TableError>; + fn get_incoming_response_mut( + &mut self, + id: u32, + ) -> Result<&mut HostIncomingResponse, TableError>; + fn delete_incoming_response(&mut self, id: u32) -> Result; - fn body(&self) -> Option { - self.body - } + fn push_fields(&mut self, fields: HostFields) -> Result; + fn get_fields(&mut self, id: u32) -> Result<&mut FieldMap, TableError>; + fn delete_fields(&mut self, id: u32) -> Result; - fn set_body(&mut self, body: u32) { - self.body = Some(body); - } + fn push_future_incoming_response( + &mut self, + response: HostFutureIncomingResponse, + ) -> Result; + fn get_future_incoming_response( + &self, + id: u32, + ) -> Result<&HostFutureIncomingResponse, TableError>; + fn get_future_incoming_response_mut( + &mut self, + id: u32, + ) -> Result<&mut HostFutureIncomingResponse, TableError>; + fn delete_future_incoming_response( + &mut self, + id: u32, + ) -> Result; - fn trailers(&self) -> Option { - self.trailers - } + fn push_incoming_body(&mut self, body: HostIncomingBody) -> Result; + fn get_incoming_body(&mut self, id: IncomingBody) -> Result<&mut HostIncomingBody, TableError>; + fn delete_incoming_body(&mut self, id: IncomingBody) -> Result; - fn set_trailers(&mut self, trailers: u32) { - self.trailers = Some(trailers); - } -} + fn push_outgoing_body(&mut self, body: HostOutgoingBody) -> Result; + fn get_outgoing_body(&mut self, id: OutgoingBody) -> Result<&mut HostOutgoingBody, TableError>; + fn delete_outgoing_body(&mut self, id: OutgoingBody) -> Result; -#[derive(Clone, Debug)] -pub struct ActiveFuture { - request_id: OutgoingRequest, - options: Option, - response_id: Option, - pollable_id: Option, + fn push_future_trailers( + &mut self, + trailers: HostFutureTrailers, + ) -> Result; + fn get_future_trailers( + &mut self, + id: FutureTrailers, + ) -> Result<&mut HostFutureTrailers, TableError>; + fn delete_future_trailers( + &mut self, + id: FutureTrailers, + ) -> Result; } -impl ActiveFuture { - pub fn new(request_id: OutgoingRequest, options: Option) -> Self { - Self { - request_id, - options, - response_id: None, - pollable_id: None, - } - } - - pub fn request_id(&self) -> u32 { - self.request_id - } - - pub fn options(&self) -> Option { - self.options - } - - pub fn response_id(&self) -> Option { - self.response_id +#[async_trait::async_trait] +impl TableHttpExt for Table { + fn push_outgoing_response(&mut self, request: HostOutgoingRequest) -> Result { + self.push(Box::new(request)) } - - pub fn set_response_id(&mut self, response_id: u32) { - self.response_id = Some(response_id); + fn get_outgoing_request(&self, id: u32) -> Result<&HostOutgoingRequest, TableError> { + self.get::(id) } - - pub fn pollable_id(&self) -> Option { - self.pollable_id + fn get_outgoing_request_mut( + &mut self, + id: u32, + ) -> Result<&mut HostOutgoingRequest, TableError> { + self.get_mut::(id) } - - pub fn set_pollable_id(&mut self, pollable_id: u32) { - self.pollable_id = Some(pollable_id); + fn delete_outgoing_request(&mut self, id: u32) -> Result { + let req = self.delete::(id)?; + Ok(req) } -} - -#[derive(Clone, Debug)] -pub struct ActiveFields(HashMap>>); -impl ActiveFields { - pub fn new() -> Self { - Self(FieldsMap::new()) + fn push_incoming_response( + &mut self, + response: HostIncomingResponse, + ) -> Result { + self.push(Box::new(response)) } -} - -pub trait HttpFields: Send + Sync { - fn as_any(&self) -> &dyn Any; -} - -impl HttpFields for ActiveFields { - fn as_any(&self) -> &dyn Any { - self + fn get_incoming_response(&self, id: u32) -> Result<&HostIncomingResponse, TableError> { + self.get::(id) } -} - -impl Deref for ActiveFields { - type Target = FieldsMap; - fn deref(&self) -> &FieldsMap { - &self.0 + fn get_incoming_response_mut( + &mut self, + id: u32, + ) -> Result<&mut HostIncomingResponse, TableError> { + self.get_mut::(id) } -} - -impl DerefMut for ActiveFields { - fn deref_mut(&mut self) -> &mut FieldsMap { - &mut self.0 + fn delete_incoming_response(&mut self, id: u32) -> Result { + let resp = self.delete::(id)?; + Ok(resp) } -} - -#[derive(Clone, Debug)] -pub struct Stream { - input_id: u32, - output_id: u32, - parent_id: u32, -} -impl Stream { - pub fn new(input_id: u32, output_id: u32, parent_id: u32) -> Self { - Self { - input_id, - output_id, - parent_id, + fn push_fields(&mut self, fields: HostFields) -> Result { + match fields { + HostFields::Ref { parent, .. } => self.push_child(Box::new(fields), parent), + HostFields::Owned { .. } => self.push(Box::new(fields)), } } + fn get_fields(&mut self, id: u32) -> Result<&mut FieldMap, TableError> { + let fields = self.get_mut::(id)?; + if let HostFields::Ref { parent, get_fields } = *fields { + let entry = self.get_any_mut(parent)?; + return Ok(get_fields(entry)); + } - pub fn incoming(&self) -> IncomingStream { - self.input_id + match self.get_mut::(id)? { + HostFields::Owned { fields } => Ok(fields), + // NB: ideally the `if let` above would go here instead. That makes + // the borrow-checker unhappy. Unclear why. If you, dear reader, can + // refactor this to remove the `unreachable!` please do. + HostFields::Ref { .. } => unreachable!(), + } } - - pub fn outgoing(&self) -> OutgoingStream { - self.output_id + fn delete_fields(&mut self, id: u32) -> Result { + let fields = self.delete::(id)?; + Ok(fields) } - pub fn parent_id(&self) -> u32 { - self.parent_id - } -} - -#[async_trait::async_trait] -pub trait TableHttpExt { - fn push_request(&mut self, request: Box) -> Result; - fn get_request(&self, id: u32) -> Result<&(dyn HttpRequest), TableError>; - fn get_request_mut(&mut self, id: u32) -> Result<&mut Box, TableError>; - fn delete_request(&mut self, id: u32) -> Result<(), TableError>; - - fn push_response(&mut self, response: Box) -> Result; - fn get_response(&self, id: u32) -> Result<&dyn HttpResponse, TableError>; - fn get_response_mut(&mut self, id: u32) -> Result<&mut Box, TableError>; - fn delete_response(&mut self, id: u32) -> Result<(), TableError>; - - fn push_future(&mut self, future: Box) -> Result; - fn get_future(&self, id: u32) -> Result<&ActiveFuture, TableError>; - fn get_future_mut(&mut self, id: u32) -> Result<&mut Box, TableError>; - fn delete_future(&mut self, id: u32) -> Result<(), TableError>; - - fn push_fields(&mut self, fields: Box) -> Result; - fn get_fields(&self, id: u32) -> Result<&ActiveFields, TableError>; - fn get_fields_mut(&mut self, id: u32) -> Result<&mut Box, TableError>; - fn delete_fields(&mut self, id: u32) -> Result<(), TableError>; - - async fn push_stream( + fn push_future_incoming_response( &mut self, - content: Bytes, - parent: u32, - ) -> Result<(u32, Stream), TableError>; - fn get_stream(&self, id: u32) -> Result<&Stream, TableError>; - fn get_stream_mut(&mut self, id: u32) -> Result<&mut Box, TableError>; - fn delete_stream(&mut self, id: u32) -> Result<(), TableError>; -} - -#[async_trait::async_trait] -impl TableHttpExt for Table { - fn push_request(&mut self, request: Box) -> Result { - self.push(Box::new(request)) - } - fn get_request(&self, id: u32) -> Result<&dyn HttpRequest, TableError> { - self.get::>(id).map(|f| f.as_ref()) - } - fn get_request_mut(&mut self, id: u32) -> Result<&mut Box, TableError> { - self.get_mut::>(id) - } - fn delete_request(&mut self, id: u32) -> Result<(), TableError> { - self.delete::>(id).map(|_old| ()) - } - - fn push_response(&mut self, response: Box) -> Result { + response: HostFutureIncomingResponse, + ) -> Result { self.push(Box::new(response)) } - fn get_response(&self, id: u32) -> Result<&dyn HttpResponse, TableError> { - self.get::>(id).map(|f| f.as_ref()) + fn get_future_incoming_response( + &self, + id: u32, + ) -> Result<&HostFutureIncomingResponse, TableError> { + self.get::(id) } - fn get_response_mut(&mut self, id: u32) -> Result<&mut Box, TableError> { - self.get_mut::>(id) + fn get_future_incoming_response_mut( + &mut self, + id: u32, + ) -> Result<&mut HostFutureIncomingResponse, TableError> { + self.get_mut::(id) } - fn delete_response(&mut self, id: u32) -> Result<(), TableError> { - self.delete::>(id).map(|_old| ()) + fn delete_future_incoming_response( + &mut self, + id: u32, + ) -> Result { + self.delete(id) } - fn push_future(&mut self, future: Box) -> Result { - self.push(Box::new(future)) - } - fn get_future(&self, id: u32) -> Result<&ActiveFuture, TableError> { - self.get::>(id).map(|f| f.as_ref()) + fn push_incoming_body(&mut self, body: HostIncomingBody) -> Result { + self.push(Box::new(body)) } - fn get_future_mut(&mut self, id: u32) -> Result<&mut Box, TableError> { - self.get_mut::>(id) - } - fn delete_future(&mut self, id: u32) -> Result<(), TableError> { - self.delete::>(id).map(|_old| ()) + + fn get_incoming_body(&mut self, id: IncomingBody) -> Result<&mut HostIncomingBody, TableError> { + self.get_mut(id) } - fn push_fields(&mut self, fields: Box) -> Result { - self.push(Box::new(fields)) + fn delete_incoming_body(&mut self, id: IncomingBody) -> Result { + self.delete(id) } - fn get_fields(&self, id: u32) -> Result<&ActiveFields, TableError> { - self.get::>(id).map(|f| f.as_ref()) + + fn push_outgoing_body(&mut self, body: HostOutgoingBody) -> Result { + self.push(Box::new(body)) } - fn get_fields_mut(&mut self, id: u32) -> Result<&mut Box, TableError> { - self.get_mut::>(id) + + fn get_outgoing_body(&mut self, id: OutgoingBody) -> Result<&mut HostOutgoingBody, TableError> { + self.get_mut(id) } - fn delete_fields(&mut self, id: u32) -> Result<(), TableError> { - self.delete::>(id).map(|_old| ()) + + fn delete_outgoing_body(&mut self, id: OutgoingBody) -> Result { + self.delete(id) } - async fn push_stream( + fn push_future_trailers( &mut self, - mut content: Bytes, - parent: u32, - ) -> Result<(u32, Stream), TableError> { - tracing::debug!("preparing http body stream"); - let (a, b) = tokio::io::duplex(MAX_BUF_SIZE); - let (_, write_stream) = tokio::io::split(a); - let (read_stream, _) = tokio::io::split(b); - let input_stream = AsyncReadStream::new(read_stream); - // TODO: more informed budget here - let mut output_stream = AsyncWriteStream::new(4096, write_stream); - - while !content.is_empty() { - let permit = output_stream - .write_ready() - .await - .map_err(|_| TableError::NotPresent)?; - - let len = content.len().min(permit); - let chunk = content.split_to(len); - - output_stream - .write(chunk) - .map_err(|_| TableError::NotPresent)?; - } - output_stream.flush().map_err(|_| TableError::NotPresent)?; - let _readiness = tokio::time::timeout( - std::time::Duration::from_millis(10), - output_stream.write_ready(), - ) - .await; - - let input_stream = Box::new(input_stream); - let output_id = self.push_output_stream(Box::new(output_stream))?; - let input_id = self.push_input_stream(input_stream)?; - let stream = Stream::new(input_id, output_id, parent); - let cloned_stream = stream.clone(); - let stream_id = self.push(Box::new(Box::new(stream)))?; - tracing::trace!( - "http body stream details ( id: {:?}, input: {:?}, output: {:?} )", - stream_id, - input_id, - output_id - ); - Ok((stream_id, cloned_stream)) + trailers: HostFutureTrailers, + ) -> Result { + self.push(Box::new(trailers)) } - fn get_stream(&self, id: u32) -> Result<&Stream, TableError> { - self.get::>(id).map(|f| f.as_ref()) - } - fn get_stream_mut(&mut self, id: u32) -> Result<&mut Box, TableError> { - self.get_mut::>(id) - } - fn delete_stream(&mut self, id: u32) -> Result<(), TableError> { - let stream = self.get_stream_mut(id)?; - let input_stream = stream.incoming(); - let output_stream = stream.outgoing(); - self.delete::>(id).map(|_old| ())?; - self.delete::>(input_stream) - .map(|_old| ())?; - self.delete::>(output_stream) - .map(|_old| ()) - } -} -#[cfg(test)] -mod test { - use super::*; + fn get_future_trailers( + &mut self, + id: FutureTrailers, + ) -> Result<&mut HostFutureTrailers, TableError> { + self.get_mut(id) + } - #[test] - fn instantiate() { - WasiHttpCtx::new(); + fn delete_future_trailers( + &mut self, + id: FutureTrailers, + ) -> Result { + self.delete(id) } } diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index c044cd1bdde9..1b2326f3553b 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -1,191 +1,156 @@ use crate::bindings::http::types::{ - Error, Fields, FutureIncomingResponse, Headers, IncomingRequest, IncomingResponse, - IncomingStream, Method, OutgoingRequest, OutgoingResponse, OutgoingStream, ResponseOutparam, + Error, Fields, FutureIncomingResponse, FutureTrailers, Headers, IncomingBody, IncomingRequest, + IncomingResponse, Method, OutgoingBody, OutgoingRequest, OutgoingResponse, ResponseOutparam, Scheme, StatusCode, Trailers, }; -use crate::http_impl::WasiHttpViewExt; -use crate::types::{ActiveFields, ActiveRequest, HttpRequest, TableHttpExt}; +use crate::body::{HostFutureTrailers, HostFutureTrailersState}; +use crate::types::FieldMap; use crate::WasiHttpView; -use anyhow::{anyhow, bail, Context}; -use bytes::Bytes; -use wasmtime_wasi::preview2::{bindings::poll::poll::Pollable, HostPollable, TablePollableExt}; - -#[async_trait::async_trait] -impl crate::bindings::http::types::Host for T { - async fn drop_fields(&mut self, fields: Fields) -> wasmtime::Result<()> { - self.table_mut() +use crate::{ + body::{HostIncomingBodyBuilder, HostOutgoingBody}, + types::{ + HostFields, HostFutureIncomingResponse, HostIncomingResponse, HostOutgoingRequest, + TableHttpExt, + }, +}; +use anyhow::{anyhow, Context}; +use std::any::Any; +use wasmtime_wasi::preview2::{ + bindings::io::streams::{InputStream, OutputStream}, + bindings::poll::poll::Pollable, + HostPollable, PollableFuture, TablePollableExt, TableStreamExt, +}; + +impl crate::bindings::http::types::Host for T { + fn drop_fields(&mut self, fields: Fields) -> wasmtime::Result<()> { + self.table() .delete_fields(fields) .context("[drop_fields] deleting fields")?; Ok(()) } - async fn new_fields(&mut self, entries: Vec<(String, String)>) -> wasmtime::Result { - let mut map = ActiveFields::new(); - for (key, value) in entries { - map.insert(key, vec![value.clone().into_bytes()]); + fn new_fields(&mut self, entries: Vec<(String, Vec)>) -> wasmtime::Result { + let mut map = hyper::HeaderMap::new(); + + for (header, value) in entries { + let header = hyper::header::HeaderName::from_bytes(header.as_bytes())?; + let value = hyper::header::HeaderValue::from_bytes(&value)?; + map.append(header, value); } let id = self - .table_mut() - .push_fields(Box::new(map)) + .table() + .push_fields(HostFields::Owned { fields: map }) .context("[new_fields] pushing fields")?; Ok(id) } - async fn fields_get(&mut self, fields: Fields, name: String) -> wasmtime::Result>> { + fn fields_get(&mut self, fields: Fields, name: String) -> wasmtime::Result>> { let res = self - .table_mut() + .table() .get_fields(fields) .context("[fields_get] getting fields")? - .get(&name) - .ok_or_else(|| anyhow!("key not found: {name}"))? - .clone(); + .get_all(hyper::header::HeaderName::from_bytes(name.as_bytes())?) + .into_iter() + .map(|val| val.as_bytes().to_owned()) + .collect(); Ok(res) } - async fn fields_set( + fn fields_set( &mut self, fields: Fields, name: String, - value: Vec>, + values: Vec>, ) -> wasmtime::Result<()> { - match self.table_mut().get_fields_mut(fields) { - Ok(m) => { - m.insert(name, value.clone()); - Ok(()) - } - Err(_) => bail!("fields not found"), + let m = self.table().get_fields(fields)?; + + let header = hyper::header::HeaderName::from_bytes(name.as_bytes())?; + + m.remove(&header); + for value in values { + let value = hyper::header::HeaderValue::from_bytes(&value)?; + m.append(&header, value); } + + Ok(()) } - async fn fields_delete(&mut self, fields: Fields, name: String) -> wasmtime::Result<()> { - match self.table_mut().get_fields_mut(fields) { - Ok(m) => m.remove(&name), - Err(_) => None, - }; + fn fields_delete(&mut self, fields: Fields, name: String) -> wasmtime::Result<()> { + let m = self.table().get_fields(fields)?; + let header = hyper::header::HeaderName::from_bytes(name.as_bytes())?; + m.remove(header); Ok(()) } - async fn fields_append( + fn fields_append( &mut self, fields: Fields, name: String, value: Vec, ) -> wasmtime::Result<()> { let m = self - .table_mut() - .get_fields_mut(fields) + .table() + .get_fields(fields) .context("[fields_append] getting mutable fields")?; - match m.get_mut(&name) { - Some(v) => v.push(value), - None => { - let mut vec = std::vec::Vec::new(); - vec.push(value); - m.insert(name, vec); - } - }; + let header = hyper::header::HeaderName::from_bytes(name.as_bytes())?; + let value = hyper::header::HeaderValue::from_bytes(&value)?; + m.append(header, value); Ok(()) } - async fn fields_entries(&mut self, fields: Fields) -> wasmtime::Result)>> { - let field_map = match self.table().get_fields(fields) { - Ok(m) => m.iter(), - Err(_) => bail!("fields not found."), - }; - let mut result = Vec::new(); - for (name, value) in field_map { - result.push((name.clone(), value[0].clone())); - } + fn fields_entries(&mut self, fields: Fields) -> wasmtime::Result)>> { + let fields = self.table().get_fields(fields)?; + let result = fields + .iter() + .map(|(name, value)| (name.as_str().to_owned(), value.as_bytes().to_owned())) + .collect(); Ok(result) } - async fn fields_clone(&mut self, fields: Fields) -> wasmtime::Result { - let table = self.table_mut(); - let m = table + fn fields_clone(&mut self, fields: Fields) -> wasmtime::Result { + let fields = self + .table() .get_fields(fields) - .context("[fields_clone] getting fields")?; - let id = table - .push_fields(Box::new(m.clone())) + .context("[fields_clone] getting fields")? + .clone(); + let id = self + .table() + .push_fields(HostFields::Owned { fields }) .context("[fields_clone] pushing fields")?; Ok(id) } - async fn finish_incoming_stream( - &mut self, - stream_id: IncomingStream, - ) -> wasmtime::Result> { - for (_, stream) in self.http_ctx().streams.iter() { - if stream_id == stream.incoming() { - let response = self - .table() - .get_response(stream.parent_id()) - .context("[finish_incoming_stream] get trailers from response")?; - return Ok(response.trailers()); - } - } - bail!("unknown stream!") + fn drop_incoming_request(&mut self, _request: IncomingRequest) -> wasmtime::Result<()> { + todo!("we haven't implemented the server side of wasi-http yet") } - async fn finish_outgoing_stream( - &mut self, - _s: OutgoingStream, - _trailers: Option, - ) -> wasmtime::Result<()> { - bail!("unimplemented: finish_outgoing_stream") - } - async fn drop_incoming_request(&mut self, _request: IncomingRequest) -> wasmtime::Result<()> { - bail!("unimplemented: drop_incoming_request") - } - async fn drop_outgoing_request(&mut self, request: OutgoingRequest) -> wasmtime::Result<()> { - let r = self - .table_mut() - .get_request(request) - .context("[drop_outgoing_request] getting fields")?; - - // Cleanup dependent resources - let body = r.body(); - let headers = r.headers(); - if let Some(b) = body { - self.table_mut().delete_stream(b).ok(); - } - if let Some(h) = headers { - self.table_mut().delete_fields(h).ok(); - } - - self.table_mut() - .delete_request(request) - .context("[drop_outgoing_request] deleting request")?; - + fn drop_outgoing_request(&mut self, request: OutgoingRequest) -> wasmtime::Result<()> { + self.table().delete_outgoing_request(request)?; Ok(()) } - async fn incoming_request_method( - &mut self, - _request: IncomingRequest, - ) -> wasmtime::Result { - bail!("unimplemented: incoming_request_method") + fn incoming_request_method(&mut self, _request: IncomingRequest) -> wasmtime::Result { + todo!("we haven't implemented the server side of wasi-http yet") } - async fn incoming_request_path_with_query( + fn incoming_request_path_with_query( &mut self, _request: IncomingRequest, ) -> wasmtime::Result> { - bail!("unimplemented: incoming_request_path") + todo!("we haven't implemented the server side of wasi-http yet") } - async fn incoming_request_scheme( + fn incoming_request_scheme( &mut self, _request: IncomingRequest, ) -> wasmtime::Result> { - bail!("unimplemented: incoming_request_scheme") + todo!("we haven't implemented the server side of wasi-http yet") } - async fn incoming_request_authority( + fn incoming_request_authority( &mut self, _request: IncomingRequest, ) -> wasmtime::Result> { - bail!("unimplemented: incoming_request_authority") + todo!("we haven't implemented the server side of wasi-http yet") } - async fn incoming_request_headers( - &mut self, - _request: IncomingRequest, - ) -> wasmtime::Result { - bail!("unimplemented: incoming_request_headers") + fn incoming_request_headers(&mut self, _request: IncomingRequest) -> wasmtime::Result { + todo!("we haven't implemented the server side of wasi-http yet") } - async fn incoming_request_consume( + fn incoming_request_consume( &mut self, _request: IncomingRequest, - ) -> wasmtime::Result> { - bail!("unimplemented: incoming_request_consume") + ) -> wasmtime::Result> { + todo!("we haven't implemented the server side of wasi-http yet") } - async fn new_outgoing_request( + fn new_outgoing_request( &mut self, method: Method, path_with_query: Option, @@ -193,507 +158,305 @@ impl crate::bindings::http::types::Host for T authority: Option, headers: Headers, ) -> wasmtime::Result { - let mut req = ActiveRequest::new(); - req.path_with_query = path_with_query.unwrap_or("".to_string()); - req.authority = authority.unwrap_or("".to_string()); - req.method = method; - req.headers = Some(headers); - req.scheme = scheme; + let headers = self.table().get_fields(headers)?.clone(); + + let req = HostOutgoingRequest { + path_with_query: path_with_query.unwrap_or("".to_string()), + authority: authority.unwrap_or("".to_string()), + method, + headers, + scheme, + body: None, + }; let id = self - .table_mut() - .push_request(Box::new(req)) + .table() + .push_outgoing_response(req) .context("[new_outgoing_request] pushing request")?; Ok(id) } - async fn outgoing_request_write( + fn outgoing_request_write( &mut self, request: OutgoingRequest, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> { let req = self .table() - .get_request(request) + .get_outgoing_request_mut(request) .context("[outgoing_request_write] getting request")?; - let stream_id = if let Some(stream_id) = req.body() { - stream_id - } else { - let (new, stream) = self - .table_mut() - .push_stream(Bytes::new(), request) - .await - .expect("[outgoing_request_write] valid output stream"); - self.http_ctx_mut().streams.insert(new, stream); - let req = self - .table_mut() - .get_request_mut(request) - .expect("[outgoing_request_write] request to be found"); - req.set_body(new); - new - }; - let stream = self - .table() - .get_stream(stream_id) - .context("[outgoing_request_write] getting stream")?; - Ok(Ok(stream.outgoing())) + + if req.body.is_some() { + return Ok(Err(())); + } + + let (host_body, hyper_body) = HostOutgoingBody::new(); + + req.body = Some(hyper_body); + + // The output stream will necessarily outlive the request, because we could be still + // writing to the stream after `outgoing-handler.handle` is called. + let outgoing_body = self.table().push_outgoing_body(host_body)?; + + Ok(Ok(outgoing_body)) } - async fn drop_response_outparam( - &mut self, - _response: ResponseOutparam, - ) -> wasmtime::Result<()> { - bail!("unimplemented: drop_response_outparam") + fn drop_response_outparam(&mut self, _response: ResponseOutparam) -> wasmtime::Result<()> { + todo!("we haven't implemented the server side of wasi-http yet") } - async fn set_response_outparam( + fn set_response_outparam( &mut self, _outparam: ResponseOutparam, _response: Result, - ) -> wasmtime::Result> { - bail!("unimplemented: set_response_outparam") + ) -> wasmtime::Result<()> { + todo!("we haven't implemented the server side of wasi-http yet") } - async fn drop_incoming_response(&mut self, response: IncomingResponse) -> wasmtime::Result<()> { - let r = self - .table() - .get_response(response) - .context("[drop_incoming_response] getting response")?; - - // Cleanup dependent resources - let body = r.body(); - let headers = r.headers(); - if let Some(id) = body { - let stream = self - .table() - .get_stream(id) - .context("[drop_incoming_response] getting stream")?; - let incoming_id = stream.incoming(); - if let Some(trailers) = self.finish_incoming_stream(incoming_id).await? { - self.table_mut() - .delete_fields(trailers) - .context("[drop_incoming_response] deleting trailers") - .unwrap_or_else(|_| ()); - } - self.table_mut().delete_stream(id).ok(); - } - if let Some(h) = headers { - self.table_mut().delete_fields(h).ok(); - } - - self.table_mut() - .delete_response(response) + fn drop_incoming_response(&mut self, response: IncomingResponse) -> wasmtime::Result<()> { + self.table() + .delete_incoming_response(response) .context("[drop_incoming_response] deleting response")?; Ok(()) } - async fn drop_outgoing_response( - &mut self, - _response: OutgoingResponse, - ) -> wasmtime::Result<()> { - bail!("unimplemented: drop_outgoing_response") + fn drop_outgoing_response(&mut self, _response: OutgoingResponse) -> wasmtime::Result<()> { + todo!("we haven't implemented the server side of wasi-http yet") } - async fn incoming_response_status( + fn incoming_response_status( &mut self, response: IncomingResponse, ) -> wasmtime::Result { let r = self .table() - .get_response(response) + .get_incoming_response(response) .context("[incoming_response_status] getting response")?; - Ok(r.status()) + Ok(r.status) } - async fn incoming_response_headers( + fn incoming_response_headers( &mut self, response: IncomingResponse, ) -> wasmtime::Result { - let r = self + let _ = self .table() - .get_response(response) + .get_incoming_response_mut(response) .context("[incoming_response_headers] getting response")?; - Ok(r.headers().unwrap_or(0 as Headers)) + + fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { + &mut elem.downcast_mut::().unwrap().headers + } + + let id = self.table().push_fields(HostFields::Ref { + parent: response, + get_fields, + })?; + + Ok(id) } - async fn incoming_response_consume( + fn incoming_response_consume( &mut self, response: IncomingResponse, - ) -> wasmtime::Result> { - let table = self.table_mut(); + ) -> wasmtime::Result> { + let table = self.table(); let r = table - .get_response(response) + .get_incoming_response_mut(response) .context("[incoming_response_consume] getting response")?; - Ok(Ok(r - .body() - .map(|id| { - table - .get_stream(id) - .map(|stream| stream.incoming()) - .expect("[incoming_response_consume] response body stream") - }) - .unwrap_or(0 as IncomingStream))) - } - async fn new_outgoing_response( + + match r.body.take() { + Some(builder) => { + let id = self.table().push_incoming_body(builder.build())?; + Ok(Ok(id)) + } + + None => Ok(Err(())), + } + } + fn drop_future_trailers(&mut self, id: FutureTrailers) -> wasmtime::Result<()> { + self.table() + .delete_future_trailers(id) + .context("[drop future-trailers] deleting future-trailers")?; + Ok(()) + } + + fn future_trailers_subscribe(&mut self, index: FutureTrailers) -> wasmtime::Result { + // Eagerly force errors about the validity of the index. + let _ = self.table().get_future_trailers(index)?; + + fn make_future(elem: &mut dyn Any) -> PollableFuture { + Box::pin(elem.downcast_mut::().unwrap().ready()) + } + + let id = self + .table() + .push_host_pollable(HostPollable::TableEntry { index, make_future })?; + + Ok(id) + } + + fn future_trailers_get( + &mut self, + id: FutureTrailers, + ) -> wasmtime::Result>> { + let trailers = self.table().get_future_trailers(id)?; + match &trailers.state { + HostFutureTrailersState::Waiting(_) => return Ok(None), + HostFutureTrailersState::Done(Err(e)) => return Ok(Some(Err(e.clone()))), + HostFutureTrailersState::Done(Ok(_)) => {} + } + + fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { + let trailers = elem.downcast_mut::().unwrap(); + match &mut trailers.state { + HostFutureTrailersState::Done(Ok(e)) => e, + _ => unreachable!(), + } + } + + let hdrs = self.table().push_fields(HostFields::Ref { + parent: id, + get_fields, + })?; + + Ok(Some(Ok(hdrs))) + } + + fn new_outgoing_response( &mut self, _status_code: StatusCode, _headers: Headers, ) -> wasmtime::Result { - bail!("unimplemented: new_outgoing_response") + todo!("we haven't implemented the server side of wasi-http yet") } - async fn outgoing_response_write( + fn outgoing_response_write( &mut self, _response: OutgoingResponse, - ) -> wasmtime::Result> { - bail!("unimplemented: outgoing_response_write") + ) -> wasmtime::Result> { + todo!("we haven't implemented the server side of wasi-http yet") } - async fn drop_future_incoming_response( + fn drop_future_incoming_response( &mut self, - future: FutureIncomingResponse, + id: FutureIncomingResponse, ) -> wasmtime::Result<()> { - self.table_mut() - .delete_future(future) - .context("[drop_future_incoming_response] deleting future")?; + let _ = self.table().delete_future_incoming_response(id)?; Ok(()) } - async fn future_incoming_response_get( + fn future_incoming_response_get( &mut self, - future: FutureIncomingResponse, - ) -> wasmtime::Result>> { - let f = self - .table() - .get_future(future) - .context("[future_incoming_response_get] getting future")?; - Ok(match f.pollable_id() { - Some(_) => { - let result = match f.response_id() { - Some(id) => Ok(id), - None => { - let response = self.handle_async(f.request_id(), f.options()).await; - match response { - Ok(id) => { - tracing::debug!( - "including response id to future incoming response" - ); - let future_mut = self.table_mut().get_future_mut(future)?; - future_mut.set_response_id(id); - tracing::trace!( - "future incoming response details {:?}", - *future_mut - ); - } - _ => {} - } - response - } - }; - Some(result) - } - None => None, - }) + id: FutureIncomingResponse, + ) -> wasmtime::Result, ()>>> { + let resp = self.table().get_future_incoming_response_mut(id)?; + + match resp { + HostFutureIncomingResponse::Pending(_) => return Ok(None), + HostFutureIncomingResponse::Consumed => return Ok(Some(Err(()))), + HostFutureIncomingResponse::Ready(_) => {} + } + + let resp = + match std::mem::replace(resp, HostFutureIncomingResponse::Consumed).unwrap_ready() { + Err(e) => { + // Trapping if it's not possible to downcast to an wasi-http error + let e = e.downcast::()?; + return Ok(Some(Ok(Err(e)))); + } + + Ok(resp) => resp, + }; + + let (parts, body) = resp.resp.into_parts(); + + let resp = self.table().push_incoming_response(HostIncomingResponse { + status: parts.status.as_u16(), + headers: FieldMap::from(parts.headers), + body: Some(HostIncomingBodyBuilder { + body, + between_bytes_timeout: resp.between_bytes_timeout, + }), + worker: resp.worker, + })?; + + Ok(Some(Ok(Ok(resp)))) } - async fn listen_to_future_incoming_response( + fn listen_to_future_incoming_response( &mut self, - future: FutureIncomingResponse, + id: FutureIncomingResponse, ) -> wasmtime::Result { - let f = self - .table() - .get_future(future) - .context("[listen_to_future_incoming_response] getting future")?; - Ok(match f.pollable_id() { - Some(pollable_id) => pollable_id, - None => { - tracing::debug!("including pollable id to future incoming response"); - let pollable = - HostPollable::Closure(Box::new(|| Box::pin(futures::future::ready(Ok(()))))); - let pollable_id = self - .table_mut() - .push_host_pollable(pollable) - .context("[listen_to_future_incoming_response] pushing host pollable")?; - let f = self - .table_mut() - .get_future_mut(future) - .context("[listen_to_future_incoming_response] getting future")?; - f.set_pollable_id(pollable_id); - tracing::trace!("future incoming response details {:?}", *f); - pollable_id - } - }) - } -} + let _ = self.table().get_future_incoming_response(id)?; -#[cfg(feature = "sync")] -pub mod sync { - use crate::bindings::http::types::{ - Error as AsyncError, Host as AsyncHost, Method as AsyncMethod, Scheme as AsyncScheme, - }; - use crate::bindings::sync::http::types::{ - Error, Fields, FutureIncomingResponse, Headers, IncomingRequest, IncomingResponse, - IncomingStream, Method, OutgoingRequest, OutgoingResponse, OutgoingStream, - ResponseOutparam, Scheme, StatusCode, Trailers, - }; - use crate::http_impl::WasiHttpViewExt; - use crate::WasiHttpView; - use wasmtime_wasi::preview2::{bindings::poll::poll::Pollable, in_tokio}; - - // same boilerplate everywhere, converting between two identical types with different - // definition sites. one day wasmtime-wit-bindgen will make all this unnecessary - impl From for Error { - fn from(other: AsyncError) -> Self { - match other { - AsyncError::InvalidUrl(v) => Self::InvalidUrl(v), - AsyncError::ProtocolError(v) => Self::ProtocolError(v), - AsyncError::TimeoutError(v) => Self::TimeoutError(v), - AsyncError::UnexpectedError(v) => Self::UnexpectedError(v), - } + fn make_future<'a>(elem: &'a mut dyn Any) -> PollableFuture<'a> { + Box::pin( + elem.downcast_mut::() + .expect("parent resource is HostFutureIncomingResponse"), + ) } + + let pollable = self.table().push_host_pollable(HostPollable::TableEntry { + index: id, + make_future, + })?; + + Ok(pollable) } - impl From for AsyncError { - fn from(other: Error) -> Self { - match other { - Error::InvalidUrl(v) => Self::InvalidUrl(v), - Error::ProtocolError(v) => Self::ProtocolError(v), - Error::TimeoutError(v) => Self::TimeoutError(v), - Error::UnexpectedError(v) => Self::UnexpectedError(v), - } + fn incoming_body_stream( + &mut self, + id: IncomingBody, + ) -> wasmtime::Result> { + let body = self.table().get_incoming_body(id)?; + + if let Some(stream) = body.stream.take() { + let stream = self.table().push_input_stream_child(Box::new(stream), id)?; + return Ok(Ok(stream)); } + + Ok(Err(())) } - impl From for Method { - fn from(other: AsyncMethod) -> Self { - match other { - AsyncMethod::Connect => Self::Connect, - AsyncMethod::Delete => Self::Delete, - AsyncMethod::Get => Self::Get, - AsyncMethod::Head => Self::Head, - AsyncMethod::Options => Self::Options, - AsyncMethod::Patch => Self::Patch, - AsyncMethod::Post => Self::Post, - AsyncMethod::Put => Self::Put, - AsyncMethod::Trace => Self::Trace, - AsyncMethod::Other(v) => Self::Other(v), - } - } + fn incoming_body_finish(&mut self, id: IncomingBody) -> wasmtime::Result { + let body = self.table().delete_incoming_body(id)?; + let trailers = self + .table() + .push_future_trailers(body.into_future_trailers())?; + Ok(trailers) } - impl From for AsyncMethod { - fn from(other: Method) -> Self { - match other { - Method::Connect => Self::Connect, - Method::Delete => Self::Delete, - Method::Get => Self::Get, - Method::Head => Self::Head, - Method::Options => Self::Options, - Method::Patch => Self::Patch, - Method::Post => Self::Post, - Method::Put => Self::Put, - Method::Trace => Self::Trace, - Method::Other(v) => Self::Other(v), - } - } + fn drop_incoming_body(&mut self, id: IncomingBody) -> wasmtime::Result<()> { + let _ = self.table().delete_incoming_body(id)?; + Ok(()) } - impl From for Scheme { - fn from(other: AsyncScheme) -> Self { - match other { - AsyncScheme::Http => Self::Http, - AsyncScheme::Https => Self::Https, - AsyncScheme::Other(v) => Self::Other(v), - } + fn outgoing_body_write( + &mut self, + id: OutgoingBody, + ) -> wasmtime::Result> { + let body = self.table().get_outgoing_body(id)?; + if let Some(stream) = body.body_output_stream.take() { + let id = self.table().push_output_stream_child(stream, id)?; + Ok(Ok(id)) + } else { + Ok(Err(())) } } - impl From for AsyncScheme { - fn from(other: Scheme) -> Self { - match other { - Scheme::Http => Self::Http, - Scheme::Https => Self::Https, - Scheme::Other(v) => Self::Other(v), - } + fn outgoing_body_write_trailers( + &mut self, + id: OutgoingBody, + ts: Trailers, + ) -> wasmtime::Result<()> { + let mut body = self.table().delete_outgoing_body(id)?; + let trailers = self.table().get_fields(ts)?.clone(); + + match body + .trailers_sender + .take() + // Should be unreachable - this is the only place we take the trailers sender, + // at the end of the HostOutgoingBody's lifetime + .ok_or_else(|| anyhow!("trailers_sender missing"))? + .send(trailers.into()) + { + Ok(()) => {} + Err(_) => {} // Ignoring failure: receiver died sending body, but we can't report that + // here. } + + Ok(()) } - impl crate::bindings::sync::http::types::Host for T { - fn drop_fields(&mut self, fields: Fields) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::drop_fields(self, fields).await }) - } - fn new_fields(&mut self, entries: Vec<(String, String)>) -> wasmtime::Result { - in_tokio(async { AsyncHost::new_fields(self, entries).await }) - } - fn fields_get(&mut self, fields: Fields, name: String) -> wasmtime::Result>> { - in_tokio(async { AsyncHost::fields_get(self, fields, name).await }) - } - fn fields_set( - &mut self, - fields: Fields, - name: String, - value: Vec>, - ) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::fields_set(self, fields, name, value).await }) - } - fn fields_delete(&mut self, fields: Fields, name: String) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::fields_delete(self, fields, name).await }) - } - fn fields_append( - &mut self, - fields: Fields, - name: String, - value: Vec, - ) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::fields_append(self, fields, name, value).await }) - } - fn fields_entries(&mut self, fields: Fields) -> wasmtime::Result)>> { - in_tokio(async { AsyncHost::fields_entries(self, fields).await }) - } - fn fields_clone(&mut self, fields: Fields) -> wasmtime::Result { - in_tokio(async { AsyncHost::fields_clone(self, fields).await }) - } - fn finish_incoming_stream( - &mut self, - stream_id: IncomingStream, - ) -> wasmtime::Result> { - in_tokio(async { AsyncHost::finish_incoming_stream(self, stream_id).await }) - } - fn finish_outgoing_stream( - &mut self, - stream: OutgoingStream, - trailers: Option, - ) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::finish_outgoing_stream(self, stream, trailers).await }) - } - fn drop_incoming_request(&mut self, request: IncomingRequest) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::drop_incoming_request(self, request).await }) - } - fn drop_outgoing_request(&mut self, request: OutgoingRequest) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::drop_outgoing_request(self, request).await }) - } - fn incoming_request_method( - &mut self, - request: IncomingRequest, - ) -> wasmtime::Result { - in_tokio(async { AsyncHost::incoming_request_method(self, request).await }) - .map(Method::from) - } - fn incoming_request_path_with_query( - &mut self, - request: IncomingRequest, - ) -> wasmtime::Result> { - in_tokio(async { AsyncHost::incoming_request_path_with_query(self, request).await }) - } - fn incoming_request_scheme( - &mut self, - request: IncomingRequest, - ) -> wasmtime::Result> { - Ok( - in_tokio(async { AsyncHost::incoming_request_scheme(self, request).await })? - .map(Scheme::from), - ) - } - fn incoming_request_authority( - &mut self, - request: IncomingRequest, - ) -> wasmtime::Result> { - in_tokio(async { AsyncHost::incoming_request_authority(self, request).await }) - } - fn incoming_request_headers( - &mut self, - request: IncomingRequest, - ) -> wasmtime::Result { - in_tokio(async { AsyncHost::incoming_request_headers(self, request).await }) - } - fn incoming_request_consume( - &mut self, - request: IncomingRequest, - ) -> wasmtime::Result> { - in_tokio(async { AsyncHost::incoming_request_consume(self, request).await }) - } - fn new_outgoing_request( - &mut self, - method: Method, - path_with_query: Option, - scheme: Option, - authority: Option, - headers: Headers, - ) -> wasmtime::Result { - in_tokio(async { - AsyncHost::new_outgoing_request( - self, - method.into(), - path_with_query, - scheme.map(AsyncScheme::from), - authority, - headers, - ) - .await - }) - } - fn outgoing_request_write( - &mut self, - request: OutgoingRequest, - ) -> wasmtime::Result> { - in_tokio(async { AsyncHost::outgoing_request_write(self, request).await }) - } - fn drop_response_outparam(&mut self, response: ResponseOutparam) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::drop_response_outparam(self, response).await }) - } - fn set_response_outparam( - &mut self, - outparam: ResponseOutparam, - response: Result, - ) -> wasmtime::Result> { - in_tokio(async { - AsyncHost::set_response_outparam(self, outparam, response.map_err(AsyncError::from)) - .await - }) - } - fn drop_incoming_response(&mut self, response: IncomingResponse) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::drop_incoming_response(self, response).await }) - } - fn drop_outgoing_response(&mut self, response: OutgoingResponse) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::drop_outgoing_response(self, response).await }) - } - fn incoming_response_status( - &mut self, - response: IncomingResponse, - ) -> wasmtime::Result { - in_tokio(async { AsyncHost::incoming_response_status(self, response).await }) - } - fn incoming_response_headers( - &mut self, - response: IncomingResponse, - ) -> wasmtime::Result { - in_tokio(async { AsyncHost::incoming_response_headers(self, response).await }) - } - fn incoming_response_consume( - &mut self, - response: IncomingResponse, - ) -> wasmtime::Result> { - in_tokio(async { AsyncHost::incoming_response_consume(self, response).await }) - } - fn new_outgoing_response( - &mut self, - status_code: StatusCode, - headers: Headers, - ) -> wasmtime::Result { - in_tokio(async { AsyncHost::new_outgoing_response(self, status_code, headers).await }) - } - fn outgoing_response_write( - &mut self, - response: OutgoingResponse, - ) -> wasmtime::Result> { - in_tokio(async { AsyncHost::outgoing_response_write(self, response).await }) - } - fn drop_future_incoming_response( - &mut self, - future: FutureIncomingResponse, - ) -> wasmtime::Result<()> { - in_tokio(async { AsyncHost::drop_future_incoming_response(self, future).await }) - } - fn future_incoming_response_get( - &mut self, - future: FutureIncomingResponse, - ) -> wasmtime::Result>> { - Ok( - in_tokio(async { AsyncHost::future_incoming_response_get(self, future).await })? - .map(|v| v.map_err(Error::from)), - ) - } - fn listen_to_future_incoming_response( - &mut self, - future: FutureIncomingResponse, - ) -> wasmtime::Result { - in_tokio(async { AsyncHost::listen_to_future_incoming_response(self, future).await }) - } + fn drop_outgoing_body(&mut self, id: OutgoingBody) -> wasmtime::Result<()> { + let _ = self.table().delete_outgoing_body(id)?; + Ok(()) } } diff --git a/crates/wasi-http/wit/deps/http/incoming-handler.wit b/crates/wasi-http/wit/deps/http/incoming-handler.wit index d0e270465593..ad8a43f8ccf0 100644 --- a/crates/wasi-http/wit/deps/http/incoming-handler.wit +++ b/crates/wasi-http/wit/deps/http/incoming-handler.wit @@ -12,13 +12,13 @@ interface incoming-handler { // The `handle` function takes an outparam instead of returning its response // so that the component may stream its response while streaming any other // request or response bodies. The callee MUST write a response to the - // `response-out` and then finish the response before returning. The `handle` + // `response-outparam` and then finish the response before returning. The `handle` // function is allowed to continue execution after finishing the response's // output stream. While this post-response execution is taken off the // critical path, since there is no return value, there is no way to report // its success or failure. handle: func( - request: incoming-request, - response-out: response-outparam + request: /* own */ incoming-request, + response-out: /* own */ response-outparam ) } diff --git a/crates/wasi-http/wit/deps/http/outgoing-handler.wit b/crates/wasi-http/wit/deps/http/outgoing-handler.wit index 06c8e469f95b..3e03327d742b 100644 --- a/crates/wasi-http/wit/deps/http/outgoing-handler.wit +++ b/crates/wasi-http/wit/deps/http/outgoing-handler.wit @@ -8,11 +8,20 @@ interface outgoing-handler { use types.{outgoing-request, request-options, future-incoming-response} + // FIXME: we would want to use the types.error here but there is a + // wasmtime-wit-bindgen bug that prevents us from using the same error in + // the two different interfaces, right now... + variant error { + invalid(string) + } + // The parameter and result types of the `handle` function allow the caller // to concurrently stream the bodies of the outgoing request and the incoming // response. + // Consumes the outgoing-request. Gives an error if the outgoing-request + // is invalid or cannot be satisfied by this handler. handle: func( - request: outgoing-request, + request: /* own */ outgoing-request, options: option - ) -> future-incoming-response + ) -> result } diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index 7b7b015529c0..821e15f96213 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -41,30 +41,28 @@ interface types { // fields = u32` type alias can be replaced by a proper `resource fields` // definition containing all the functions using the method syntactic sugar. type fields = u32 - drop-fields: func(fields: fields) - new-fields: func(entries: list>) -> fields - fields-get: func(fields: fields, name: string) -> list> - fields-set: func(fields: fields, name: string, value: list>) - fields-delete: func(fields: fields, name: string) - fields-append: func(fields: fields, name: string, value: list) - fields-entries: func(fields: fields) -> list>> - fields-clone: func(fields: fields) -> fields + drop-fields: func(fields: /* own */ fields) + // Multiple values for a header are multiple entries in the list with the + // same key. + new-fields: func(entries: list>>) -> fields + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + fields-get: func(fields: /* borrow */ fields, name: string) -> list> + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + fields-set: func(fields: /* borrow */ fields, name: string, value: list>) + fields-delete: func(fields: /* borrow */ fields, name: string) + fields-append: func(fields: /* borrow */ fields, name: string, value: list) + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + fields-entries: func(fields: /* borrow */ fields) -> list>> + // Deep copy of all contents in a fields. + fields-clone: func(fields: /* borrow */ fields) -> fields type headers = fields type trailers = fields - // The following block defines stream types which corresponds to the HTTP - // standard Contents and Trailers. With Preview3, all of these fields can be - // replaced by a stream>. In the interim, we need to - // build on separate resource types defined by `wasi:io/streams`. The - // `finish-` functions emulate the stream's result value and MUST be called - // exactly once after the final read/write from/to the stream before dropping - // the stream. - type incoming-stream = input-stream - type outgoing-stream = output-stream - finish-incoming-stream: func(s: incoming-stream) -> option - finish-outgoing-stream: func(s: outgoing-stream, trailers: option) - // The following block defines the `incoming-request` and `outgoing-request` // resource types that correspond to HTTP standard Requests. Soon, when // resource types are added, the `u32` type aliases can be replaced by @@ -74,23 +72,30 @@ interface types { // above). The `consume` and `write` methods may only be called once (and // return failure thereafter). type incoming-request = u32 + drop-incoming-request: func(request: /* own */ incoming-request) + incoming-request-method: func(request: /* borrow */ incoming-request) -> method + incoming-request-path-with-query: func(request: /* borrow */ incoming-request) -> option + incoming-request-scheme: func(request: /* borrow */ incoming-request) -> option + incoming-request-authority: func(request: /* borrow */ incoming-request) -> option + + incoming-request-headers: func(request: /* borrow */ incoming-request) -> /* child */ headers + // Will return the input-stream child at most once. If called more than + // once, subsequent calls will return error. + incoming-request-consume: func(request: /* borrow */ incoming-request) -> result< /* child */ input-stream> + type outgoing-request = u32 - drop-incoming-request: func(request: incoming-request) - drop-outgoing-request: func(request: outgoing-request) - incoming-request-method: func(request: incoming-request) -> method - incoming-request-path-with-query: func(request: incoming-request) -> option - incoming-request-scheme: func(request: incoming-request) -> option - incoming-request-authority: func(request: incoming-request) -> option - incoming-request-headers: func(request: incoming-request) -> headers - incoming-request-consume: func(request: incoming-request) -> result + drop-outgoing-request: func(request: /* own */ outgoing-request) new-outgoing-request: func( method: method, path-with-query: option, scheme: option, authority: option, - headers: headers + headers: /* borrow */ headers ) -> outgoing-request - outgoing-request-write: func(request: outgoing-request) -> result + + // Will return the outgoing-body child at most once. If called more than + // once, subsequent calls will return error. + outgoing-request-write: func(request: /* borrow */ outgoing-request) -> result< /* child */ outgoing-body> // Additional optional parameters that can be set when making a request. record request-options { @@ -115,8 +120,8 @@ interface types { // (the `wasi:http/handler` interface used for both incoming and outgoing can // simply return a `stream`). type response-outparam = u32 - drop-response-outparam: func(response: response-outparam) - set-response-outparam: func(param: response-outparam, response: result) -> result + drop-response-outparam: func(response: /* own */ response-outparam) + set-response-outparam: func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) // This type corresponds to the HTTP standard Status Code. type status-code = u16 @@ -129,27 +134,72 @@ interface types { // type (that uses the single `stream` type mentioned above). The `consume` and // `write` methods may only be called once (and return failure thereafter). type incoming-response = u32 + drop-incoming-response: func(response: /* own */ incoming-response) + incoming-response-status: func(response: /* borrow */ incoming-response) -> status-code + incoming-response-headers: func(response: /* borrow */ incoming-response) -> /* child */ headers + // May be called at most once. returns error if called additional times. + // TODO: make incoming-request-consume work the same way, giving a child + // incoming-body. + incoming-response-consume: func(response: /* borrow */ incoming-response) -> result + + type incoming-body = u32 + drop-incoming-body: func(this: /* own */ incoming-body) + + // returned input-stream is a child - the implementation may trap if + // incoming-body is dropped (or consumed by call to + // incoming-body-finish) before the input-stream is dropped. + // May be called at most once. returns error if called additional times. + incoming-body-stream: func(this: /* borrow */ incoming-body) -> + result + // takes ownership of incoming-body. this will trap if the + // incoming-body-stream child is still alive! + incoming-body-finish: func(this: /* own */ incoming-body) -> + /* transitive child of the incoming-response of incoming-body */ future-trailers + + type future-trailers = u32 + drop-future-trailers: func(this: /* own */ future-trailers) + /// Pollable that resolves when the body has been fully read, and the trailers + /// are ready to be consumed. + future-trailers-subscribe: func(this: /* borrow */ future-trailers) -> /* child */ pollable + + /// Retrieve reference to trailers, if they are ready. + future-trailers-get: func(response: /* borrow */ future-trailers) -> option> + type outgoing-response = u32 - drop-incoming-response: func(response: incoming-response) - drop-outgoing-response: func(response: outgoing-response) - incoming-response-status: func(response: incoming-response) -> status-code - incoming-response-headers: func(response: incoming-response) -> headers - incoming-response-consume: func(response: incoming-response) -> result + drop-outgoing-response: func(response: /* own */ outgoing-response) new-outgoing-response: func( status-code: status-code, - headers: headers + headers: /* borrow */ headers ) -> outgoing-response - outgoing-response-write: func(response: outgoing-response) -> result - // The following block defines a special resource type used by the - // `wasi:http/outgoing-handler` interface to emulate - // `future>` in advance of Preview3. Given a - // `future-incoming-response`, the client can call the non-blocking `get` - // method to get the result if it is available. If the result is not available, - // the client can call `listen` to get a `pollable` that can be passed to - // `io.poll.poll-oneoff`. + /// Will give the child outgoing-response at most once. subsequent calls will + /// return an error. + outgoing-response-write: func(this: /* borrow */ outgoing-response) -> result + + type outgoing-body = u32 + drop-outgoing-body: func(this: /* own */ outgoing-body) + /// Will give the child output-stream at most once. subsequent calls will + /// return an error. + outgoing-body-write: func(this: /* borrow */ outgoing-body) -> result + /// Write trailers as the way to finish an outgoing-body. To finish an + /// outgoing-body without writing trailers, use drop-outgoing-body. + outgoing-body-write-trailers: func(this: /* own */ outgoing-body, trailers: /* own */ trailers) + + /// The following block defines a special resource type used by the + /// `wasi:http/outgoing-handler` interface to emulate + /// `future>` in advance of Preview3. Given a + /// `future-incoming-response`, the client can call the non-blocking `get` + /// method to get the result if it is available. If the result is not available, + /// the client can call `listen` to get a `pollable` that can be passed to + /// `io.poll.poll-oneoff`. type future-incoming-response = u32 - drop-future-incoming-response: func(f: future-incoming-response) - future-incoming-response-get: func(f: future-incoming-response) -> option> - listen-to-future-incoming-response: func(f: future-incoming-response) -> pollable + drop-future-incoming-response: func(f: /* own */ future-incoming-response) + /// option indicates readiness. + /// outer result indicates you are allowed to get the + /// incoming-response-or-error at most once. subsequent calls after ready + /// will return an error here. + /// inner result indicates whether the incoming-response was available, or an + /// error occured. + future-incoming-response-get: func(f: /* borrow */ future-incoming-response) -> option>> + listen-to-future-incoming-response: func(f: /* borrow */ future-incoming-response) -> /* child */ pollable } diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index 2e0a3e95e114..6d9ad182dcec 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -31,6 +31,7 @@ mod stdio; mod stream; mod table; mod tcp; +mod write_stream; pub use self::clocks::{HostMonotonicClock, HostWallClock}; pub use self::ctx::{WasiCtx, WasiCtxBuilder, WasiView}; @@ -151,7 +152,7 @@ pub(crate) static RUNTIME: once_cell::sync::Lazy = .unwrap() }); -pub(crate) struct AbortOnDropJoinHandle(tokio::task::JoinHandle); +pub struct AbortOnDropJoinHandle(tokio::task::JoinHandle); impl Drop for AbortOnDropJoinHandle { fn drop(&mut self) { self.0.abort() @@ -188,7 +189,7 @@ impl std::future::Future for AbortOnDropJoinHandle { } } -pub(crate) fn spawn(f: F) -> AbortOnDropJoinHandle +pub fn spawn(f: F) -> AbortOnDropJoinHandle where F: std::future::Future + Send + 'static, G: Send + 'static, diff --git a/crates/wasi/src/preview2/pipe.rs b/crates/wasi/src/preview2/pipe.rs index a01215c11323..69749db9d43c 100644 --- a/crates/wasi/src/preview2/pipe.rs +++ b/crates/wasi/src/preview2/pipe.rs @@ -10,9 +10,10 @@ use crate::preview2::{HostInputStream, HostOutputStream, OutputStreamError, StreamState}; use anyhow::{anyhow, Error}; use bytes::Bytes; -use std::sync::{Arc, Mutex}; use tokio::sync::mpsc; +pub use crate::preview2::write_stream::AsyncWriteStream; + #[derive(Debug)] pub struct MemoryInputPipe { buffer: std::io::Cursor, @@ -221,198 +222,6 @@ impl HostInputStream for AsyncReadStream { } } -#[derive(Debug)] -struct WorkerState { - alive: bool, - items: std::collections::VecDeque, - write_budget: usize, - flush_pending: bool, - error: Option, -} - -impl WorkerState { - fn check_error(&mut self) -> Result<(), OutputStreamError> { - if let Some(e) = self.error.take() { - return Err(OutputStreamError::LastOperationFailed(e)); - } - if !self.alive { - return Err(OutputStreamError::Closed); - } - Ok(()) - } -} - -struct Worker { - state: Mutex, - new_work: tokio::sync::Notify, - write_ready_changed: tokio::sync::Notify, -} - -enum Job { - Flush, - Write(Bytes), -} - -enum WriteStatus<'a> { - Done(Result), - Pending(tokio::sync::futures::Notified<'a>), -} - -impl Worker { - fn new(write_budget: usize) -> Self { - Self { - state: Mutex::new(WorkerState { - alive: true, - items: std::collections::VecDeque::new(), - write_budget, - flush_pending: false, - error: None, - }), - new_work: tokio::sync::Notify::new(), - write_ready_changed: tokio::sync::Notify::new(), - } - } - fn check_write(&self) -> WriteStatus<'_> { - let mut state = self.state(); - if let Err(e) = state.check_error() { - return WriteStatus::Done(Err(e)); - } - - if state.flush_pending || state.write_budget == 0 { - return WriteStatus::Pending(self.write_ready_changed.notified()); - } - - WriteStatus::Done(Ok(state.write_budget)) - } - fn state(&self) -> std::sync::MutexGuard { - self.state.lock().unwrap() - } - fn pop(&self) -> Option { - let mut state = self.state(); - if state.items.is_empty() { - if state.flush_pending { - return Some(Job::Flush); - } - } else if let Some(bytes) = state.items.pop_front() { - return Some(Job::Write(bytes)); - } - - None - } - fn report_error(&self, e: std::io::Error) { - { - let mut state = self.state(); - state.alive = false; - state.error = Some(e.into()); - state.flush_pending = false; - } - self.write_ready_changed.notify_waiters(); - } - async fn work(&self, mut writer: T) { - use tokio::io::AsyncWriteExt; - loop { - let notified = self.new_work.notified(); - while let Some(job) = self.pop() { - match job { - Job::Flush => { - if let Err(e) = writer.flush().await { - self.report_error(e); - return; - } - - tracing::debug!("worker marking flush complete"); - self.state().flush_pending = false; - } - - Job::Write(mut bytes) => { - tracing::debug!("worker writing: {bytes:?}"); - let len = bytes.len(); - match writer.write_all_buf(&mut bytes).await { - Err(e) => { - self.report_error(e); - return; - } - Ok(_) => { - self.state().write_budget += len; - } - } - } - } - - self.write_ready_changed.notify_waiters(); - } - - notified.await; - } - } -} - -/// Provides a [`HostOutputStream`] impl from a [`tokio::io::AsyncWrite`] impl -pub struct AsyncWriteStream { - worker: Arc, - _join_handle: crate::preview2::AbortOnDropJoinHandle<()>, -} - -impl AsyncWriteStream { - /// Create a [`AsyncWriteStream`]. In order to use the [`HostOutputStream`] impl - /// provided by this struct, the argument must impl [`tokio::io::AsyncWrite`]. - pub fn new( - write_budget: usize, - writer: T, - ) -> Self { - let worker = Arc::new(Worker::new(write_budget)); - - let w = Arc::clone(&worker); - let join_handle = crate::preview2::spawn(async move { w.work(writer).await }); - - AsyncWriteStream { - worker, - _join_handle: join_handle, - } - } -} - -#[async_trait::async_trait] -impl HostOutputStream for AsyncWriteStream { - fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError> { - let mut state = self.worker.state(); - state.check_error()?; - if state.flush_pending { - return Err(OutputStreamError::Trap(anyhow!( - "write not permitted while flush pending" - ))); - } - match state.write_budget.checked_sub(bytes.len()) { - Some(remaining_budget) => { - state.write_budget = remaining_budget; - state.items.push_back(bytes); - } - None => return Err(OutputStreamError::Trap(anyhow!("write exceeded budget"))), - } - drop(state); - self.worker.new_work.notify_waiters(); - Ok(()) - } - fn flush(&mut self) -> Result<(), OutputStreamError> { - let mut state = self.worker.state(); - state.check_error()?; - - state.flush_pending = true; - self.worker.new_work.notify_waiters(); - - Ok(()) - } - - async fn write_ready(&mut self) -> Result { - loop { - match self.worker.check_write() { - WriteStatus::Done(r) => return r, - WriteStatus::Pending(notifier) => notifier.await, - } - } - } -} - /// An output stream that consumes all input written to it, and is always ready. pub struct SinkOutputStream; diff --git a/crates/wasi/src/preview2/table.rs b/crates/wasi/src/preview2/table.rs index f4efd8005ed1..de9b50a6f772 100644 --- a/crates/wasi/src/preview2/table.rs +++ b/crates/wasi/src/preview2/table.rs @@ -174,6 +174,15 @@ impl Table { } } + /// Get a mutable reference to the underlying untyped cell for an entry in the table. + pub fn get_any_mut(&mut self, key: u32) -> Result<&mut dyn Any, TableError> { + if let Some(r) = self.map.get_mut(&key) { + Ok(&mut *r.entry) + } else { + Err(TableError::NotPresent) + } + } + /// Get an immutable reference to a resource of a given type at a given index. Multiple /// immutable references can be borrowed at any given time. Borrow failure /// results in a trapping error. @@ -203,7 +212,7 @@ impl Table { /// remove or replace the entry based on its contents. The methods available are a subset of /// [`std::collections::hash_map::OccupiedEntry`] - it does not give access to the key, it /// restricts replacing the entry to items of the same type, and it does not allow for deletion. - pub fn entry(&mut self, index: u32) -> Result { + pub fn entry<'a>(&'a mut self, index: u32) -> Result, TableError> { if self.map.contains_key(&index) { Ok(OccupiedEntry { table: self, index }) } else { diff --git a/crates/wasi/src/preview2/write_stream.rs b/crates/wasi/src/preview2/write_stream.rs new file mode 100644 index 000000000000..bf154aec0720 --- /dev/null +++ b/crates/wasi/src/preview2/write_stream.rs @@ -0,0 +1,196 @@ +use crate::preview2::{HostOutputStream, OutputStreamError}; +use anyhow::anyhow; +use bytes::Bytes; +use std::sync::{Arc, Mutex}; + +#[derive(Debug)] +struct WorkerState { + alive: bool, + items: std::collections::VecDeque, + write_budget: usize, + flush_pending: bool, + error: Option, +} + +impl WorkerState { + fn check_error(&mut self) -> Result<(), OutputStreamError> { + if let Some(e) = self.error.take() { + return Err(OutputStreamError::LastOperationFailed(e)); + } + if !self.alive { + return Err(OutputStreamError::Closed); + } + Ok(()) + } +} + +struct Worker { + state: Mutex, + new_work: tokio::sync::Notify, + write_ready_changed: tokio::sync::Notify, +} + +enum Job { + Flush, + Write(Bytes), +} + +enum WriteStatus<'a> { + Done(Result), + Pending(tokio::sync::futures::Notified<'a>), +} + +impl Worker { + fn new(write_budget: usize) -> Self { + Self { + state: Mutex::new(WorkerState { + alive: true, + items: std::collections::VecDeque::new(), + write_budget, + flush_pending: false, + error: None, + }), + new_work: tokio::sync::Notify::new(), + write_ready_changed: tokio::sync::Notify::new(), + } + } + fn check_write(&self) -> WriteStatus<'_> { + let mut state = self.state(); + if let Err(e) = state.check_error() { + return WriteStatus::Done(Err(e)); + } + + if state.flush_pending || state.write_budget == 0 { + return WriteStatus::Pending(self.write_ready_changed.notified()); + } + + WriteStatus::Done(Ok(state.write_budget)) + } + fn state(&self) -> std::sync::MutexGuard { + self.state.lock().unwrap() + } + fn pop(&self) -> Option { + let mut state = self.state(); + if state.items.is_empty() { + if state.flush_pending { + return Some(Job::Flush); + } + } else if let Some(bytes) = state.items.pop_front() { + return Some(Job::Write(bytes)); + } + + None + } + fn report_error(&self, e: std::io::Error) { + { + let mut state = self.state(); + state.alive = false; + state.error = Some(e.into()); + state.flush_pending = false; + } + self.write_ready_changed.notify_waiters(); + } + async fn work(&self, mut writer: T) { + use tokio::io::AsyncWriteExt; + loop { + let notified = self.new_work.notified(); + while let Some(job) = self.pop() { + match job { + Job::Flush => { + if let Err(e) = writer.flush().await { + self.report_error(e); + return; + } + + tracing::debug!("worker marking flush complete"); + self.state().flush_pending = false; + } + + Job::Write(mut bytes) => { + tracing::debug!("worker writing: {bytes:?}"); + let len = bytes.len(); + match writer.write_all_buf(&mut bytes).await { + Err(e) => { + self.report_error(e); + return; + } + Ok(_) => { + self.state().write_budget += len; + } + } + } + } + + self.write_ready_changed.notify_waiters(); + } + + notified.await; + } + } +} + +/// Provides a [`HostOutputStream`] impl from a [`tokio::io::AsyncWrite`] impl +pub struct AsyncWriteStream { + worker: Arc, + _join_handle: crate::preview2::AbortOnDropJoinHandle<()>, +} + +impl AsyncWriteStream { + /// Create a [`AsyncWriteStream`]. In order to use the [`HostOutputStream`] impl + /// provided by this struct, the argument must impl [`tokio::io::AsyncWrite`]. + pub fn new( + write_budget: usize, + writer: T, + ) -> Self { + let worker = Arc::new(Worker::new(write_budget)); + + let w = Arc::clone(&worker); + let join_handle = crate::preview2::spawn(async move { w.work(writer).await }); + + AsyncWriteStream { + worker, + _join_handle: join_handle, + } + } +} + +#[async_trait::async_trait] +impl HostOutputStream for AsyncWriteStream { + fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError> { + let mut state = self.worker.state(); + state.check_error()?; + if state.flush_pending { + return Err(OutputStreamError::Trap(anyhow!( + "write not permitted while flush pending" + ))); + } + match state.write_budget.checked_sub(bytes.len()) { + Some(remaining_budget) => { + state.write_budget = remaining_budget; + state.items.push_back(bytes); + } + None => return Err(OutputStreamError::Trap(anyhow!("write exceeded budget"))), + } + drop(state); + self.worker.new_work.notify_waiters(); + Ok(()) + } + fn flush(&mut self) -> Result<(), OutputStreamError> { + let mut state = self.worker.state(); + state.check_error()?; + + state.flush_pending = true; + self.worker.new_work.notify_waiters(); + + Ok(()) + } + + async fn write_ready(&mut self) -> Result { + loop { + match self.worker.check_write() { + WriteStatus::Done(r) => return r, + WriteStatus::Pending(notifier) => notifier.await, + } + } + } +} diff --git a/crates/wasi/wit/deps/http/incoming-handler.wit b/crates/wasi/wit/deps/http/incoming-handler.wit index d0e270465593..ad8a43f8ccf0 100644 --- a/crates/wasi/wit/deps/http/incoming-handler.wit +++ b/crates/wasi/wit/deps/http/incoming-handler.wit @@ -12,13 +12,13 @@ interface incoming-handler { // The `handle` function takes an outparam instead of returning its response // so that the component may stream its response while streaming any other // request or response bodies. The callee MUST write a response to the - // `response-out` and then finish the response before returning. The `handle` + // `response-outparam` and then finish the response before returning. The `handle` // function is allowed to continue execution after finishing the response's // output stream. While this post-response execution is taken off the // critical path, since there is no return value, there is no way to report // its success or failure. handle: func( - request: incoming-request, - response-out: response-outparam + request: /* own */ incoming-request, + response-out: /* own */ response-outparam ) } diff --git a/crates/wasi/wit/deps/http/outgoing-handler.wit b/crates/wasi/wit/deps/http/outgoing-handler.wit index 06c8e469f95b..3e03327d742b 100644 --- a/crates/wasi/wit/deps/http/outgoing-handler.wit +++ b/crates/wasi/wit/deps/http/outgoing-handler.wit @@ -8,11 +8,20 @@ interface outgoing-handler { use types.{outgoing-request, request-options, future-incoming-response} + // FIXME: we would want to use the types.error here but there is a + // wasmtime-wit-bindgen bug that prevents us from using the same error in + // the two different interfaces, right now... + variant error { + invalid(string) + } + // The parameter and result types of the `handle` function allow the caller // to concurrently stream the bodies of the outgoing request and the incoming // response. + // Consumes the outgoing-request. Gives an error if the outgoing-request + // is invalid or cannot be satisfied by this handler. handle: func( - request: outgoing-request, + request: /* own */ outgoing-request, options: option - ) -> future-incoming-response + ) -> result } diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index 7b7b015529c0..821e15f96213 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -41,30 +41,28 @@ interface types { // fields = u32` type alias can be replaced by a proper `resource fields` // definition containing all the functions using the method syntactic sugar. type fields = u32 - drop-fields: func(fields: fields) - new-fields: func(entries: list>) -> fields - fields-get: func(fields: fields, name: string) -> list> - fields-set: func(fields: fields, name: string, value: list>) - fields-delete: func(fields: fields, name: string) - fields-append: func(fields: fields, name: string, value: list) - fields-entries: func(fields: fields) -> list>> - fields-clone: func(fields: fields) -> fields + drop-fields: func(fields: /* own */ fields) + // Multiple values for a header are multiple entries in the list with the + // same key. + new-fields: func(entries: list>>) -> fields + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + fields-get: func(fields: /* borrow */ fields, name: string) -> list> + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + fields-set: func(fields: /* borrow */ fields, name: string, value: list>) + fields-delete: func(fields: /* borrow */ fields, name: string) + fields-append: func(fields: /* borrow */ fields, name: string, value: list) + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + fields-entries: func(fields: /* borrow */ fields) -> list>> + // Deep copy of all contents in a fields. + fields-clone: func(fields: /* borrow */ fields) -> fields type headers = fields type trailers = fields - // The following block defines stream types which corresponds to the HTTP - // standard Contents and Trailers. With Preview3, all of these fields can be - // replaced by a stream>. In the interim, we need to - // build on separate resource types defined by `wasi:io/streams`. The - // `finish-` functions emulate the stream's result value and MUST be called - // exactly once after the final read/write from/to the stream before dropping - // the stream. - type incoming-stream = input-stream - type outgoing-stream = output-stream - finish-incoming-stream: func(s: incoming-stream) -> option - finish-outgoing-stream: func(s: outgoing-stream, trailers: option) - // The following block defines the `incoming-request` and `outgoing-request` // resource types that correspond to HTTP standard Requests. Soon, when // resource types are added, the `u32` type aliases can be replaced by @@ -74,23 +72,30 @@ interface types { // above). The `consume` and `write` methods may only be called once (and // return failure thereafter). type incoming-request = u32 + drop-incoming-request: func(request: /* own */ incoming-request) + incoming-request-method: func(request: /* borrow */ incoming-request) -> method + incoming-request-path-with-query: func(request: /* borrow */ incoming-request) -> option + incoming-request-scheme: func(request: /* borrow */ incoming-request) -> option + incoming-request-authority: func(request: /* borrow */ incoming-request) -> option + + incoming-request-headers: func(request: /* borrow */ incoming-request) -> /* child */ headers + // Will return the input-stream child at most once. If called more than + // once, subsequent calls will return error. + incoming-request-consume: func(request: /* borrow */ incoming-request) -> result< /* child */ input-stream> + type outgoing-request = u32 - drop-incoming-request: func(request: incoming-request) - drop-outgoing-request: func(request: outgoing-request) - incoming-request-method: func(request: incoming-request) -> method - incoming-request-path-with-query: func(request: incoming-request) -> option - incoming-request-scheme: func(request: incoming-request) -> option - incoming-request-authority: func(request: incoming-request) -> option - incoming-request-headers: func(request: incoming-request) -> headers - incoming-request-consume: func(request: incoming-request) -> result + drop-outgoing-request: func(request: /* own */ outgoing-request) new-outgoing-request: func( method: method, path-with-query: option, scheme: option, authority: option, - headers: headers + headers: /* borrow */ headers ) -> outgoing-request - outgoing-request-write: func(request: outgoing-request) -> result + + // Will return the outgoing-body child at most once. If called more than + // once, subsequent calls will return error. + outgoing-request-write: func(request: /* borrow */ outgoing-request) -> result< /* child */ outgoing-body> // Additional optional parameters that can be set when making a request. record request-options { @@ -115,8 +120,8 @@ interface types { // (the `wasi:http/handler` interface used for both incoming and outgoing can // simply return a `stream`). type response-outparam = u32 - drop-response-outparam: func(response: response-outparam) - set-response-outparam: func(param: response-outparam, response: result) -> result + drop-response-outparam: func(response: /* own */ response-outparam) + set-response-outparam: func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) // This type corresponds to the HTTP standard Status Code. type status-code = u16 @@ -129,27 +134,72 @@ interface types { // type (that uses the single `stream` type mentioned above). The `consume` and // `write` methods may only be called once (and return failure thereafter). type incoming-response = u32 + drop-incoming-response: func(response: /* own */ incoming-response) + incoming-response-status: func(response: /* borrow */ incoming-response) -> status-code + incoming-response-headers: func(response: /* borrow */ incoming-response) -> /* child */ headers + // May be called at most once. returns error if called additional times. + // TODO: make incoming-request-consume work the same way, giving a child + // incoming-body. + incoming-response-consume: func(response: /* borrow */ incoming-response) -> result + + type incoming-body = u32 + drop-incoming-body: func(this: /* own */ incoming-body) + + // returned input-stream is a child - the implementation may trap if + // incoming-body is dropped (or consumed by call to + // incoming-body-finish) before the input-stream is dropped. + // May be called at most once. returns error if called additional times. + incoming-body-stream: func(this: /* borrow */ incoming-body) -> + result + // takes ownership of incoming-body. this will trap if the + // incoming-body-stream child is still alive! + incoming-body-finish: func(this: /* own */ incoming-body) -> + /* transitive child of the incoming-response of incoming-body */ future-trailers + + type future-trailers = u32 + drop-future-trailers: func(this: /* own */ future-trailers) + /// Pollable that resolves when the body has been fully read, and the trailers + /// are ready to be consumed. + future-trailers-subscribe: func(this: /* borrow */ future-trailers) -> /* child */ pollable + + /// Retrieve reference to trailers, if they are ready. + future-trailers-get: func(response: /* borrow */ future-trailers) -> option> + type outgoing-response = u32 - drop-incoming-response: func(response: incoming-response) - drop-outgoing-response: func(response: outgoing-response) - incoming-response-status: func(response: incoming-response) -> status-code - incoming-response-headers: func(response: incoming-response) -> headers - incoming-response-consume: func(response: incoming-response) -> result + drop-outgoing-response: func(response: /* own */ outgoing-response) new-outgoing-response: func( status-code: status-code, - headers: headers + headers: /* borrow */ headers ) -> outgoing-response - outgoing-response-write: func(response: outgoing-response) -> result - // The following block defines a special resource type used by the - // `wasi:http/outgoing-handler` interface to emulate - // `future>` in advance of Preview3. Given a - // `future-incoming-response`, the client can call the non-blocking `get` - // method to get the result if it is available. If the result is not available, - // the client can call `listen` to get a `pollable` that can be passed to - // `io.poll.poll-oneoff`. + /// Will give the child outgoing-response at most once. subsequent calls will + /// return an error. + outgoing-response-write: func(this: /* borrow */ outgoing-response) -> result + + type outgoing-body = u32 + drop-outgoing-body: func(this: /* own */ outgoing-body) + /// Will give the child output-stream at most once. subsequent calls will + /// return an error. + outgoing-body-write: func(this: /* borrow */ outgoing-body) -> result + /// Write trailers as the way to finish an outgoing-body. To finish an + /// outgoing-body without writing trailers, use drop-outgoing-body. + outgoing-body-write-trailers: func(this: /* own */ outgoing-body, trailers: /* own */ trailers) + + /// The following block defines a special resource type used by the + /// `wasi:http/outgoing-handler` interface to emulate + /// `future>` in advance of Preview3. Given a + /// `future-incoming-response`, the client can call the non-blocking `get` + /// method to get the result if it is available. If the result is not available, + /// the client can call `listen` to get a `pollable` that can be passed to + /// `io.poll.poll-oneoff`. type future-incoming-response = u32 - drop-future-incoming-response: func(f: future-incoming-response) - future-incoming-response-get: func(f: future-incoming-response) -> option> - listen-to-future-incoming-response: func(f: future-incoming-response) -> pollable + drop-future-incoming-response: func(f: /* own */ future-incoming-response) + /// option indicates readiness. + /// outer result indicates you are allowed to get the + /// incoming-response-or-error at most once. subsequent calls after ready + /// will return an error here. + /// inner result indicates whether the incoming-response was available, or an + /// error occured. + future-incoming-response-get: func(f: /* borrow */ future-incoming-response) -> option>> + listen-to-future-incoming-response: func(f: /* borrow */ future-incoming-response) -> /* child */ pollable } diff --git a/src/commands/run.rs b/src/commands/run.rs index 1c1bfe281799..01b04809b10b 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -848,22 +848,22 @@ impl RunCommand { } if self.common.wasi.http == Some(true) { - #[cfg(not(feature = "wasi-http"))] + #[cfg(not(all(feature = "wasi-http", feature = "component-model")))] { bail!("Cannot enable wasi-http when the binary is not compiled with this feature."); } - #[cfg(feature = "wasi-http")] + #[cfg(all(feature = "wasi-http", feature = "component-model"))] { match linker { - CliLinker::Core(linker) => { - wasmtime_wasi_http::sync::add_to_linker(linker)?; + CliLinker::Core(_) => { + bail!("Cannot enable wasi-http for core wasm modules"); } - #[cfg(feature = "component-model")] CliLinker::Component(linker) => { - wasmtime_wasi_http::proxy::sync::add_to_linker(linker)?; + wasmtime_wasi_http::proxy::add_to_linker(linker)?; } } - store.data_mut().wasi_http = Some(Arc::new(WasiHttpCtx::new())); + + store.data_mut().wasi_http = Some(Arc::new(WasiHttpCtx {})); } } @@ -998,13 +998,13 @@ impl preview2::preview1::WasiPreview1View for Host { #[cfg(feature = "wasi-http")] impl wasmtime_wasi_http::types::WasiHttpView for Host { - fn http_ctx(&self) -> &WasiHttpCtx { - self.wasi_http.as_ref().unwrap() + fn ctx(&mut self) -> &mut WasiHttpCtx { + let ctx = self.wasi_http.as_mut().unwrap(); + Arc::get_mut(ctx).expect("preview2 is not compatible with threads") } - fn http_ctx_mut(&mut self) -> &mut WasiHttpCtx { - let ctx = self.wasi_http.as_mut().unwrap(); - Arc::get_mut(ctx).expect("wasi-http is not compatible with threads") + fn table(&mut self) -> &mut preview2::Table { + Arc::get_mut(&mut self.preview2_table).expect("preview2 is not compatible with threads") } } diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs index de5f2cb06a03..4f7dd4968dc0 100644 --- a/tests/all/cli_tests.rs +++ b/tests/all/cli_tests.rs @@ -791,6 +791,7 @@ fn run_basic_component() -> Result<()> { } #[cfg(feature = "wasi-http")] +#[ignore = "needs to be ported to components"] #[test] fn run_wasi_http_module() -> Result<()> { let output = run_wasmtime_for_output( From 6aca67c15dc33beb89e929de598fad13b723950b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 21 Sep 2023 18:05:30 -0500 Subject: [PATCH 003/199] Support resource maps in `component::bindgen!` (#7069) * Support resource maps in `component::bindgen!` This commit adds support to `component::bindgen!` to specify resource types using the `with` key of the macro. This can be used to configure the `T` of `Resource` to use a preexisting type rather than unconditionally generating a new empty enum to have a fresh type. * Reenable tests --- crates/component-macro/tests/codegen.rs | 82 +++++++++++++++++++++++++ crates/wasmtime/src/component/mod.rs | 18 +++--- crates/wit-bindgen/src/lib.rs | 59 ++++++++++++------ 3 files changed, 132 insertions(+), 27 deletions(-) diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs index 216d03e52062..90d58bd890f2 100644 --- a/crates/component-macro/tests/codegen.rs +++ b/crates/component-macro/tests/codegen.rs @@ -24,3 +24,85 @@ macro_rules! gentest { } component_macro_test_helpers::foreach!(gentest); + +mod with_key_and_resources { + use anyhow::Result; + use wasmtime::component::Resource; + + wasmtime::component::bindgen!({ + inline: " + package demo:pkg + + interface bar { + resource a + resource b + } + + world foo { + resource a + resource b + + import foo: interface { + resource a + resource b + } + + import bar + } + ", + with: { + "a": MyA, + "b": MyA, + "foo/a": MyA, + "foo/b": MyA, + "demo:pkg/bar/a": MyA, + "demo:pkg/bar/b": MyA, + }, + }); + + pub struct MyA; + + struct MyComponent; + + impl FooImports for MyComponent {} + + impl HostA for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } + + impl HostB for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } + + impl foo::Host for MyComponent {} + + impl foo::HostA for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } + + impl foo::HostB for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } + + impl demo::pkg::bar::Host for MyComponent {} + + impl demo::pkg::bar::HostA for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } + + impl demo::pkg::bar::HostB for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } +} diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs index 618f829b2540..73aaad8e1cfc 100644 --- a/crates/wasmtime/src/component/mod.rs +++ b/crates/wasmtime/src/component/mod.rs @@ -326,16 +326,20 @@ pub(crate) use self::store::ComponentStoreData; /// // Restrict the code generated to what's needed for the interface /// // imports in the inlined WIT document fragment. /// interfaces: " -/// import package.foo +/// import wasi:cli/command /// ", /// -/// // Remap interface names to module names, imported from elsewhere. -/// // Using this option will prevent any code from being generated -/// // for the names mentioned in the mapping, assuming instead that the -/// // names mentioned come from a previous use of the `bindgen!` macro -/// // with `only_interfaces: true`. +/// // Remap imported interfaces or resources to types defined in Rust +/// // elsewhere. Using this option will prevent any code from being +/// // generated for interfaces mentioned here. Resources named here will +/// // not have a type generated to represent the resource. +/// // +/// // Interfaces mapped with this option should be previously generated +/// // with an invocation of this macro. Resources need to be mapped to a +/// // Rust type name. /// with: { -/// "a": somewhere::else::a, +/// "wasi:random/random": some::other::wasi::random::random, +/// "wasi:filesystem/types/descriptor": MyDescriptorType, /// }, /// }); /// ``` diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index 2a0d6d68b8aa..45fe6aedf3b1 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -912,16 +912,28 @@ impl<'a> InterfaceGenerator<'a> { self.assert_type(id, &name); } - fn type_resource(&mut self, id: TypeId, _name: &str, resource: &TypeDef, docs: &Docs) { - let camel = resource - .name - .as_ref() - .expect("resources are required to be named") - .to_upper_camel_case(); + fn type_resource(&mut self, id: TypeId, name: &str, resource: &TypeDef, docs: &Docs) { + let camel = name.to_upper_camel_case(); if self.types_imported() { self.rustdoc(docs); - uwriteln!(self.src, "pub enum {camel} {{}}"); + + let with_key = match self.current_interface { + Some((_, key, _)) => format!("{}/{name}", self.resolve.name_world_key(key)), + None => name.to_string(), + }; + match self.gen.opts.with.get(&with_key) { + Some(path) => { + uwriteln!( + self.src, + "pub use {}{path} as {camel};", + self.path_to_root() + ); + } + None => { + uwriteln!(self.src, "pub enum {camel} {{}}"); + } + } if self.gen.opts.async_.maybe_async() { uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]") @@ -1913,6 +1925,24 @@ impl<'a> InterfaceGenerator<'a> { self.push_str("\n"); } } + + fn path_to_root(&self) -> String { + let mut path_to_root = String::new(); + if let Some((_, key, is_export)) = self.current_interface { + match key { + WorldKey::Name(_) => { + path_to_root.push_str("super::"); + } + WorldKey::Interface(_) => { + path_to_root.push_str("super::super::super::"); + } + } + if is_export { + path_to_root.push_str("super::"); + } + } + path_to_root + } } impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { @@ -1925,23 +1955,12 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { } fn path_to_interface(&self, interface: InterfaceId) -> Option { - let mut path_to_root = String::new(); - if let Some((cur, key, is_export)) = self.current_interface { + if let Some((cur, _, _)) = self.current_interface { if cur == interface { return None; } - match key { - WorldKey::Name(_) => { - path_to_root.push_str("super::"); - } - WorldKey::Interface(_) => { - path_to_root.push_str("super::super::super::"); - } - } - if is_export { - path_to_root.push_str("super::"); - } } + let mut path_to_root = self.path_to_root(); let InterfaceName { path, .. } = &self.gen.interface_names[&interface]; path_to_root.push_str(path); Some(path_to_root) From 428ab601c437af8d37e615c539c420f4b11bdcf9 Mon Sep 17 00:00:00 2001 From: Rainy Sinclair <844493+itsrainy@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:02:24 -0500 Subject: [PATCH 004/199] Include Wasm-defined globals and memories in core dumps (#6935) * Include Wasm-defined globals and memories in core dumps Co-authored-by: Nick Fitzgerald * Narrow scope of unsafe block * Add missing skip-miri attribute for test that calls into Wasm --------- Co-authored-by: Nick Fitzgerald --- crates/runtime/src/instance.rs | 45 ++++++ crates/wasmtime/src/coredump.rs | 40 ++--- crates/wasmtime/src/externals.rs | 5 - crates/wasmtime/src/instance.rs | 14 +- crates/wasmtime/src/memory.rs | 6 +- crates/wasmtime/src/store.rs | 99 +++++++++--- crates/wasmtime/src/store/data.rs | 6 + crates/wasmtime/src/trampoline.rs | 2 +- crates/wasmtime/src/trampoline/global.rs | 4 +- crates/wasmtime/src/trampoline/memory.rs | 2 +- crates/wasmtime/src/trap.rs | 2 +- crates/wasmtime/src/types.rs | 9 ++ tests/all/coredump.rs | 196 ++++++++++++++++++----- 13 files changed, 329 insertions(+), 101 deletions(-) diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index a43bf11ad7c4..29c91450501e 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -350,6 +350,15 @@ impl Instance { unsafe { *self.vmctx_plus_offset(self.offsets().vmctx_vmmemory_pointer(index)) } } + /// Return the memories defined within this instance (not imported). + pub fn defined_memories<'a>( + &'a self, + ) -> impl ExactSizeIterator + 'a { + self.memories + .iter() + .map(|(index, (_alloc_index, memory))| (index, memory)) + } + /// Return the indexed `VMGlobalDefinition`. fn global(&mut self, index: DefinedGlobalIndex) -> &VMGlobalDefinition { unsafe { &*self.global_ptr(index) } @@ -375,6 +384,25 @@ impl Instance { } } + /// Get the globals defined in this instance (not imported). + pub fn defined_globals<'a>( + &'a mut self, + ) -> impl ExactSizeIterator + 'a { + let module = self.module().clone(); + module + .globals + .keys() + .skip(module.num_imported_globals) + .map(move |global_idx| { + let def_idx = module.defined_global_index(global_idx).unwrap(); + let global = ExportGlobal { + definition: self.global_ptr(def_idx), + global: self.module().globals[global_idx], + }; + (def_idx, global) + }) + } + /// Return a pointer to the interrupts structure pub fn runtime_limits(&mut self) -> *mut *const VMRuntimeLimits { unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_runtime_limits()) } @@ -1322,6 +1350,23 @@ impl InstanceHandle { self.instance_mut().get_table_with_lazy_init(index, range) } + /// Return the memories defined in this instance (not imported). + pub fn defined_memories<'a>(&'a mut self) -> impl ExactSizeIterator + 'a { + let idxs = (0..self.module().memory_plans.len()) + .skip(self.module().num_imported_memories) + .map(|i| MemoryIndex::new(i)) + .collect::>(); + idxs.into_iter() + .map(|memidx| self.get_exported_memory(memidx)) + } + + /// Get the globals defined in this instance (not imported). + pub fn defined_globals<'a>( + &'a mut self, + ) -> impl ExactSizeIterator + 'a { + self.instance_mut().defined_globals() + } + /// Return a reference to the contained `Instance`. #[inline] pub(crate) fn instance(&self) -> &Instance { diff --git a/crates/wasmtime/src/coredump.rs b/crates/wasmtime/src/coredump.rs index d7047c5102dc..ad4d22363eba 100644 --- a/crates/wasmtime/src/coredump.rs +++ b/crates/wasmtime/src/coredump.rs @@ -28,13 +28,13 @@ pub struct WasmCoreDump { name: String, modules: Vec, instances: Vec, - store_memories: Vec, - store_globals: Vec, + memories: Vec, + globals: Vec, backtrace: WasmBacktrace, } impl WasmCoreDump { - pub(crate) fn new(store: &StoreOpaque, backtrace: WasmBacktrace) -> WasmCoreDump { + pub(crate) fn new(store: &mut StoreOpaque, backtrace: WasmBacktrace) -> WasmCoreDump { let modules: Vec<_> = store.modules().all_modules().cloned().collect(); let instances: Vec = store.all_instances().collect(); let store_memories: Vec = store.all_memories().collect(); @@ -44,37 +44,41 @@ impl WasmCoreDump { name: String::from("store_name"), modules, instances, - store_memories, - store_globals, + memories: store_memories, + globals: store_globals, backtrace, } } - /// The stack frames for the CoreDump + /// The stack frames for this core dump. + /// + /// Frames appear in callee to caller order, that is youngest to oldest + /// frames. pub fn frames(&self) -> &[FrameInfo] { self.backtrace.frames() } - /// The names of the modules involved in the CoreDump + /// All modules instantiated inside the store when the core dump was + /// created. pub fn modules(&self) -> &[Module] { self.modules.as_ref() } - /// The instances involved in the CoreDump + /// All instances within the store when the core dump was created. pub fn instances(&self) -> &[Instance] { self.instances.as_ref() } - /// The imported globals that belong to the store, rather than a specific - /// instance - pub fn store_globals(&self) -> &[Global] { - self.store_globals.as_ref() + /// All globals, instance- or host-defined, within the store when the core + /// dump was created. + pub fn globals(&self) -> &[Global] { + self.globals.as_ref() } - /// The imported memories that belong to the store, rather than a specific - /// instance. - pub fn store_memories(&self) -> &[Memory] { - self.store_memories.as_ref() + /// All memories, instance- or host-defined, within the store when the core + /// dump was created. + pub fn memories(&self) -> &[Memory] { + self.memories.as_ref() } } @@ -92,12 +96,12 @@ impl fmt::Display for WasmCoreDump { } writeln!(f, "memories:")?; - for memory in self.store_memories.iter() { + for memory in self.memories.iter() { writeln!(f, " {:?}", memory)?; } writeln!(f, "globals:")?; - for global in self.store_globals.iter() { + for global in self.globals.iter() { writeln!(f, " {:?}", global)?; } diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs index 1757a688720f..a43b90338a91 100644 --- a/crates/wasmtime/src/externals.rs +++ b/crates/wasmtime/src/externals.rs @@ -5,7 +5,6 @@ use crate::{ SharedMemory, TableType, Val, ValType, }; use anyhow::{anyhow, bail, Result}; -use runtime::ExportGlobal; use std::mem; use std::ptr; use wasmtime_runtime::{self as runtime}; @@ -249,10 +248,6 @@ impl Global { } } - pub(crate) fn from_stored(stored: Stored) -> Global { - Global(stored) - } - /// Returns the underlying type of this `global`. /// /// # Panics diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index 1489903012ab..95a0d4d3c0cd 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -42,6 +42,15 @@ pub(crate) struct InstanceData { exports: Vec>, } +impl InstanceData { + pub fn from_id(id: InstanceId) -> InstanceData { + InstanceData { + id, + exports: vec![], + } + } +} + impl Instance { /// Creates a new [`Instance`] from the previously compiled [`Module`] and /// list of `imports` specified. @@ -289,7 +298,7 @@ impl Instance { // conflicts with the borrow on `store.engine`) but this doesn't // matter in practice since initialization isn't even running any // code here anyway. - let id = store.add_instance(instance_handle.clone(), false); + let id = store.add_instance(instance_handle.clone()); // Additionally, before we start doing fallible instantiation, we // do one more step which is to insert an `InstanceData` @@ -330,9 +339,6 @@ impl Instance { Ok((instance, compiled_module.module().start_func)) } - pub(crate) fn from_stored(stored: Stored) -> Instance { - Instance(stored) - } pub(crate) fn from_wasmtime(handle: InstanceData, store: &mut StoreOpaque) -> Instance { Instance(store.store_data_mut().insert(handle)) diff --git a/crates/wasmtime/src/memory.rs b/crates/wasmtime/src/memory.rs index 7548091005c9..c473fa143dd1 100644 --- a/crates/wasmtime/src/memory.rs +++ b/crates/wasmtime/src/memory.rs @@ -9,7 +9,7 @@ use std::ops::Range; use std::slice; use std::time::Instant; use wasmtime_environ::MemoryPlan; -use wasmtime_runtime::{ExportMemory, RuntimeLinearMemory, VMMemoryImport}; +use wasmtime_runtime::{RuntimeLinearMemory, VMMemoryImport}; pub use wasmtime_runtime::WaitResult; @@ -271,10 +271,6 @@ impl Memory { } } - pub(crate) fn from_stored(stored: Stored) -> Memory { - Memory(stored) - } - /// Returns the underlying type of this memory. /// /// # Panics diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index 0f78c579c7d1..88d7af66152f 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -96,9 +96,9 @@ use std::sync::atomic::AtomicU64; use std::sync::Arc; use std::task::{Context, Poll}; use wasmtime_runtime::{ - ExportGlobal, ExportMemory, InstanceAllocationRequest, InstanceAllocator, InstanceHandle, - ModuleInfo, OnDemandInstanceAllocator, SignalHandler, StoreBox, StorePtr, VMContext, - VMExternRef, VMExternRefActivationsTable, VMFuncRef, VMRuntimeLimits, WasmFault, + ExportGlobal, InstanceAllocationRequest, InstanceAllocator, InstanceHandle, ModuleInfo, + OnDemandInstanceAllocator, SignalHandler, StoreBox, StorePtr, VMContext, VMExternRef, + VMExternRefActivationsTable, VMFuncRef, VMRuntimeLimits, WasmFault, }; mod context; @@ -433,8 +433,14 @@ where /// instance allocator. struct StoreInstance { handle: InstanceHandle, - // Stores whether or not to use the on-demand allocator to deallocate the instance - ondemand: bool, + + /// Whether or not this is a dummy instance that is just an implementation + /// detail for something else. For example, host-created memories internally + /// create a dummy instance. + /// + /// Regardless of the configured instance allocator for this engine, dummy + /// instances always use the on-demand allocator to deallocate the instance. + dummy: bool, } #[derive(Copy, Clone)] @@ -1247,10 +1253,23 @@ impl StoreOpaque { &mut self.host_globals } - pub unsafe fn add_instance(&mut self, handle: InstanceHandle, ondemand: bool) -> InstanceId { + pub unsafe fn add_instance(&mut self, handle: InstanceHandle) -> InstanceId { + self.instances.push(StoreInstance { + handle: handle.clone(), + dummy: false, + }); + InstanceId(self.instances.len() - 1) + } + + /// Add a dummy instance that to the store. + /// + /// These are instances that are just implementation details of something + /// else (e.g. host-created memories that are not actually defined in any + /// Wasm module) and therefore shouldn't show up in things like core dumps. + pub unsafe fn add_dummy_instance(&mut self, handle: InstanceHandle) -> InstanceId { self.instances.push(StoreInstance { handle: handle.clone(), - ondemand, + dummy: true, }); InstanceId(self.instances.len() - 1) } @@ -1263,22 +1282,62 @@ impl StoreOpaque { &mut self.instances[id.0].handle } - pub fn all_instances(&self) -> impl ExactSizeIterator { - self.store_data() - .iter::() - .map(Instance::from_stored) + /// Get all instances (ignoring dummy instances) within this store. + pub fn all_instances<'a>(&'a mut self) -> impl ExactSizeIterator + 'a { + let instances = self + .instances + .iter() + .enumerate() + .filter_map(|(idx, inst)| { + let id = InstanceId::from_index(idx); + if inst.dummy { + None + } else { + Some(InstanceData::from_id(id)) + } + }) + .collect::>(); + instances + .into_iter() + .map(|i| Instance::from_wasmtime(i, self)) } - pub fn all_memories(&self) -> impl ExactSizeIterator { - self.store_data() - .iter::() - .map(Memory::from_stored) + /// Get all memories (host- or Wasm-defined) within this store. + pub fn all_memories<'a>(&'a mut self) -> impl Iterator + 'a { + let mems = self + .instances + .iter_mut() + .flat_map(|instance| instance.handle.defined_memories()) + .collect::>(); + mems.into_iter() + .map(|memory| unsafe { Memory::from_wasmtime_memory(memory, self) }) } - pub fn all_globals(&self) -> impl ExactSizeIterator { - self.store_data() - .iter::() - .map(Global::from_stored) + /// Iterate over all globals (host- or Wasm-defined) within this store. + pub fn all_globals<'a>(&'a mut self) -> impl Iterator + 'a { + unsafe { + // First gather all the host-created globals. + let mut globals = self + .host_globals() + .iter() + .map(|global| ExportGlobal { + definition: &mut (*global.get()).global as *mut _, + global: (*global.get()).ty.to_wasm_type(), + }) + .collect::>(); + + // Then iterate over all instances and yield each of their defined + // globals. + globals.extend( + self.instances.iter_mut().flat_map(|instance| { + instance.handle.defined_globals().map(|(_i, global)| global) + }), + ); + + globals + .into_iter() + .map(|g| Global::from_wasmtime_global(g, self)) + } } #[cfg_attr(not(target_os = "linux"), allow(dead_code))] // not used on all platforms @@ -2207,7 +2266,7 @@ impl Drop for StoreOpaque { let allocator = self.engine.allocator(); let ondemand = OnDemandInstanceAllocator::default(); for instance in self.instances.iter_mut() { - if instance.ondemand { + if instance.dummy { ondemand.deallocate_module(&mut instance.handle); } else { allocator.deallocate_module(&mut instance.handle); diff --git a/crates/wasmtime/src/store/data.rs b/crates/wasmtime/src/store/data.rs index 08764d293258..390374985f90 100644 --- a/crates/wasmtime/src/store/data.rs +++ b/crates/wasmtime/src/store/data.rs @@ -13,6 +13,12 @@ use std::sync::atomic::{AtomicU64, Ordering::Relaxed}; #[derive(Copy, Clone)] pub struct InstanceId(pub(super) usize); +impl InstanceId { + pub fn from_index(idx: usize) -> InstanceId { + InstanceId(idx) + } +} + pub struct StoreData { id: StoreId, funcs: Vec, diff --git a/crates/wasmtime/src/trampoline.rs b/crates/wasmtime/src/trampoline.rs index 928cc2cc2974..3822f6027777 100644 --- a/crates/wasmtime/src/trampoline.rs +++ b/crates/wasmtime/src/trampoline.rs @@ -50,7 +50,7 @@ fn create_handle( wmemcheck: false, })?; - Ok(store.add_instance(handle, true)) + Ok(store.add_dummy_instance(handle)) } } diff --git a/crates/wasmtime/src/trampoline/global.rs b/crates/wasmtime/src/trampoline/global.rs index 2aa49a6bd1c6..263e91b2e34f 100644 --- a/crates/wasmtime/src/trampoline/global.rs +++ b/crates/wasmtime/src/trampoline/global.rs @@ -5,8 +5,8 @@ use wasmtime_runtime::{StoreBox, VMGlobalDefinition}; #[repr(C)] pub struct VMHostGlobalContext { - ty: GlobalType, - global: VMGlobalDefinition, + pub(crate) ty: GlobalType, + pub(crate) global: VMGlobalDefinition, } impl Drop for VMHostGlobalContext { diff --git a/crates/wasmtime/src/trampoline/memory.rs b/crates/wasmtime/src/trampoline/memory.rs index a4ef32097960..bc1423991783 100644 --- a/crates/wasmtime/src/trampoline/memory.rs +++ b/crates/wasmtime/src/trampoline/memory.rs @@ -71,7 +71,7 @@ pub fn create_memory( ondemand: OnDemandInstanceAllocator::default(), } .allocate_module(request)?; - let instance_id = store.add_instance(handle.clone(), true); + let instance_id = store.add_dummy_instance(handle.clone()); Ok(instance_id) } } diff --git a/crates/wasmtime/src/trap.rs b/crates/wasmtime/src/trap.rs index e62ba73b1f46..1d0437aa1b28 100644 --- a/crates/wasmtime/src/trap.rs +++ b/crates/wasmtime/src/trap.rs @@ -77,7 +77,7 @@ pub(crate) unsafe fn raise(error: anyhow::Error) -> ! { #[cold] // traps are exceptional, this helps move handling off the main path pub(crate) fn from_runtime_box( - store: &StoreOpaque, + store: &mut StoreOpaque, runtime_trap: Box, ) -> Error { let wasmtime_runtime::Trap { diff --git a/crates/wasmtime/src/types.rs b/crates/wasmtime/src/types.rs index b1d33709417a..98b3ca73a67b 100644 --- a/crates/wasmtime/src/types.rs +++ b/crates/wasmtime/src/types.rs @@ -272,6 +272,15 @@ impl GlobalType { self.mutability } + pub(crate) fn to_wasm_type(&self) -> Global { + let wasm_ty = self.content().to_wasm_type(); + let mutability = matches!(self.mutability(), Mutability::Var); + Global { + wasm_ty, + mutability, + } + } + /// Returns `None` if the wasmtime global has a type that we can't /// represent, but that should only very rarely happen and indicate a bug. pub(crate) fn from_wasmtime_global(global: &Global) -> GlobalType { diff --git a/tests/all/coredump.rs b/tests/all/coredump.rs index d372965ccef1..a7ee535f0235 100644 --- a/tests/all/coredump.rs +++ b/tests/all/coredump.rs @@ -3,17 +3,17 @@ use wasmtime::*; #[test] #[cfg_attr(miri, ignore)] -fn test_coredump_attached_to_error() -> Result<()> { +fn coredump_attached_to_error() -> Result<()> { let mut config = Config::default(); config.coredump_on_trap(true); let engine = Engine::new(&config).unwrap(); let mut store = Store::<()>::new(&engine, ()); let wat = r#" - (module - (func $hello (import "" "hello")) - (func (export "run") (call $hello)) - ) + (module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) + ) "#; let module = Module::new(store.engine(), wat)?; @@ -36,7 +36,7 @@ fn test_coredump_attached_to_error() -> Result<()> { #[test] #[cfg_attr(miri, ignore)] -fn test_coredump_has_stack() -> Result<()> { +fn coredump_has_stack() -> Result<()> { let mut config = Config::default(); config.coredump_on_trap(true); let engine = Engine::new(&config).unwrap(); @@ -44,15 +44,15 @@ fn test_coredump_has_stack() -> Result<()> { let wat = r#" (module - (func $a (export "a") - call $b - ) - (func $b - call $c - ) - (func $c - unreachable - ) + (func $a (export "a") + call $b + ) + (func $b + call $c + ) + (func $c + unreachable + ) ) "#; @@ -71,7 +71,7 @@ fn test_coredump_has_stack() -> Result<()> { #[test] #[cfg_attr(miri, ignore)] -fn test_coredump_has_modules_and_instances() -> Result<()> { +fn coredump_has_modules_and_instances() -> Result<()> { let mut config = Config::default(); config.coredump_on_trap(true); let engine = Engine::new(&config).unwrap(); @@ -79,19 +79,19 @@ fn test_coredump_has_modules_and_instances() -> Result<()> { let mut store = Store::<()>::new(&engine, ()); let wat1 = r#" - (module $foo - (import "bar" "b" (func $b)) - (func (export "a") - call $b + (module $foo + (import "bar" "b" (func $b)) + (func (export "a") + call $b + ) ) - ) "#; let wat2 = r#" - (module $bar - (func (export "b") - unreachable + (module $bar + (func (export "b") + unreachable + ) ) - ) "#; let module1 = Module::new(store.engine(), wat1)?; let module2 = Module::new(store.engine(), wat2)?; @@ -110,38 +110,146 @@ fn test_coredump_has_modules_and_instances() -> Result<()> { #[test] #[cfg_attr(miri, ignore)] -fn test_coredump_has_import_globals_and_memory() -> Result<()> { +fn coredump_has_host_globals_and_memory() -> Result<()> { let mut config = Config::default(); config.coredump_on_trap(true); let engine = Engine::new(&config).unwrap(); + + let module = Module::new( + &engine, + r#" + (module + (import "memory" "memory" (memory 1)) + (global $myglobal (import "global" "global") (mut i32)) + (func (export "a") (result i32) + unreachable + ) + (export "memory" (memory 0)) + (export "global" (global 0)) + ) + "#, + )?; + let mut store = Store::<()>::new(&engine, ()); let mut linker = Linker::new(&engine); - let wat = r#" - (module - (import "memory" "memory" (memory 1)) - (global $myglobal (import "js" "global") (mut i32)) - (func (export "a") (result i32) - unreachable - ) - ) - "#; + let memory = Memory::new(&mut store, MemoryType::new(1, None))?; + linker.define(&mut store, "memory", "memory", memory)?; - let module = Module::new(store.engine(), wat)?; - let m = wasmtime::Memory::new(&mut store, MemoryType::new(1, None))?; - linker.define(&mut store, "memory", "memory", m)?; - let g = wasmtime::Global::new( + let global = Global::new( &mut store, GlobalType::new(ValType::I32, Mutability::Var), Val::I32(0), )?; - linker.define(&mut store, "js", "global", g)?; + linker.define(&mut store, "global", "global", global)?; + let instance = linker.instantiate(&mut store, &module)?; + + // Each time we extract the exports, it puts them in the `StoreData`. Our + // core dumps need to be robust to duplicate entries in the `StoreData`. + for _ in 0..10 { + let _ = instance.get_global(&mut store, "global").unwrap(); + let _ = instance.get_memory(&mut store, "memory").unwrap(); + } + let a_func = instance.get_typed_func::<(), i32>(&mut store, "a")?; - let e = a_func.call(&mut store, ()).unwrap_err(); - let cd = e.downcast_ref::().unwrap(); - assert_eq!(cd.store_globals().len(), 1); - assert_eq!(cd.store_memories().len(), 1); + let err = a_func.call(&mut store, ()).unwrap_err(); + let core_dump = err.downcast_ref::().unwrap(); + assert_eq!(core_dump.globals().len(), 1); + assert_eq!(core_dump.memories().len(), 1); + assert_eq!(core_dump.instances().len(), 1); + + Ok(()) +} + +#[test] +#[cfg_attr(miri, ignore)] +fn coredump_has_defined_globals_and_memory() -> Result<()> { + let mut config = Config::default(); + config.coredump_on_trap(true); + let engine = Engine::new(&config).unwrap(); + + let module = Module::new( + &engine, + r#" + (module + (global (mut i32) (i32.const 42)) + (memory 1) + (func (export "a") + unreachable + ) + ) + "#, + )?; + + let mut store = Store::<()>::new(&engine, ()); + let instance = Instance::new(&mut store, &module, &[])?; + + let a_func = instance.get_typed_func::<(), ()>(&mut store, "a")?; + let err = a_func.call(&mut store, ()).unwrap_err(); + let core_dump = err.downcast_ref::().unwrap(); + assert_eq!(core_dump.globals().len(), 1); + assert_eq!(core_dump.memories().len(), 1); + assert_eq!(core_dump.instances().len(), 1); + + Ok(()) +} + +#[test] +#[cfg_attr(miri, ignore)] +fn multiple_globals_memories_and_instances() -> Result<()> { + let mut config = Config::default(); + config.wasm_multi_memory(true); + config.coredump_on_trap(true); + let engine = Engine::new(&config).unwrap(); + let mut store = Store::<()>::new(&engine, ()); + let mut linker = Linker::new(&engine); + + let memory = Memory::new(&mut store, MemoryType::new(1, None))?; + linker.define(&mut store, "host", "memory", memory)?; + + let global = Global::new( + &mut store, + GlobalType::new(ValType::I32, Mutability::Var), + Val::I32(0), + )?; + linker.define(&mut store, "host", "global", global)?; + + let module_a = Module::new( + &engine, + r#" + (module + (memory (export "memory") 1) + (global (export "global") (mut i32) (i32.const 0)) + ) + "#, + )?; + let instance_a = linker.instantiate(&mut store, &module_a)?; + linker.instance(&mut store, "a", instance_a)?; + + let module_b = Module::new( + &engine, + r#" + (module + (import "host" "memory" (memory 1)) + (import "host" "global" (global (mut i32))) + (import "a" "memory" (memory 1)) + (import "a" "global" (global (mut i32))) + + (func (export "trap") + unreachable + ) + ) + "#, + )?; + let instance_b = linker.instantiate(&mut store, &module_b)?; + + let trap_func = instance_b.get_typed_func::<(), ()>(&mut store, "trap")?; + let err = trap_func.call(&mut store, ()).unwrap_err(); + let core_dump = err.downcast_ref::().unwrap(); + assert_eq!(core_dump.globals().len(), 2); + assert_eq!(core_dump.memories().len(), 2); + assert_eq!(core_dump.instances().len(), 2); Ok(()) } From 4ba8b6c0d99d258aa0a4ed4ee7c687fcddae6c8e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 22 Sep 2023 12:45:18 -0500 Subject: [PATCH 005/199] Remove manual impl of Error/Display (#7076) * Remove manual impl of Error/Display These conflict with auto-generated ones. * Fix tests --- .../bin/outbound_request_unknown_method.rs | 2 +- .../outbound_request_unsupported_scheme.rs | 2 +- crates/wasi-http/src/http_impl.rs | 4 ++-- crates/wasi-http/src/lib.rs | 23 ------------------- .../wit/deps/http/outgoing-handler.wit | 9 +------- .../wasi/wit/deps/http/outgoing-handler.wit | 9 +------- 6 files changed, 6 insertions(+), 43 deletions(-) diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs index 727294861b00..a2ab5e48dc02 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs @@ -18,6 +18,6 @@ async fn run() { let error = res.unwrap_err(); assert_eq!( error.to_string(), - "Error::Invalid(\"unknown method OTHER\")" + "Error::InvalidUrl(\"unknown method OTHER\")" ); } diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs index c8a66b7da648..482550627e8d 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs @@ -18,6 +18,6 @@ async fn run() { let error = res.unwrap_err(); assert_eq!( error.to_string(), - "Error::Invalid(\"unsupported scheme WS\")" + "Error::InvalidUrl(\"unsupported scheme WS\")" ); } diff --git a/crates/wasi-http/src/http_impl.rs b/crates/wasi-http/src/http_impl.rs index 570d229cf7b2..8c9e6358fb55 100644 --- a/crates/wasi-http/src/http_impl.rs +++ b/crates/wasi-http/src/http_impl.rs @@ -50,7 +50,7 @@ impl outgoing_handler::Host for T { crate::bindings::http::types::Method::Trace => Method::TRACE, crate::bindings::http::types::Method::Patch => Method::PATCH, crate::bindings::http::types::Method::Other(method) => { - return Ok(Err(outgoing_handler::Error::Invalid(format!( + return Ok(Err(outgoing_handler::Error::InvalidUrl(format!( "unknown method {method}" )))); } @@ -60,7 +60,7 @@ impl outgoing_handler::Host for T { Scheme::Http => (false, "http://", 80), Scheme::Https => (true, "https://", 443), Scheme::Other(scheme) => { - return Ok(Err(outgoing_handler::Error::Invalid(format!( + return Ok(Err(outgoing_handler::Error::InvalidUrl(format!( "unsupported scheme {scheme}" )))) } diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs index c6b072b1e60f..96a6fb835fb1 100644 --- a/crates/wasi-http/src/lib.rs +++ b/crates/wasi-http/src/lib.rs @@ -1,6 +1,4 @@ pub use crate::types::{WasiHttpCtx, WasiHttpView}; -use core::fmt::Formatter; -use std::fmt::{self, Display}; pub mod body; pub mod http_impl; @@ -28,27 +26,6 @@ pub mod bindings { pub use wasi::http; } -impl std::error::Error for crate::bindings::http::types::Error {} - -impl Display for crate::bindings::http::types::Error { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - crate::bindings::http::types::Error::InvalidUrl(m) => { - write!(f, "[InvalidUrl] {}", m) - } - crate::bindings::http::types::Error::ProtocolError(m) => { - write!(f, "[ProtocolError] {}", m) - } - crate::bindings::http::types::Error::TimeoutError(m) => { - write!(f, "[TimeoutError] {}", m) - } - crate::bindings::http::types::Error::UnexpectedError(m) => { - write!(f, "[UnexpectedError] {}", m) - } - } - } -} - impl From for crate::bindings::http::types::Error { fn from(err: wasmtime_wasi::preview2::TableError) -> Self { Self::UnexpectedError(err.to_string()) diff --git a/crates/wasi-http/wit/deps/http/outgoing-handler.wit b/crates/wasi-http/wit/deps/http/outgoing-handler.wit index 3e03327d742b..43bd0035fc06 100644 --- a/crates/wasi-http/wit/deps/http/outgoing-handler.wit +++ b/crates/wasi-http/wit/deps/http/outgoing-handler.wit @@ -6,14 +6,7 @@ // that takes a `request` parameter and returns a `response` result. // interface outgoing-handler { - use types.{outgoing-request, request-options, future-incoming-response} - - // FIXME: we would want to use the types.error here but there is a - // wasmtime-wit-bindgen bug that prevents us from using the same error in - // the two different interfaces, right now... - variant error { - invalid(string) - } + use types.{outgoing-request, request-options, future-incoming-response, error} // The parameter and result types of the `handle` function allow the caller // to concurrently stream the bodies of the outgoing request and the incoming diff --git a/crates/wasi/wit/deps/http/outgoing-handler.wit b/crates/wasi/wit/deps/http/outgoing-handler.wit index 3e03327d742b..43bd0035fc06 100644 --- a/crates/wasi/wit/deps/http/outgoing-handler.wit +++ b/crates/wasi/wit/deps/http/outgoing-handler.wit @@ -6,14 +6,7 @@ // that takes a `request` parameter and returns a `response` result. // interface outgoing-handler { - use types.{outgoing-request, request-options, future-incoming-response} - - // FIXME: we would want to use the types.error here but there is a - // wasmtime-wit-bindgen bug that prevents us from using the same error in - // the two different interfaces, right now... - variant error { - invalid(string) - } + use types.{outgoing-request, request-options, future-incoming-response, error} // The parameter and result types of the `handle` function allow the caller // to concurrently stream the bodies of the outgoing request and the incoming From 38bc7e9177e1137f27233518cce3fdaf790ef86a Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sun, 24 Sep 2023 07:00:39 +0100 Subject: [PATCH 006/199] riscv64: Better AMode Matching (#7079) * riscv64: Delete `int_load_op` helper * riscv64: Delete `default_memflags` * riscv64: Rename `gen_amode` * riscv64: Better matching for amode * riscv64: Delete `emit_load` helper * riscv64: Rename some load variables * riscv64: Merge `iadd` into amode computations --- cranelift/codegen/src/isa/riscv64/inst.isle | 131 ++- cranelift/codegen/src/isa/riscv64/lower.isle | 134 ++- .../codegen/src/isa/riscv64/lower/isle.rs | 28 +- .../filetests/isa/riscv64/amodes-fp.clif | 37 + .../filetests/isa/riscv64/amodes.clif | 129 ++- .../filetests/isa/riscv64/issue-6954.clif | 1015 ++++++++--------- .../filetests/isa/riscv64/reftypes.clif | 92 +- .../filetests/isa/riscv64/stack.clif | 84 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 46 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 46 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 36 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 36 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 40 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 40 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 22 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 22 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 40 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 40 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 24 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 24 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 24 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 24 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 30 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 30 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 30 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 30 +- 26 files changed, 1046 insertions(+), 1188 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/riscv64/amodes-fp.clif diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index dc5aca2d130e..f7f92fc20d54 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -1664,10 +1664,10 @@ ;; Otherwise we fall back to loading the immediate from the constant pool. (rule 0 (imm (ty_int ty) c) - (emit_load + (gen_load + (gen_const_amode (emit_u64_le_const c)) (LoadOP.Ld) - (mem_flags_trusted) - (gen_const_amode (emit_u64_le_const c)))) + (mem_flags_trusted))) ;; Imm12 Rules @@ -2402,57 +2402,88 @@ (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 low high) ))) -(decl gen_amode (Reg Offset32 Type) AMode) -(extern constructor gen_amode gen_amode) +;; Generates a AMode that points to a register plus an offset. +(decl gen_reg_offset_amode (Reg i64 Type) AMode) +(extern constructor gen_reg_offset_amode gen_reg_offset_amode) + +;; Generates a AMode that an offset from the stack pointer. +(decl gen_sp_offset_amode (i64 Type) AMode) +(extern constructor gen_sp_offset_amode gen_sp_offset_amode) + +;; Generates a AMode that an offset from the frame pointer. +(decl gen_fp_offset_amode (i64 Type) AMode) +(extern constructor gen_fp_offset_amode gen_fp_offset_amode) + +;; Generates an AMode that points to a stack slot + offset. +(decl gen_stack_slot_amode (StackSlot i64 Type) AMode) +(extern constructor gen_stack_slot_amode gen_stack_slot_amode) ;; Generates a AMode that points to a constant in the constant pool. (decl gen_const_amode (VCodeConstant) AMode) (extern constructor gen_const_amode gen_const_amode) + +;; Tries to match a Value + Offset into an AMode +(decl amode (Value i32 Type) AMode) +(rule 0 (amode addr offset ty) (amode_inner addr offset ty)) + +;; If we are adding a constant offset with an iadd we can instead make that +;; offset part of the amode offset. +;; +;; We can't recurse into `amode` again since that could cause stack overflows. +;; See: https://github.com/bytecodealliance/wasmtime/pull/6968 +(rule 1 (amode (iadd addr (iconst (simm32 y))) offset ty) + (if-let new_offset (s32_add_fallible y offset)) + (amode_inner addr new_offset ty)) +(rule 2 (amode (iadd (iconst (simm32 x)) addr) offset ty) + (if-let new_offset (s32_add_fallible x offset)) + (amode_inner addr new_offset ty)) + + +;; These are the normal rules for generating an AMode. +(decl amode_inner (Value i32 Type) AMode) + +;; In the simplest case we just lower into a Reg+Offset +(rule 0 (amode_inner r @ (value_type (ty_addr64 _)) offset ty) + (gen_reg_offset_amode r offset ty)) + +;; If the value is a `get_frame_pointer`, we can just use the offset from that. +(rule 1 (amode_inner (get_frame_pointer) offset ty) + (gen_fp_offset_amode offset ty)) + +;; If the value is a `get_stack_pointer`, we can just use the offset from that. +(rule 1 (amode_inner (get_stack_pointer) offset ty) + (gen_sp_offset_amode offset ty)) + +;; Similarly if the value is a `stack_addr` we can also turn that into an sp offset. +(rule 1 (amode_inner (stack_addr ss ss_offset) amode_offset ty) + (if-let combined_offset (s32_add_fallible ss_offset amode_offset)) + (gen_stack_slot_amode ss combined_offset ty)) + + + + ;; Returns a canonical type for a LoadOP. We only return I64 or F64. (decl load_op_reg_type (LoadOP) Type) (rule 1 (load_op_reg_type (LoadOP.Fld)) $F64) (rule 1 (load_op_reg_type (LoadOP.Flw)) $F64) (rule 0 (load_op_reg_type _) $I64) -(decl emit_load (LoadOP MemFlags AMode) Reg) -(rule (emit_load op flags from) - (let ((dst WritableReg (temp_writable_reg (load_op_reg_type op))) - (_ Unit (emit (MInst.Load dst op flags from)))) - dst)) - ;; helper function to load from memory. -(decl gen_load (Reg Offset32 LoadOP MemFlags Type) Reg) -(rule (gen_load p offset op flags ty) - (emit_load op flags (gen_amode p offset $I64))) - -(decl gen_load_128 (Reg Offset32 MemFlags) ValueRegs) -(rule (gen_load_128 p offset flags) - (let ((low Reg (gen_load p offset (LoadOP.Ld) flags $I64)) - (high Reg (gen_load p (offset32_add offset 8) (LoadOP.Ld) flags $I64))) - (value_regs low high))) +(decl gen_load (AMode LoadOP MemFlags) Reg) +(rule (gen_load amode op flags) + (let ((dst WritableReg (temp_writable_reg (load_op_reg_type op))) + (_ Unit (emit (MInst.Load dst op flags amode)))) + dst)) -(decl default_memflags () MemFlags) -(extern constructor default_memflags default_memflags) +;; helper function to store to memory. +(decl gen_store (AMode StoreOP MemFlags Reg) InstOutput) +(rule (gen_store amode op flags src) + (side_effect (SideEffectNoResult.Inst (MInst.Store amode op flags src)))) -(decl offset32_add (Offset32 i64) Offset32) -(extern constructor offset32_add offset32_add) -;; helper function to store to memory. -(decl gen_store (Reg Offset32 StoreOP MemFlags Reg) InstOutput) -(rule - (gen_store base offset op flags src) - (side_effect (SideEffectNoResult.Inst (MInst.Store (gen_amode base offset $I64) op flags src))) -) -(decl gen_store_128 (Reg Offset32 MemFlags ValueRegs) InstOutput) -(rule - (gen_store_128 p offset flags src) - (side_effect - (SideEffectNoResult.Inst2 - (MInst.Store (gen_amode p offset $I64) (StoreOP.Sd) flags (value_regs_get src 0)) - (MInst.Store (gen_amode p (offset32_add offset 8) $I64) (StoreOP.Sd) flags (value_regs_get src 1))))) (decl valid_atomic_transaction (Type) Type) (extern extractor valid_atomic_transaction valid_atomic_transaction) @@ -2611,32 +2642,6 @@ (decl store_op (Type) StoreOP) (extern constructor store_op store_op) -;; bool is "is_signed" -(decl int_load_op (bool u8) LoadOP) -(rule - (int_load_op $false 8) - (LoadOP.Lbu)) - -(rule - (int_load_op $true 8) - (LoadOP.Lb)) - -(rule - (int_load_op $false 16) - (LoadOP.Lhu)) -(rule - (int_load_op $true 16) - (LoadOP.Lh)) -(rule - (int_load_op $false 32) - (LoadOP.Lwu)) -(rule - (int_load_op $true 32) - (LoadOP.Lw)) - -(rule - (int_load_op _ 64) - (LoadOP.Ld)) ;;;; load extern name (decl load_ext_name (ExternalName i64) Reg) diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index d67adcc198e2..cb8f8ff0e49f 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -1480,46 +1480,43 @@ (udf code)) ;;;;; Rules for `uload8`;;;;;;;;; -(rule - (lower (uload8 flags p @ (value_type (ty_addr64 _)) offset)) - (gen_load p offset (int_load_op $false 8) flags $I64)) +(rule (lower (uload8 flags addr offset)) + (gen_load (amode addr offset $I8) (LoadOP.Lbu) flags)) + ;;;;; Rules for `sload8`;;;;;;;;; -(rule - (lower (sload8 flags p @ (value_type (ty_addr64 _)) offset)) - (gen_load p offset (int_load_op $true 8) flags $I64)) +(rule (lower (sload8 flags addr offset)) + (gen_load (amode addr offset $I8) (LoadOP.Lb) flags)) + ;;;;; Rules for `uload16`;;;;;;;;; -(rule - (lower (uload16 flags p @ (value_type (ty_addr64 _)) offset)) - (gen_load p offset (int_load_op $false 16) flags $I64)) +(rule (lower (uload16 flags addr offset)) + (gen_load (amode addr offset $I16) (LoadOP.Lhu) flags)) ;;;;; Rules for `iload16`;;;;;;;;; -(rule - (lower (sload16 flags p @ (value_type (ty_addr64 _)) offset)) - (gen_load p offset (int_load_op $true 16) flags $I64)) +(rule (lower (sload16 flags addr offset)) + (gen_load (amode addr offset $I16) (LoadOP.Lh) flags)) ;;;;; Rules for `uload32`;;;;;;;;; -(rule - (lower (uload32 flags p @ (value_type (ty_addr64 _)) offset)) - (gen_load p offset (int_load_op $false 32) flags $I64)) +(rule (lower (uload32 flags addr offset)) + (gen_load (amode addr offset $I32) (LoadOP.Lwu) flags)) -;;;;; Rules for `iload16`;;;;;;;;; -(rule - (lower (sload32 flags p @ (value_type (ty_addr64 _)) offset)) - (gen_load p offset (int_load_op $true 32) flags $I64)) +;;;;; Rules for `sload32`;;;;;;;;; +(rule (lower (sload32 flags addr offset)) + (gen_load (amode addr offset $I32) (LoadOP.Lw) flags)) -(rule - (lower (has_type ty (load flags p @ (value_type (ty_addr64 _)) offset))) - (gen_load p offset (load_op ty) flags ty) -) -;;;; for I128 -(rule 1 - (lower (has_type $I128 (load flags p @ (value_type (ty_addr64 _)) offset))) - (gen_load_128 p offset flags)) +;;;;; Rules for `load`;;;;;;;;; +(rule (lower (has_type ty (load flags addr offset))) + (gen_load (amode addr offset ty) (load_op ty) flags)) -(rule 2 - (lower (has_type (ty_vec_fits_in_register ty) (load flags p @ (value_type (ty_addr64 _)) offset))) - (let ((eew VecElementWidth (element_width_from_type ty))) - (vec_load eew (VecAMode.UnitStride (gen_amode p offset $I64)) flags (unmasked) ty))) +(rule 1 (lower (has_type $I128 (load flags addr offset))) + (if-let offset_plus_8 (s32_add_fallible offset 8)) + (let ((lo XReg (gen_load (amode addr offset $I64) (LoadOP.Ld) flags)) + (hi XReg (gen_load (amode addr offset_plus_8 $I64) (LoadOP.Ld) flags))) + (value_regs lo hi))) + +(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (load flags addr offset))) + (let ((eew VecElementWidth (element_width_from_type ty)) + (amode AMode (amode addr offset ty))) + (vec_load eew (VecAMode.UnitStride amode) flags (unmasked) ty))) ;;;;; Rules for Load + Extend Combos ;;;;;;;;; @@ -1528,72 +1525,72 @@ ;; do a SEW/2 extension. This only reads half width elements from the source vector register ;; extends it, and writes the back the full register. -(decl gen_load64_extend (Type ExtendOp MemFlags XReg Offset32) VReg) +(decl gen_load64_extend (Type ExtendOp MemFlags AMode) VReg) -(rule (gen_load64_extend ty (ExtendOp.Signed) flags addr offset) +(rule (gen_load64_extend ty (ExtendOp.Signed) flags amode) (let ((eew VecElementWidth (element_width_from_type $I64)) (load_state VState (vstate_from_type $I64)) - (loaded VReg (vec_load eew (VecAMode.UnitStride (gen_amode addr offset $I64)) flags (unmasked) load_state))) + (loaded VReg (vec_load eew (VecAMode.UnitStride amode) flags (unmasked) load_state))) (rv_vsext_vf2 loaded (unmasked) ty))) -(rule (gen_load64_extend ty (ExtendOp.Zero) flags addr offset) +(rule (gen_load64_extend ty (ExtendOp.Zero) flags amode) (let ((eew VecElementWidth (element_width_from_type $I64)) (load_state VState (vstate_from_type $I64)) - (loaded VReg (vec_load eew (VecAMode.UnitStride (gen_amode addr offset $I64)) flags (unmasked) load_state))) + (loaded VReg (vec_load eew (VecAMode.UnitStride amode) flags (unmasked) load_state))) (rv_vzext_vf2 loaded (unmasked) ty))) ;;;;; Rules for `uload8x8`;;;;;;;;;; -(rule (lower (has_type (ty_vec_fits_in_register ty @ $I16X8) (uload8x8 flags addr @ (value_type (ty_addr64 _)) offset))) - (gen_load64_extend ty (ExtendOp.Zero) flags addr offset)) +(rule (lower (has_type (ty_vec_fits_in_register ty @ $I16X8) (uload8x8 flags addr offset))) + (gen_load64_extend ty (ExtendOp.Zero) flags (amode addr offset ty))) ;;;;; Rules for `uload16x4`;;;;;;;;; -(rule (lower (has_type (ty_vec_fits_in_register ty @ $I32X4) (uload16x4 flags addr @ (value_type (ty_addr64 _)) offset))) - (gen_load64_extend ty (ExtendOp.Zero) flags addr offset)) +(rule (lower (has_type (ty_vec_fits_in_register ty @ $I32X4) (uload16x4 flags addr offset))) + (gen_load64_extend ty (ExtendOp.Zero) flags (amode addr offset ty))) ;;;;; Rules for `uload32x2`;;;;;;;;; -(rule (lower (has_type (ty_vec_fits_in_register ty @ $I64X2) (uload32x2 flags addr @ (value_type (ty_addr64 _)) offset))) - (gen_load64_extend ty (ExtendOp.Zero) flags addr offset)) +(rule (lower (has_type (ty_vec_fits_in_register ty @ $I64X2) (uload32x2 flags addr offset))) + (gen_load64_extend ty (ExtendOp.Zero) flags (amode addr offset ty))) ;;;;; Rules for `sload8x8`;;;;;;;;;; -(rule (lower (has_type (ty_vec_fits_in_register ty @ $I16X8) (sload8x8 flags addr @ (value_type (ty_addr64 _)) offset))) - (gen_load64_extend ty (ExtendOp.Signed) flags addr offset)) +(rule (lower (has_type (ty_vec_fits_in_register ty @ $I16X8) (sload8x8 flags addr offset))) + (gen_load64_extend ty (ExtendOp.Signed) flags (amode addr offset ty))) ;;;;; Rules for `sload16x4`;;;;;;;;; -(rule (lower (has_type (ty_vec_fits_in_register ty @ $I32X4) (sload16x4 flags addr @ (value_type (ty_addr64 _)) offset))) - (gen_load64_extend ty (ExtendOp.Signed) flags addr offset)) +(rule (lower (has_type (ty_vec_fits_in_register ty @ $I32X4) (sload16x4 flags addr offset))) + (gen_load64_extend ty (ExtendOp.Signed) flags (amode addr offset ty))) ;;;;; Rules for `sload32x2`;;;;;;;;; -(rule (lower (has_type (ty_vec_fits_in_register ty @ $I64X2) (sload32x2 flags addr @ (value_type (ty_addr64 _)) offset))) - (gen_load64_extend ty (ExtendOp.Signed) flags addr offset)) +(rule (lower (has_type (ty_vec_fits_in_register ty @ $I64X2) (sload32x2 flags addr offset))) + (gen_load64_extend ty (ExtendOp.Signed) flags (amode addr offset ty))) ;;;;; Rules for `istore8`;;;;;;;;; -(rule - (lower (istore8 flags x p @ (value_type (ty_addr64 _)) offset)) - (gen_store p offset (StoreOP.Sb) flags x)) +(rule (lower (istore8 flags src addr offset)) + (gen_store (amode addr offset $I8) (StoreOP.Sb) flags src)) + ;;;;; Rules for `istore16`;;;;;;;;; -(rule - (lower (istore16 flags x p @ (value_type (ty_addr64 _)) offset)) - (gen_store p offset (StoreOP.Sh) flags x)) +(rule (lower (istore16 flags src addr offset)) + (gen_store (amode addr offset $I16) (StoreOP.Sh) flags src)) ;;;;; Rules for `istore32`;;;;;;;;; -(rule - (lower (istore32 flags x p @ (value_type (ty_addr64 _)) offset)) - (gen_store p offset (StoreOP.Sw) flags x)) +(rule (lower (istore32 flags src addr offset)) + (gen_store (amode addr offset $I32) (StoreOP.Sw) flags src)) ;;;;; Rules for `store`;;;;;;;;; -(rule - (lower (store flags x @ (value_type ty) p @ (value_type (ty_addr64 _)) offset)) - (gen_store p offset (store_op ty) flags x)) +(rule (lower (store flags src @ (value_type ty) addr offset)) + (gen_store (amode addr offset ty) (store_op ty) flags src)) -;;; special for I128 -(rule 1 - (lower (store flags x @ (value_type $I128 ) p @ (value_type (ty_addr64 _)) offset)) - (gen_store_128 p offset flags x)) +(rule 1 (lower (store flags src @ (value_type $I128) addr offset)) + (if-let offset_plus_8 (s32_add_fallible offset 8)) + (let ((_ InstOutput (gen_store (amode addr offset $I64) (StoreOP.Sd) flags (value_regs_get src 0)))) + (gen_store (amode addr offset_plus_8 $I64) (StoreOP.Sd) flags (value_regs_get src 1)))) + +(rule 2 (lower (store flags src @ (value_type (ty_vec_fits_in_register ty)) addr offset)) + (let ((eew VecElementWidth (element_width_from_type ty)) + (amode AMode (amode addr offset ty))) + (vec_store eew (VecAMode.UnitStride amode) src flags (unmasked) ty))) -(rule 2 - (lower (store flags x @ (value_type (ty_vec_fits_in_register ty)) p @ (value_type (ty_addr64 _)) offset)) - (let ((eew VecElementWidth (element_width_from_type ty))) - (vec_store eew (VecAMode.UnitStride (gen_amode p offset $I64)) x flags (unmasked) ty))) + +;;;;; Rules for `icmp`;;;;;;;;; (decl gen_icmp (IntCC ValueRegs ValueRegs Type) XReg) (rule @@ -1603,7 +1600,6 @@ (_ Unit (emit (MInst.Icmp cc result x y ty)))) result)) -;;;;; Rules for `icmp`;;;;;;;;; (rule 0 (lower (icmp cc x @ (value_type (ty_int ty)) y)) (lower_icmp cc x y ty)) diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs index c22fff8096ff..88cf010f34ec 100644 --- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs +++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs @@ -446,16 +446,28 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> self.backend.isa_flags.has_zbs() } - fn default_memflags(&mut self) -> MemFlags { - MemFlags::new() - } - fn int_convert_2_float_op(&mut self, from: Type, is_signed: bool, to: Type) -> FpuOPRR { FpuOPRR::int_convert_2_float_op(from, is_signed, to) } - fn gen_amode(&mut self, base: Reg, offset: Offset32, ty: Type) -> AMode { - AMode::RegOffset(base, i64::from(offset), ty) + fn gen_reg_offset_amode(&mut self, base: Reg, offset: i64, ty: Type) -> AMode { + AMode::RegOffset(base, offset, ty) + } + + fn gen_sp_offset_amode(&mut self, offset: i64, ty: Type) -> AMode { + AMode::SPOffset(offset, ty) + } + + fn gen_fp_offset_amode(&mut self, offset: i64, ty: Type) -> AMode { + AMode::FPOffset(offset, ty) + } + + fn gen_stack_slot_amode(&mut self, ss: StackSlot, offset: i64, ty: Type) -> AMode { + // Offset from beginning of stackslot area, which is at nominal SP (see + // [MemArg::NominalSPOffset] for more details on nominal SP tracking). + let stack_off = self.lower_ctx.abi().sized_stackslot_offsets()[ss] as i64; + let sp_off: i64 = stack_off + offset; + AMode::NominalSPOffset(sp_off, ty) } fn gen_const_amode(&mut self, c: VCodeConstant) -> AMode { @@ -495,10 +507,6 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> tmp.to_reg() } - fn offset32_add(&mut self, a: Offset32, adden: i64) -> Offset32 { - a.try_add_i64(adden).expect("offset exceed range.") - } - fn gen_stack_addr(&mut self, slot: StackSlot, offset: Offset32) -> Reg { let result = self.temp_writable_reg(I64); let i = self diff --git a/cranelift/filetests/filetests/isa/riscv64/amodes-fp.clif b/cranelift/filetests/filetests/isa/riscv64/amodes-fp.clif new file mode 100644 index 000000000000..6d138aaccc6c --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/amodes-fp.clif @@ -0,0 +1,37 @@ +test compile precise-output +set unwind_info=false +set preserve_frame_pointers=true +target riscv64 + +function %load_from_get_frame_pointer() -> i64 { +block0: + v0 = get_frame_pointer.i64 + v1 = load.i64 v0+24 + return v1 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; block0: +; ld a0,24(fp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; block1: ; offset 0x10 +; ld a0, 0x18(s0) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/amodes.clif b/cranelift/filetests/filetests/isa/riscv64/amodes.clif index c2882cd77c26..0c6743ac0cb0 100644 --- a/cranelift/filetests/filetests/isa/riscv64/amodes.clif +++ b/cranelift/filetests/filetests/isa/riscv64/amodes.clif @@ -116,18 +116,16 @@ block0(v0: i64, v1: i64, v2: i64): ; VCode: ; block0: -; add a1,a0,a1 -; add a1,a1,a2 -; addi a1,a1,48 -; lw a0,0(a1) +; add a0,a0,a1 +; add a0,a0,a2 +; lw a0,48(a0) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; add a1, a0, a1 -; add a1, a1, a2 -; addi a1, a1, 0x30 -; lw a0, 0(a1) +; add a0, a0, a1 +; add a0, a0, a2 +; lw a0, 0x30(a0) ; ret function %f10(i64, i64, i64) -> i32 { @@ -142,22 +140,19 @@ block0(v0: i64, v1: i64, v2: i64): ; VCode: ; block0: -; lui a3,1 -; addi a4,a3,4 -; add a3,a0,a1 -; add a3,a3,a2 -; add a3,a3,a4 -; lw a0,0(a3) +; add a0,a0,a1 +; add a0,a0,a2 +; lw a0,4100(a0) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; lui a3, 1 -; addi a4, a3, 4 -; add a3, a0, a1 -; add a3, a3, a2 -; add a3, a3, a4 -; lw a0, 0(a3) +; add a0, a0, a1 +; add a0, a0, a2 +; lui t6, 1 +; addi t6, t6, 4 +; add t6, t6, a0 +; lw a0, 0(t6) ; ret function %f10() -> i32 { @@ -189,16 +184,14 @@ block0(v0: i64): ; VCode: ; block0: -; lui a4,2048 -; add a4,a0,a4 -; lw a0,0(a4) +; lw a0,8388608(a0) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; lui a4, 0x800 -; add a4, a0, a4 -; lw a0, 0(a4) +; lui t6, 0x800 +; add t6, t6, a0 +; lw a0, 0(t6) ; ret function %f12(i64) -> i32 { @@ -211,14 +204,12 @@ block0(v0: i64): ; VCode: ; block0: -; addi a3,a0,-4 -; lw a0,0(a3) +; lw a0,-4(a0) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, a0, -4 -; lw a0, 0(a3) +; lw a0, -4(a0) ; ret function %f13(i64) -> i32 { @@ -231,18 +222,15 @@ block0(v0: i64): ; VCode: ; block0: -; lui a4,244141 -; addi a1,a4,-1536 -; add a5,a0,a1 -; lw a0,0(a5) +; lw a0,1000000000(a0) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; lui a4, 0x3b9ad -; addi a1, a4, -0x600 -; add a5, a0, a1 -; lw a0, 0(a5) +; lui t6, 0x3b9ad +; addi t6, t6, -0x600 +; add t6, t6, a0 +; lw a0, 0(t6) ; ret function %f14(i32) -> i32 { @@ -507,20 +495,22 @@ block0(v0: i64): ; VCode: ; block0: -; addi a4,a0,32 -; ld a0,0(a4) -; ld a1,8(a4) -; sd a0,0(a4) -; sd a1,8(a4) +; ld a2,32(a0) +; mv a4,a2 +; ld a1,40(a0) +; sd a2,32(a0) +; sd a1,40(a0) +; mv a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a4, a0, 0x20 -; ld a0, 0(a4) -; ld a1, 8(a4) -; sd a0, 0(a4) -; sd a1, 8(a4) +; ld a2, 0x20(a0) +; mv a4, a2 +; ld a1, 0x28(a0) +; sd a2, 0x20(a0) +; sd a1, 0x28(a0) +; mv a0, a4 ; ret function %i128_32bit_sextend_simple(i32) -> i128 { @@ -562,22 +552,41 @@ block0(v0: i64, v1: i32): ; VCode: ; block0: ; sext.w a1,a1 -; add a1,a0,a1 -; addi a2,a1,24 -; ld a0,0(a2) -; ld a1,8(a2) -; sd a0,0(a2) -; sd a1,8(a2) +; add a0,a0,a1 +; ld a5,24(a0) +; mv a2,a5 +; ld a1,32(a0) +; sd a5,24(a0) +; sd a1,32(a0) +; mv a0,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; sext.w a1, a1 -; add a1, a0, a1 -; addi a2, a1, 0x18 -; ld a0, 0(a2) -; ld a1, 8(a2) -; sd a0, 0(a2) -; sd a1, 8(a2) +; add a0, a0, a1 +; ld a5, 0x18(a0) +; mv a2, a5 +; ld a1, 0x20(a0) +; sd a5, 0x18(a0) +; sd a1, 0x20(a0) +; mv a0, a2 +; ret + +function %load_from_get_stack_pointer() -> i64 { +block0: + v0 = get_stack_pointer.i64 + v1 = load.i64 v0+8 + return v1 +} + +; VCode: +; block0: +; ld a0,8(sp) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; ld a0, 8(sp) ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif b/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif index 6cce49e7f4f3..44a45b85236e 100644 --- a/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif +++ b/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif @@ -123,249 +123,198 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s1,-8(sp) -; sd s2,-16(sp) -; sd s3,-24(sp) -; sd s4,-32(sp) -; sd s5,-40(sp) -; sd s6,-48(sp) -; sd s7,-56(sp) -; sd s8,-64(sp) -; sd s9,-72(sp) -; sd s10,-80(sp) -; sd s11,-88(sp) -; add sp,-480 +; add sp,-384 ; block0: ; vle8.v v11,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v15,32(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v10,48(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v12,64(fp) #avl=16, #vtype=(e8, m1, ta, ma) +; li a3,0 ; li a4,0 ; li a0,0 ; li a2,0 -; li a3,0 -; load_addr s4,0(nominal_sp) -; sd a2,0(s4) -; sd a3,8(s4) -; load_addr s6,16(nominal_sp) -; sd a2,0(s6) -; sd a3,8(s6) -; load_addr s8,32(nominal_sp) -; sd a2,0(s8) -; sd a3,8(s8) -; load_addr s10,48(nominal_sp) -; sd a2,0(s10) -; sd a3,8(s10) -; load_addr t0,64(nominal_sp) -; sd a2,0(t0) -; sd a3,8(t0) -; load_addr t2,80(nominal_sp) -; sd a2,0(t2) -; sd a3,8(t2) -; load_addr a7,96(nominal_sp) -; sd a2,0(a7) -; sd a3,8(a7) -; load_addr t4,112(nominal_sp) -; sd a2,0(t4) -; load_addr s1,120(nominal_sp) -; sw a0,0(s1) -; load_addr s2,124(nominal_sp) -; sh a4,0(s2) -; load_addr s3,128(nominal_sp) -; sd a2,0(s3) -; sd a3,8(s3) -; load_addr s5,144(nominal_sp) -; sd a2,0(s5) -; sd a3,8(s5) -; load_addr s7,160(nominal_sp) -; sd a2,0(s7) -; sd a3,8(s7) -; load_addr s9,176(nominal_sp) -; sd a2,0(s9) -; sd a3,8(s9) -; load_addr s11,192(nominal_sp) -; sd a2,0(s11) -; sd a3,8(s11) -; load_addr t1,208(nominal_sp) -; sd a2,0(t1) -; sd a3,8(t1) -; load_addr a7,224(nominal_sp) -; sd a2,0(a7) -; sd a3,8(a7) -; load_addr t3,240(nominal_sp) -; sd a2,0(t3) -; load_addr t4,248(nominal_sp) -; sw a0,0(t4) -; load_addr s1,252(nominal_sp) -; sh a4,0(s1) -; load_addr s2,256(nominal_sp) -; sd a2,0(s2) -; sd a3,8(s2) -; load_addr s4,272(nominal_sp) -; sd a2,0(s4) -; sd a3,8(s4) -; load_addr s6,288(nominal_sp) -; sd a2,0(s6) -; sd a3,8(s6) -; load_addr s8,304(nominal_sp) -; sd a2,0(s8) -; sd a3,8(s8) -; load_addr s10,320(nominal_sp) -; sd a2,0(s10) -; sd a3,8(s10) -; load_addr t0,336(nominal_sp) -; sd a2,0(t0) -; sd a3,8(t0) -; load_addr t2,352(nominal_sp) -; sd a2,0(t2) -; sd a3,8(t2) -; load_addr a3,368(nominal_sp) -; sd a2,0(a3) -; load_addr a2,376(nominal_sp) -; sw a0,0(a2) -; load_addr a0,380(nominal_sp) -; sh a4,0(a0) +; sd a0,0(nominal_sp) +; sd a2,8(nominal_sp) +; sd a0,16(nominal_sp) +; sd a2,24(nominal_sp) +; sd a0,32(nominal_sp) +; sd a2,40(nominal_sp) +; sd a0,48(nominal_sp) +; sd a2,56(nominal_sp) +; sd a0,64(nominal_sp) +; sd a2,72(nominal_sp) +; sd a0,80(nominal_sp) +; sd a2,88(nominal_sp) +; sd a0,96(nominal_sp) +; sd a2,104(nominal_sp) +; sd a0,112(nominal_sp) +; sw a4,120(nominal_sp) +; sh a3,124(nominal_sp) +; sd a0,128(nominal_sp) +; sd a2,136(nominal_sp) +; sd a0,144(nominal_sp) +; sd a2,152(nominal_sp) +; sd a0,160(nominal_sp) +; sd a2,168(nominal_sp) +; sd a0,176(nominal_sp) +; sd a2,184(nominal_sp) +; sd a0,192(nominal_sp) +; sd a2,200(nominal_sp) +; sd a0,208(nominal_sp) +; sd a2,216(nominal_sp) +; sd a0,224(nominal_sp) +; sd a2,232(nominal_sp) +; sd a0,240(nominal_sp) +; sw a4,248(nominal_sp) +; sh a3,252(nominal_sp) +; sd a0,256(nominal_sp) +; sd a2,264(nominal_sp) +; sd a0,272(nominal_sp) +; sd a2,280(nominal_sp) +; sd a0,288(nominal_sp) +; sd a2,296(nominal_sp) +; sd a0,304(nominal_sp) +; sd a2,312(nominal_sp) +; sd a0,320(nominal_sp) +; sd a2,328(nominal_sp) +; sd a0,336(nominal_sp) +; sd a2,344(nominal_sp) +; sd a0,352(nominal_sp) +; sd a2,360(nominal_sp) +; sd a0,368(nominal_sp) +; sw a4,376(nominal_sp) +; sh a3,380(nominal_sp) ; zext.w a0,a1 -; select_i16x8 v8,v12,v12##condition=a0 +; select_i16x8 v9,v12,v12##condition=a0 ; zext.w a0,a1 -; select_i16x8 v10,v8,v8##condition=a0 +; select_i16x8 v12,v9,v9##condition=a0 ; zext.w a0,a1 -; select_i16x8 v9,v10,v10##condition=a0 +; select_i16x8 v10,v12,v12##condition=a0 ; vfsqrt.v v8,v11 #avl=2, #vtype=(e64, m1, ta, ma) ; ld a0,[const(0)] -; fmv.d.x fa2,a0 -; vfmv.v.f v10,fa2 #avl=2, #vtype=(e64, m1, ta, ma) +; fmv.d.x fa1,a0 +; vfmv.v.f v9,fa1 #avl=2, #vtype=(e64, m1, ta, ma) ; vmfne.vv v0,v8,v8 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v11,v8,v10,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vfsqrt.v v10,v11 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v11,v8,v9,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vfsqrt.v v8,v11 #avl=2, #vtype=(e64, m1, ta, ma) ; ld a0,[const(0)] -; fmv.d.x fa2,a0 -; vfmv.v.f v11,fa2 #avl=2, #vtype=(e64, m1, ta, ma) -; vmfne.vv v0,v10,v10 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v8,v10,v11,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a0,a1 -; select_i16x8 v10,v9,v9##condition=a0 -; zext.w a0,a1 -; select_i16x8 v9,v10,v10##condition=a0 -; zext.w a0,a1 -; select_i16x8 v10,v9,v9##condition=a0 -; zext.w a0,a1 -; select_i16x8 v9,v10,v10##condition=a0 -; zext.w a0,a1 -; select_i16x8 v10,v9,v9##condition=a0 -; zext.w a0,a1 -; select_i16x8 v9,v10,v10##condition=a0 +; fmv.d.x fa1,a0 +; vfmv.v.f v11,fa1 #avl=2, #vtype=(e64, m1, ta, ma) +; vmfne.vv v0,v8,v8 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v9,v8,v11,v0.t #avl=2, #vtype=(e64, m1, ta, ma) ; zext.w a0,a1 -; select_i16x8 v10,v9,v9##condition=a0 +; select_i16x8 v11,v10,v10##condition=a0 ; zext.w a0,a1 -; select_i16x8 v9,v10,v10##condition=a0 -; add a1,a1,a1 +; select_i16x8 v10,v11,v11##condition=a0 ; zext.w a0,a1 -; select_i16x8 v10,v9,v9##condition=a0 +; select_i16x8 v11,v10,v10##condition=a0 ; zext.w a0,a1 -; select_i16x8 v9,v10,v10##condition=a0 +; select_i16x8 v10,v11,v11##condition=a0 ; zext.w a0,a1 -; select_i16x8 v10,v9,v9##condition=a0 +; select_i16x8 v11,v10,v10##condition=a0 ; zext.w a0,a1 -; select_i16x8 v9,v10,v10##condition=a0 +; select_i16x8 v10,v11,v11##condition=a0 ; zext.w a0,a1 -; select_i16x8 v10,v9,v9##condition=a0 -; vmax.vv v15,v15,v15 #avl=2, #vtype=(e64, m1, ta, ma) +; select_i16x8 v11,v10,v10##condition=a0 ; zext.w a0,a1 -; select_i16x8 v9,v10,v10##condition=a0 -; load_addr a2,3(nominal_sp) -; addi a2,a2,0 -; andi a0,a2,3 -; slli a3,a0,3 -; andi a4,a2,-4 -; atomic_rmw.i8 and a0,a5,(a4)##t0=a2 offset=a3 -; zext.w a5,a1 -; select_i16x8 v10,v9,v9##condition=a5 -; zext.w a5,a1 -; select_i16x8 v9,v10,v10##condition=a5 -; zext.w a5,a1 -; select_i16x8 v10,v9,v9##condition=a5 -; zext.w a5,a1 -; select_i16x8 v9,v10,v10##condition=a5 -; zext.w a5,a1 -; select_i16x8 v10,v9,v9##condition=a5 -; zext.w a5,a1 +; select_i16x8 v10,v11,v11##condition=a0 +; add a0,a1,a1 +; zext.w a1,a0 +; select_i16x8 v11,v10,v10##condition=a1 +; zext.w a1,a0 +; select_i16x8 v10,v11,v11##condition=a1 +; zext.w a1,a0 +; select_i16x8 v11,v10,v10##condition=a1 +; zext.w a1,a0 +; select_i16x8 v10,v11,v11##condition=a1 +; zext.w a1,a0 +; select_i16x8 v11,v10,v10##condition=a1 +; vmax.vv v8,v15,v15 #avl=2, #vtype=(e64, m1, ta, ma) +; zext.w a1,a0 +; select_i16x8 v10,v11,v11##condition=a1 +; load_addr a1,3(nominal_sp) +; addi a1,a1,0 +; andi a4,a1,3 +; slli a2,a4,3 +; andi a3,a1,-4 +; atomic_rmw.i8 and a4,a5,(a3)##t0=a1 offset=a2 +; mv a1,a4 +; zext.w a4,a0 +; select_i16x8 v11,v10,v10##condition=a4 +; zext.w a4,a0 +; select_i16x8 v10,v11,v11##condition=a4 +; zext.w a4,a0 +; select_i16x8 v11,v10,v10##condition=a4 +; zext.w a4,a0 +; select_i16x8 v10,v11,v11##condition=a4 +; zext.w a4,a0 +; select_i16x8 v11,v10,v10##condition=a4 +; zext.w a4,a0 +; select_i16x8 v12,v11,v11##condition=a4 +; zext.w a4,a0 +; select_i16x8 v10,v12,v12##condition=a4 +; vse64.v v8,33(nominal_sp) #avl=2, #vtype=(e64, m1, ta, ma) +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 ; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a1 -; select_i16x8 v9,v11,v11##condition=a5 -; load_addr a2,33(nominal_sp) -; vse64.v v15,0(a2) #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a2,a1 -; select_i16x8 v9,v10,v10##condition=a2 -; zext.w a2,a1 -; select_i16x8 v10,v9,v9##condition=a2 -; zext.w a1,a1 -; select_i16x8 v9,v10,v10##condition=a1 -; vse8.v v8,0(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v9,16(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v8,32(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v9,48(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v9,64(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v9,80(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v9,96(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; add sp,+480 -; ld s1,-8(sp) -; ld s2,-16(sp) -; ld s3,-24(sp) -; ld s4,-32(sp) -; ld s5,-40(sp) -; ld s6,-48(sp) -; ld s7,-56(sp) -; ld s8,-64(sp) -; ld s9,-72(sp) -; ld s10,-80(sp) -; ld s11,-88(sp) +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; zext.w a5,a0 +; select_i16x8 v11,v10,v10##condition=a5 +; zext.w a5,a0 +; select_i16x8 v10,v11,v11##condition=a5 +; vse8.v v9,0(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v10,16(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v9,32(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v10,48(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v10,64(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v10,80(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v10,96(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; mv a0,a1 +; add sp,+384 ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -377,19 +326,8 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s1, -8(sp) -; sd s2, -0x10(sp) -; sd s3, -0x18(sp) -; sd s4, -0x20(sp) -; sd s5, -0x28(sp) -; sd s6, -0x30(sp) -; sd s7, -0x38(sp) -; sd s8, -0x40(sp) -; sd s9, -0x48(sp) -; sd s10, -0x50(sp) -; sd s11, -0x58(sp) -; addi sp, sp, -0x1e0 -; block1: ; offset 0x40 +; addi sp, sp, -0x180 +; block1: ; offset 0x14 ; .byte 0x57, 0x70, 0x08, 0xcc ; addi t6, s0, 0x10 ; .byte 0x87, 0x85, 0x0f, 0x02 @@ -399,405 +337,366 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; .byte 0x07, 0x85, 0x0f, 0x02 ; addi t6, s0, 0x40 ; .byte 0x07, 0x86, 0x0f, 0x02 +; mv a3, zero ; mv a4, zero ; mv a0, zero ; mv a2, zero -; mv a3, zero -; mv s4, sp -; sd a2, 0(s4) -; sd a3, 8(s4) -; addi s6, sp, 0x10 -; sd a2, 0(s6) -; sd a3, 8(s6) -; addi s8, sp, 0x20 -; sd a2, 0(s8) -; sd a3, 8(s8) -; addi s10, sp, 0x30 -; sd a2, 0(s10) -; sd a3, 8(s10) -; addi t0, sp, 0x40 -; sd a2, 0(t0) -; sd a3, 8(t0) -; addi t2, sp, 0x50 -; sd a2, 0(t2) -; sd a3, 8(t2) -; addi a7, sp, 0x60 -; sd a2, 0(a7) -; sd a3, 8(a7) -; addi t4, sp, 0x70 -; sd a2, 0(t4) -; addi s1, sp, 0x78 -; sw a0, 0(s1) -; addi s2, sp, 0x7c -; sh a4, 0(s2) -; addi s3, sp, 0x80 -; sd a2, 0(s3) -; sd a3, 8(s3) -; addi s5, sp, 0x90 -; sd a2, 0(s5) -; sd a3, 8(s5) -; addi s7, sp, 0xa0 -; sd a2, 0(s7) -; sd a3, 8(s7) -; addi s9, sp, 0xb0 -; sd a2, 0(s9) -; sd a3, 8(s9) -; addi s11, sp, 0xc0 -; sd a2, 0(s11) -; sd a3, 8(s11) -; addi t1, sp, 0xd0 -; sd a2, 0(t1) -; sd a3, 8(t1) -; addi a7, sp, 0xe0 -; sd a2, 0(a7) -; sd a3, 8(a7) -; addi t3, sp, 0xf0 -; sd a2, 0(t3) -; addi t4, sp, 0xf8 -; sw a0, 0(t4) -; addi s1, sp, 0xfc -; sh a4, 0(s1) -; addi s2, sp, 0x100 -; sd a2, 0(s2) -; sd a3, 8(s2) -; addi s4, sp, 0x110 -; sd a2, 0(s4) -; sd a3, 8(s4) -; addi s6, sp, 0x120 -; sd a2, 0(s6) -; sd a3, 8(s6) -; addi s8, sp, 0x130 -; sd a2, 0(s8) -; sd a3, 8(s8) -; addi s10, sp, 0x140 -; sd a2, 0(s10) -; sd a3, 8(s10) -; addi t0, sp, 0x150 -; sd a2, 0(t0) -; sd a3, 8(t0) -; addi t2, sp, 0x160 -; sd a2, 0(t2) -; sd a3, 8(t2) -; addi a3, sp, 0x170 -; sd a2, 0(a3) -; addi a2, sp, 0x178 -; sw a0, 0(a2) -; addi a0, sp, 0x17c -; sh a4, 0(a0) +; sd a0, 0(sp) +; sd a2, 8(sp) +; sd a0, 0x10(sp) +; sd a2, 0x18(sp) +; sd a0, 0x20(sp) +; sd a2, 0x28(sp) +; sd a0, 0x30(sp) +; sd a2, 0x38(sp) +; sd a0, 0x40(sp) +; sd a2, 0x48(sp) +; sd a0, 0x50(sp) +; sd a2, 0x58(sp) +; sd a0, 0x60(sp) +; sd a2, 0x68(sp) +; sd a0, 0x70(sp) +; sw a4, 0x78(sp) +; sh a3, 0x7c(sp) +; sd a0, 0x80(sp) +; sd a2, 0x88(sp) +; sd a0, 0x90(sp) +; sd a2, 0x98(sp) +; sd a0, 0xa0(sp) +; sd a2, 0xa8(sp) +; sd a0, 0xb0(sp) +; sd a2, 0xb8(sp) +; sd a0, 0xc0(sp) +; sd a2, 0xc8(sp) +; sd a0, 0xd0(sp) +; sd a2, 0xd8(sp) +; sd a0, 0xe0(sp) +; sd a2, 0xe8(sp) +; sd a0, 0xf0(sp) +; sw a4, 0xf8(sp) +; sh a3, 0xfc(sp) +; sd a0, 0x100(sp) +; sd a2, 0x108(sp) +; sd a0, 0x110(sp) +; sd a2, 0x118(sp) +; sd a0, 0x120(sp) +; sd a2, 0x128(sp) +; sd a0, 0x130(sp) +; sd a2, 0x138(sp) +; sd a0, 0x140(sp) +; sd a2, 0x148(sp) +; sd a0, 0x150(sp) +; sd a2, 0x158(sp) +; sd a0, 0x160(sp) +; sd a2, 0x168(sp) +; sd a0, 0x170(sp) +; sw a4, 0x178(sp) +; sh a3, 0x17c(sp) ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0x57, 0x34, 0xc0, 0x9e +; .byte 0xd7, 0x34, 0xc0, 0x9e ; j 8 -; .byte 0x57, 0x34, 0xc0, 0x9e +; .byte 0xd7, 0x34, 0xc0, 0x9e ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0x57, 0x35, 0x80, 0x9e +; .byte 0x57, 0x36, 0x90, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x80, 0x9e +; .byte 0x57, 0x36, 0x90, 0x9e ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xc0, 0x9e ; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xc0, 0x9e ; .byte 0x57, 0x70, 0x81, 0xcd ; .byte 0x57, 0x14, 0xb0, 0x4e ; auipc a0, 0 -; ld a0, 0x4b4(a0) -; fmv.d.x fa2, a0 -; .byte 0x57, 0x55, 0x06, 0x5e +; ld a0, 0x490(a0) +; fmv.d.x fa1, a0 +; .byte 0xd7, 0xd4, 0x05, 0x5e ; .byte 0x57, 0x10, 0x84, 0x72 -; .byte 0xd7, 0x05, 0x85, 0x5c -; .byte 0x57, 0x15, 0xb0, 0x4e +; .byte 0xd7, 0x85, 0x84, 0x5c +; .byte 0x57, 0x14, 0xb0, 0x4e ; auipc a0, 0 -; ld a0, 0x498(a0) -; fmv.d.x fa2, a0 -; .byte 0xd7, 0x55, 0x06, 0x5e -; .byte 0x57, 0x10, 0xa5, 0x72 -; .byte 0x57, 0x84, 0xa5, 0x5c +; ld a0, 0x474(a0) +; fmv.d.x fa1, a0 +; .byte 0xd7, 0xd5, 0x05, 0x5e +; .byte 0x57, 0x10, 0x84, 0x72 +; .byte 0xd7, 0x84, 0x85, 0x5c ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e ; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e ; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e ; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; .byte 0x3b, 0x85, 0x05, 0x08 ; beqz a0, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e ; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; add a1, a1, a1 -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e +; add a0, a1, a1 +; .byte 0xbb, 0x05, 0x05, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x05, 0x05, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e ; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x05, 0x05, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x05, 0x05, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e ; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x05, 0x05, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0xd7, 0x87, 0xf7, 0x1e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; addi a2, sp, 3 -; mv a2, a2 -; andi a0, a2, 3 -; slli a3, a0, 3 -; andi a4, a2, -4 -; lr.w.aqrl a0, (a4) -; srl a0, a0, a3 -; andi a0, a0, 0xff -; and a2, a0, a5 -; lr.w.aqrl t5, (a4) +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0x57, 0x84, 0xf7, 0x1e +; .byte 0xbb, 0x05, 0x05, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; addi a1, sp, 3 +; mv a1, a1 +; andi a4, a1, 3 +; slli a2, a4, 3 +; andi a3, a1, -4 +; lr.w.aqrl a4, (a3) +; srl a4, a4, a2 +; andi a4, a4, 0xff +; and a1, a4, a5 +; lr.w.aqrl t5, (a3) ; addi t6, zero, 0xff -; sll t6, t6, a3 +; sll t6, t6, a2 ; not t6, t6 ; and t5, t5, t6 -; andi t6, a2, 0xff -; sll t6, t6, a3 +; andi t6, a1, 0xff +; sll t6, t6, a2 ; or t5, t5, t6 -; sc.w.aqrl a2, t5, (a4) -; bnez a2, -0x34 -; .byte 0xbb, 0x87, 0x05, 0x08 +; sc.w.aqrl a1, t5, (a3) +; bnez a1, -0x34 +; mv a1, a4 +; .byte 0x3b, 0x07, 0x05, 0x08 +; beqz a4, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0x3b, 0x07, 0x05, 0x08 +; beqz a4, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0x3b, 0x07, 0x05, 0x08 +; beqz a4, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0x3b, 0x07, 0x05, 0x08 +; beqz a4, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0x3b, 0x07, 0x05, 0x08 +; beqz a4, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0x3b, 0x07, 0x05, 0x08 +; beqz a4, 0xc +; .byte 0x57, 0x36, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xb0, 0x9e +; .byte 0x3b, 0x07, 0x05, 0x08 +; beqz a4, 0xc +; .byte 0x57, 0x35, 0xc0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xc0, 0x9e +; addi t6, sp, 0x21 +; .byte 0x27, 0xf4, 0x0f, 0x02 +; .byte 0xbb, 0x07, 0x05, 0x08 ; beqz a5, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0xbb, 0x87, 0x05, 0x08 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 ; beqz a5, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e ; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0xbb, 0x87, 0x05, 0x08 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 ; beqz a5, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0xbb, 0x87, 0x05, 0x08 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 ; beqz a5, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e ; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0xbb, 0x87, 0x05, 0x08 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 ; beqz a5, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0xbb, 0x87, 0x05, 0x08 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 ; beqz a5, 0xc ; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 ; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x87, 0x05, 0x08 +; .byte 0xbb, 0x07, 0x05, 0x08 ; beqz a5, 0xc -; .byte 0xd7, 0x34, 0xb0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xb0, 0x9e -; addi a2, sp, 0x21 -; .byte 0xa7, 0x77, 0x06, 0x02 -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x35, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x35, 0x90, 0x9e -; .byte 0xbb, 0x85, 0x05, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 -; .byte 0xd7, 0x34, 0xa0, 0x9e +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e +; .byte 0xbb, 0x07, 0x05, 0x08 +; beqz a5, 0xc +; .byte 0x57, 0x35, 0xb0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xb0, 0x9e ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0x27, 0x04, 0x08, 0x02 +; .byte 0xa7, 0x04, 0x08, 0x02 ; addi t6, a6, 0x10 -; .byte 0xa7, 0x84, 0x0f, 0x02 +; .byte 0x27, 0x85, 0x0f, 0x02 ; addi t6, a6, 0x20 -; .byte 0x27, 0x84, 0x0f, 0x02 -; addi t6, a6, 0x30 ; .byte 0xa7, 0x84, 0x0f, 0x02 +; addi t6, a6, 0x30 +; .byte 0x27, 0x85, 0x0f, 0x02 ; addi t6, a6, 0x40 -; .byte 0xa7, 0x84, 0x0f, 0x02 +; .byte 0x27, 0x85, 0x0f, 0x02 ; addi t6, a6, 0x50 -; .byte 0xa7, 0x84, 0x0f, 0x02 +; .byte 0x27, 0x85, 0x0f, 0x02 ; addi t6, a6, 0x60 -; .byte 0xa7, 0x84, 0x0f, 0x02 -; addi sp, sp, 0x1e0 -; ld s1, -8(sp) -; ld s2, -0x10(sp) -; ld s3, -0x18(sp) -; ld s4, -0x20(sp) -; ld s5, -0x28(sp) -; ld s6, -0x30(sp) -; ld s7, -0x38(sp) -; ld s8, -0x40(sp) -; ld s9, -0x48(sp) -; ld s10, -0x50(sp) -; ld s11, -0x58(sp) +; .byte 0x27, 0x85, 0x0f, 0x02 +; mv a0, a1 +; addi sp, sp, 0x180 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 diff --git a/cranelift/filetests/filetests/isa/riscv64/reftypes.clif b/cranelift/filetests/filetests/isa/riscv64/reftypes.clif index 95ec9d58e841..85bcfdadfce9 100644 --- a/cranelift/filetests/filetests/isa/riscv64/reftypes.clif +++ b/cranelift/filetests/filetests/isa/riscv64/reftypes.clif @@ -90,39 +90,37 @@ block3(v7: r64, v8: r64): ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s7,-8(sp) -; sd s11,-16(sp) +; sd s5,-8(sp) +; sd s9,-16(sp) ; add sp,-48 ; block0: +; mv a3,a0 ; sd a1,16(nominal_sp) -; mv s7,a2 -; mv a1,a0 -; mv a1,a0 -; mv s11,a1 -; load_sym a1,%f+0 -; sd s11,8(nominal_sp) -; callind a1 -; load_addr a2,0(nominal_sp) -; mv a1,s11 -; sd a1,0(a2) -; andi a1,a0,255 -; bne a1,zero,taken(label2),not_taken(label1) +; mv s5,a2 +; mv a3,a0 +; mv s9,a3 +; load_sym a5,%f+0 +; sd s9,8(nominal_sp) +; callind a5 +; mv a3,s9 +; sd a3,0(nominal_sp) +; andi a5,a0,255 +; bne a5,zero,taken(label2),not_taken(label1) ; block1: -; mv a1,s11 +; mv a1,s9 ; ld a0,16(nominal_sp) ; j label3 ; block2: -; mv a0,s11 +; mv a0,s9 ; ld a1,16(nominal_sp) ; j label3 ; block3: -; load_addr a4,0(nominal_sp) -; ld a4,0(a4) -; mv a2,s7 -; sd a4,0(a2) +; ld a2,0(nominal_sp) +; mv a3,s5 +; sd a2,0(a3) ; add sp,+48 -; ld s7,-8(sp) -; ld s11,-16(sp) +; ld s5,-8(sp) +; ld s9,-16(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -134,42 +132,40 @@ block3(v7: r64, v8: r64): ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s7, -8(sp) -; sd s11, -0x10(sp) +; sd s5, -8(sp) +; sd s9, -0x10(sp) ; addi sp, sp, -0x30 ; block1: ; offset 0x1c +; mv a3, a0 ; sd a1, 0x10(sp) -; mv s7, a2 -; mv a1, a0 -; mv a0, a1 -; mv s11, a1 -; auipc a1, 0 -; ld a1, 0xc(a1) +; mv s5, a2 +; mv a0, a3 +; mv s9, a3 +; auipc a5, 0 +; ld a5, 0xc(a5) ; j 0xc ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %f 0 ; .byte 0x00, 0x00, 0x00, 0x00 -; sd s11, 8(sp) -; jalr a1 -; mv a2, sp -; mv a1, s11 -; sd a1, 0(a2) -; andi a1, a0, 0xff -; bnez a1, 0x10 -; block2: ; offset 0x60 -; mv a1, s11 +; sd s9, 8(sp) +; jalr a5 +; mv a3, s9 +; sd a3, 0(sp) +; andi a5, a0, 0xff +; bnez a5, 0x10 +; block2: ; offset 0x5c +; mv a1, s9 ; ld a0, 0x10(sp) ; j 0xc -; block3: ; offset 0x6c -; mv a0, s11 +; block3: ; offset 0x68 +; mv a0, s9 ; ld a1, 0x10(sp) -; block4: ; offset 0x74 -; mv a4, sp -; ld a4, 0(a4) -; mv a2, s7 -; sd a4, 0(a2) +; block4: ; offset 0x70 +; ld a2, 0(sp) +; mv a3, s5 +; sd a2, 0(a3) ; addi sp, sp, 0x30 -; ld s7, -8(sp) -; ld s11, -0x10(sp) +; ld s5, -8(sp) +; ld s9, -0x10(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 diff --git a/cranelift/filetests/filetests/isa/riscv64/stack.clif b/cranelift/filetests/filetests/isa/riscv64/stack.clif index 52f46d024d59..08a73c80436c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/stack.clif +++ b/cranelift/filetests/filetests/isa/riscv64/stack.clif @@ -108,8 +108,7 @@ block0: ; mv fp,sp ; add sp,-16 ; block0: -; load_addr a1,0(nominal_sp) -; ld a0,0(a1) +; ld a0,0(nominal_sp) ; add sp,+16 ; ld ra,8(sp) ; ld fp,0(sp) @@ -124,8 +123,7 @@ block0: ; mv s0, sp ; addi sp, sp, -0x10 ; block1: ; offset 0x14 -; mv a1, sp -; ld a0, 0(a1) +; ld a0, 0(sp) ; addi sp, sp, 0x10 ; ld ra, 8(sp) ; ld s0, 0(sp) @@ -151,8 +149,7 @@ block0: ; call %Probestack ; add sp,-100016 ; block0: -; load_addr a1,0(nominal_sp) -; ld a0,0(a1) +; ld a0,0(nominal_sp) ; add sp,+100016 ; ld ra,8(sp) ; ld fp,0(sp) @@ -177,8 +174,7 @@ block0: ; addi t6, t6, -0x6b0 ; add sp, t6, sp ; block1: ; offset 0x3c -; mv a1, sp -; ld a0, 0(a1) +; ld a0, 0(sp) ; lui t6, 0x18 ; addi t6, t6, 0x6b0 ; add sp, t6, sp @@ -202,8 +198,7 @@ block0(v0: i64): ; mv fp,sp ; add sp,-16 ; block0: -; load_addr a2,0(nominal_sp) -; sd a0,0(a2) +; sd a0,0(nominal_sp) ; add sp,+16 ; ld ra,8(sp) ; ld fp,0(sp) @@ -218,8 +213,7 @@ block0(v0: i64): ; mv s0, sp ; addi sp, sp, -0x10 ; block1: ; offset 0x14 -; mv a2, sp -; sd a0, 0(a2) +; sd a0, 0(sp) ; addi sp, sp, 0x10 ; ld ra, 8(sp) ; ld s0, 0(sp) @@ -245,8 +239,7 @@ block0(v0: i64): ; call %Probestack ; add sp,-100016 ; block0: -; load_addr a2,0(nominal_sp) -; sd a0,0(a2) +; sd a0,0(nominal_sp) ; add sp,+100016 ; ld ra,8(sp) ; ld fp,0(sp) @@ -271,8 +264,7 @@ block0(v0: i64): ; addi t6, t6, -0x6b0 ; add sp, t6, sp ; block1: ; offset 0x3c -; mv a2, sp -; sd a0, 0(a2) +; sd a0, 0(sp) ; lui t6, 0x18 ; addi t6, t6, 0x6b0 ; add sp, t6, sp @@ -858,9 +850,8 @@ block0(v0: i128): ; mv fp,sp ; add sp,-16 ; block0: -; load_addr a3,0(nominal_sp) -; sd a0,0(a3) -; sd a1,8(a3) +; sd a0,0(nominal_sp) +; sd a1,8(nominal_sp) ; add sp,+16 ; ld ra,8(sp) ; ld fp,0(sp) @@ -875,9 +866,8 @@ block0(v0: i128): ; mv s0, sp ; addi sp, sp, -0x10 ; block1: ; offset 0x14 -; mv a3, sp -; sd a0, 0(a3) -; sd a1, 8(a3) +; sd a0, 0(sp) +; sd a1, 8(sp) ; addi sp, sp, 0x10 ; ld ra, 8(sp) ; ld s0, 0(sp) @@ -900,9 +890,8 @@ block0(v0: i128): ; mv fp,sp ; add sp,-32 ; block0: -; load_addr a3,32(nominal_sp) -; sd a0,0(a3) -; sd a1,8(a3) +; sd a0,32(nominal_sp) +; sd a1,40(nominal_sp) ; add sp,+32 ; ld ra,8(sp) ; ld fp,0(sp) @@ -917,9 +906,8 @@ block0(v0: i128): ; mv s0, sp ; addi sp, sp, -0x20 ; block1: ; offset 0x14 -; addi a3, sp, 0x20 -; sd a0, 0(a3) -; sd a1, 8(a3) +; sd a0, 0x20(sp) +; sd a1, 0x28(sp) ; addi sp, sp, 0x20 ; ld ra, 8(sp) ; ld s0, 0(sp) @@ -945,9 +933,8 @@ block0(v0: i128): ; call %Probestack ; add sp,-100016 ; block0: -; load_addr a3,0(nominal_sp) -; sd a0,0(a3) -; sd a1,8(a3) +; sd a0,0(nominal_sp) +; sd a1,8(nominal_sp) ; add sp,+100016 ; ld ra,8(sp) ; ld fp,0(sp) @@ -972,9 +959,8 @@ block0(v0: i128): ; addi t6, t6, -0x6b0 ; add sp, t6, sp ; block1: ; offset 0x3c -; mv a3, sp -; sd a0, 0(a3) -; sd a1, 8(a3) +; sd a0, 0(sp) +; sd a1, 8(sp) ; lui t6, 0x18 ; addi t6, t6, 0x6b0 ; add sp, t6, sp @@ -998,9 +984,8 @@ block0: ; mv fp,sp ; add sp,-16 ; block0: -; load_addr a2,0(nominal_sp) -; ld a0,0(a2) -; ld a1,8(a2) +; ld a0,0(nominal_sp) +; ld a1,8(nominal_sp) ; add sp,+16 ; ld ra,8(sp) ; ld fp,0(sp) @@ -1015,9 +1000,8 @@ block0: ; mv s0, sp ; addi sp, sp, -0x10 ; block1: ; offset 0x14 -; mv a2, sp -; ld a0, 0(a2) -; ld a1, 8(a2) +; ld a0, 0(sp) +; ld a1, 8(sp) ; addi sp, sp, 0x10 ; ld ra, 8(sp) ; ld s0, 0(sp) @@ -1040,9 +1024,8 @@ block0: ; mv fp,sp ; add sp,-32 ; block0: -; load_addr a2,32(nominal_sp) -; ld a0,0(a2) -; ld a1,8(a2) +; ld a0,32(nominal_sp) +; ld a1,40(nominal_sp) ; add sp,+32 ; ld ra,8(sp) ; ld fp,0(sp) @@ -1057,9 +1040,8 @@ block0: ; mv s0, sp ; addi sp, sp, -0x20 ; block1: ; offset 0x14 -; addi a2, sp, 0x20 -; ld a0, 0(a2) -; ld a1, 8(a2) +; ld a0, 0x20(sp) +; ld a1, 0x28(sp) ; addi sp, sp, 0x20 ; ld ra, 8(sp) ; ld s0, 0(sp) @@ -1085,9 +1067,8 @@ block0: ; call %Probestack ; add sp,-100016 ; block0: -; load_addr a2,0(nominal_sp) -; ld a0,0(a2) -; ld a1,8(a2) +; ld a0,0(nominal_sp) +; ld a1,8(nominal_sp) ; add sp,+100016 ; ld ra,8(sp) ; ld fp,0(sp) @@ -1112,9 +1093,8 @@ block0: ; addi t6, t6, -0x6b0 ; add sp, t6, sp ; block1: ; offset 0x3c -; mv a2, sp -; ld a0, 0(a2) -; ld a1, 8(a2) +; ld a0, 0(sp) +; ld a1, 8(sp) ; lui t6, 0x18 ; addi t6, t6, 0x6b0 ; add sp, t6, sp diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index ba9a33c1df02..d27c4fd77cb6 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -41,20 +41,18 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a3,a3,32 -;; ld a4,8(a2) +;; slli a5,a0,32 +;; srli a3,a5,32 +;; ld a0,8(a2) ;; lui a5,-1 -;; addi a5,a5,-4 -;; add a4,a4,a5 -;; ugt a4,a3,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a4,a5,-4 +;; add a0,a0,a4 +;; ugt a0,a3,a0##ty=i64 +;; bne a0,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a2) -;; add a3,a4,a3 -;; lui a4,1 -;; add a3,a3,a4 -;; sw a1,0(a3) +;; ld a2,0(a2) +;; add a2,a2,a3 +;; sw a1,4096(a2) ;; j label2 ;; block2: ;; ret @@ -63,20 +61,18 @@ ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a3,a2,32 -;; ld a2,8(a1) -;; lui a4,-1 -;; addi a4,a4,-4 -;; add a2,a2,a4 -;; ugt a2,a3,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; slli a5,a0,32 +;; srli a2,a5,32 +;; ld a0,8(a1) +;; lui a5,-1 +;; addi a3,a5,-4 +;; add a0,a0,a3 +;; ugt a0,a2,a0##ty=i64 +;; bne a0,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a1) -;; add a3,a4,a3 -;; lui a4,1 -;; add a3,a3,a4 -;; lw a0,0(a3) +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lw a0,4096(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index 1a8ec73ed37c..789410db035a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -41,20 +41,18 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a3,a3,32 -;; ld a4,8(a2) +;; slli a5,a0,32 +;; srli a3,a5,32 +;; ld a0,8(a2) ;; lui a5,-1 -;; addi a5,a5,-1 -;; add a4,a4,a5 -;; ugt a4,a3,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a4,a5,-1 +;; add a0,a0,a4 +;; ugt a0,a3,a0##ty=i64 +;; bne a0,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a2) -;; add a3,a4,a3 -;; lui a4,1 -;; add a3,a3,a4 -;; sb a1,0(a3) +;; ld a2,0(a2) +;; add a2,a2,a3 +;; sb a1,4096(a2) ;; j label2 ;; block2: ;; ret @@ -63,20 +61,18 @@ ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a3,a2,32 -;; ld a2,8(a1) -;; lui a4,-1 -;; addi a4,a4,-1 -;; add a2,a2,a4 -;; ugt a2,a3,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; slli a5,a0,32 +;; srli a2,a5,32 +;; ld a0,8(a1) +;; lui a5,-1 +;; addi a3,a5,-1 +;; add a0,a0,a3 +;; ugt a0,a2,a0##ty=i64 +;; bne a0,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a1) -;; add a3,a4,a3 -;; lui a4,1 -;; add a3,a3,a4 -;; lbu a0,0(a3) +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lbu a0,4096(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index 667a6e5ea6f8..533e1999b405 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -41,17 +41,15 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a2) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; slli a3,a0,32 +;; srli a4,a3,32 +;; ld a3,8(a2) +;; ugt a3,a4,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a2,1 -;; add a0,a0,a2 -;; sw a1,0(a0) +;; ld a5,0(a2) +;; add a4,a5,a4 +;; sw a1,4096(a4) ;; j label2 ;; block2: ;; ret @@ -60,17 +58,15 @@ ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a1) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,8(a1) +;; ugt a3,a4,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a1,1 -;; add a0,a0,a1 -;; lw a0,0(a0) +;; ld a5,0(a1) +;; add a4,a5,a4 +;; lw a0,4096(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index 00f04a204cd9..62e15263099d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -41,17 +41,15 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a2) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; slli a3,a0,32 +;; srli a4,a3,32 +;; ld a3,8(a2) +;; ugt a3,a4,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a2,1 -;; add a0,a0,a2 -;; sb a1,0(a0) +;; ld a5,0(a2) +;; add a4,a5,a4 +;; sb a1,4096(a4) ;; j label2 ;; block2: ;; ret @@ -60,17 +58,15 @@ ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a1) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,8(a1) +;; ugt a3,a4,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a1,1 -;; add a0,a0,a1 -;; lbu a0,0(a0) +;; ld a5,0(a1) +;; add a4,a5,a4 +;; lbu a0,4096(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index 1d8d35da02b1..7dcefcb76636 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -41,18 +41,16 @@ ;; function u0:0: ;; block0: -;; ld a3,8(a2) -;; lui a5,-1 -;; addi a4,a5,-4 -;; add a3,a3,a4 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; ld a4,8(a2) +;; lui a3,-1 +;; addi a5,a3,-4 +;; add a4,a4,a5 +;; ugt a4,a0,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a2,a2,a0 -;; lui a3,1 -;; add a2,a2,a3 -;; sw a1,0(a2) +;; ld a5,0(a2) +;; add a5,a5,a0 +;; sw a1,4096(a5) ;; j label2 ;; block2: ;; ret @@ -61,18 +59,16 @@ ;; ;; function u0:1: ;; block0: -;; ld a2,8(a1) -;; lui a5,-1 -;; addi a3,a5,-4 -;; add a2,a2,a3 -;; ugt a2,a0,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; ld a4,8(a1) +;; lui a3,-1 +;; addi a5,a3,-4 +;; add a4,a4,a5 +;; ugt a4,a0,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a1,a1,a0 -;; lui a2,1 -;; add a1,a1,a2 -;; lw a0,0(a1) +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lw a0,4096(a5) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index a506870ffeba..1d0c8afc3d5a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -41,18 +41,16 @@ ;; function u0:0: ;; block0: -;; ld a3,8(a2) -;; lui a5,-1 -;; addi a4,a5,-1 -;; add a3,a3,a4 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; ld a4,8(a2) +;; lui a3,-1 +;; addi a5,a3,-1 +;; add a4,a4,a5 +;; ugt a4,a0,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a2,a2,a0 -;; lui a3,1 -;; add a2,a2,a3 -;; sb a1,0(a2) +;; ld a5,0(a2) +;; add a5,a5,a0 +;; sb a1,4096(a5) ;; j label2 ;; block2: ;; ret @@ -61,18 +59,16 @@ ;; ;; function u0:1: ;; block0: -;; ld a2,8(a1) -;; lui a5,-1 -;; addi a3,a5,-1 -;; add a2,a2,a3 -;; ugt a2,a0,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; ld a4,8(a1) +;; lui a3,-1 +;; addi a5,a3,-1 +;; add a4,a4,a5 +;; ugt a4,a0,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a1,a1,a0 -;; lui a2,1 -;; add a1,a1,a2 -;; lbu a0,0(a1) +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lbu a0,4096(a5) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index dc8294ba3a4d..8fa233529260 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -45,11 +45,9 @@ ;; ugt a3,a0,a3##ty=i64 ;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a2) -;; add a4,a4,a0 -;; lui a5,1 -;; add a4,a4,a5 -;; sw a1,0(a4) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sw a1,4096(a2) ;; j label2 ;; block2: ;; ret @@ -58,15 +56,13 @@ ;; ;; function u0:1: ;; block0: -;; ld a3,8(a1) -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; ld a2,8(a1) +;; ugt a2,a0,a2##ty=i64 +;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a1) -;; add a4,a4,a0 -;; lui a5,1 -;; add a4,a4,a5 -;; lw a0,0(a4) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lw a0,4096(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index ae0916d97f76..d793c28abcb6 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -45,11 +45,9 @@ ;; ugt a3,a0,a3##ty=i64 ;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a2) -;; add a4,a4,a0 -;; lui a5,1 -;; add a4,a4,a5 -;; sb a1,0(a4) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sb a1,4096(a2) ;; j label2 ;; block2: ;; ret @@ -58,15 +56,13 @@ ;; ;; function u0:1: ;; block0: -;; ld a3,8(a1) -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; ld a2,8(a1) +;; ugt a2,a0,a2##ty=i64 +;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a1) -;; add a4,a4,a0 -;; lui a5,1 -;; add a4,a4,a5 -;; lbu a0,0(a4) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lbu a0,4096(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index f52de3ba484f..d8c6b3db0004 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -39,18 +39,16 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; lui a5,65535 -;; addi a4,a5,-4 -;; ugt a0,a3,a4##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a3,a0,32 +;; srli a5,a3,32 +;; lui a3,65535 +;; addi a0,a3,-4 +;; ugt a4,a5,a0##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a2,a2,a3 -;; lui a3,1 -;; add a2,a2,a3 -;; sw a1,0(a2) +;; ld a0,0(a2) +;; add a5,a0,a5 +;; sw a1,4096(a5) ;; j label2 ;; block2: ;; ret @@ -59,18 +57,16 @@ ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; lui a5,65535 -;; addi a3,a5,-4 -;; ugt a0,a2,a3##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a3,a0,32 +;; srli a5,a3,32 +;; lui a3,65535 +;; addi a0,a3,-4 +;; ugt a4,a5,a0##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a1,a1,a2 -;; lui a2,1 -;; add a1,a1,a2 -;; lw a0,0(a1) +;; ld a0,0(a1) +;; add a5,a0,a5 +;; lw a0,4096(a5) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index 8570a3a7c40b..54bfd5069289 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -39,18 +39,16 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; lui a5,65535 -;; addi a4,a5,-1 -;; ugt a0,a3,a4##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a3,a0,32 +;; srli a5,a3,32 +;; lui a3,65535 +;; addi a0,a3,-1 +;; ugt a4,a5,a0##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a2,a2,a3 -;; lui a3,1 -;; add a2,a2,a3 -;; sb a1,0(a2) +;; ld a0,0(a2) +;; add a5,a0,a5 +;; sb a1,4096(a5) ;; j label2 ;; block2: ;; ret @@ -59,18 +57,16 @@ ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; lui a5,65535 -;; addi a3,a5,-1 -;; ugt a0,a2,a3##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a3,a0,32 +;; srli a5,a3,32 +;; lui a3,65535 +;; addi a0,a3,-1 +;; ugt a4,a5,a0##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a1,a1,a2 -;; lui a2,1 -;; add a1,a1,a2 -;; lbu a0,0(a1) +;; ld a0,0(a1) +;; add a5,a0,a5 +;; lbu a0,4096(a5) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index c1a79596e9a8..0d16208377cd 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -39,26 +39,22 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,0(a2) -;; add a3,a3,a4 -;; lui a4,1 -;; add a3,a3,a4 -;; sw a1,0(a3) +;; slli a0,a0,32 +;; srli a3,a0,32 +;; ld a2,0(a2) +;; add a2,a2,a3 +;; sw a1,4096(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,0(a1) -;; add a3,a3,a4 -;; lui a4,1 -;; add a3,a3,a4 -;; lw a0,0(a3) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lw a0,4096(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index 46016dc22f2a..1a3f9d4396f3 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -39,26 +39,22 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,0(a2) -;; add a3,a3,a4 -;; lui a4,1 -;; add a3,a3,a4 -;; sb a1,0(a3) +;; slli a0,a0,32 +;; srli a3,a0,32 +;; ld a2,0(a2) +;; add a2,a2,a3 +;; sb a1,4096(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,0(a1) -;; add a3,a3,a4 -;; lui a4,1 -;; add a3,a3,a4 -;; lbu a0,0(a3) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lbu a0,4096(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index a967a82735a6..d7355dbe7037 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -39,26 +39,22 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,0(a2) -;; add a3,a3,a4 -;; lui a4,1 -;; add a3,a3,a4 -;; sw a1,0(a3) +;; slli a0,a0,32 +;; srli a3,a0,32 +;; ld a2,0(a2) +;; add a2,a2,a3 +;; sw a1,4096(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,0(a1) -;; add a3,a3,a4 -;; lui a4,1 -;; add a3,a3,a4 -;; lw a0,0(a3) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lw a0,4096(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index 2eb455c6cedc..a6cd0f771bd9 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -39,26 +39,22 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,0(a2) -;; add a3,a3,a4 -;; lui a4,1 -;; add a3,a3,a4 -;; sb a1,0(a3) +;; slli a0,a0,32 +;; srli a3,a0,32 +;; ld a2,0(a2) +;; add a2,a2,a3 +;; sb a1,4096(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,0(a1) -;; add a3,a3,a4 -;; lui a4,1 -;; add a3,a3,a4 -;; lbu a0,0(a3) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lbu a0,4096(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index f44f814a9eb4..9f15ae9dd19f 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -40,15 +40,13 @@ ;; function u0:0: ;; block0: ;; lui a3,65535 -;; addi a5,a3,-4 -;; ugt a4,a0,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a3,a3,-4 +;; ugt a3,a0,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; sw a1,0(a5) +;; ld a3,0(a2) +;; add a3,a3,a0 +;; sw a1,4096(a3) ;; j label2 ;; block2: ;; ret @@ -57,16 +55,14 @@ ;; ;; function u0:1: ;; block0: -;; lui a3,65535 -;; addi a5,a3,-4 -;; ugt a4,a0,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; lui a2,65535 +;; addi a3,a2,-4 +;; ugt a2,a0,a3##ty=i64 +;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; lw a0,0(a5) +;; ld a3,0(a1) +;; add a3,a3,a0 +;; lw a0,4096(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index a571703d9f91..bf67a6fe8b65 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -40,15 +40,13 @@ ;; function u0:0: ;; block0: ;; lui a3,65535 -;; addi a5,a3,-1 -;; ugt a4,a0,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a3,a3,-1 +;; ugt a3,a0,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; sb a1,0(a5) +;; ld a3,0(a2) +;; add a3,a3,a0 +;; sb a1,4096(a3) ;; j label2 ;; block2: ;; ret @@ -57,16 +55,14 @@ ;; ;; function u0:1: ;; block0: -;; lui a3,65535 -;; addi a5,a3,-1 -;; ugt a4,a0,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; lui a2,65535 +;; addi a3,a2,-1 +;; ugt a2,a0,a3##ty=i64 +;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; lbu a0,0(a5) +;; ld a3,0(a1) +;; add a3,a3,a0 +;; lbu a0,4096(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index 1df188f25318..5cd4036a9196 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -40,15 +40,13 @@ ;; function u0:0: ;; block0: ;; lui a3,65535 -;; addi a5,a3,-4 -;; ugt a4,a0,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a3,a3,-4 +;; ugt a3,a0,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; sw a1,0(a5) +;; ld a3,0(a2) +;; add a3,a3,a0 +;; sw a1,4096(a3) ;; j label2 ;; block2: ;; ret @@ -57,16 +55,14 @@ ;; ;; function u0:1: ;; block0: -;; lui a3,65535 -;; addi a5,a3,-4 -;; ugt a4,a0,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; lui a2,65535 +;; addi a3,a2,-4 +;; ugt a2,a0,a3##ty=i64 +;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; lw a0,0(a5) +;; ld a3,0(a1) +;; add a3,a3,a0 +;; lw a0,4096(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index c870967303d4..1c81770d8647 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -40,15 +40,13 @@ ;; function u0:0: ;; block0: ;; lui a3,65535 -;; addi a5,a3,-1 -;; ugt a4,a0,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a3,a3,-1 +;; ugt a3,a0,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; sb a1,0(a5) +;; ld a3,0(a2) +;; add a3,a3,a0 +;; sb a1,4096(a3) ;; j label2 ;; block2: ;; ret @@ -57,16 +55,14 @@ ;; ;; function u0:1: ;; block0: -;; lui a3,65535 -;; addi a5,a3,-1 -;; ugt a4,a0,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; lui a2,65535 +;; addi a3,a2,-1 +;; ugt a2,a0,a3##ty=i64 +;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; lbu a0,0(a5) +;; ld a3,0(a1) +;; add a3,a3,a0 +;; lbu a0,4096(a3) ;; j label2 ;; block2: ;; ret From 6c438d4b64736c34292e94392cca350b2bf5b9bd Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Mon, 25 Sep 2023 15:35:23 +0100 Subject: [PATCH 007/199] riscv64: Add the remainder of Zca and Zcd instructions (#7080) * riscv64: Add `c.li` and `c.lui` * riscv64: Add CB type instructions `c.srli` / `c.srai` / `c.andi` * riscv64: Add `sp` relative load instructions * riscv64: Return Option from try_emit_compressed * riscv64: Implement stack based stores * riscv64: Add compressed stores * riscv64: Add compressed loads --- cranelift/codegen/src/isa/riscv64/inst.isle | 32 ++ .../codegen/src/isa/riscv64/inst/args.rs | 111 +++++- .../codegen/src/isa/riscv64/inst/emit.rs | 235 ++++++++++- .../codegen/src/isa/riscv64/inst/encode.rs | 166 +++++++- .../codegen/src/isa/riscv64/inst/imms.rs | 64 +++ .../filetests/filetests/isa/riscv64/zca.clif | 377 +++++++++++++++++- .../filetests/filetests/isa/riscv64/zcd.clif | 114 ++++++ 7 files changed, 1058 insertions(+), 41 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/riscv64/zcd.clif diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index f7f92fc20d54..aece273057ca 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -741,6 +741,11 @@ (CAddiw) (CAddi16sp) (CSlli) + (CLi) + (CLui) + (CLwsp) + (CLdsp) + (CFldsp) )) ;; Opcodes for the CIW compressed instruction format @@ -748,6 +753,33 @@ (CAddi4spn) )) +;; Opcodes for the CB compressed instruction format +(type CbOp (enum + (CSrli) + (CSrai) + (CAndi) +)) + +;; Opcodes for the CSS compressed instruction format +(type CssOp (enum + (CSwsp) + (CSdsp) + (CFsdsp) +)) + +;; Opcodes for the CS compressed instruction format +(type CsOp (enum + (CSw) + (CSd) + (CFsd) +)) + +;; Opcodes for the CL compressed instruction format +(type ClOp (enum + (CLw) + (CLd) + (CFld) +)) (type CsrRegOP (enum ;; Atomic Read/Write CSR diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs index 469dc531e20c..a75b07bfdfb8 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/args.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs @@ -8,7 +8,7 @@ use crate::ir::condcodes::CondCode; use crate::isa::riscv64::inst::{reg_name, reg_to_gpr_num}; use crate::isa::riscv64::lower::isle::generated_code::{ - COpcodeSpace, CaOp, CiOp, CiwOp, CjOp, CrOp, + COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, CjOp, ClOp, CrOp, CsOp, CssOp, }; use crate::machinst::isle::WritableReg; @@ -1317,6 +1317,15 @@ impl LoadOP { } } + pub(crate) fn size(&self) -> i64 { + match self { + Self::Lb | Self::Lbu => 1, + Self::Lh | Self::Lhu => 2, + Self::Lw | Self::Lwu | Self::Flw => 4, + Self::Ld | Self::Fld => 8, + } + } + pub(crate) fn op_code(self) -> u32 { match self { Self::Lb | Self::Lh | Self::Lw | Self::Lbu | Self::Lhu | Self::Lwu | Self::Ld => { @@ -1363,6 +1372,16 @@ impl StoreOP { _ => unreachable!(), } } + + pub(crate) fn size(&self) -> i64 { + match self { + Self::Sb => 1, + Self::Sh => 2, + Self::Sw | Self::Fsw => 4, + Self::Sd | Self::Fsd => 8, + } + } + pub(crate) fn op_code(self) -> u32 { match self { Self::Sb | Self::Sh | Self::Sw | Self::Sd => 0b0100011, @@ -1983,16 +2002,19 @@ impl CiOp { // https://github.com/michaeljclark/riscv-meta/blob/master/opcodes match self { CiOp::CAddi | CiOp::CSlli => 0b000, - CiOp::CAddiw => 0b001, - CiOp::CAddi16sp => 0b011, + CiOp::CAddiw | CiOp::CFldsp => 0b001, + CiOp::CLi | CiOp::CLwsp => 0b010, + CiOp::CAddi16sp | CiOp::CLui | CiOp::CLdsp => 0b011, } } pub fn op(&self) -> COpcodeSpace { // https://five-embeddev.com/riscv-isa-manual/latest/rvc-opcode-map.html#rvcopcodemap match self { - CiOp::CAddi | CiOp::CAddiw | CiOp::CAddi16sp => COpcodeSpace::C1, - CiOp::CSlli => COpcodeSpace::C2, + CiOp::CAddi | CiOp::CAddiw | CiOp::CAddi16sp | CiOp::CLi | CiOp::CLui => { + COpcodeSpace::C1 + } + CiOp::CSlli | CiOp::CLwsp | CiOp::CLdsp | CiOp::CFldsp => COpcodeSpace::C2, } } } @@ -2012,3 +2034,82 @@ impl CiwOp { } } } + +impl CbOp { + pub fn funct3(&self) -> u32 { + // https://github.com/michaeljclark/riscv-meta/blob/master/opcodes + match self { + CbOp::CSrli | CbOp::CSrai | CbOp::CAndi => 0b100, + } + } + + pub fn funct2(&self) -> u32 { + // https://github.com/michaeljclark/riscv-meta/blob/master/opcodes + match self { + CbOp::CSrli => 0b00, + CbOp::CSrai => 0b01, + CbOp::CAndi => 0b10, + } + } + + pub fn op(&self) -> COpcodeSpace { + // https://five-embeddev.com/riscv-isa-manual/latest/rvc-opcode-map.html#rvcopcodemap + match self { + CbOp::CSrli | CbOp::CSrai | CbOp::CAndi => COpcodeSpace::C1, + } + } +} + +impl CssOp { + pub fn funct3(&self) -> u32 { + // https://github.com/michaeljclark/riscv-meta/blob/master/opcodes + match self { + CssOp::CFsdsp => 0b101, + CssOp::CSwsp => 0b110, + CssOp::CSdsp => 0b111, + } + } + + pub fn op(&self) -> COpcodeSpace { + // https://five-embeddev.com/riscv-isa-manual/latest/rvc-opcode-map.html#rvcopcodemap + match self { + CssOp::CSwsp | CssOp::CSdsp | CssOp::CFsdsp => COpcodeSpace::C2, + } + } +} + +impl CsOp { + pub fn funct3(&self) -> u32 { + // https://github.com/michaeljclark/riscv-meta/blob/master/opcodes + match self { + CsOp::CFsd => 0b101, + CsOp::CSw => 0b110, + CsOp::CSd => 0b111, + } + } + + pub fn op(&self) -> COpcodeSpace { + // https://five-embeddev.com/riscv-isa-manual/latest/rvc-opcode-map.html#rvcopcodemap + match self { + CsOp::CSw | CsOp::CSd | CsOp::CFsd => COpcodeSpace::C0, + } + } +} + +impl ClOp { + pub fn funct3(&self) -> u32 { + // https://github.com/michaeljclark/riscv-meta/blob/master/opcodes + match self { + ClOp::CFld => 0b001, + ClOp::CLw => 0b010, + ClOp::CLd => 0b011, + } + } + + pub fn op(&self) -> COpcodeSpace { + // https://five-embeddev.com/riscv-isa-manual/latest/rvc-opcode-map.html#rvcopcodemap + match self { + ClOp::CLw | ClOp::CLd | ClOp::CFld => COpcodeSpace::C0, + } + } +} diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs index ffc1173d493f..eea635150c97 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs @@ -3,7 +3,9 @@ use crate::binemit::StackMap; use crate::ir::{self, LibCall, RelSourceLoc, TrapCode}; use crate::isa::riscv64::inst::*; -use crate::isa::riscv64::lower::isle::generated_code::{CaOp, CiOp, CiwOp, CrOp}; +use crate::isa::riscv64::lower::isle::generated_code::{ + CaOp, CbOp, CiOp, CiwOp, ClOp, CrOp, CsOp, CssOp, +}; use crate::machinst::{AllocationConsumer, Reg, Writable}; use crate::trace; use cranelift_control::ControlPlane; @@ -432,8 +434,8 @@ impl MachInstEmit for Inst { let mut start_off = sink.cur_offset(); // First try to emit this as a compressed instruction - let success = inst.try_emit_compressed(sink, emit_info, state, &mut start_off); - if !success { + let res = inst.try_emit_compressed(sink, emit_info, state, &mut start_off); + if res.is_none() { // If we can't lets emit it as a normal instruction inst.emit_uncompressed(sink, emit_info, state, &mut start_off); } @@ -462,13 +464,14 @@ impl Inst { emit_info: &EmitInfo, state: &mut EmitState, start_off: &mut u32, - ) -> bool { + ) -> Option<()> { let has_zca = emit_info.isa_flags.has_zca(); + let has_zcd = emit_info.isa_flags.has_zcd(); // Currently all compressed extensions (Zcb, Zcd, Zcmp, Zcmt, etc..) require Zca // to be enabled, so check it early. if !has_zca { - return false; + return None; } fn reg_is_compressible(r: Reg) -> bool { @@ -613,6 +616,17 @@ impl Inst { sink.put2(encode_ciw_type(CiwOp::CAddi4spn, rd, imm)); } + // c.li + Inst::AluRRImm12 { + alu_op: AluOPRRI::Addi, + rd, + rs, + imm12, + } if rd.to_reg() != zero_reg() && rs == zero_reg() && imm12.as_i16() != 0 => { + let imm6 = Imm6::maybe_from_imm12(imm12)?; + sink.put2(encode_ci_type(CiOp::CLi, rd, imm6)); + } + // c.addi Inst::AluRRImm12 { alu_op: AluOPRRI::Addi, @@ -620,11 +634,7 @@ impl Inst { rs, imm12, } if rd.to_reg() == rs && rs != zero_reg() && imm12.as_i16() != 0 => { - let imm6 = match Imm6::maybe_from_imm12(imm12) { - Some(imm6) => imm6, - None => return false, - }; - + let imm6 = Imm6::maybe_from_imm12(imm12)?; sink.put2(encode_ci_type(CiOp::CAddi, rd, imm6)); } @@ -635,13 +645,29 @@ impl Inst { rs, imm12, } if rd.to_reg() == rs && rs != zero_reg() => { - let imm6 = match Imm6::maybe_from_imm12(imm12) { - Some(imm6) => imm6, - None => return false, - }; + let imm6 = Imm6::maybe_from_imm12(imm12)?; sink.put2(encode_ci_type(CiOp::CAddiw, rd, imm6)); } + // c.lui + // + // c.lui loads the non-zero 6-bit immediate field into bits 17–12 + // of the destination register, clears the bottom 12 bits, and + // sign-extends bit 17 into all higher bits of the destination. + Inst::Lui { rd, imm: imm20 } + if rd.to_reg() != zero_reg() + && rd.to_reg() != stack_reg() + && imm20.as_i32() != 0 => + { + // Check that the top bits are sign extended + let imm = imm20.as_i32() << 14 >> 14; + if imm != imm20.as_i32() { + return None; + } + let imm6 = Imm6::maybe_from_i32(imm)?; + sink.put2(encode_ci_type(CiOp::CLui, rd, imm6)); + } + // c.slli Inst::AluRRImm12 { alu_op: AluOPRRI::Slli, @@ -654,10 +680,187 @@ impl Inst { let imm6 = Imm6::maybe_from_i16(shift << 10 >> 10).unwrap(); sink.put2(encode_ci_type(CiOp::CSlli, rd, imm6)); } - _ => return false, + + // c.srli / c.srai + Inst::AluRRImm12 { + alu_op: op @ (AluOPRRI::Srli | AluOPRRI::Srai), + rd, + rs, + imm12, + } if rd.to_reg() == rs && reg_is_compressible(rs) && imm12.as_i16() != 0 => { + let op = match op { + AluOPRRI::Srli => CbOp::CSrli, + AluOPRRI::Srai => CbOp::CSrai, + _ => unreachable!(), + }; + + // The shift amount is unsigned, but we encode it as signed. + let shift = imm12.as_i16() & 0x3f; + let imm6 = Imm6::maybe_from_i16(shift << 10 >> 10).unwrap(); + sink.put2(encode_cb_type(op, rd, imm6)); + } + + // c.andi + Inst::AluRRImm12 { + alu_op: AluOPRRI::Andi, + rd, + rs, + imm12, + } if rd.to_reg() == rs && reg_is_compressible(rs) => { + let imm6 = Imm6::maybe_from_imm12(imm12)?; + sink.put2(encode_cb_type(CbOp::CAndi, rd, imm6)); + } + + // Stack Based Loads + Inst::Load { + rd, + op: op @ (LoadOP::Lw | LoadOP::Ld | LoadOP::Fld), + from, + flags, + } if from.get_base_register() == Some(stack_reg()) + && (from.get_offset_with_state(state) % op.size()) == 0 => + { + // We encode the offset in multiples of the load size. + let offset = from.get_offset_with_state(state); + let imm6 = u8::try_from(offset / op.size()) + .ok() + .and_then(Uimm6::maybe_from_u8)?; + + // Some additional constraints on these instructions. + // + // Integer loads are not allowed to target x0, but floating point loads + // are, since f0 is not a special register. + // + // Floating point loads are not included in the base Zca extension + // but in a separate Zcd extension. Both of these are part of the C Extension. + let rd_is_zero = rd.to_reg() == zero_reg(); + let op = match op { + LoadOP::Lw if !rd_is_zero => CiOp::CLwsp, + LoadOP::Ld if !rd_is_zero => CiOp::CLdsp, + LoadOP::Fld if has_zcd => CiOp::CFldsp, + _ => return None, + }; + + let srcloc = state.cur_srcloc(); + if !srcloc.is_default() && !flags.notrap() { + // Register the offset at which the actual load instruction starts. + sink.add_trap(TrapCode::HeapOutOfBounds); + } + sink.put2(encode_ci_sp_load(op, rd, imm6)); + } + + // Regular Loads + Inst::Load { + rd, + op: op @ (LoadOP::Lw | LoadOP::Ld | LoadOP::Fld), + from, + flags, + } if reg_is_compressible(rd.to_reg()) + && from + .get_base_register() + .map(reg_is_compressible) + .unwrap_or(false) + && (from.get_offset_with_state(state) % op.size()) == 0 => + { + let base = from.get_base_register().unwrap(); + + // We encode the offset in multiples of the store size. + let offset = from.get_offset_with_state(state); + let imm5 = u8::try_from(offset / op.size()) + .ok() + .and_then(Uimm5::maybe_from_u8)?; + + // Floating point loads are not included in the base Zca extension + // but in a separate Zcd extension. Both of these are part of the C Extension. + let op = match op { + LoadOP::Lw => ClOp::CLw, + LoadOP::Ld => ClOp::CLd, + LoadOP::Fld if has_zcd => ClOp::CFld, + _ => return None, + }; + + let srcloc = state.cur_srcloc(); + if !srcloc.is_default() && !flags.notrap() { + // Register the offset at which the actual load instruction starts. + sink.add_trap(TrapCode::HeapOutOfBounds); + } + sink.put2(encode_cl_type(op, rd, base, imm5)); + } + + // Stack Based Stores + Inst::Store { + src, + op: op @ (StoreOP::Sw | StoreOP::Sd | StoreOP::Fsd), + to, + flags, + } if to.get_base_register() == Some(stack_reg()) + && (to.get_offset_with_state(state) % op.size()) == 0 => + { + // We encode the offset in multiples of the store size. + let offset = to.get_offset_with_state(state); + let imm6 = u8::try_from(offset / op.size()) + .ok() + .and_then(Uimm6::maybe_from_u8)?; + + // Floating point stores are not included in the base Zca extension + // but in a separate Zcd extension. Both of these are part of the C Extension. + let op = match op { + StoreOP::Sw => CssOp::CSwsp, + StoreOP::Sd => CssOp::CSdsp, + StoreOP::Fsd if has_zcd => CssOp::CFsdsp, + _ => return None, + }; + + let srcloc = state.cur_srcloc(); + if !srcloc.is_default() && !flags.notrap() { + // Register the offset at which the actual load instruction starts. + sink.add_trap(TrapCode::HeapOutOfBounds); + } + sink.put2(encode_css_type(op, src, imm6)); + } + + // Regular Stores + Inst::Store { + src, + op: op @ (StoreOP::Sw | StoreOP::Sd | StoreOP::Fsd), + to, + flags, + } if reg_is_compressible(src) + && to + .get_base_register() + .map(reg_is_compressible) + .unwrap_or(false) + && (to.get_offset_with_state(state) % op.size()) == 0 => + { + let base = to.get_base_register().unwrap(); + + // We encode the offset in multiples of the store size. + let offset = to.get_offset_with_state(state); + let imm5 = u8::try_from(offset / op.size()) + .ok() + .and_then(Uimm5::maybe_from_u8)?; + + // Floating point stores are not included in the base Zca extension + // but in a separate Zcd extension. Both of these are part of the C Extension. + let op = match op { + StoreOP::Sw => CsOp::CSw, + StoreOP::Sd => CsOp::CSd, + StoreOP::Fsd if has_zcd => CsOp::CFsd, + _ => return None, + }; + + let srcloc = state.cur_srcloc(); + if !srcloc.is_default() && !flags.notrap() { + // Register the offset at which the actual load instruction starts. + sink.add_trap(TrapCode::HeapOutOfBounds); + } + sink.put2(encode_cs_type(op, src, base, imm5)); + } + + _ => return None, } - return true; + return Some(()); } fn emit_uncompressed( diff --git a/cranelift/codegen/src/isa/riscv64/inst/encode.rs b/cranelift/codegen/src/isa/riscv64/inst/encode.rs index 2a06b1093a5f..e3054bafb927 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/encode.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/encode.rs @@ -9,8 +9,9 @@ use super::*; use crate::isa::riscv64::inst::reg_to_gpr_num; use crate::isa::riscv64::lower::isle::generated_code::{ - CaOp, CiOp, CiwOp, CjOp, CrOp, VecAluOpRImm5, VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, - VecAluOpRRRImm5, VecAluOpRRRR, VecElementWidth, VecOpCategory, VecOpMasking, + COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, CjOp, ClOp, CrOp, CsOp, CssOp, VecAluOpRImm5, + VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, VecAluOpRRRImm5, VecAluOpRRRR, VecElementWidth, + VecOpCategory, VecOpMasking, }; use crate::machinst::isle::WritableReg; use crate::Reg; @@ -407,6 +408,41 @@ pub fn encode_ci_type(op: CiOp, rd: WritableReg, imm: Imm6) -> u16 { bits.try_into().unwrap() } +// Stack-Pointer relative loads are regular CI instructions, but, the immediate +// is zero extended, and with a slightly different immediate field encoding. +pub fn encode_ci_sp_load(op: CiOp, rd: WritableReg, imm: Uimm6) -> u16 { + let imm = imm.bits(); + + // These are the spec encoded offsets. + // LWSP: [5|4:2|7:6] + // LDSP: [5|4:3|8:6] + // FLDSP: [5|4:3|8:6] + // + // We don't recieve the entire offset in `imm`, just a multiple of the load-size. + + // Number of bits in the lowest position of imm. 3 for lwsp, 2 for {f,}ldsp. + let low_bits = match op { + CiOp::CLwsp => 3, // [4:2] + CiOp::CLdsp | CiOp::CFldsp => 2, // [4:3] + _ => unreachable!(), + }; + let high_bits = 6 - 1 - low_bits; + let mut enc_imm = 0; + + // Encode [7:6] at the bottom of imm + enc_imm |= imm >> (6 - high_bits); + + // Next place [4:2] in the middle + enc_imm |= (imm & ((1 << low_bits) - 1)) << high_bits; + + // Finally place [5] at the top + enc_imm |= ((imm >> low_bits) & 1) << 5; + + let enc_imm = Imm6::maybe_from_i16((enc_imm as i16) << 10 >> 10).unwrap(); + + encode_ci_type(op, rd, enc_imm) +} + /// c.addi16sp is a regular CI op, but the immediate field is encoded in a weird way pub fn encode_c_addi16sp(imm: Imm6) -> u16 { let imm = imm.bits(); @@ -442,3 +478,129 @@ pub fn encode_ciw_type(op: CiwOp, rd: WritableReg, imm: u8) -> u16 { bits |= unsigned_field_width(op.funct3(), 3) << 13; bits.try_into().unwrap() } + +// Encode a CB type instruction. +// +// The imm field is a 6 bit signed immediate. +// +// 0--1-2-------6-7-------9-10-------11-12-------13--------15 +// |op | imm[4:0] | dst | funct2 | imm[5] | funct3 | +pub fn encode_cb_type(op: CbOp, rd: WritableReg, imm: Imm6) -> u16 { + let imm = imm.bits(); + + let mut bits = 0; + bits |= unsigned_field_width(op.op().bits(), 2); + bits |= unsigned_field_width((imm & 0x1f) as u32, 5) << 2; + bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7; + bits |= unsigned_field_width(op.funct2(), 2) << 10; + bits |= unsigned_field_width(((imm >> 5) & 1) as u32, 1) << 12; + bits |= unsigned_field_width(op.funct3(), 3) << 13; + bits.try_into().unwrap() +} + +// Encode a CSS type instruction. +// +// The imm field is a 6 bit unsigned immediate. +// +// 0--1-2-------6-7--------12-13-------15 +// |op | src | imm | funct3 | +pub fn encode_css_type(op: CssOp, src: Reg, imm: Uimm6) -> u16 { + let imm = imm.bits(); + + // These are the spec encoded offsets. + // c.swsp: [5:2|7:6] + // c.sdsp: [5:3|8:6] + // c.fsdsp: [5:3|8:6] + // + // We don't recieve the entire offset in `imm`, just a multiple of the load-size. + + // Number of bits in the lowest position of imm. 4 for c.swsp, 3 for c.{f,}sdsp. + let low_bits = match op { + CssOp::CSwsp => 4, // [5:2] + CssOp::CSdsp | CssOp::CFsdsp => 3, // [5:3] + }; + let high_bits = 6 - low_bits; + + let mut enc_imm = 0; + enc_imm |= (imm & ((1 << low_bits) - 1)) << high_bits; + enc_imm |= imm >> low_bits; + + let mut bits = 0; + bits |= unsigned_field_width(op.op().bits(), 2); + bits |= reg_to_gpr_num(src) << 2; + bits |= unsigned_field_width(enc_imm as u32, 6) << 7; + bits |= unsigned_field_width(op.funct3(), 3) << 13; + bits.try_into().unwrap() +} + +// Encode a CS type instruction. +// +// The imm field is a 5 bit unsigned immediate. +// +// 0--1-2-----4-5----------6-7---------9-10----------12-13-----15 +// |op | src | imm(2-bit) | base | imm(3-bit) | funct3 | +pub fn encode_cs_type(op: CsOp, src: Reg, base: Reg, imm: Uimm5) -> u16 { + let size = match op { + CsOp::CFsd | CsOp::CSd => 8, + CsOp::CSw => 4, + }; + + encode_cs_cl_type_bits(op.op(), op.funct3(), size, src, base, imm) +} + +// Encode a CL type instruction. +// +// The imm field is a 5 bit unsigned immediate. +// +// 0--1-2------4-5----------6-7---------9-10----------12-13-----15 +// |op | dest | imm(2-bit) | base | imm(3-bit) | funct3 | +pub fn encode_cl_type(op: ClOp, dest: WritableReg, base: Reg, imm: Uimm5) -> u16 { + let size = match op { + ClOp::CFld | ClOp::CLd => 8, + ClOp::CLw => 4, + }; + + encode_cs_cl_type_bits(op.op(), op.funct3(), size, dest.to_reg(), base, imm) +} + +// CL and CS type instructions have the same physical layout. +// +// 0--1-2----------4-5----------6-7---------9-10----------12-13-----15 +// |op | dest/src | imm(2-bit) | base | imm(3-bit) | funct3 | +fn encode_cs_cl_type_bits( + op: COpcodeSpace, + funct3: u32, + size: u32, + dest_src: Reg, + base: Reg, + imm: Uimm5, +) -> u16 { + let imm = imm.bits(); + + // c.sw / c.lw: [2|6] + // c.sd / c.ld: [7:6] + // c.fsd / c.fld: [7:6] + // + // We differentiate these based on the operation size + let imm2 = match size { + 4 => ((imm >> 4) & 1) | ((imm & 1) << 1), + 8 => (imm >> 3) & 0b11, + _ => unreachable!(), + }; + + // [5:3] on all opcodes + let imm3 = match size { + 4 => (imm >> 1) & 0b111, + 8 => (imm >> 0) & 0b111, + _ => unreachable!(), + }; + + let mut bits = 0; + bits |= unsigned_field_width(op.bits(), 2); + bits |= reg_to_compressed_gpr_num(dest_src) << 2; + bits |= unsigned_field_width(imm2 as u32, 2) << 5; + bits |= reg_to_compressed_gpr_num(base) << 7; + bits |= unsigned_field_width(imm3 as u32, 3) << 10; + bits |= unsigned_field_width(funct3, 3) << 13; + bits.try_into().unwrap() +} diff --git a/cranelift/codegen/src/isa/riscv64/inst/imms.rs b/cranelift/codegen/src/isa/riscv64/inst/imms.rs index 6f4d7075db70..a17edcdde291 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/imms.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/imms.rs @@ -183,6 +183,14 @@ impl Imm6 { } } + pub fn maybe_from_i32(value: i32) -> Option { + value.try_into().ok().and_then(Imm6::maybe_from_i16) + } + + pub fn maybe_from_i64(value: i64) -> Option { + value.try_into().ok().and_then(Imm6::maybe_from_i16) + } + pub fn maybe_from_imm12(value: Imm12) -> Option { Imm6::maybe_from_i16(value.as_i16()) } @@ -199,6 +207,62 @@ impl Display for Imm6 { } } +/// A unsigned 6-bit immediate. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Uimm6 { + value: u8, +} + +impl Uimm6 { + /// Create an unsigned 6-bit immediate from an u8 + pub fn maybe_from_u8(value: u8) -> Option { + if value <= 63 { + Some(Self { value }) + } else { + None + } + } + + /// Bits for encoding. + pub fn bits(&self) -> u8 { + self.value & 0x3f + } +} + +impl Display for Uimm6 { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.value) + } +} + +/// A unsigned 5-bit immediate. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Uimm5 { + value: u8, +} + +impl Uimm5 { + /// Create an unsigned 5-bit immediate from an u8 + pub fn maybe_from_u8(value: u8) -> Option { + if value <= 31 { + Some(Self { value }) + } else { + None + } + } + + /// Bits for encoding. + pub fn bits(&self) -> u8 { + self.value & 0x1f + } +} + +impl Display for Uimm5 { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.value) + } +} + impl Inst { pub(crate) fn imm_min() -> i64 { let imm20_max: i64 = (1 << 19) << 12; diff --git a/cranelift/filetests/filetests/isa/riscv64/zca.clif b/cranelift/filetests/filetests/isa/riscv64/zca.clif index 84ee5c541fb3..cc8a16140c49 100644 --- a/cranelift/filetests/filetests/isa/riscv64/zca.clif +++ b/cranelift/filetests/filetests/isa/riscv64/zca.clif @@ -189,10 +189,10 @@ block0(v0: i8): ; Disassembled: ; block0: ; offset 0x0 ; c.addi16sp sp, -0x10 -; sd ra, 8(sp) -; sd s0, 0(sp) +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) ; c.mv s0, sp -; block1: ; offset 0xc +; block1: ; offset 0x8 ; auipc a2, 0 ; ld a2, 0xa(a2) ; c.j 0xa @@ -242,13 +242,13 @@ block0(v0: i64, v1: i64): ; Disassembled: ; block0: ; offset 0x0 ; c.addi16sp sp, -0x10 -; sd ra, 8(sp) -; sd s0, 0(sp) +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) ; c.mv s0, sp -; block1: ; offset 0xc +; block1: ; offset 0x8 ; c.jalr a1 -; ld ra, 8(sp) -; ld s0, 0(sp) +; c.ldsp ra, 8(sp) +; c.ldsp s0, 0(sp) ; c.addi16sp sp, 0x10 ; c.jr ra @@ -372,15 +372,15 @@ block0: ; Disassembled: ; block0: ; offset 0x0 ; c.addi16sp sp, -0x10 -; sd ra, 8(sp) -; sd s0, 0(sp) +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) ; c.mv s0, sp ; c.addi16sp sp, -0x10 -; block1: ; offset 0xe +; block1: ; offset 0xa ; c.mv a0, sp ; c.addi16sp sp, 0x10 -; ld ra, 8(sp) -; ld s0, 0(sp) +; c.ldsp ra, 8(sp) +; c.ldsp s0, 0(sp) ; c.addi16sp sp, 0x10 ; c.jr ra @@ -400,6 +400,37 @@ block0(v0: i64): ; c.slli a0, 0x3f ; c.jr ra +function %c_srai(i64) -> i64 { +block0(v0: i64): + v1 = sshr_imm.i64 v0, 63 + return v1 +} + +; VCode: +; block0: +; srai a0,a0,63 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.srai a0, 0x3f +; c.jr ra + +function %c_srli(i64) -> i64 { +block0(v0: i64): + v1 = ushr_imm.i64 v0, 20 + return v1 +} + +; VCode: +; block0: +; srli a0,a0,20 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.srli a0, 0x14 +; c.jr ra function %c_addi4spn() -> i64 { ss0 = explicit_slot 64 @@ -426,15 +457,325 @@ block0: ; Disassembled: ; block0: ; offset 0x0 ; c.addi16sp sp, -0x10 -; sd ra, 8(sp) -; sd s0, 0(sp) +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) ; c.mv s0, sp ; c.addi16sp sp, -0x40 -; block1: ; offset 0xe +; block1: ; offset 0xa ; c.addi4spn a0, sp, 0x18 ; c.addi16sp sp, 0x40 -; ld ra, 8(sp) -; ld s0, 0(sp) +; c.ldsp ra, 8(sp) +; c.ldsp s0, 0(sp) +; c.addi16sp sp, 0x10 +; c.jr ra + +function %c_li() -> i64 { +block0: + v0 = iconst.i64 1 + return v0 +} + +; VCode: +; block0: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.li a0, 1 +; c.jr ra + + +function %c_lui() -> i64, i64, i64 { +block0: + v0 = iconst.i64 0x4000 + v1 = iconst.i64 0xffffffff_fffff000 + v2 = iconst.i64 0xffffffff_fffe0000 + return v0, v1, v2 +} + +; VCode: +; block0: +; mv a2,a0 +; lui a0,4 +; lui a1,-1 +; lui a4,-32 +; sd a4,0(a2) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.mv a2, a0 +; c.lui a0, 4 +; c.lui a1, 0xfffff +; c.lui a4, 0xfffe0 +; c.sd a4, 0(a2) +; c.jr ra + +function %c_andi_f(i64) -> i64 { +block0(v0: i64): + v1 = band_imm.i64 v0, 0xf + return v1 +} + +; VCode: +; block0: +; andi a0,a0,15 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.andi a0, 0xf +; c.jr ra + +function %c_andi_neg_16(i64) -> i64 { +block0(v0: i64): + v1 = band_imm.i64 v0, -16 + return v1 +} + +; VCode: +; block0: +; andi a0,a0,-16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.andi a0, -0x10 +; c.jr ra + +function %c_andi_zero(i64) -> i64 { +block0(v0: i64): + v1 = band_imm.i64 v0, 0 + return v1 +} + +; VCode: +; block0: +; andi a0,a0,0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.andi a0, 0 +; c.jr ra + +function %c_lwsp() -> i32 { + ss0 = explicit_slot 16 + +block0: + v0 = stack_load.i32 ss0+12 + return v0 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; add sp,-16 +; block0: +; lw a0,12(nominal_sp) +; add sp,+16 +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.addi16sp sp, -0x10 +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) +; c.mv s0, sp +; c.addi16sp sp, -0x10 +; block1: ; offset 0xa +; c.lwsp a0, 0xc(sp) ; c.addi16sp sp, 0x10 +; c.ldsp ra, 8(sp) +; c.ldsp s0, 0(sp) +; c.addi16sp sp, 0x10 +; c.jr ra + +function %c_ldsp() -> i64 { + ss0 = explicit_slot 128 + +block0: + v0 = stack_load.i64 ss0+64 + return v0 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; add sp,-128 +; block0: +; ld a0,64(nominal_sp) +; add sp,+128 +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.addi16sp sp, -0x10 +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) +; c.mv s0, sp +; c.addi16sp sp, -0x80 +; block1: ; offset 0xa +; c.ldsp a0, 0x40(sp) +; c.addi16sp sp, 0x80 +; c.ldsp ra, 8(sp) +; c.ldsp s0, 0(sp) +; c.addi16sp sp, 0x10 +; c.jr ra + +function %c_swsp(i32) { + ss0 = explicit_slot 16 + +block0(v0: i32): + stack_store.i32 v0, ss0+12 + return +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; add sp,-16 +; block0: +; sw a0,12(nominal_sp) +; add sp,+16 +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.addi16sp sp, -0x10 +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) +; c.mv s0, sp +; c.addi16sp sp, -0x10 +; block1: ; offset 0xa +; c.swsp a0, 0xc(sp) +; c.addi16sp sp, 0x10 +; c.ldsp ra, 8(sp) +; c.ldsp s0, 0(sp) +; c.addi16sp sp, 0x10 +; c.jr ra + +function %c_sdsp(i64) { + ss0 = explicit_slot 128 + +block0(v0: i64): + stack_store.i64 v0, ss0+64 + return +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; add sp,-128 +; block0: +; sd a0,64(nominal_sp) +; add sp,+128 +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.addi16sp sp, -0x10 +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) +; c.mv s0, sp +; c.addi16sp sp, -0x80 +; block1: ; offset 0xa +; c.sdsp a0, 0x40(sp) +; c.addi16sp sp, 0x80 +; c.ldsp ra, 8(sp) +; c.ldsp s0, 0(sp) +; c.addi16sp sp, 0x10 +; c.jr ra + + +function %c_sw(i64, i32) { +block0(v0: i64, v1: i32): + store.i32 v1, v0+12 + store.i32 v1, v0-12 + return +} + +; VCode: +; block0: +; sw a1,12(a0) +; sw a1,-12(a0) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.sw a1, 0xc(a0) +; sw a1, -0xc(a0) +; c.jr ra + +function %c_sd(i64, i64) { +block0(v0: i64, v1: i64): + store.i32 v1, v0+16 + store.i32 v1, v0-16 + return +} + +; VCode: +; block0: +; sd a1,16(a0) +; sd a1,-16(a0) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.sd a1, 0x10(a0) +; sd a1, -0x10(a0) +; c.jr ra + +function %c_lw(i64) -> i32 { +block0(v0: i64): + v1 = load.i32 v0+64 + return v1 +} + +; VCode: +; block0: +; lw a0,64(a0) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.lw a0, 0x40(a0) +; c.jr ra + +function %c_ld(i64) -> i64 { +block0(v0: i64): + v1 = load.i64 v0+64 + return v1 +} + +; VCode: +; block0: +; ld a0,64(a0) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.ld a0, 0x40(a0) ; c.jr ra diff --git a/cranelift/filetests/filetests/isa/riscv64/zcd.clif b/cranelift/filetests/filetests/isa/riscv64/zcd.clif new file mode 100644 index 000000000000..f67537775f74 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/zcd.clif @@ -0,0 +1,114 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_zca has_zcd + +function %c_fldsp() -> f64 { + ss0 = explicit_slot 16 + +block0: + v0 = stack_load.f64 ss0+8 + return v0 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; add sp,-16 +; block0: +; fld fa0,8(nominal_sp) +; add sp,+16 +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.addi16sp sp, -0x10 +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) +; c.mv s0, sp +; c.addi16sp sp, -0x10 +; block1: ; offset 0xa +; c.fldsp fa0, 8(sp) +; c.addi16sp sp, 0x10 +; c.ldsp ra, 8(sp) +; c.ldsp s0, 0(sp) +; c.addi16sp sp, 0x10 +; c.jr ra + +function %c_fsdsp(f64) { + ss0 = explicit_slot 128 + +block0(v0: f64): + stack_store.f64 v0, ss0+64 + return +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; add sp,-128 +; block0: +; fsd fa0,64(nominal_sp) +; add sp,+128 +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.addi16sp sp, -0x10 +; c.sdsp ra, 8(sp) +; c.sdsp s0, 0(sp) +; c.mv s0, sp +; c.addi16sp sp, -0x80 +; block1: ; offset 0xa +; c.fsdsp fa0, 0x40(sp) +; c.addi16sp sp, 0x80 +; c.ldsp ra, 8(sp) +; c.ldsp s0, 0(sp) +; c.addi16sp sp, 0x10 +; c.jr ra + + +function %c_fsd(i64, f64) { +block0(v0: i64, v1: f64): + store.i32 v1, v0+16 + store.i32 v1, v0-16 + return +} + +; VCode: +; block0: +; fsd fa0,16(a0) +; fsd fa0,-16(a0) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.fsd fa0, 0x10(a0) +; fsd fa0, -0x10(a0) +; c.jr ra + +function %c_fld(i64) -> f64 { +block0(v0: i64): + v1 = load.f64 v0+64 + return v1 +} + +; VCode: +; block0: +; fld fa0,64(a0) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; c.fld fa0, 0x40(a0) +; c.jr ra + From 6a7ef27b72bcd2d5cc68d99dcd54b061c4335f08 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Mon, 25 Sep 2023 13:11:45 -0700 Subject: [PATCH 008/199] Make `wasmtime::WasmCoreDump` serializable (#7078) This commit makes it so that the library type for core dumps is serializable into the standard binary format for core dumps. Additionally, this commit makes it so that we use the library type for generating core dumps in the CLI. We previously were using a one-off implementation of core dump generation that only had backtrace information and no instances, modules, globals, or memories included. The library type has all that information, so the core dumps produced by our CLI will both be more featureful and be generated by shared code paths going forward. Along the way, implementing all this required some new helper methods sprinkled throughout `wasmtime` and `wasmtime-runtime`: * `wasmtime::Instance::module`: get the module that a `wasmtime::Instance` is an instance of. This is public, since it seems generally useful. This involved adding a new return value from `ModuleRegistry::register_module` that is an identifier that can be used to recover a reference to the registered module. * `wasmtime::Instance::all_{globals,memories}`: get the full global/memory index space. I made these `pub(crate)` out of caution. I don't think we want to commit to exposing non-exported things in the public API, even if we internally need them for debugging-related features like core dumps. These also needed corresponding methods inside `wasmtime-runtime`. * `wasmtime::{Global,Memory}::hash_key`: this was needed to work around the fact that each time you call `{Global,Memory}::from_wasmtime`, it creates a new entry in the `StoreData` and so you can get duplicates. But we need to key some hash maps on globals and memories when constructing core dumps, so we can't treat the underlying `Stored` as a hash key because it isn't stable across duplicate `StoreData` entries. So we have these new methods. They are only `pub(crate)`, are definitely implementation details, and aren't exposed in the public API. * `wasmtime::FrameInfo::module`: Each frame in a backtrace now keeps a handle to its associated module instead of just the name. This is publicly exposed because it seems generally useful. This means I also deprecated `wasmtime::FrameInfo::module_name` since you can now instead do `frame.module().name()` to get that exact same info. I updated callers inside the repo. --- RELEASES.md | 7 + crates/c-api/src/trap.rs | 3 +- crates/cli-flags/src/lib.rs | 3 + crates/runtime/src/instance.rs | 63 +++++++- crates/wasmtime/src/coredump.rs | 198 ++++++++++++++++++++++++- crates/wasmtime/src/externals.rs | 52 +++++++ crates/wasmtime/src/instance.rs | 56 ++++++- crates/wasmtime/src/memory.rs | 46 ++++++ crates/wasmtime/src/module.rs | 16 +- crates/wasmtime/src/module/registry.rs | 56 +++++-- crates/wasmtime/src/store.rs | 54 +++++-- crates/wasmtime/src/trap.rs | 50 +++---- src/commands/run.rs | 76 ++++------ tests/all/component_model/import.rs | 8 +- tests/all/coredump.rs | 5 + tests/all/traps.rs | 10 +- 16 files changed, 587 insertions(+), 116 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 6d1ba437dda8..cc89c3f2231c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,8 +6,15 @@ Unreleased. ### Added +* Added the `wasmtime::FrameInfo::module` method, which returns the + `wasmtime::Module` associated with the stack frame. + ### Changed +* The `wasmtime::FrameInfo::module_name` has been removed, however you can now + get identical results by chaining `wasmtime::FrameInfo::module` and + `wasmtime::Module::name`: `my_frame.module().name()`. + -------------------------------------------------------------------------------- ## 13.0.0 diff --git a/crates/c-api/src/trap.rs b/crates/c-api/src/trap.rs index 44b9dfb6043b..f2a4a64b6082 100644 --- a/crates/c-api/src/trap.rs +++ b/crates/c-api/src/trap.rs @@ -167,7 +167,8 @@ pub extern "C" fn wasmtime_frame_module_name<'a>( .module_name .get_or_init(|| { frame.trace.frames()[frame.idx] - .module_name() + .module() + .name() .map(|s| wasm_name_t::from(s.to_string().into_bytes())) }) .as_ref() diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index 9df8c1147ab8..6e53576d3ee4 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -350,6 +350,9 @@ impl CommonOptions { if let Some(enable) = self.debug.debug_info { config.debug_info(enable); } + if self.debug.coredump.is_some() { + config.coredump_on_trap(true); + } if let Some(level) = self.opts.opt_level { config.cranelift_opt_level(level); } diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index 29c91450501e..7ec1470af249 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -384,6 +384,28 @@ impl Instance { } } + /// Get all globals within this instance. + /// + /// Returns both import and defined globals. + /// + /// Returns both exported and non-exported globals. + /// + /// Gives access to the full globals space. + pub fn all_globals<'a>( + &'a mut self, + ) -> impl ExactSizeIterator + 'a { + let module = self.module().clone(); + module.globals.keys().map(move |idx| { + ( + idx, + ExportGlobal { + definition: self.defined_or_imported_global_ptr(idx), + global: self.module().globals[idx], + }, + ) + }) + } + /// Get the globals defined in this instance (not imported). pub fn defined_globals<'a>( &'a mut self, @@ -1350,14 +1372,43 @@ impl InstanceHandle { self.instance_mut().get_table_with_lazy_init(index, range) } - /// Return the memories defined in this instance (not imported). - pub fn defined_memories<'a>(&'a mut self) -> impl ExactSizeIterator + 'a { - let idxs = (0..self.module().memory_plans.len()) - .skip(self.module().num_imported_memories) + /// Get all memories within this instance. + /// + /// Returns both import and defined memories. + /// + /// Returns both exported and non-exported memories. + /// + /// Gives access to the full memories space. + pub fn all_memories<'a>( + &'a mut self, + ) -> impl ExactSizeIterator + 'a { + let indices = (0..self.module().memory_plans.len()) .map(|i| MemoryIndex::new(i)) .collect::>(); - idxs.into_iter() - .map(|memidx| self.get_exported_memory(memidx)) + indices + .into_iter() + .map(|i| (i, self.get_exported_memory(i))) + } + + /// Return the memories defined in this instance (not imported). + pub fn defined_memories<'a>(&'a mut self) -> impl ExactSizeIterator + 'a { + let num_imported = self.module().num_imported_memories; + self.all_memories() + .skip(num_imported) + .map(|(_i, memory)| memory) + } + + /// Get all globals within this instance. + /// + /// Returns both import and defined globals. + /// + /// Returns both exported and non-exported globals. + /// + /// Gives access to the full globals space. + pub fn all_globals<'a>( + &'a mut self, + ) -> impl ExactSizeIterator + 'a { + self.instance_mut().all_globals() } /// Get the globals defined in this instance (not imported). diff --git a/crates/wasmtime/src/coredump.rs b/crates/wasmtime/src/coredump.rs index ad4d22363eba..3193364eae89 100644 --- a/crates/wasmtime/src/coredump.rs +++ b/crates/wasmtime/src/coredump.rs @@ -1,6 +1,9 @@ -use std::fmt; +use std::{collections::HashMap, fmt}; -use crate::{store::StoreOpaque, FrameInfo, Global, Instance, Memory, Module, WasmBacktrace}; +use crate::{ + store::StoreOpaque, AsContextMut, FrameInfo, Global, Instance, Memory, Module, StoreContextMut, + Val, ValType, WasmBacktrace, +}; /// Representation of a core dump of a WebAssembly module /// @@ -80,6 +83,197 @@ impl WasmCoreDump { pub fn memories(&self) -> &[Memory] { self.memories.as_ref() } + + /// Serialize this core dump into [the standard core dump binary + /// format][spec]. + /// + /// The `name` parameter may be a file path, URL, or arbitrary name for the + /// "main" Wasm service or executable that was running in this store. + /// + /// Once serialized, you can write this core dump to disk, send it over the + /// network, or pass it to other debugging tools that consume Wasm core + /// dumps. + /// + /// [spec]: https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md + pub fn serialize(&self, mut store: impl AsContextMut, name: &str) -> Vec { + let store = store.as_context_mut(); + self._serialize(store, name) + } + + fn _serialize(&self, mut store: StoreContextMut<'_, T>, name: &str) -> Vec { + let mut core_dump = wasm_encoder::Module::new(); + + core_dump.section(&wasm_encoder::CoreDumpSection::new(name)); + + // A map from each memory to its index in the core dump's memories + // section. + let mut memory_to_idx = HashMap::new(); + + let mut data = wasm_encoder::DataSection::new(); + + { + let mut memories = wasm_encoder::MemorySection::new(); + for mem in self.memories() { + let memory_idx = memories.len(); + memory_to_idx.insert(mem.hash_key(&store.0), memory_idx); + let ty = mem.ty(&store); + memories.memory(wasm_encoder::MemoryType { + minimum: mem.size(&store), + maximum: ty.maximum(), + memory64: ty.is_64(), + shared: ty.is_shared(), + }); + + // Attach the memory data, balancing number of data segments and + // binary size. We don't want to attach the whole memory in one + // big segment, since it likely contains a bunch of large runs + // of zeroes. But we can't encode the data without any potential + // runs of zeroes (i.e. including only non-zero data in our + // segments) because we can run up against the implementation + // limits for number of segments in a Wasm module this way. So + // to balance these conflicting desires, we break the memory up + // into reasonably-sized chunks and then trim runs of zeroes + // from the start and end of each chunk. + const CHUNK_SIZE: u32 = 4096; + for (i, chunk) in mem + .data(&store) + .chunks_exact(CHUNK_SIZE as usize) + .enumerate() + { + if let Some(start) = chunk.iter().position(|byte| *byte != 0) { + let end = chunk.iter().rposition(|byte| *byte != 0).unwrap() + 1; + let offset = (i as u32) * CHUNK_SIZE + (start as u32); + let offset = wasm_encoder::ConstExpr::i32_const(offset as i32); + data.active(memory_idx, &offset, chunk[start..end].iter().copied()); + } + } + } + core_dump.section(&memories); + } + + // A map from each global to its index in the core dump's globals + // section. + let mut global_to_idx = HashMap::new(); + + { + let mut globals = wasm_encoder::GlobalSection::new(); + for g in self.globals() { + global_to_idx.insert(g.hash_key(&store.0), globals.len()); + let ty = g.ty(&store); + let mutable = matches!(ty.mutability(), crate::Mutability::Var); + let val_type = match ty.content() { + ValType::I32 => wasm_encoder::ValType::I32, + ValType::I64 => wasm_encoder::ValType::I64, + ValType::F32 => wasm_encoder::ValType::F32, + ValType::F64 => wasm_encoder::ValType::F64, + ValType::V128 => wasm_encoder::ValType::V128, + ValType::FuncRef => wasm_encoder::ValType::FUNCREF, + ValType::ExternRef => wasm_encoder::ValType::EXTERNREF, + }; + let init = match g.get(&mut store) { + Val::I32(x) => wasm_encoder::ConstExpr::i32_const(x), + Val::I64(x) => wasm_encoder::ConstExpr::i64_const(x), + Val::F32(x) => { + wasm_encoder::ConstExpr::f32_const(unsafe { std::mem::transmute(x) }) + } + Val::F64(x) => { + wasm_encoder::ConstExpr::f64_const(unsafe { std::mem::transmute(x) }) + } + Val::V128(x) => wasm_encoder::ConstExpr::v128_const(x.as_u128() as i128), + Val::FuncRef(_) => { + wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::Func) + } + Val::ExternRef(_) => { + wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::Extern) + } + }; + globals.global(wasm_encoder::GlobalType { val_type, mutable }, &init); + } + core_dump.section(&globals); + } + + core_dump.section(&data); + drop(data); + + // A map from module id to its index within the core dump's modules + // section. + let mut module_to_index = HashMap::new(); + + { + let mut modules = wasm_encoder::CoreDumpModulesSection::new(); + for module in self.modules() { + module_to_index.insert(module.id(), modules.len()); + match module.name() { + Some(name) => modules.module(name), + None => modules.module(&format!("", modules.len())), + }; + } + core_dump.section(&modules); + } + + // TODO: We can't currently recover instances from stack frames. We can + // recover module via the frame's PC, but if there are multiple + // instances of the same module, we don't know which instance the frame + // is associated with. Therefore, we do a best effort job: remember the + // last instance of each module and always choose that one. We record + // that information here. + let mut module_to_instance = HashMap::new(); + + { + let mut instances = wasm_encoder::CoreDumpInstancesSection::new(); + for instance in self.instances() { + let module = instance.module(&store); + module_to_instance.insert(module.id(), instances.len()); + + let module_index = module_to_index[&module.id()]; + + let memories = instance + .all_memories(&mut store.0) + .collect::>() + .into_iter() + .map(|(_i, memory)| memory_to_idx[&memory.hash_key(&store.0)]) + .collect::>(); + + let globals = instance + .all_globals(&mut store.0) + .collect::>() + .into_iter() + .map(|(_i, global)| global_to_idx[&global.hash_key(&store.0)]) + .collect::>(); + + instances.instance(module_index, memories, globals); + } + core_dump.section(&instances); + } + + { + let thread_name = "main"; + let mut stack = wasm_encoder::CoreDumpStackSection::new(thread_name); + for frame in self.frames() { + // This isn't necessarily the right instance if there are + // multiple instances of the same module. See comment above + // `module_to_instance` for details. + let instance = module_to_instance[&frame.module().id()]; + + let func = frame.func_index(); + + let offset = frame + .func_offset() + .and_then(|o| u32::try_from(o).ok()) + .unwrap_or(0); + + // We can't currently recover locals and the operand stack. We + // should eventually be able to do that with Winch though. + let locals = []; + let operand_stack = []; + + stack.frame(instance, func, offset, locals, operand_stack); + } + core_dump.section(&stack); + } + + core_dump.finish() + } } impl fmt::Display for WasmCoreDump { diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs index a43b90338a91..2f90384ed77b 100644 --- a/crates/wasmtime/src/externals.rs +++ b/crates/wasmtime/src/externals.rs @@ -349,6 +349,58 @@ impl Global { from: store[self.0].definition, } } + + /// Get a stable hash key for this global. + /// + /// Even if the same underlying global definition is added to the + /// `StoreData` multiple times and becomes multiple `wasmtime::Global`s, + /// this hash key will be consistent across all of these globals. + pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl std::hash::Hash + Eq { + store[self.0].definition as usize + } +} + +#[cfg(test)] +mod global_tests { + use super::*; + use crate::{Instance, Module, Store}; + + #[test] + fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> { + let mut store = Store::<()>::default(); + let module = Module::new( + store.engine(), + r#" + (module + (global (export "g") (mut i32) (i32.const 0)) + ) + "#, + )?; + let instance = Instance::new(&mut store, &module, &[])?; + + // Each time we `get_global`, we call `Global::from_wasmtime` which adds + // a new entry to `StoreData`, so `g1` and `g2` will have different + // indices into `StoreData`. + let g1 = instance.get_global(&mut store, "g").unwrap(); + let g2 = instance.get_global(&mut store, "g").unwrap(); + + // That said, they really point to the same global. + assert_eq!(g1.get(&mut store).unwrap_i32(), 0); + assert_eq!(g2.get(&mut store).unwrap_i32(), 0); + g1.set(&mut store, Val::I32(42))?; + assert_eq!(g1.get(&mut store).unwrap_i32(), 42); + assert_eq!(g2.get(&mut store).unwrap_i32(), 42); + + // And therefore their hash keys are the same. + assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0)); + + // But the hash keys are different from different globals. + let instance2 = Instance::new(&mut store, &module, &[])?; + let g3 = instance2.get_global(&mut store, "g").unwrap(); + assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0)); + + Ok(()) + } } /// A WebAssembly `table`, or an array of values. diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index 95a0d4d3c0cd..0adb96c8d2ed 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -2,7 +2,7 @@ use crate::linker::{Definition, DefinitionType}; use crate::store::{InstanceId, StoreOpaque, Stored}; use crate::types::matching; use crate::{ - AsContextMut, Engine, Export, Extern, Func, Global, Memory, Module, SharedMemory, + AsContextMut, Engine, Export, Extern, Func, Global, Memory, Module, SharedMemory, StoreContext, StoreContextMut, Table, TypedFunc, }; use anyhow::{anyhow, bail, Context, Result}; @@ -260,7 +260,7 @@ impl Instance { // Register the module just before instantiation to ensure we keep the module // properly referenced while in use by the store. - store.modules_mut().register_module(module); + let module_id = store.modules_mut().register_module(module); store.fill_func_refs(); // The first thing we do is issue an instance allocation request @@ -298,7 +298,7 @@ impl Instance { // conflicts with the borrow on `store.engine`) but this doesn't // matter in practice since initialization isn't even running any // code here anyway. - let id = store.add_instance(instance_handle.clone()); + let id = store.add_instance(instance_handle.clone(), module_id); // Additionally, before we start doing fallible instantiation, we // do one more step which is to insert an `InstanceData` @@ -363,6 +363,16 @@ impl Instance { Ok(()) } + /// Get this instance's module. + pub fn module<'a, T: 'a>(&self, store: impl Into>) -> &'a Module { + self._module(store.into().0) + } + + fn _module<'a>(&self, store: &'a StoreOpaque) -> &'a Module { + let InstanceData { id, .. } = store[self.0]; + store.module_for_instance(id).unwrap() + } + /// Returns the list of exported items from this [`Instance`]. /// /// # Panics @@ -536,6 +546,46 @@ impl Instance { pub(crate) fn id(&self, store: &StoreOpaque) -> InstanceId { store[self.0].id } + + /// Get all globals within this instance. + /// + /// Returns both import and defined globals. + /// + /// Returns both exported and non-exported globals. + /// + /// Gives access to the full globals space. + pub(crate) fn all_globals<'a>( + &'a self, + store: &'a mut StoreOpaque, + ) -> impl ExactSizeIterator + 'a { + let data = &store[self.0]; + let instance = store.instance_mut(data.id); + instance + .all_globals() + .collect::>() + .into_iter() + .map(|(i, g)| (i, unsafe { Global::from_wasmtime_global(g, store) })) + } + + /// Get all memories within this instance. + /// + /// Returns both import and defined memories. + /// + /// Returns both exported and non-exported memories. + /// + /// Gives access to the full memories space. + pub(crate) fn all_memories<'a>( + &'a self, + store: &'a mut StoreOpaque, + ) -> impl ExactSizeIterator + 'a { + let data = &store[self.0]; + let instance = store.instance_mut(data.id); + instance + .all_memories() + .collect::>() + .into_iter() + .map(|(i, m)| (i, unsafe { Memory::from_wasmtime_memory(m, store) })) + } } pub(crate) struct OwnedImports { diff --git a/crates/wasmtime/src/memory.rs b/crates/wasmtime/src/memory.rs index c473fa143dd1..94cb354d4246 100644 --- a/crates/wasmtime/src/memory.rs +++ b/crates/wasmtime/src/memory.rs @@ -577,6 +577,15 @@ impl Memory { pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { store.store_data().contains(self.0) } + + /// Get a stable hash key for this memory. + /// + /// Even if the same underlying memory definition is added to the + /// `StoreData` multiple times and becomes multiple `wasmtime::Memory`s, + /// this hash key will be consistent across all of these memories. + pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl std::hash::Hash + Eq { + store[self.0].definition as usize + } } /// A linear memory. This trait provides an interface for raw memory buffers @@ -947,4 +956,41 @@ mod tests { other => panic!("unexpected style {:?}", other), } } + + #[test] + fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> { + let mut store = Store::<()>::default(); + let module = Module::new( + store.engine(), + r#" + (module + (memory (export "m") 1 1) + ) + "#, + )?; + let instance = Instance::new(&mut store, &module, &[])?; + + // Each time we `get_memory`, we call `Memory::from_wasmtime` which adds + // a new entry to `StoreData`, so `g1` and `g2` will have different + // indices into `StoreData`. + let m1 = instance.get_memory(&mut store, "m").unwrap(); + let m2 = instance.get_memory(&mut store, "m").unwrap(); + + // That said, they really point to the same memory. + assert_eq!(m1.data(&store)[0], 0); + assert_eq!(m2.data(&store)[0], 0); + m1.data_mut(&mut store)[0] = 42; + assert_eq!(m1.data(&mut store)[0], 42); + assert_eq!(m2.data(&mut store)[0], 42); + + // And therefore their hash keys are the same. + assert!(m1.hash_key(&store.as_context().0) == m2.hash_key(&store.as_context().0)); + + // But the hash keys are different from different memories. + let instance2 = Instance::new(&mut store, &module, &[])?; + let m3 = instance2.get_memory(&mut store, "m").unwrap(); + assert!(m1.hash_key(&store.as_context().0) != m3.hash_key(&store.as_context().0)); + + Ok(()) + } } diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs index 3e812cfc8543..38a51fcd7ea0 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/module.rs @@ -26,7 +26,9 @@ use wasmtime_runtime::{ mod registry; -pub use registry::{is_wasm_trap_pc, register_code, unregister_code, ModuleRegistry}; +pub use registry::{ + is_wasm_trap_pc, register_code, unregister_code, ModuleRegistry, RegisteredModuleId, +}; /// A compiled WebAssembly module, ready to be instantiated. /// @@ -130,6 +132,14 @@ struct ModuleInner { offsets: VMOffsets, } +impl std::fmt::Debug for Module { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Module") + .field("name", &self.name()) + .finish_non_exhaustive() + } +} + impl Module { /// Creates a new WebAssembly `Module` from the given in-memory `bytes`. /// @@ -1094,6 +1104,10 @@ impl Module { (loc.start as usize, loc.length as usize) }) } + + pub(crate) fn id(&self) -> CompiledModuleId { + self.inner.module.unique_id() + } } impl ModuleInner { diff --git a/crates/wasmtime/src/module/registry.rs b/crates/wasmtime/src/module/registry.rs index 46fb18262be9..65de4bf22536 100644 --- a/crates/wasmtime/src/module/registry.rs +++ b/crates/wasmtime/src/module/registry.rs @@ -44,10 +44,33 @@ struct LoadedCode { modules: BTreeMap, } +/// An identifier of a module that has previously been inserted into a +/// `ModuleRegistry`. +#[derive(Clone, Copy)] +pub enum RegisteredModuleId { + /// Index into `ModuleRegistry::modules_without_code`. + WithoutCode(usize), + /// Start address of the module's code so that we can get it again via + /// `ModuleRegistry::lookup_module`. + LoadedCode(usize), +} + impl ModuleRegistry { + /// Get a previously-registered module by id. + pub fn lookup_module_by_id(&self, id: RegisteredModuleId) -> Option<&Module> { + match id { + RegisteredModuleId::WithoutCode(idx) => self.modules_without_code.get(idx), + RegisteredModuleId::LoadedCode(pc) => { + let (module, _) = self.module_and_offset(pc)?; + Some(module) + } + } + } + /// Fetches information about a registered module given a program counter value. - pub fn lookup_module(&self, pc: usize) -> Option<&dyn ModuleInfo> { - self.module(pc).map(|(m, _)| m.module_info()) + pub fn lookup_module_info(&self, pc: usize) -> Option<&dyn ModuleInfo> { + let (module, _) = self.module_and_offset(pc)?; + Some(module.module_info()) } fn code(&self, pc: usize) -> Option<(&LoadedCode, usize)> { @@ -58,7 +81,7 @@ impl ModuleRegistry { Some((code, pc - *start)) } - fn module(&self, pc: usize) -> Option<(&Module, usize)> { + fn module_and_offset(&self, pc: usize) -> Option<(&Module, usize)> { let (code, offset) = self.code(pc)?; Some((code.module(pc)?, offset)) } @@ -72,17 +95,21 @@ impl ModuleRegistry { } /// Registers a new module with the registry. - pub fn register_module(&mut self, module: &Module) { - self.register(module.code_object(), Some(module)) + pub fn register_module(&mut self, module: &Module) -> RegisteredModuleId { + self.register(module.code_object(), Some(module)).unwrap() } #[cfg(feature = "component-model")] pub fn register_component(&mut self, component: &Component) { - self.register(component.code_object(), None) + self.register(component.code_object(), None); } /// Registers a new module with the registry. - fn register(&mut self, code: &Arc, module: Option<&Module>) { + fn register( + &mut self, + code: &Arc, + module: Option<&Module>, + ) -> Option { let text = code.code_memory().text(); // If there's not actually any functions in this module then we may @@ -92,14 +119,18 @@ impl ModuleRegistry { // module in the future. For that reason we continue to register empty // modules and retain them. if text.is_empty() { - self.modules_without_code.extend(module.cloned()); - return; + return module.map(|module| { + let id = RegisteredModuleId::WithoutCode(self.modules_without_code.len()); + self.modules_without_code.push(module.clone()); + id + }); } // The module code range is exclusive for end, so make it inclusive as // it may be a valid PC value let start_addr = text.as_ptr() as usize; let end_addr = start_addr + text.len() - 1; + let id = module.map(|_| RegisteredModuleId::LoadedCode(start_addr)); // If this module is already present in the registry then that means // it's either an overlapping image, for example for two modules @@ -110,7 +141,7 @@ impl ModuleRegistry { if let Some(module) = module { prev.push_module(module); } - return; + return id; } // Assert that this module's code doesn't collide with any other @@ -131,6 +162,7 @@ impl ModuleRegistry { } let prev = self.loaded_code.insert(end_addr, (start_addr, item)); assert!(prev.is_none()); + id } /// Fetches trap information about a program counter in a backtrace. @@ -148,8 +180,8 @@ impl ModuleRegistry { /// boolean indicates whether the engine used to compile this module is /// using environment variables to control debuginfo parsing. pub(crate) fn lookup_frame_info(&self, pc: usize) -> Option<(FrameInfo, &Module)> { - let (module, offset) = self.module(pc)?; - let info = FrameInfo::new(module, offset)?; + let (module, offset) = self.module_and_offset(pc)?; + let info = FrameInfo::new(module.clone(), offset)?; Some((info, module)) } diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index 88d7af66152f..d6f2bc574475 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -78,7 +78,7 @@ use crate::instance::InstanceData; use crate::linker::Definition; -use crate::module::BareModuleInfo; +use crate::module::{BareModuleInfo, RegisteredModuleId}; use crate::trampoline::VMHostGlobalContext; use crate::{module::ModuleRegistry, Engine, Module, Trap, Val, ValRaw}; use crate::{Global, Instance, Memory}; @@ -433,14 +433,24 @@ where /// instance allocator. struct StoreInstance { handle: InstanceHandle, + kind: StoreInstanceKind, +} + +enum StoreInstanceKind { + /// An actual, non-dummy instance. + Real { + /// The id of this instance's module inside our owning store's + /// `ModuleRegistry`. + module_id: RegisteredModuleId, + }, - /// Whether or not this is a dummy instance that is just an implementation - /// detail for something else. For example, host-created memories internally - /// create a dummy instance. + /// This is a dummy instance that is just an implementation detail for + /// something else. For example, host-created memories internally create a + /// dummy instance. /// - /// Regardless of the configured instance allocator for this engine, dummy + /// Regardless of the configured instance allocator for the engine, dummy /// instances always use the on-demand allocator to deallocate the instance. - dummy: bool, + Dummy, } #[derive(Copy, Clone)] @@ -1253,10 +1263,27 @@ impl StoreOpaque { &mut self.host_globals } - pub unsafe fn add_instance(&mut self, handle: InstanceHandle) -> InstanceId { + pub fn module_for_instance(&self, instance: InstanceId) -> Option<&'_ Module> { + match self.instances[instance.0].kind { + StoreInstanceKind::Dummy => None, + StoreInstanceKind::Real { module_id } => { + let module = self + .modules() + .lookup_module_by_id(module_id) + .expect("should always have a registered module for real instances"); + Some(module) + } + } + } + + pub unsafe fn add_instance( + &mut self, + handle: InstanceHandle, + module_id: RegisteredModuleId, + ) -> InstanceId { self.instances.push(StoreInstance { handle: handle.clone(), - dummy: false, + kind: StoreInstanceKind::Real { module_id }, }); InstanceId(self.instances.len() - 1) } @@ -1269,7 +1296,7 @@ impl StoreOpaque { pub unsafe fn add_dummy_instance(&mut self, handle: InstanceHandle) -> InstanceId { self.instances.push(StoreInstance { handle: handle.clone(), - dummy: true, + kind: StoreInstanceKind::Dummy, }); InstanceId(self.instances.len() - 1) } @@ -1290,7 +1317,7 @@ impl StoreOpaque { .enumerate() .filter_map(|(idx, inst)| { let id = InstanceId::from_index(idx); - if inst.dummy { + if let StoreInstanceKind::Dummy = inst.kind { None } else { Some(InstanceData::from_id(id)) @@ -1304,6 +1331,9 @@ impl StoreOpaque { /// Get all memories (host- or Wasm-defined) within this store. pub fn all_memories<'a>(&'a mut self) -> impl Iterator + 'a { + // NB: Host-created memories have dummy instances. Therefore, we can get + // all memories in the store by iterating over all instances (including + // dummy instances) and getting each of their defined memories. let mems = self .instances .iter_mut() @@ -2266,7 +2296,7 @@ impl Drop for StoreOpaque { let allocator = self.engine.allocator(); let ondemand = OnDemandInstanceAllocator::default(); for instance in self.instances.iter_mut() { - if instance.dummy { + if let StoreInstanceKind::Dummy = instance.kind { ondemand.deallocate_module(&mut instance.handle); } else { allocator.deallocate_module(&mut instance.handle); @@ -2291,7 +2321,7 @@ impl Drop for StoreOpaque { impl wasmtime_runtime::ModuleInfoLookup for ModuleRegistry { fn lookup(&self, pc: usize) -> Option<&dyn ModuleInfo> { - self.lookup_module(pc) + self.lookup_module_info(pc) } } diff --git a/crates/wasmtime/src/trap.rs b/crates/wasmtime/src/trap.rs index 1d0437aa1b28..3f8b2b0f7c49 100644 --- a/crates/wasmtime/src/trap.rs +++ b/crates/wasmtime/src/trap.rs @@ -373,7 +373,7 @@ impl fmt::Display for WasmBacktrace { } else { needs_newline = true; } - let name = frame.module_name().unwrap_or(""); + let name = frame.module().name().unwrap_or(""); write!(f, " {:>3}: ", i)?; if let Some(offset) = frame.module_offset() { @@ -425,7 +425,7 @@ impl fmt::Display for WasmBacktrace { /// to acquire this `FrameInfo`. For more information see [`WasmBacktrace`]. #[derive(Debug)] pub struct FrameInfo { - module_name: Option, + module: Module, func_index: u32, func_name: Option, func_start: FilePos, @@ -438,12 +438,18 @@ impl FrameInfo { /// /// Returns an object if this `pc` is known to this module, or returns `None` /// if no information can be found. - pub(crate) fn new(module: &Module, text_offset: usize) -> Option { - let module = module.compiled_module(); - let (index, _func_offset) = module.func_by_text_offset(text_offset)?; - let info = module.wasm_func_info(index); - let instr = - wasmtime_environ::lookup_file_pos(module.code_memory().address_map_data(), text_offset); + pub(crate) fn new(module: Module, text_offset: usize) -> Option { + let compiled_module = module.compiled_module(); + let (index, _func_offset) = compiled_module.func_by_text_offset(text_offset)?; + let info = compiled_module.wasm_func_info(index); + let func_start = info.start_srcloc; + let instr = wasmtime_environ::lookup_file_pos( + compiled_module.code_memory().address_map_data(), + text_offset, + ); + let index = compiled_module.module().func_index(index); + let func_index = index.index() as u32; + let func_name = compiled_module.func_name(index).map(|s| s.to_string()); // In debug mode for now assert that we found a mapping for `pc` within // the function, because otherwise something is buggy along the way and @@ -453,7 +459,7 @@ impl FrameInfo { // Note that if the module doesn't even have an address map due to // compilation settings then it's expected that `instr` is `None`. debug_assert!( - instr.is_some() || !module.has_address_map(), + instr.is_some() || !compiled_module.has_address_map(), "failed to find instruction for {:#x}", text_offset ); @@ -468,7 +474,7 @@ impl FrameInfo { // custom section contents. let mut symbols = Vec::new(); - if let Some(s) = &module.symbolize_context().ok().and_then(|c| c) { + if let Some(s) = &compiled_module.symbolize_context().ok().and_then(|c| c) { if let Some(offset) = instr.and_then(|i| i.file_offset()) { let to_lookup = u64::from(offset) - s.code_section_offset(); if let Ok(mut frames) = s.addr2line().find_frames(to_lookup).skip_all_loads() { @@ -492,14 +498,12 @@ impl FrameInfo { } } - let index = module.module().func_index(index); - Some(FrameInfo { - module_name: module.module().name.clone(), - func_index: index.index() as u32, - func_name: module.func_name(index).map(|s| s.to_string()), + module, + func_index, + func_name, instr, - func_start: info.start_srcloc, + func_start, symbols, }) } @@ -512,17 +516,11 @@ impl FrameInfo { self.func_index } - /// Returns the identifer of the module that this frame is for. - /// - /// Module identifiers are present in the `name` section of a WebAssembly - /// binary, but this may not return the exact item in the `name` section. - /// Module names can be overwritten at construction time or perhaps inferred - /// from file names. The primary purpose of this function is to assist in - /// debugging and therefore may be tweaked over time. + /// Returns the module for this frame. /// - /// This function returns `None` when no name can be found or inferred. - pub fn module_name(&self) -> Option<&str> { - self.module_name.as_deref() + /// This is the module who's code was being run in this frame. + pub fn module(&self) -> &Module { + &self.module } /// Returns a descriptive name of the function for this frame, if one is diff --git a/src/commands/run.rs b/src/commands/run.rs index 01b04809b10b..93c67c1463b1 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -225,8 +225,8 @@ impl RunCommand { let main = self.load_module(&engine, &self.module_and_args[0])?; // Validate coredump-on-trap argument - if let Some(coredump_path) = &self.common.debug.coredump { - if coredump_path.contains("%") { + if let Some(path) = &self.common.debug.coredump { + if path.contains("%") { bail!("the coredump-on-trap path does not support patterns yet.") } } @@ -529,7 +529,7 @@ impl RunCommand { .wasi_cli_run() .call_run(&mut *store) .context("failed to invoke `run` function") - .map_err(|e| self.handle_coredump(e)); + .map_err(|e| self.handle_core_dump(&mut *store, e)); // Translate the `Result<(),()>` produced by wasm into a feigned // explicit exit here with status 1 if `Err(())` is returned. @@ -583,16 +583,18 @@ impl RunCommand { // Invoke the function and then afterwards print all the results that came // out, if there are any. let mut results = vec![Val::null(); ty.results().len()]; - let invoke_res = func.call(store, &values, &mut results).with_context(|| { - if let Some(name) = &self.invoke { - format!("failed to invoke `{}`", name) - } else { - format!("failed to invoke command default") - } - }); + let invoke_res = func + .call(&mut *store, &values, &mut results) + .with_context(|| { + if let Some(name) = &self.invoke { + format!("failed to invoke `{}`", name) + } else { + format!("failed to invoke command default") + } + }); if let Err(err) = invoke_res { - return Err(self.handle_coredump(err)); + return Err(self.handle_core_dump(&mut *store, err)); } if !results.is_empty() { @@ -617,7 +619,7 @@ impl RunCommand { Ok(()) } - fn handle_coredump(&self, err: Error) -> Error { + fn handle_core_dump(&self, store: &mut Store, err: Error) -> Error { let coredump_path = match &self.common.debug.coredump { Some(path) => path, None => return err, @@ -629,7 +631,7 @@ impl RunCommand { .to_str() .unwrap_or_else(|| "unknown"); - if let Err(coredump_err) = generate_coredump(&err, &source_name, coredump_path) { + if let Err(coredump_err) = write_core_dump(store, &err, &source_name, coredump_path) { eprintln!("warning: coredump failed to generate: {}", coredump_err); err } else { @@ -1037,36 +1039,22 @@ fn ctx_set_listenfd(mut num_fd: usize, builder: &mut WasiCtxBuilder) -> Result Result<()> { - let bt = err - .downcast_ref::() - .ok_or_else(|| anyhow!("no wasm backtrace found to generate coredump with"))?; - - let coredump = wasm_encoder::CoreDumpSection::new(source_name); - let mut stacksection = wasm_encoder::CoreDumpStackSection::new("main"); - for f in bt.frames() { - // We don't have the information at this point to map frames to - // individual instances of a module, so we won't be able to create the - // "frame ∈ instance ∈ module" hierarchy described in the core dump spec - // until we move core dump generation into the runtime. So for now - // instanceidx will be 0 for all frames - let instanceidx = 0; - stacksection.frame( - instanceidx, - f.func_index(), - u32::try_from(f.func_offset().unwrap_or(0)).unwrap(), - // We don't currently have access to locals/stack values - [], - [], - ); - } - let mut module = wasm_encoder::Module::new(); - module.section(&coredump); - module.section(&stacksection); - - let mut f = File::create(coredump_path) - .context(format!("failed to create file at `{}`", coredump_path))?; - f.write_all(module.as_slice()) - .with_context(|| format!("failed to write coredump file at `{}`", coredump_path))?; +fn write_core_dump( + store: &mut Store, + err: &anyhow::Error, + name: &str, + path: &str, +) -> Result<()> { + let core_dump = err + .downcast_ref::() + .expect("should have been configured to capture core dumps"); + + let core_dump = core_dump.serialize(store, name); + + let mut core_dump_file = + File::create(path).context(format!("failed to create file at `{}`", path))?; + core_dump_file + .write_all(&core_dump) + .with_context(|| format!("failed to write core dump file at `{}`", path))?; Ok(()) } diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index e77b9a73433b..9f8ae09f0a7c 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -373,23 +373,23 @@ fn attempt_to_leave_during_malloc() -> Result<()> { assert_eq!(trace.len(), 4); // This was our entry point... - assert_eq!(trace[3].module_name(), Some("m")); + assert_eq!(trace[3].module().name(), Some("m")); assert_eq!(trace[3].func_name(), Some("run")); // ... which called an imported function which ends up being originally // defined by the shim instance. The shim instance then does an indirect // call through a table which goes to the `canon.lower`'d host function - assert_eq!(trace[2].module_name(), Some("host_shim")); + assert_eq!(trace[2].module().name(), Some("host_shim")); assert_eq!(trace[2].func_name(), Some("shim_ret_string")); // ... and the lowered host function will call realloc to allocate space for // the result - assert_eq!(trace[1].module_name(), Some("m")); + assert_eq!(trace[1].module().name(), Some("m")); assert_eq!(trace[1].func_name(), Some("realloc")); // ... but realloc calls the shim instance and tries to exit the // component, triggering a dynamic trap - assert_eq!(trace[0].module_name(), Some("host_shim")); + assert_eq!(trace[0].module().name(), Some("host_shim")); assert_eq!(trace[0].func_name(), Some("shim_thunk")); // In addition to the above trap also ensure that when we enter a wasm diff --git a/tests/all/coredump.rs b/tests/all/coredump.rs index a7ee535f0235..492bce76db64 100644 --- a/tests/all/coredump.rs +++ b/tests/all/coredump.rs @@ -66,6 +66,7 @@ fn coredump_has_stack() -> Result<()> { assert_eq!(cd.frames()[0].func_name().unwrap(), "c"); assert_eq!(cd.frames()[1].func_name().unwrap(), "b"); assert_eq!(cd.frames()[2].func_name().unwrap(), "a"); + let _ = cd.serialize(&mut store, "stack"); Ok(()) } @@ -105,6 +106,7 @@ fn coredump_has_modules_and_instances() -> Result<()> { let cd = e.downcast_ref::().unwrap(); assert_eq!(cd.modules().len(), 2); assert_eq!(cd.instances().len(), 2); + let _ = cd.serialize(&mut store, "modules-and-instances"); Ok(()) } @@ -158,6 +160,7 @@ fn coredump_has_host_globals_and_memory() -> Result<()> { assert_eq!(core_dump.globals().len(), 1); assert_eq!(core_dump.memories().len(), 1); assert_eq!(core_dump.instances().len(), 1); + let _ = core_dump.serialize(&mut store, "host-globals-and-memory"); Ok(()) } @@ -191,6 +194,7 @@ fn coredump_has_defined_globals_and_memory() -> Result<()> { assert_eq!(core_dump.globals().len(), 1); assert_eq!(core_dump.memories().len(), 1); assert_eq!(core_dump.instances().len(), 1); + let _ = core_dump.serialize(&mut store, "defined-globals-and-memory"); Ok(()) } @@ -250,6 +254,7 @@ fn multiple_globals_memories_and_instances() -> Result<()> { assert_eq!(core_dump.globals().len(), 2); assert_eq!(core_dump.memories().len(), 2); assert_eq!(core_dump.instances().len(), 2); + let _ = core_dump.serialize(&mut store, "multi"); Ok(()) } diff --git a/tests/all/traps.rs b/tests/all/traps.rs index 5b7377fa1537..a77443ac209c 100644 --- a/tests/all/traps.rs +++ b/tests/all/traps.rs @@ -130,12 +130,12 @@ fn test_trap_trace() -> Result<()> { let trace = e.downcast_ref::().unwrap().frames(); assert_eq!(trace.len(), 2); - assert_eq!(trace[0].module_name().unwrap(), "hello_mod"); + assert_eq!(trace[0].module().name().unwrap(), "hello_mod"); assert_eq!(trace[0].func_index(), 1); assert_eq!(trace[0].func_name(), Some("hello")); assert_eq!(trace[0].func_offset(), Some(1)); assert_eq!(trace[0].module_offset(), Some(0x26)); - assert_eq!(trace[1].module_name().unwrap(), "hello_mod"); + assert_eq!(trace[1].module().name().unwrap(), "hello_mod"); assert_eq!(trace[1].func_index(), 0); assert_eq!(trace[1].func_name(), None); assert_eq!(trace[1].func_offset(), Some(1)); @@ -254,9 +254,9 @@ fn test_trap_trace_cb() -> Result<()> { let trace = e.downcast_ref::().unwrap().frames(); assert_eq!(trace.len(), 2); - assert_eq!(trace[0].module_name().unwrap(), "hello_mod"); + assert_eq!(trace[0].module().name().unwrap(), "hello_mod"); assert_eq!(trace[0].func_index(), 2); - assert_eq!(trace[1].module_name().unwrap(), "hello_mod"); + assert_eq!(trace[1].module().name().unwrap(), "hello_mod"); assert_eq!(trace[1].func_index(), 1); assert!(format!("{e:?}").contains("cb throw")); @@ -281,7 +281,7 @@ fn test_trap_stack_overflow() -> Result<()> { let trace = e.downcast_ref::().unwrap().frames(); assert!(trace.len() >= 32); for i in 0..trace.len() { - assert_eq!(trace[i].module_name().unwrap(), "rec_mod"); + assert_eq!(trace[i].module().name().unwrap(), "rec_mod"); assert_eq!(trace[i].func_index(), 0); assert_eq!(trace[i].func_name(), Some("run")); } From 2850d7236c398d0d86571e911d46677e51e49bf2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Sep 2023 16:23:18 -0500 Subject: [PATCH 009/199] Optimize wasm calls ever-so-slightly (#7084) * Optimize wasm calls ever-so-slightly * Fix riscv64 * Remove stray comment * Shuffle around where `unsafe` lies --- crates/runtime/src/instance.rs | 1 + crates/wasmtime/Cargo.toml | 4 +++- crates/wasmtime/src/func.rs | 39 +++++++++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index 7ec1470af249..8035f5db7f4a 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -426,6 +426,7 @@ impl Instance { } /// Return a pointer to the interrupts structure + #[inline] pub fn runtime_limits(&mut self) -> *mut *const VMRuntimeLimits { unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_runtime_limits()) } } diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index 6aa56e8f54f1..d735b74b6feb 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -41,7 +41,6 @@ serde_json = { workspace = true } bincode = "1.2.1" indexmap = { workspace = true } paste = "1.0.3" -psm = "0.1.11" once_cell = { workspace = true } rayon = { version = "1.0", optional = true } object = { workspace = true } @@ -56,6 +55,9 @@ features = [ "Win32_System_Diagnostics_Debug", ] +[target.'cfg(target_arch = "s390x")'.dependencies] +psm = "0.1.11" + [dev-dependencies] tempfile = "3.0" wasmtime-wasi = { path = "../wasi", default-features = true } diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 60e2a5047903..3ec071c72980 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -1408,7 +1408,7 @@ fn enter_wasm(store: &mut StoreContextMut<'_, T>) -> Option { return None; } - let stack_pointer = psm::stack_pointer() as usize; + let stack_pointer = stack_pointer(); // Determine the stack pointer where, after which, any wasm code will // immediately trap. This is checked on the entry to all wasm functions. @@ -1435,6 +1435,43 @@ fn enter_wasm(store: &mut StoreContextMut<'_, T>) -> Option { Some(prev_stack) } +#[inline] +fn stack_pointer() -> usize { + let stack_pointer: usize; + cfg_if::cfg_if! { + if #[cfg(target_arch = "x86_64")] { + unsafe { + std::arch::asm!( + "mov {}, rsp", + out(reg) stack_pointer, + options(nostack,nomem), + ); + } + } else if #[cfg(target_arch = "aarch64")] { + unsafe { + std::arch::asm!( + "mov {}, sp", + out(reg) stack_pointer, + options(nostack,nomem), + ); + } + } else if #[cfg(target_arch = "riscv64")] { + unsafe { + std::arch::asm!( + "mv {}, sp", + out(reg) stack_pointer, + options(nostack,nomem), + ); + } + } else if #[cfg(target_arch = "s390x")] { + stack_pointer = psm::stack_pointer() as usize; + } else { + compile_error!("unsupported platform"); + } + } + stack_pointer +} + fn exit_wasm(store: &mut StoreContextMut<'_, T>, prev_stack: Option) { // If we don't have a previous stack pointer to restore, then there's no // cleanup we need to perform here. From d127a14a93a1440a1f1049bb5079bf5e36280875 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Mon, 25 Sep 2023 17:37:39 -0700 Subject: [PATCH 010/199] Add `Func::hash_key` and `Table::hash_key` methods (#7086) * Wasmtime: Move `Global` and `Table` to `externals` submodules Just mechanical code motion, not functional changes. * Wasmtime: Add `hash_key` methods for `Func` and `Table` * Rename `wasmtime::Func::caller_checked_func_ref` to `wasmtime::Func::vm_func_ref` We removed the "caller checked" part of the `VMFuncRef` type's name a while ago, so update this method to be in line with that. No functional changes, just mechanical renaming. * cargo fmt * Fix doc link --- crates/wasmtime/src/externals.rs | 595 +---------------------- crates/wasmtime/src/externals/global.rs | 238 +++++++++ crates/wasmtime/src/externals/table.rs | 403 +++++++++++++++ crates/wasmtime/src/func.rs | 60 ++- crates/wasmtime/src/func/typed.rs | 6 +- crates/wasmtime/src/trampoline/global.rs | 5 +- crates/wasmtime/src/values.rs | 4 +- 7 files changed, 711 insertions(+), 600 deletions(-) create mode 100644 crates/wasmtime/src/externals/global.rs create mode 100644 crates/wasmtime/src/externals/table.rs diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs index 2f90384ed77b..c627f38e2040 100644 --- a/crates/wasmtime/src/externals.rs +++ b/crates/wasmtime/src/externals.rs @@ -1,13 +1,11 @@ -use crate::store::{StoreData, StoreOpaque, Stored}; -use crate::trampoline::{generate_global_export, generate_table_export}; -use crate::{ - AsContext, AsContextMut, Engine, ExternRef, ExternType, Func, GlobalType, Memory, Mutability, - SharedMemory, TableType, Val, ValType, -}; -use anyhow::{anyhow, bail, Result}; -use std::mem; -use std::ptr; -use wasmtime_runtime::{self as runtime}; +use crate::store::StoreOpaque; +use crate::{AsContext, Engine, ExternType, Func, Memory, SharedMemory}; + +mod global; +mod table; + +pub use global::Global; +pub use table::Table; // Externals @@ -170,583 +168,6 @@ impl From for Extern { } } -/// A WebAssembly `global` value which can be read and written to. -/// -/// A `global` in WebAssembly is sort of like a global variable within an -/// [`Instance`](crate::Instance). The `global.get` and `global.set` -/// instructions will modify and read global values in a wasm module. Globals -/// can either be imported or exported from wasm modules. -/// -/// A [`Global`] "belongs" to the store that it was originally created within -/// (either via [`Global::new`] or via instantiating a -/// [`Module`](crate::Module)). Operations on a [`Global`] only work with the -/// store it belongs to, and if another store is passed in by accident then -/// methods will panic. -#[derive(Copy, Clone, Debug)] -#[repr(transparent)] // here for the C API -pub struct Global(Stored); - -impl Global { - /// Creates a new WebAssembly `global` value with the provide type `ty` and - /// initial value `val`. - /// - /// The `store` argument will be the owner of the [`Global`] returned. Using - /// the returned [`Global`] other items in the store may access this global. - /// For example this could be provided as an argument to - /// [`Instance::new`](crate::Instance::new) or - /// [`Linker::define`](crate::Linker::define). - /// - /// # Errors - /// - /// Returns an error if the `ty` provided does not match the type of the - /// value `val`, or if `val` comes from a different store than `store`. - /// - /// # Examples - /// - /// ``` - /// # use wasmtime::*; - /// # fn main() -> anyhow::Result<()> { - /// let engine = Engine::default(); - /// let mut store = Store::new(&engine, ()); - /// - /// let ty = GlobalType::new(ValType::I32, Mutability::Const); - /// let i32_const = Global::new(&mut store, ty, 1i32.into())?; - /// let ty = GlobalType::new(ValType::F64, Mutability::Var); - /// let f64_mut = Global::new(&mut store, ty, 2.0f64.into())?; - /// - /// let module = Module::new( - /// &engine, - /// "(module - /// (global (import \"\" \"i32-const\") i32) - /// (global (import \"\" \"f64-mut\") (mut f64)) - /// )" - /// )?; - /// - /// let mut linker = Linker::new(&engine); - /// linker.define(&store, "", "i32-const", i32_const)?; - /// linker.define(&store, "", "f64-mut", f64_mut)?; - /// - /// let instance = linker.instantiate(&mut store, &module)?; - /// // ... - /// # Ok(()) - /// # } - /// ``` - pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result { - Global::_new(store.as_context_mut().0, ty, val) - } - - fn _new(store: &mut StoreOpaque, ty: GlobalType, val: Val) -> Result { - if !val.comes_from_same_store(store) { - bail!("cross-`Store` globals are not supported"); - } - if val.ty() != *ty.content() { - bail!("value provided does not match the type of this global"); - } - unsafe { - let wasmtime_export = generate_global_export(store, ty, val); - Ok(Global::from_wasmtime_global(wasmtime_export, store)) - } - } - - /// Returns the underlying type of this `global`. - /// - /// # Panics - /// - /// Panics if `store` does not own this global. - pub fn ty(&self, store: impl AsContext) -> GlobalType { - let store = store.as_context(); - let ty = &store[self.0].global; - GlobalType::from_wasmtime_global(&ty) - } - - /// Returns the current [`Val`] of this global. - /// - /// # Panics - /// - /// Panics if `store` does not own this global. - pub fn get(&self, mut store: impl AsContextMut) -> Val { - unsafe { - let store = store.as_context_mut(); - let definition = &*store[self.0].definition; - match self.ty(&store).content() { - ValType::I32 => Val::from(*definition.as_i32()), - ValType::I64 => Val::from(*definition.as_i64()), - ValType::F32 => Val::F32(*definition.as_u32()), - ValType::F64 => Val::F64(*definition.as_u64()), - ValType::ExternRef => Val::ExternRef( - definition - .as_externref() - .clone() - .map(|inner| ExternRef { inner }), - ), - ValType::FuncRef => { - Val::FuncRef(Func::from_raw(store, definition.as_func_ref().cast())) - } - ValType::V128 => Val::V128((*definition.as_u128()).into()), - } - } - } - - /// Attempts to set the current value of this global to [`Val`]. - /// - /// # Errors - /// - /// Returns an error if this global has a different type than `Val`, if - /// it's not a mutable global, or if `val` comes from a different store than - /// the one provided. - /// - /// # Panics - /// - /// Panics if `store` does not own this global. - pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> { - let store = store.as_context_mut().0; - let ty = self.ty(&store); - if ty.mutability() != Mutability::Var { - bail!("immutable global cannot be set"); - } - let ty = ty.content(); - if val.ty() != *ty { - bail!("global of type {:?} cannot be set to {:?}", ty, val.ty()); - } - if !val.comes_from_same_store(store) { - bail!("cross-`Store` values are not supported"); - } - unsafe { - let definition = &mut *store[self.0].definition; - match val { - Val::I32(i) => *definition.as_i32_mut() = i, - Val::I64(i) => *definition.as_i64_mut() = i, - Val::F32(f) => *definition.as_u32_mut() = f, - Val::F64(f) => *definition.as_u64_mut() = f, - Val::FuncRef(f) => { - *definition.as_func_ref_mut() = f.map_or(ptr::null_mut(), |f| { - f.caller_checked_func_ref(store).as_ptr().cast() - }); - } - Val::ExternRef(x) => { - let old = mem::replace(definition.as_externref_mut(), x.map(|x| x.inner)); - drop(old); - } - Val::V128(i) => *definition.as_u128_mut() = i.into(), - } - } - Ok(()) - } - - pub(crate) unsafe fn from_wasmtime_global( - wasmtime_export: wasmtime_runtime::ExportGlobal, - store: &mut StoreOpaque, - ) -> Global { - Global(store.store_data_mut().insert(wasmtime_export)) - } - - pub(crate) fn wasmtime_ty<'a>(&self, data: &'a StoreData) -> &'a wasmtime_environ::Global { - &data[self.0].global - } - - pub(crate) fn vmimport(&self, store: &StoreOpaque) -> wasmtime_runtime::VMGlobalImport { - wasmtime_runtime::VMGlobalImport { - from: store[self.0].definition, - } - } - - /// Get a stable hash key for this global. - /// - /// Even if the same underlying global definition is added to the - /// `StoreData` multiple times and becomes multiple `wasmtime::Global`s, - /// this hash key will be consistent across all of these globals. - pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl std::hash::Hash + Eq { - store[self.0].definition as usize - } -} - -#[cfg(test)] -mod global_tests { - use super::*; - use crate::{Instance, Module, Store}; - - #[test] - fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> { - let mut store = Store::<()>::default(); - let module = Module::new( - store.engine(), - r#" - (module - (global (export "g") (mut i32) (i32.const 0)) - ) - "#, - )?; - let instance = Instance::new(&mut store, &module, &[])?; - - // Each time we `get_global`, we call `Global::from_wasmtime` which adds - // a new entry to `StoreData`, so `g1` and `g2` will have different - // indices into `StoreData`. - let g1 = instance.get_global(&mut store, "g").unwrap(); - let g2 = instance.get_global(&mut store, "g").unwrap(); - - // That said, they really point to the same global. - assert_eq!(g1.get(&mut store).unwrap_i32(), 0); - assert_eq!(g2.get(&mut store).unwrap_i32(), 0); - g1.set(&mut store, Val::I32(42))?; - assert_eq!(g1.get(&mut store).unwrap_i32(), 42); - assert_eq!(g2.get(&mut store).unwrap_i32(), 42); - - // And therefore their hash keys are the same. - assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0)); - - // But the hash keys are different from different globals. - let instance2 = Instance::new(&mut store, &module, &[])?; - let g3 = instance2.get_global(&mut store, "g").unwrap(); - assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0)); - - Ok(()) - } -} - -/// A WebAssembly `table`, or an array of values. -/// -/// Like [`Memory`] a table is an indexed array of values, but unlike [`Memory`] -/// it's an array of WebAssembly reference type values rather than bytes. One of -/// the most common usages of a table is a function table for wasm modules (a -/// `funcref` table), where each element has the `ValType::FuncRef` type. -/// -/// A [`Table`] "belongs" to the store that it was originally created within -/// (either via [`Table::new`] or via instantiating a -/// [`Module`](crate::Module)). Operations on a [`Table`] only work with the -/// store it belongs to, and if another store is passed in by accident then -/// methods will panic. -#[derive(Copy, Clone, Debug)] -#[repr(transparent)] // here for the C API -pub struct Table(Stored); - -impl Table { - /// Creates a new [`Table`] with the given parameters. - /// - /// * `store` - the owner of the resulting [`Table`] - /// * `ty` - the type of this table, containing both the element type as - /// well as the initial size and maximum size, if any. - /// * `init` - the initial value to fill all table entries with, if the - /// table starts with an initial size. - /// - /// # Errors - /// - /// Returns an error if `init` does not match the element type of the table, - /// or if `init` does not belong to the `store` provided. - /// - /// # Panics - /// - /// This function will panic when used with a [`Store`](`crate::Store`) - /// which has a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) - /// (see also: [`Store::limiter_async`](`crate::Store::limiter_async`). - /// When using an async resource limiter, use [`Table::new_async`] - /// instead. - /// - /// # Examples - /// - /// ``` - /// # use wasmtime::*; - /// # fn main() -> anyhow::Result<()> { - /// let engine = Engine::default(); - /// let mut store = Store::new(&engine, ()); - /// - /// let ty = TableType::new(ValType::FuncRef, 2, None); - /// let table = Table::new(&mut store, ty, Val::FuncRef(None))?; - /// - /// let module = Module::new( - /// &engine, - /// "(module - /// (table (import \"\" \"\") 2 funcref) - /// (func $f (result i32) - /// i32.const 10) - /// (elem (i32.const 0) $f) - /// )" - /// )?; - /// - /// let instance = Instance::new(&mut store, &module, &[table.into()])?; - /// // ... - /// # Ok(()) - /// # } - /// ``` - pub fn new(mut store: impl AsContextMut, ty: TableType, init: Val) -> Result
{ - Table::_new(store.as_context_mut().0, ty, init) - } - - #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] - /// Async variant of [`Table::new`]. You must use this variant with - /// [`Store`](`crate::Store`)s which have a - /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`). - /// - /// # Panics - /// - /// This function will panic when used with a non-async - /// [`Store`](`crate::Store`) - #[cfg(feature = "async")] - pub async fn new_async( - mut store: impl AsContextMut, - ty: TableType, - init: Val, - ) -> Result
- where - T: Send, - { - let mut store = store.as_context_mut(); - assert!( - store.0.async_support(), - "cannot use `new_async` without enabling async support on the config" - ); - store - .on_fiber(|store| Table::_new(store.0, ty, init)) - .await? - } - - fn _new(store: &mut StoreOpaque, ty: TableType, init: Val) -> Result
{ - let wasmtime_export = generate_table_export(store, &ty)?; - let init = init.into_table_element(store, ty.element())?; - unsafe { - let table = Table::from_wasmtime_table(wasmtime_export, store); - (*table.wasmtime_table(store, std::iter::empty())).fill(0, init, ty.minimum())?; - - Ok(table) - } - } - - /// Returns the underlying type of this table, including its element type as - /// well as the maximum/minimum lower bounds. - /// - /// # Panics - /// - /// Panics if `store` does not own this table. - pub fn ty(&self, store: impl AsContext) -> TableType { - let store = store.as_context(); - let ty = &store[self.0].table.table; - TableType::from_wasmtime_table(ty) - } - - fn wasmtime_table( - &self, - store: &mut StoreOpaque, - lazy_init_range: impl Iterator, - ) -> *mut runtime::Table { - unsafe { - let export = &store[self.0]; - wasmtime_runtime::Instance::from_vmctx(export.vmctx, |handle| { - let idx = handle.table_index(&*export.definition); - handle.get_defined_table_with_lazy_init(idx, lazy_init_range) - }) - } - } - - /// Returns the table element value at `index`. - /// - /// Returns `None` if `index` is out of bounds. - /// - /// # Panics - /// - /// Panics if `store` does not own this table. - pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Option { - let store = store.as_context_mut().0; - let table = self.wasmtime_table(store, std::iter::once(index)); - unsafe { - match (*table).get(index)? { - runtime::TableElement::FuncRef(f) => { - let func = Func::from_caller_checked_func_ref(store, f); - Some(Val::FuncRef(func)) - } - runtime::TableElement::ExternRef(None) => Some(Val::ExternRef(None)), - runtime::TableElement::ExternRef(Some(x)) => { - Some(Val::ExternRef(Some(ExternRef { inner: x }))) - } - runtime::TableElement::UninitFunc => { - unreachable!("lazy init above should have converted UninitFunc") - } - } - } - } - - /// Writes the `val` provided into `index` within this table. - /// - /// # Errors - /// - /// Returns an error if `index` is out of bounds, if `val` does not have - /// the right type to be stored in this table, or if `val` belongs to a - /// different store. - /// - /// # Panics - /// - /// Panics if `store` does not own this table. - pub fn set(&self, mut store: impl AsContextMut, index: u32, val: Val) -> Result<()> { - let store = store.as_context_mut().0; - let ty = self.ty(&store).element().clone(); - let val = val.into_table_element(store, ty)?; - let table = self.wasmtime_table(store, std::iter::empty()); - unsafe { - (*table) - .set(index, val) - .map_err(|()| anyhow!("table element index out of bounds")) - } - } - - /// Returns the current size of this table. - /// - /// # Panics - /// - /// Panics if `store` does not own this table. - pub fn size(&self, store: impl AsContext) -> u32 { - self.internal_size(store.as_context().0) - } - - pub(crate) fn internal_size(&self, store: &StoreOpaque) -> u32 { - unsafe { (*store[self.0].definition).current_elements } - } - - /// Grows the size of this table by `delta` more elements, initialization - /// all new elements to `init`. - /// - /// Returns the previous size of this table if successful. - /// - /// # Errors - /// - /// Returns an error if the table cannot be grown by `delta`, for example - /// if it would cause the table to exceed its maximum size. Also returns an - /// error if `init` is not of the right type or if `init` does not belong to - /// `store`. - /// - /// # Panics - /// - /// Panics if `store` does not own this table. - /// - /// This function will panic when used with a [`Store`](`crate::Store`) - /// which has a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) - /// (see also: [`Store::limiter_async`](`crate::Store::limiter_async`)). - /// When using an async resource limiter, use [`Table::grow_async`] - /// instead. - pub fn grow(&self, mut store: impl AsContextMut, delta: u32, init: Val) -> Result { - let store = store.as_context_mut().0; - let ty = self.ty(&store).element().clone(); - let init = init.into_table_element(store, ty)?; - let table = self.wasmtime_table(store, std::iter::empty()); - unsafe { - match (*table).grow(delta, init, store)? { - Some(size) => { - let vm = (*table).vmtable(); - *store[self.0].definition = vm; - Ok(size) - } - None => bail!("failed to grow table by `{}`", delta), - } - } - } - - #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] - /// Async variant of [`Table::grow`]. Required when using a - /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`). - /// - /// # Panics - /// - /// This function will panic when used with a non-async - /// [`Store`](`crate::Store`). - #[cfg(feature = "async")] - pub async fn grow_async( - &self, - mut store: impl AsContextMut, - delta: u32, - init: Val, - ) -> Result - where - T: Send, - { - let mut store = store.as_context_mut(); - assert!( - store.0.async_support(), - "cannot use `grow_async` without enabling async support on the config" - ); - store - .on_fiber(|store| self.grow(store, delta, init)) - .await? - } - - /// Copy `len` elements from `src_table[src_index..]` into - /// `dst_table[dst_index..]`. - /// - /// # Errors - /// - /// Returns an error if the range is out of bounds of either the source or - /// destination tables. - /// - /// # Panics - /// - /// Panics if `store` does not own either `dst_table` or `src_table`. - pub fn copy( - mut store: impl AsContextMut, - dst_table: &Table, - dst_index: u32, - src_table: &Table, - src_index: u32, - len: u32, - ) -> Result<()> { - let store = store.as_context_mut().0; - if dst_table.ty(&store).element() != src_table.ty(&store).element() { - bail!("tables do not have the same element type"); - } - - let dst_table = dst_table.wasmtime_table(store, std::iter::empty()); - let src_range = src_index..(src_index.checked_add(len).unwrap_or(u32::MAX)); - let src_table = src_table.wasmtime_table(store, src_range); - unsafe { - runtime::Table::copy(dst_table, src_table, dst_index, src_index, len)?; - } - Ok(()) - } - - /// Fill `table[dst..(dst + len)]` with the given value. - /// - /// # Errors - /// - /// Returns an error if - /// - /// * `val` is not of the same type as this table's - /// element type, - /// - /// * the region to be filled is out of bounds, or - /// - /// * `val` comes from a different `Store` from this table. - /// - /// # Panics - /// - /// Panics if `store` does not own either `dst_table` or `src_table`. - pub fn fill(&self, mut store: impl AsContextMut, dst: u32, val: Val, len: u32) -> Result<()> { - let store = store.as_context_mut().0; - let ty = self.ty(&store).element().clone(); - let val = val.into_table_element(store, ty)?; - - let table = self.wasmtime_table(store, std::iter::empty()); - unsafe { - (*table).fill(dst, val, len)?; - } - - Ok(()) - } - - pub(crate) unsafe fn from_wasmtime_table( - wasmtime_export: wasmtime_runtime::ExportTable, - store: &mut StoreOpaque, - ) -> Table { - Table(store.store_data_mut().insert(wasmtime_export)) - } - - pub(crate) fn wasmtime_ty<'a>(&self, data: &'a StoreData) -> &'a wasmtime_environ::Table { - &data[self.0].table.table - } - - pub(crate) fn vmimport(&self, store: &StoreOpaque) -> wasmtime_runtime::VMTableImport { - let export = &store[self.0]; - wasmtime_runtime::VMTableImport { - from: export.definition, - vmctx: export.vmctx, - } - } -} - // Exports /// An exported WebAssembly value. diff --git a/crates/wasmtime/src/externals/global.rs b/crates/wasmtime/src/externals/global.rs new file mode 100644 index 000000000000..fb089ba9f52b --- /dev/null +++ b/crates/wasmtime/src/externals/global.rs @@ -0,0 +1,238 @@ +use crate::store::{StoreData, StoreOpaque, Stored}; +use crate::trampoline::generate_global_export; +use crate::{AsContext, AsContextMut, ExternRef, Func, GlobalType, Mutability, Val, ValType}; +use anyhow::{bail, Result}; +use std::mem; +use std::ptr; + +/// A WebAssembly `global` value which can be read and written to. +/// +/// A `global` in WebAssembly is sort of like a global variable within an +/// [`Instance`](crate::Instance). The `global.get` and `global.set` +/// instructions will modify and read global values in a wasm module. Globals +/// can either be imported or exported from wasm modules. +/// +/// A [`Global`] "belongs" to the store that it was originally created within +/// (either via [`Global::new`] or via instantiating a +/// [`Module`](crate::Module)). Operations on a [`Global`] only work with the +/// store it belongs to, and if another store is passed in by accident then +/// methods will panic. +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] // here for the C API +pub struct Global(pub(super) Stored); + +impl Global { + /// Creates a new WebAssembly `global` value with the provide type `ty` and + /// initial value `val`. + /// + /// The `store` argument will be the owner of the [`Global`] returned. Using + /// the returned [`Global`] other items in the store may access this global. + /// For example this could be provided as an argument to + /// [`Instance::new`](crate::Instance::new) or + /// [`Linker::define`](crate::Linker::define). + /// + /// # Errors + /// + /// Returns an error if the `ty` provided does not match the type of the + /// value `val`, or if `val` comes from a different store than `store`. + /// + /// # Examples + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// let engine = Engine::default(); + /// let mut store = Store::new(&engine, ()); + /// + /// let ty = GlobalType::new(ValType::I32, Mutability::Const); + /// let i32_const = Global::new(&mut store, ty, 1i32.into())?; + /// let ty = GlobalType::new(ValType::F64, Mutability::Var); + /// let f64_mut = Global::new(&mut store, ty, 2.0f64.into())?; + /// + /// let module = Module::new( + /// &engine, + /// "(module + /// (global (import \"\" \"i32-const\") i32) + /// (global (import \"\" \"f64-mut\") (mut f64)) + /// )" + /// )?; + /// + /// let mut linker = Linker::new(&engine); + /// linker.define(&store, "", "i32-const", i32_const)?; + /// linker.define(&store, "", "f64-mut", f64_mut)?; + /// + /// let instance = linker.instantiate(&mut store, &module)?; + /// // ... + /// # Ok(()) + /// # } + /// ``` + pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result { + Global::_new(store.as_context_mut().0, ty, val) + } + + fn _new(store: &mut StoreOpaque, ty: GlobalType, val: Val) -> Result { + if !val.comes_from_same_store(store) { + bail!("cross-`Store` globals are not supported"); + } + if val.ty() != *ty.content() { + bail!("value provided does not match the type of this global"); + } + unsafe { + let wasmtime_export = generate_global_export(store, ty, val); + Ok(Global::from_wasmtime_global(wasmtime_export, store)) + } + } + + /// Returns the underlying type of this `global`. + /// + /// # Panics + /// + /// Panics if `store` does not own this global. + pub fn ty(&self, store: impl AsContext) -> GlobalType { + let store = store.as_context(); + let ty = &store[self.0].global; + GlobalType::from_wasmtime_global(&ty) + } + + /// Returns the current [`Val`] of this global. + /// + /// # Panics + /// + /// Panics if `store` does not own this global. + pub fn get(&self, mut store: impl AsContextMut) -> Val { + unsafe { + let store = store.as_context_mut(); + let definition = &*store[self.0].definition; + match self.ty(&store).content() { + ValType::I32 => Val::from(*definition.as_i32()), + ValType::I64 => Val::from(*definition.as_i64()), + ValType::F32 => Val::F32(*definition.as_u32()), + ValType::F64 => Val::F64(*definition.as_u64()), + ValType::ExternRef => Val::ExternRef( + definition + .as_externref() + .clone() + .map(|inner| ExternRef { inner }), + ), + ValType::FuncRef => { + Val::FuncRef(Func::from_raw(store, definition.as_func_ref().cast())) + } + ValType::V128 => Val::V128((*definition.as_u128()).into()), + } + } + } + + /// Attempts to set the current value of this global to [`Val`]. + /// + /// # Errors + /// + /// Returns an error if this global has a different type than `Val`, if + /// it's not a mutable global, or if `val` comes from a different store than + /// the one provided. + /// + /// # Panics + /// + /// Panics if `store` does not own this global. + pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> { + let store = store.as_context_mut().0; + let ty = self.ty(&store); + if ty.mutability() != Mutability::Var { + bail!("immutable global cannot be set"); + } + let ty = ty.content(); + if val.ty() != *ty { + bail!("global of type {:?} cannot be set to {:?}", ty, val.ty()); + } + if !val.comes_from_same_store(store) { + bail!("cross-`Store` values are not supported"); + } + unsafe { + let definition = &mut *store[self.0].definition; + match val { + Val::I32(i) => *definition.as_i32_mut() = i, + Val::I64(i) => *definition.as_i64_mut() = i, + Val::F32(f) => *definition.as_u32_mut() = f, + Val::F64(f) => *definition.as_u64_mut() = f, + Val::FuncRef(f) => { + *definition.as_func_ref_mut() = + f.map_or(ptr::null_mut(), |f| f.vm_func_ref(store).as_ptr().cast()); + } + Val::ExternRef(x) => { + let old = mem::replace(definition.as_externref_mut(), x.map(|x| x.inner)); + drop(old); + } + Val::V128(i) => *definition.as_u128_mut() = i.into(), + } + } + Ok(()) + } + + pub(crate) unsafe fn from_wasmtime_global( + wasmtime_export: wasmtime_runtime::ExportGlobal, + store: &mut StoreOpaque, + ) -> Global { + Global(store.store_data_mut().insert(wasmtime_export)) + } + + pub(crate) fn wasmtime_ty<'a>(&self, data: &'a StoreData) -> &'a wasmtime_environ::Global { + &data[self.0].global + } + + pub(crate) fn vmimport(&self, store: &StoreOpaque) -> wasmtime_runtime::VMGlobalImport { + wasmtime_runtime::VMGlobalImport { + from: store[self.0].definition, + } + } + + /// Get a stable hash key for this global. + /// + /// Even if the same underlying global definition is added to the + /// `StoreData` multiple times and becomes multiple `wasmtime::Global`s, + /// this hash key will be consistent across all of these globals. + pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl std::hash::Hash + Eq { + store[self.0].definition as usize + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Instance, Module, Store}; + + #[test] + fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> { + let mut store = Store::<()>::default(); + let module = Module::new( + store.engine(), + r#" + (module + (global (export "g") (mut i32) (i32.const 0)) + ) + "#, + )?; + let instance = Instance::new(&mut store, &module, &[])?; + + // Each time we `get_global`, we call `Global::from_wasmtime` which adds + // a new entry to `StoreData`, so `g1` and `g2` will have different + // indices into `StoreData`. + let g1 = instance.get_global(&mut store, "g").unwrap(); + let g2 = instance.get_global(&mut store, "g").unwrap(); + + // That said, they really point to the same global. + assert_eq!(g1.get(&mut store).unwrap_i32(), 0); + assert_eq!(g2.get(&mut store).unwrap_i32(), 0); + g1.set(&mut store, Val::I32(42))?; + assert_eq!(g1.get(&mut store).unwrap_i32(), 42); + assert_eq!(g2.get(&mut store).unwrap_i32(), 42); + + // And therefore their hash keys are the same. + assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0)); + + // But the hash keys are different from different globals. + let instance2 = Instance::new(&mut store, &module, &[])?; + let g3 = instance2.get_global(&mut store, "g").unwrap(); + assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0)); + + Ok(()) + } +} diff --git a/crates/wasmtime/src/externals/table.rs b/crates/wasmtime/src/externals/table.rs new file mode 100644 index 000000000000..cbd688207d5d --- /dev/null +++ b/crates/wasmtime/src/externals/table.rs @@ -0,0 +1,403 @@ +use crate::store::{StoreData, StoreOpaque, Stored}; +use crate::trampoline::generate_table_export; +use crate::{AsContext, AsContextMut, ExternRef, Func, TableType, Val}; +use anyhow::{anyhow, bail, Result}; +use wasmtime_runtime::{self as runtime}; + +/// A WebAssembly `table`, or an array of values. +/// +/// Like [`Memory`][crate::Memory] a table is an indexed array of values, but +/// unlike [`Memory`][crate::Memory] it's an array of WebAssembly reference type +/// values rather than bytes. One of the most common usages of a table is a +/// function table for wasm modules (a `funcref` table), where each element has +/// the `ValType::FuncRef` type. +/// +/// A [`Table`] "belongs" to the store that it was originally created within +/// (either via [`Table::new`] or via instantiating a +/// [`Module`](crate::Module)). Operations on a [`Table`] only work with the +/// store it belongs to, and if another store is passed in by accident then +/// methods will panic. +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] // here for the C API +pub struct Table(pub(super) Stored); + +impl Table { + /// Creates a new [`Table`] with the given parameters. + /// + /// * `store` - the owner of the resulting [`Table`] + /// * `ty` - the type of this table, containing both the element type as + /// well as the initial size and maximum size, if any. + /// * `init` - the initial value to fill all table entries with, if the + /// table starts with an initial size. + /// + /// # Errors + /// + /// Returns an error if `init` does not match the element type of the table, + /// or if `init` does not belong to the `store` provided. + /// + /// # Panics + /// + /// This function will panic when used with a [`Store`](`crate::Store`) + /// which has a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) + /// (see also: [`Store::limiter_async`](`crate::Store::limiter_async`). + /// When using an async resource limiter, use [`Table::new_async`] + /// instead. + /// + /// # Examples + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// let engine = Engine::default(); + /// let mut store = Store::new(&engine, ()); + /// + /// let ty = TableType::new(ValType::FuncRef, 2, None); + /// let table = Table::new(&mut store, ty, Val::FuncRef(None))?; + /// + /// let module = Module::new( + /// &engine, + /// "(module + /// (table (import \"\" \"\") 2 funcref) + /// (func $f (result i32) + /// i32.const 10) + /// (elem (i32.const 0) $f) + /// )" + /// )?; + /// + /// let instance = Instance::new(&mut store, &module, &[table.into()])?; + /// // ... + /// # Ok(()) + /// # } + /// ``` + pub fn new(mut store: impl AsContextMut, ty: TableType, init: Val) -> Result
{ + Table::_new(store.as_context_mut().0, ty, init) + } + + #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] + /// Async variant of [`Table::new`]. You must use this variant with + /// [`Store`](`crate::Store`)s which have a + /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`). + /// + /// # Panics + /// + /// This function will panic when used with a non-async + /// [`Store`](`crate::Store`) + #[cfg(feature = "async")] + pub async fn new_async( + mut store: impl AsContextMut, + ty: TableType, + init: Val, + ) -> Result
+ where + T: Send, + { + let mut store = store.as_context_mut(); + assert!( + store.0.async_support(), + "cannot use `new_async` without enabling async support on the config" + ); + store + .on_fiber(|store| Table::_new(store.0, ty, init)) + .await? + } + + fn _new(store: &mut StoreOpaque, ty: TableType, init: Val) -> Result
{ + let wasmtime_export = generate_table_export(store, &ty)?; + let init = init.into_table_element(store, ty.element())?; + unsafe { + let table = Table::from_wasmtime_table(wasmtime_export, store); + (*table.wasmtime_table(store, std::iter::empty())).fill(0, init, ty.minimum())?; + + Ok(table) + } + } + + /// Returns the underlying type of this table, including its element type as + /// well as the maximum/minimum lower bounds. + /// + /// # Panics + /// + /// Panics if `store` does not own this table. + pub fn ty(&self, store: impl AsContext) -> TableType { + let store = store.as_context(); + let ty = &store[self.0].table.table; + TableType::from_wasmtime_table(ty) + } + + fn wasmtime_table( + &self, + store: &mut StoreOpaque, + lazy_init_range: impl Iterator, + ) -> *mut runtime::Table { + unsafe { + let export = &store[self.0]; + wasmtime_runtime::Instance::from_vmctx(export.vmctx, |handle| { + let idx = handle.table_index(&*export.definition); + handle.get_defined_table_with_lazy_init(idx, lazy_init_range) + }) + } + } + + /// Returns the table element value at `index`. + /// + /// Returns `None` if `index` is out of bounds. + /// + /// # Panics + /// + /// Panics if `store` does not own this table. + pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Option { + let store = store.as_context_mut().0; + let table = self.wasmtime_table(store, std::iter::once(index)); + unsafe { + match (*table).get(index)? { + runtime::TableElement::FuncRef(f) => { + let func = Func::from_caller_checked_func_ref(store, f); + Some(Val::FuncRef(func)) + } + runtime::TableElement::ExternRef(None) => Some(Val::ExternRef(None)), + runtime::TableElement::ExternRef(Some(x)) => { + Some(Val::ExternRef(Some(ExternRef { inner: x }))) + } + runtime::TableElement::UninitFunc => { + unreachable!("lazy init above should have converted UninitFunc") + } + } + } + } + + /// Writes the `val` provided into `index` within this table. + /// + /// # Errors + /// + /// Returns an error if `index` is out of bounds, if `val` does not have + /// the right type to be stored in this table, or if `val` belongs to a + /// different store. + /// + /// # Panics + /// + /// Panics if `store` does not own this table. + pub fn set(&self, mut store: impl AsContextMut, index: u32, val: Val) -> Result<()> { + let store = store.as_context_mut().0; + let ty = self.ty(&store).element().clone(); + let val = val.into_table_element(store, ty)?; + let table = self.wasmtime_table(store, std::iter::empty()); + unsafe { + (*table) + .set(index, val) + .map_err(|()| anyhow!("table element index out of bounds")) + } + } + + /// Returns the current size of this table. + /// + /// # Panics + /// + /// Panics if `store` does not own this table. + pub fn size(&self, store: impl AsContext) -> u32 { + self.internal_size(store.as_context().0) + } + + pub(crate) fn internal_size(&self, store: &StoreOpaque) -> u32 { + unsafe { (*store[self.0].definition).current_elements } + } + + /// Grows the size of this table by `delta` more elements, initialization + /// all new elements to `init`. + /// + /// Returns the previous size of this table if successful. + /// + /// # Errors + /// + /// Returns an error if the table cannot be grown by `delta`, for example + /// if it would cause the table to exceed its maximum size. Also returns an + /// error if `init` is not of the right type or if `init` does not belong to + /// `store`. + /// + /// # Panics + /// + /// Panics if `store` does not own this table. + /// + /// This function will panic when used with a [`Store`](`crate::Store`) + /// which has a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) + /// (see also: [`Store::limiter_async`](`crate::Store::limiter_async`)). + /// When using an async resource limiter, use [`Table::grow_async`] + /// instead. + pub fn grow(&self, mut store: impl AsContextMut, delta: u32, init: Val) -> Result { + let store = store.as_context_mut().0; + let ty = self.ty(&store).element().clone(); + let init = init.into_table_element(store, ty)?; + let table = self.wasmtime_table(store, std::iter::empty()); + unsafe { + match (*table).grow(delta, init, store)? { + Some(size) => { + let vm = (*table).vmtable(); + *store[self.0].definition = vm; + Ok(size) + } + None => bail!("failed to grow table by `{}`", delta), + } + } + } + + #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))] + /// Async variant of [`Table::grow`]. Required when using a + /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`). + /// + /// # Panics + /// + /// This function will panic when used with a non-async + /// [`Store`](`crate::Store`). + #[cfg(feature = "async")] + pub async fn grow_async( + &self, + mut store: impl AsContextMut, + delta: u32, + init: Val, + ) -> Result + where + T: Send, + { + let mut store = store.as_context_mut(); + assert!( + store.0.async_support(), + "cannot use `grow_async` without enabling async support on the config" + ); + store + .on_fiber(|store| self.grow(store, delta, init)) + .await? + } + + /// Copy `len` elements from `src_table[src_index..]` into + /// `dst_table[dst_index..]`. + /// + /// # Errors + /// + /// Returns an error if the range is out of bounds of either the source or + /// destination tables. + /// + /// # Panics + /// + /// Panics if `store` does not own either `dst_table` or `src_table`. + pub fn copy( + mut store: impl AsContextMut, + dst_table: &Table, + dst_index: u32, + src_table: &Table, + src_index: u32, + len: u32, + ) -> Result<()> { + let store = store.as_context_mut().0; + if dst_table.ty(&store).element() != src_table.ty(&store).element() { + bail!("tables do not have the same element type"); + } + + let dst_table = dst_table.wasmtime_table(store, std::iter::empty()); + let src_range = src_index..(src_index.checked_add(len).unwrap_or(u32::MAX)); + let src_table = src_table.wasmtime_table(store, src_range); + unsafe { + runtime::Table::copy(dst_table, src_table, dst_index, src_index, len)?; + } + Ok(()) + } + + /// Fill `table[dst..(dst + len)]` with the given value. + /// + /// # Errors + /// + /// Returns an error if + /// + /// * `val` is not of the same type as this table's + /// element type, + /// + /// * the region to be filled is out of bounds, or + /// + /// * `val` comes from a different `Store` from this table. + /// + /// # Panics + /// + /// Panics if `store` does not own either `dst_table` or `src_table`. + pub fn fill(&self, mut store: impl AsContextMut, dst: u32, val: Val, len: u32) -> Result<()> { + let store = store.as_context_mut().0; + let ty = self.ty(&store).element().clone(); + let val = val.into_table_element(store, ty)?; + + let table = self.wasmtime_table(store, std::iter::empty()); + unsafe { + (*table).fill(dst, val, len)?; + } + + Ok(()) + } + + pub(crate) unsafe fn from_wasmtime_table( + wasmtime_export: wasmtime_runtime::ExportTable, + store: &mut StoreOpaque, + ) -> Table { + Table(store.store_data_mut().insert(wasmtime_export)) + } + + pub(crate) fn wasmtime_ty<'a>(&self, data: &'a StoreData) -> &'a wasmtime_environ::Table { + &data[self.0].table.table + } + + pub(crate) fn vmimport(&self, store: &StoreOpaque) -> wasmtime_runtime::VMTableImport { + let export = &store[self.0]; + wasmtime_runtime::VMTableImport { + from: export.definition, + vmctx: export.vmctx, + } + } + + /// Get a stable hash key for this table. + /// + /// Even if the same underlying table definition is added to the + /// `StoreData` multiple times and becomes multiple `wasmtime::Table`s, + /// this hash key will be consistent across all of these tables. + #[allow(dead_code)] // Not used yet, but added for consistency. + pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl std::hash::Hash + Eq { + store[self.0].definition as usize + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Instance, Module, Store}; + + #[test] + fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> { + let mut store = Store::<()>::default(); + let module = Module::new( + store.engine(), + r#" + (module + (table (export "t") 1 1 externref) + ) + "#, + )?; + let instance = Instance::new(&mut store, &module, &[])?; + + // Each time we `get_table`, we call `Table::from_wasmtime` which adds + // a new entry to `StoreData`, so `t1` and `t2` will have different + // indices into `StoreData`. + let t1 = instance.get_table(&mut store, "t").unwrap(); + let t2 = instance.get_table(&mut store, "t").unwrap(); + + // That said, they really point to the same table. + assert!(t1.get(&mut store, 0).unwrap().unwrap_externref().is_none()); + assert!(t2.get(&mut store, 0).unwrap().unwrap_externref().is_none()); + t1.set(&mut store, 0, Val::ExternRef(Some(ExternRef::new(42))))?; + assert!(t1.get(&mut store, 0).unwrap().unwrap_externref().is_some()); + assert!(t2.get(&mut store, 0).unwrap().unwrap_externref().is_some()); + + // And therefore their hash keys are the same. + assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0)); + + // But the hash keys are different from different tables. + let instance2 = Instance::new(&mut store, &module, &[])?; + let t3 = instance2.get_table(&mut store, "t").unwrap(); + assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0)); + + Ok(()) + } +} diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 3ec071c72980..8e3a39d004bf 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -946,9 +946,7 @@ impl Func { /// this function is properly rooted within it. Additionally this function /// should not be liberally used since it's a very low-level knob. pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> *mut c_void { - self.caller_checked_func_ref(store.as_context_mut().0) - .as_ptr() - .cast() + self.vm_func_ref(store.as_context_mut().0).as_ptr().cast() } /// Invokes this function with the `params` given, returning the results @@ -1080,7 +1078,7 @@ impl Func { } #[inline] - pub(crate) fn caller_checked_func_ref(&self, store: &mut StoreOpaque) -> NonNull { + pub(crate) fn vm_func_ref(&self, store: &mut StoreOpaque) -> NonNull { let func_data = &mut store.store_data_mut()[self.0]; if let Some(in_store) = func_data.in_store_func_ref { in_store.as_non_null() @@ -1339,6 +1337,16 @@ impl Func { // (unsafely), which should be safe since we just did the type check above. unsafe { Ok(TypedFunc::new_unchecked(*self)) } } + + /// Get a stable hash key for this function. + /// + /// Even if the same underlying function is added to the `StoreData` + /// multiple times and becomes multiple `wasmtime::Func`s, this hash key + /// will be consistent across all of these functions. + #[allow(dead_code)] // Not used yet, but added for consistency. + pub(crate) fn hash_key(&self, store: &mut StoreOpaque) -> impl std::hash::Hash + Eq { + self.vm_func_ref(store).as_ptr() as usize + } } /// Prepares for entrance into WebAssembly. @@ -2391,3 +2399,47 @@ mod rooted { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Instance, Module, Store}; + + #[test] + fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> { + let mut store = Store::<()>::default(); + let module = Module::new( + store.engine(), + r#" + (module + (func (export "f") + nop + ) + ) + "#, + )?; + let instance = Instance::new(&mut store, &module, &[])?; + + // Each time we `get_func`, we call `Func::from_wasmtime` which adds a + // new entry to `StoreData`, so `f1` and `f2` will have different + // indices into `StoreData`. + let f1 = instance.get_func(&mut store, "f").unwrap(); + let f2 = instance.get_func(&mut store, "f").unwrap(); + + // But their hash keys are the same. + assert!( + f1.hash_key(&mut store.as_context_mut().0) + == f2.hash_key(&mut store.as_context_mut().0) + ); + + // But the hash keys are different from different funcs. + let instance2 = Instance::new(&mut store, &module, &[])?; + let f3 = instance2.get_func(&mut store, "f").unwrap(); + assert!( + f1.hash_key(&mut store.as_context_mut().0) + != f3.hash_key(&mut store.as_context_mut().0) + ); + + Ok(()) + } +} diff --git a/crates/wasmtime/src/func/typed.rs b/crates/wasmtime/src/func/typed.rs index 8229e14585b3..f1188faad4aa 100644 --- a/crates/wasmtime/src/func/typed.rs +++ b/crates/wasmtime/src/func/typed.rs @@ -84,7 +84,7 @@ where !store.0.async_support(), "must use `call_async` with async stores" ); - let func = self.func.caller_checked_func_ref(store.0); + let func = self.func.vm_func_ref(store.0); unsafe { Self::call_raw(&mut store, func, params) } } @@ -122,7 +122,7 @@ where ); store .on_fiber(|store| { - let func = self.func.caller_checked_func_ref(store.0); + let func = self.func.vm_func_ref(store.0); unsafe { Self::call_raw(store, func, params) } }) .await? @@ -439,7 +439,7 @@ unsafe impl WasmTy for Option { #[inline] fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi { if let Some(f) = self { - f.caller_checked_func_ref(store).as_ptr() + f.vm_func_ref(store).as_ptr() } else { ptr::null_mut() } diff --git a/crates/wasmtime/src/trampoline/global.rs b/crates/wasmtime/src/trampoline/global.rs index 263e91b2e34f..50a77811d1e4 100644 --- a/crates/wasmtime/src/trampoline/global.rs +++ b/crates/wasmtime/src/trampoline/global.rs @@ -53,9 +53,8 @@ pub fn generate_global_export( Val::F64(x) => *global.as_f64_bits_mut() = x, Val::V128(x) => *global.as_u128_mut() = x.into(), Val::FuncRef(f) => { - *global.as_func_ref_mut() = f.map_or(ptr::null_mut(), |f| { - f.caller_checked_func_ref(store).as_ptr() - }) + *global.as_func_ref_mut() = + f.map_or(ptr::null_mut(), |f| f.vm_func_ref(store).as_ptr()) } Val::ExternRef(x) => *global.as_externref_mut() = x.map(|x| x.inner), } diff --git a/crates/wasmtime/src/values.rs b/crates/wasmtime/src/values.rs index 87040ec6850f..d5605e962dda 100644 --- a/crates/wasmtime/src/values.rs +++ b/crates/wasmtime/src/values.rs @@ -194,9 +194,7 @@ impl Val { if !f.comes_from_same_store(store) { bail!("cross-`Store` values are not supported in tables"); } - Ok(TableElement::FuncRef( - f.caller_checked_func_ref(store).as_ptr(), - )) + Ok(TableElement::FuncRef(f.vm_func_ref(store).as_ptr())) } (Val::FuncRef(None), ValType::FuncRef) => Ok(TableElement::FuncRef(ptr::null_mut())), (Val::ExternRef(Some(x)), ValType::ExternRef) => { From b1ed9b374743a9bd3f3266fb7576b051f14ca092 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 26 Sep 2023 10:32:04 -0700 Subject: [PATCH 011/199] cranelift(x64): Make xmm{8..15} registers non-preferred (#7089) These registers require an additional byte to reference when encoded in certain AVX instruction formats (and maybe other situations as well?) so prefer xmm{0..7} when they are available and only fall back to xmm{8..15} when register pressure is higher. --- cranelift/codegen/src/isa/x64/abi.rs | 25 +-- .../filetests/isa/x64/call-conv.clif | 24 +-- .../filetests/isa/x64/cmp-mem-bug.clif | 16 +- .../filetests/filetests/isa/x64/fastcall.clif | 168 +++++++++--------- .../filetests/isa/x64/fcopysign.clif | 16 +- .../filetests/filetests/isa/x64/fcvt.clif | 94 +++++----- .../filetests/isa/x64/float-avx.clif | 36 ++-- .../filetests/filetests/isa/x64/fma-call.clif | 32 ++-- .../filetests/isa/x64/simd-arith-avx.clif | 50 +++--- .../filetests/isa/x64/simd-bitselect.clif | 11 +- .../filetests/isa/x64/simd-bitwise-avx.clif | 9 +- .../isa/x64/simd-bitwise-compile.clif | 99 ++++++----- .../filetests/isa/x64/simd-cmp-avx.clif | 72 ++++---- .../isa/x64/simd-comparison-legalize.clif | 10 +- .../filetests/isa/x64/simd-splat-avx.clif | 8 +- .../filetests/isa/x64/simd-widen-mul.clif | 36 ++-- .../wasm/x64-relaxed-simd-deterministic.wat | 76 ++++---- .../filetests/wasm/x64-relaxed-simd.wat | 34 ++-- 18 files changed, 416 insertions(+), 400 deletions(-) diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs index 51758010162a..be005e102243 100644 --- a/cranelift/codegen/src/isa/x64/abi.rs +++ b/cranelift/codegen/src/isa/x64/abi.rs @@ -1243,7 +1243,8 @@ fn create_reg_env_systemv(enable_pinned_reg: bool) -> MachineEnv { preg(regs::r10()), preg(regs::r11()), ], - // Preferred XMMs: all of them. + // Preferred XMMs: the first 8, which can have smaller encodings + // with AVX instructions. vec![ preg(regs::xmm0()), preg(regs::xmm1()), @@ -1253,14 +1254,6 @@ fn create_reg_env_systemv(enable_pinned_reg: bool) -> MachineEnv { preg(regs::xmm5()), preg(regs::xmm6()), preg(regs::xmm7()), - preg(regs::xmm8()), - preg(regs::xmm9()), - preg(regs::xmm10()), - preg(regs::xmm11()), - preg(regs::xmm12()), - preg(regs::xmm13()), - preg(regs::xmm14()), - preg(regs::xmm15()), ], // The Vector Regclass is unused vec![], @@ -1273,8 +1266,18 @@ fn create_reg_env_systemv(enable_pinned_reg: bool) -> MachineEnv { preg(regs::r13()), preg(regs::r14()), ], - // Non-preferred XMMs: none. - vec![], + // Non-preferred XMMs: the last 8 registers, which can have larger + // encodings with AVX instructions. + vec![ + preg(regs::xmm8()), + preg(regs::xmm9()), + preg(regs::xmm10()), + preg(regs::xmm11()), + preg(regs::xmm12()), + preg(regs::xmm13()), + preg(regs::xmm14()), + preg(regs::xmm15()), + ], // The Vector Regclass is unused vec![], ], diff --git a/cranelift/filetests/filetests/isa/x64/call-conv.clif b/cranelift/filetests/filetests/isa/x64/call-conv.clif index a4e383e817f0..1e0cede45e3b 100644 --- a/cranelift/filetests/filetests/isa/x64/call-conv.clif +++ b/cranelift/filetests/filetests/isa/x64/call-conv.clif @@ -202,8 +202,8 @@ block0( ; movq %rax, %rdi ; movq 16(%rbp), %r10 ; movq 24(%rbp), %r11 -; movss 32(%rbp), %xmm9 -; movsd 40(%rbp), %xmm8 +; movss 32(%rbp), %xmm11 +; movsd 40(%rbp), %xmm13 ; subq %rsp, $144, %rsp ; virtual_sp_offset_adjust 144 ; movq %r8, 32(%rsp) @@ -218,8 +218,8 @@ block0( ; movsd %xmm7, 104(%rsp) ; movq %r10, 112(%rsp) ; movl %r11d, 120(%rsp) -; movss %xmm9, 128(%rsp) -; movsd %xmm8, 136(%rsp) +; movss %xmm11, 128(%rsp) +; movsd %xmm13, 136(%rsp) ; movq %rdi, %r9 ; movq %rcx, %r8 ; movq %rsi, %rcx @@ -242,8 +242,8 @@ block0( ; movq %rax, %rdi ; movq 0x10(%rbp), %r10 ; movq 0x18(%rbp), %r11 -; movss 0x20(%rbp), %xmm9 -; movsd 0x28(%rbp), %xmm8 +; movss 0x20(%rbp), %xmm11 +; movsd 0x28(%rbp), %xmm13 ; subq $0x90, %rsp ; movq %r8, 0x20(%rsp) ; movq %r9, 0x28(%rsp) @@ -257,8 +257,8 @@ block0( ; movsd %xmm7, 0x68(%rsp) ; movq %r10, 0x70(%rsp) ; movl %r11d, 0x78(%rsp) -; movss %xmm9, 0x80(%rsp) -; movsd %xmm8, 0x88(%rsp) +; movss %xmm11, 0x80(%rsp) +; movsd %xmm13, 0x88(%rsp) ; movq %rdi, %r9 ; movq %rcx, %r8 ; movq %rsi, %rcx @@ -330,7 +330,7 @@ block0(v0: i32, v1: f32, v2: i64, v3: f64, v4: i32, v5: i32, v6: i32, v7: f32, v ; block0: ; movq %rsi, %r9 ; movq %rdi, %rsi -; movdqa %xmm1, %xmm12 +; movdqa %xmm1, %xmm6 ; movdqa %xmm0, %xmm1 ; subq %rsp, $96, %rsp ; virtual_sp_offset_adjust 96 @@ -343,7 +343,7 @@ block0(v0: i32, v1: f32, v2: i64, v3: f64, v4: i32, v5: i32, v6: i32, v7: f32, v ; movsd %xmm5, 80(%rsp) ; movq %rsi, %rcx ; movq %r9, %r8 -; movdqa %xmm12, %xmm3 +; movdqa %xmm6, %xmm3 ; call *%rcx ; addq %rsp, $96, %rsp ; virtual_sp_offset_adjust -96 @@ -358,7 +358,7 @@ block0(v0: i32, v1: f32, v2: i64, v3: f64, v4: i32, v5: i32, v6: i32, v7: f32, v ; block1: ; offset 0x4 ; movq %rsi, %r9 ; movq %rdi, %rsi -; movdqa %xmm1, %xmm12 +; movdqa %xmm1, %xmm6 ; movdqa %xmm0, %xmm1 ; subq $0x60, %rsp ; movl %edx, 0x20(%rsp) @@ -370,7 +370,7 @@ block0(v0: i32, v1: f32, v2: i64, v3: f64, v4: i32, v5: i32, v6: i32, v7: f32, v ; movsd %xmm5, 0x50(%rsp) ; movq %rsi, %rcx ; movq %r9, %r8 -; movdqa %xmm12, %xmm3 +; movdqa %xmm6, %xmm3 ; callq *%rcx ; addq $0x60, %rsp ; movq %rbp, %rsp diff --git a/cranelift/filetests/filetests/isa/x64/cmp-mem-bug.clif b/cranelift/filetests/filetests/isa/x64/cmp-mem-bug.clif index 331239f54827..2574ddfb3de5 100644 --- a/cranelift/filetests/filetests/isa/x64/cmp-mem-bug.clif +++ b/cranelift/filetests/filetests/isa/x64/cmp-mem-bug.clif @@ -54,13 +54,13 @@ block0(v0: f64, v1: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movsd 0(%rdi), %xmm9 -; ucomisd %xmm9, %xmm0 +; movsd 0(%rdi), %xmm1 +; ucomisd %xmm1, %xmm0 ; setnp %dil ; setz %al ; andl %edi, %eax, %edi ; movzbq %dil, %rax -; ucomisd %xmm9, %xmm0 +; ucomisd %xmm1, %xmm0 ; movdqa %xmm0, %xmm2 ; movsd %xmm0, %xmm0; jnp $next; movsd %xmm2, %xmm0; $next: ; movsd %xmm0, %xmm0; jz $next; movsd %xmm2, %xmm0; $next: @@ -73,17 +73,17 @@ block0(v0: f64, v1: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movsd (%rdi), %xmm9 ; trap: heap_oob -; ucomisd %xmm9, %xmm0 +; movsd (%rdi), %xmm1 ; trap: heap_oob +; ucomisd %xmm1, %xmm0 ; setnp %dil ; sete %al ; andl %eax, %edi ; movzbq %dil, %rax -; ucomisd %xmm9, %xmm0 +; ucomisd %xmm1, %xmm0 ; movdqa %xmm0, %xmm2 -; jnp 0x2f +; jnp 0x2c ; movsd %xmm2, %xmm0 -; je 0x39 +; je 0x36 ; movsd %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/fastcall.clif b/cranelift/filetests/filetests/isa/x64/fastcall.clif index 0bf874402fdb..86e865b61493 100644 --- a/cranelift/filetests/filetests/isa/x64/fastcall.clif +++ b/cranelift/filetests/filetests/isa/x64/fastcall.clif @@ -356,51 +356,53 @@ block0(v0: i64): ; unwind SaveReg { clobber_offset: 144, reg: p15f } ; block0: ; movsd 0(%rcx), %xmm0 -; movsd 8(%rcx), %xmm10 -; movdqu %xmm10, rsp(48 + virtual offset) -; movsd 16(%rcx), %xmm5 -; movsd 24(%rcx), %xmm14 -; movdqu %xmm14, rsp(32 + virtual offset) -; movsd 32(%rcx), %xmm13 -; movsd 40(%rcx), %xmm15 -; movdqu %xmm15, rsp(16 + virtual offset) +; movsd 8(%rcx), %xmm8 +; movdqu %xmm8, rsp(48 + virtual offset) +; movsd 16(%rcx), %xmm10 +; movdqu %xmm10, rsp(0 + virtual offset) +; movsd 24(%rcx), %xmm9 +; movdqa %xmm9, %xmm10 +; movsd 32(%rcx), %xmm5 +; movsd 40(%rcx), %xmm6 +; movdqu %xmm6, rsp(32 + virtual offset) ; movsd 48(%rcx), %xmm7 -; movsd 56(%rcx), %xmm8 -; movdqu %xmm8, rsp(0 + virtual offset) -; movsd 64(%rcx), %xmm12 -; movsd 72(%rcx), %xmm2 -; movsd 80(%rcx), %xmm9 -; movsd 88(%rcx), %xmm4 +; movsd 56(%rcx), %xmm12 +; movdqu %xmm12, rsp(16 + virtual offset) +; movsd 64(%rcx), %xmm4 +; movsd 72(%rcx), %xmm12 +; movsd 80(%rcx), %xmm1 +; movsd 88(%rcx), %xmm14 ; movsd 96(%rcx), %xmm3 -; movsd 104(%rcx), %xmm8 +; movsd 104(%rcx), %xmm15 ; movsd 112(%rcx), %xmm11 -; movsd 120(%rcx), %xmm10 -; movsd 128(%rcx), %xmm6 -; movsd 136(%rcx), %xmm14 -; movsd 144(%rcx), %xmm1 -; movdqu rsp(48 + virtual offset), %xmm15 -; addsd %xmm0, %xmm15, %xmm0 -; movdqu rsp(32 + virtual offset), %xmm15 -; addsd %xmm5, %xmm15, %xmm5 -; movdqu rsp(16 + virtual offset), %xmm15 -; addsd %xmm13, %xmm15, %xmm13 -; movdqu rsp(0 + virtual offset), %xmm15 -; addsd %xmm7, %xmm15, %xmm7 -; addsd %xmm12, %xmm2, %xmm12 -; addsd %xmm9, %xmm4, %xmm9 -; addsd %xmm3, %xmm8, %xmm3 -; addsd %xmm11, %xmm10, %xmm11 -; addsd %xmm6, %xmm14, %xmm6 -; addsd %xmm1, 152(%rcx), %xmm1 -; addsd %xmm0, %xmm5, %xmm0 -; addsd %xmm13, %xmm7, %xmm13 -; addsd %xmm12, %xmm9, %xmm12 -; addsd %xmm3, %xmm11, %xmm3 -; addsd %xmm6, %xmm1, %xmm6 -; addsd %xmm0, %xmm13, %xmm0 -; addsd %xmm12, %xmm3, %xmm12 -; addsd %xmm0, %xmm12, %xmm0 +; movsd 120(%rcx), %xmm8 +; movsd 128(%rcx), %xmm2 +; movsd 136(%rcx), %xmm9 +; movsd 144(%rcx), %xmm13 +; movdqu rsp(48 + virtual offset), %xmm6 ; addsd %xmm0, %xmm6, %xmm0 +; movdqa %xmm10, %xmm6 +; movdqu rsp(0 + virtual offset), %xmm10 +; addsd %xmm10, %xmm6, %xmm10 +; movdqu rsp(32 + virtual offset), %xmm6 +; addsd %xmm5, %xmm6, %xmm5 +; movdqu rsp(16 + virtual offset), %xmm6 +; addsd %xmm7, %xmm6, %xmm7 +; addsd %xmm4, %xmm12, %xmm4 +; addsd %xmm1, %xmm14, %xmm1 +; addsd %xmm3, %xmm15, %xmm3 +; addsd %xmm11, %xmm8, %xmm11 +; addsd %xmm2, %xmm9, %xmm2 +; addsd %xmm13, 152(%rcx), %xmm13 +; addsd %xmm0, %xmm10, %xmm0 +; addsd %xmm5, %xmm7, %xmm5 +; addsd %xmm4, %xmm1, %xmm4 +; addsd %xmm3, %xmm11, %xmm3 +; addsd %xmm2, %xmm13, %xmm2 +; addsd %xmm0, %xmm5, %xmm0 +; addsd %xmm4, %xmm3, %xmm4 +; addsd %xmm0, %xmm4, %xmm0 +; addsd %xmm0, %xmm2, %xmm0 ; movdqu 64(%rsp), %xmm6 ; movdqu 80(%rsp), %xmm7 ; movdqu 96(%rsp), %xmm8 @@ -433,51 +435,53 @@ block0(v0: i64): ; movdqu %xmm15, 0xd0(%rsp) ; block1: ; offset 0x61 ; movsd (%rcx), %xmm0 ; trap: heap_oob -; movsd 8(%rcx), %xmm10 ; trap: heap_oob -; movdqu %xmm10, 0x30(%rsp) -; movsd 0x10(%rcx), %xmm5 ; trap: heap_oob -; movsd 0x18(%rcx), %xmm14 ; trap: heap_oob -; movdqu %xmm14, 0x20(%rsp) -; movsd 0x20(%rcx), %xmm13 ; trap: heap_oob -; movsd 0x28(%rcx), %xmm15 ; trap: heap_oob -; movdqu %xmm15, 0x10(%rsp) +; movsd 8(%rcx), %xmm8 ; trap: heap_oob +; movdqu %xmm8, 0x30(%rsp) +; movsd 0x10(%rcx), %xmm10 ; trap: heap_oob +; movdqu %xmm10, (%rsp) +; movsd 0x18(%rcx), %xmm9 ; trap: heap_oob +; movdqa %xmm9, %xmm10 +; movsd 0x20(%rcx), %xmm5 ; trap: heap_oob +; movsd 0x28(%rcx), %xmm6 ; trap: heap_oob +; movdqu %xmm6, 0x20(%rsp) ; movsd 0x30(%rcx), %xmm7 ; trap: heap_oob -; movsd 0x38(%rcx), %xmm8 ; trap: heap_oob -; movdqu %xmm8, (%rsp) -; movsd 0x40(%rcx), %xmm12 ; trap: heap_oob -; movsd 0x48(%rcx), %xmm2 ; trap: heap_oob -; movsd 0x50(%rcx), %xmm9 ; trap: heap_oob -; movsd 0x58(%rcx), %xmm4 ; trap: heap_oob +; movsd 0x38(%rcx), %xmm12 ; trap: heap_oob +; movdqu %xmm12, 0x10(%rsp) +; movsd 0x40(%rcx), %xmm4 ; trap: heap_oob +; movsd 0x48(%rcx), %xmm12 ; trap: heap_oob +; movsd 0x50(%rcx), %xmm1 ; trap: heap_oob +; movsd 0x58(%rcx), %xmm14 ; trap: heap_oob ; movsd 0x60(%rcx), %xmm3 ; trap: heap_oob -; movsd 0x68(%rcx), %xmm8 ; trap: heap_oob +; movsd 0x68(%rcx), %xmm15 ; trap: heap_oob ; movsd 0x70(%rcx), %xmm11 ; trap: heap_oob -; movsd 0x78(%rcx), %xmm10 ; trap: heap_oob -; movsd 0x80(%rcx), %xmm6 ; trap: heap_oob -; movsd 0x88(%rcx), %xmm14 ; trap: heap_oob -; movsd 0x90(%rcx), %xmm1 ; trap: heap_oob -; movdqu 0x30(%rsp), %xmm15 -; addsd %xmm15, %xmm0 -; movdqu 0x20(%rsp), %xmm15 -; addsd %xmm15, %xmm5 -; movdqu 0x10(%rsp), %xmm15 -; addsd %xmm15, %xmm13 -; movdqu (%rsp), %xmm15 -; addsd %xmm15, %xmm7 -; addsd %xmm2, %xmm12 -; addsd %xmm4, %xmm9 -; addsd %xmm8, %xmm3 -; addsd %xmm10, %xmm11 -; addsd %xmm14, %xmm6 -; addsd 0x98(%rcx), %xmm1 ; trap: heap_oob -; addsd %xmm5, %xmm0 -; addsd %xmm7, %xmm13 -; addsd %xmm9, %xmm12 -; addsd %xmm11, %xmm3 -; addsd %xmm1, %xmm6 -; addsd %xmm13, %xmm0 -; addsd %xmm3, %xmm12 -; addsd %xmm12, %xmm0 +; movsd 0x78(%rcx), %xmm8 ; trap: heap_oob +; movsd 0x80(%rcx), %xmm2 ; trap: heap_oob +; movsd 0x88(%rcx), %xmm9 ; trap: heap_oob +; movsd 0x90(%rcx), %xmm13 ; trap: heap_oob +; movdqu 0x30(%rsp), %xmm6 ; addsd %xmm6, %xmm0 +; movdqa %xmm10, %xmm6 +; movdqu (%rsp), %xmm10 +; addsd %xmm6, %xmm10 +; movdqu 0x20(%rsp), %xmm6 +; addsd %xmm6, %xmm5 +; movdqu 0x10(%rsp), %xmm6 +; addsd %xmm6, %xmm7 +; addsd %xmm12, %xmm4 +; addsd %xmm14, %xmm1 +; addsd %xmm15, %xmm3 +; addsd %xmm8, %xmm11 +; addsd %xmm9, %xmm2 +; addsd 0x98(%rcx), %xmm13 ; trap: heap_oob +; addsd %xmm10, %xmm0 +; addsd %xmm7, %xmm5 +; addsd %xmm1, %xmm4 +; addsd %xmm11, %xmm3 +; addsd %xmm13, %xmm2 +; addsd %xmm5, %xmm0 +; addsd %xmm3, %xmm4 +; addsd %xmm4, %xmm0 +; addsd %xmm2, %xmm0 ; movdqu 0x40(%rsp), %xmm6 ; movdqu 0x50(%rsp), %xmm7 ; movdqu 0x60(%rsp), %xmm8 diff --git a/cranelift/filetests/filetests/isa/x64/fcopysign.clif b/cranelift/filetests/filetests/isa/x64/fcopysign.clif index a6140711c829..c2f39049ac96 100644 --- a/cranelift/filetests/filetests/isa/x64/fcopysign.clif +++ b/cranelift/filetests/filetests/isa/x64/fcopysign.clif @@ -13,9 +13,9 @@ block0(v0: f32, v1: f32): ; block0: ; movl $-2147483648, %ecx ; movd %ecx, %xmm7 -; movdqa %xmm0, %xmm10 +; movdqa %xmm0, %xmm2 ; movdqa %xmm7, %xmm0 -; andnps %xmm0, %xmm10, %xmm0 +; andnps %xmm0, %xmm2, %xmm0 ; andps %xmm7, %xmm1, %xmm7 ; orps %xmm0, %xmm7, %xmm0 ; movq %rbp, %rsp @@ -29,9 +29,9 @@ block0(v0: f32, v1: f32): ; block1: ; offset 0x4 ; movl $0x80000000, %ecx ; movd %ecx, %xmm7 -; movdqa %xmm0, %xmm10 +; movdqa %xmm0, %xmm2 ; movdqa %xmm7, %xmm0 -; andnps %xmm10, %xmm0 +; andnps %xmm2, %xmm0 ; andps %xmm1, %xmm7 ; orps %xmm7, %xmm0 ; movq %rbp, %rsp @@ -50,9 +50,9 @@ block0(v0: f64, v1: f64): ; block0: ; movabsq $-9223372036854775808, %rcx ; movq %rcx, %xmm7 -; movdqa %xmm0, %xmm10 +; movdqa %xmm0, %xmm2 ; movdqa %xmm7, %xmm0 -; andnpd %xmm0, %xmm10, %xmm0 +; andnpd %xmm0, %xmm2, %xmm0 ; andpd %xmm7, %xmm1, %xmm7 ; orpd %xmm0, %xmm7, %xmm0 ; movq %rbp, %rsp @@ -66,9 +66,9 @@ block0(v0: f64, v1: f64): ; block1: ; offset 0x4 ; movabsq $9223372036854775808, %rcx ; movq %rcx, %xmm7 -; movdqa %xmm0, %xmm10 +; movdqa %xmm0, %xmm2 ; movdqa %xmm7, %xmm0 -; andnpd %xmm10, %xmm0 +; andnpd %xmm2, %xmm0 ; andpd %xmm1, %xmm7 ; orpd %xmm7, %xmm0 ; movq %rbp, %rsp diff --git a/cranelift/filetests/filetests/isa/x64/fcvt.clif b/cranelift/filetests/filetests/isa/x64/fcvt.clif index cbe440745cf4..0dfa8637e0cd 100644 --- a/cranelift/filetests/filetests/isa/x64/fcvt.clif +++ b/cranelift/filetests/filetests/isa/x64/fcvt.clif @@ -257,10 +257,10 @@ block0(v0: i8, v1: i16, v2: i32, v3: i64): ; cvtsi2ss %r9, %xmm1 ; movl %edx, %r9d ; cvtsi2ss %r9, %xmm2 -; u64_to_f32_seq %rcx, %xmm14, %r9, %r10 +; u64_to_f32_seq %rcx, %xmm6, %r9, %r10 ; addss %xmm0, %xmm1, %xmm0 ; addss %xmm0, %xmm2, %xmm0 -; addss %xmm0, %xmm14, %xmm0 +; addss %xmm0, %xmm6, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -278,18 +278,18 @@ block0(v0: i8, v1: i16, v2: i32, v3: i64): ; cvtsi2ssq %r9, %xmm2 ; cmpq $0, %rcx ; jl 0x32 -; cvtsi2ssq %rcx, %xmm14 -; jmp 0x4d +; cvtsi2ssq %rcx, %xmm6 +; jmp 0x4c ; movq %rcx, %r9 ; shrq $1, %r9 ; movq %rcx, %r10 ; andq $1, %r10 ; orq %r9, %r10 -; cvtsi2ssq %r10, %xmm14 -; addss %xmm14, %xmm14 +; cvtsi2ssq %r10, %xmm6 +; addss %xmm6, %xmm6 ; addss %xmm1, %xmm0 ; addss %xmm2, %xmm0 -; addss %xmm14, %xmm0 +; addss %xmm6, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -353,13 +353,12 @@ block0(v0: i32x4): ; movdqa %xmm0, %xmm3 ; pslld %xmm3, $16, %xmm3 ; psrld %xmm3, $16, %xmm3 -; movdqa %xmm0, %xmm9 -; psubd %xmm9, %xmm3, %xmm9 -; cvtdq2ps %xmm3, %xmm8 -; psrld %xmm9, $1, %xmm9 -; cvtdq2ps %xmm9, %xmm0 +; psubd %xmm0, %xmm3, %xmm0 +; cvtdq2ps %xmm3, %xmm1 +; psrld %xmm0, $1, %xmm0 +; cvtdq2ps %xmm0, %xmm0 ; addps %xmm0, %xmm0, %xmm0 -; addps %xmm0, %xmm8, %xmm0 +; addps %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -372,13 +371,12 @@ block0(v0: i32x4): ; movdqa %xmm0, %xmm3 ; pslld $0x10, %xmm3 ; psrld $0x10, %xmm3 -; movdqa %xmm0, %xmm9 -; psubd %xmm3, %xmm9 -; cvtdq2ps %xmm3, %xmm8 -; psrld $1, %xmm9 -; cvtdq2ps %xmm9, %xmm0 +; psubd %xmm3, %xmm0 +; cvtdq2ps %xmm3, %xmm1 +; psrld $1, %xmm0 +; cvtdq2ps %xmm0, %xmm0 ; addps %xmm0, %xmm0 -; addps %xmm8, %xmm0 +; addps %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -1035,20 +1033,20 @@ block0(v0: f32x4): ; block0: ; uninit %xmm6 ; xorps %xmm6, %xmm6, %xmm6 -; movdqa %xmm0, %xmm11 -; maxps %xmm11, %xmm6, %xmm11 +; movdqa %xmm0, %xmm3 +; maxps %xmm3, %xmm6, %xmm3 ; pcmpeqd %xmm6, %xmm6, %xmm6 ; psrld %xmm6, $1, %xmm6 -; cvtdq2ps %xmm6, %xmm15 -; cvttps2dq %xmm11, %xmm14 -; subps %xmm11, %xmm15, %xmm11 -; cmpps $2, %xmm15, %xmm11, %xmm15 -; cvttps2dq %xmm11, %xmm0 -; pxor %xmm0, %xmm15, %xmm0 -; uninit %xmm9 -; pxor %xmm9, %xmm9, %xmm9 -; pmaxsd %xmm0, %xmm9, %xmm0 -; paddd %xmm0, %xmm14, %xmm0 +; cvtdq2ps %xmm6, %xmm7 +; cvttps2dq %xmm3, %xmm6 +; subps %xmm3, %xmm7, %xmm3 +; cmpps $2, %xmm7, %xmm3, %xmm7 +; cvttps2dq %xmm3, %xmm0 +; pxor %xmm0, %xmm7, %xmm0 +; uninit %xmm1 +; pxor %xmm1, %xmm1, %xmm1 +; pmaxsd %xmm0, %xmm1, %xmm0 +; paddd %xmm0, %xmm6, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -1059,19 +1057,19 @@ block0(v0: f32x4): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; xorps %xmm6, %xmm6 -; movdqa %xmm0, %xmm11 -; maxps %xmm6, %xmm11 +; movdqa %xmm0, %xmm3 +; maxps %xmm6, %xmm3 ; pcmpeqd %xmm6, %xmm6 ; psrld $1, %xmm6 -; cvtdq2ps %xmm6, %xmm15 -; cvttps2dq %xmm11, %xmm14 -; subps %xmm15, %xmm11 -; cmpleps %xmm11, %xmm15 -; cvttps2dq %xmm11, %xmm0 -; pxor %xmm15, %xmm0 -; pxor %xmm9, %xmm9 -; pmaxsd %xmm9, %xmm0 -; paddd %xmm14, %xmm0 +; cvtdq2ps %xmm6, %xmm7 +; cvttps2dq %xmm3, %xmm6 +; subps %xmm7, %xmm3 +; cmpleps %xmm3, %xmm7 +; cvttps2dq %xmm3, %xmm0 +; pxor %xmm7, %xmm0 +; pxor %xmm1, %xmm1 +; pmaxsd %xmm1, %xmm0 +; paddd %xmm6, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -1091,11 +1089,11 @@ block0(v0: f32x4): ; movdqa %xmm0, %xmm5 ; andps %xmm5, %xmm4, %xmm5 ; pxor %xmm4, %xmm5, %xmm4 -; cvttps2dq %xmm5, %xmm8 -; movdqa %xmm8, %xmm0 +; cvttps2dq %xmm5, %xmm1 +; movdqa %xmm1, %xmm0 ; pand %xmm0, %xmm4, %xmm0 ; psrad %xmm0, $31, %xmm0 -; pxor %xmm0, %xmm8, %xmm0 +; pxor %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -1110,11 +1108,11 @@ block0(v0: f32x4): ; movdqa %xmm0, %xmm5 ; andps %xmm4, %xmm5 ; pxor %xmm5, %xmm4 -; cvttps2dq %xmm5, %xmm8 -; movdqa %xmm8, %xmm0 +; cvttps2dq %xmm5, %xmm1 +; movdqa %xmm1, %xmm0 ; pand %xmm4, %xmm0 ; psrad $0x1f, %xmm0 -; pxor %xmm8, %xmm0 +; pxor %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/float-avx.clif b/cranelift/filetests/filetests/isa/x64/float-avx.clif index 948056a03d14..e7ddfa2fb7e4 100644 --- a/cranelift/filetests/filetests/isa/x64/float-avx.clif +++ b/cranelift/filetests/filetests/isa/x64/float-avx.clif @@ -444,11 +444,11 @@ block0(v0: i32x4): ; vpslld %xmm0, $16, %xmm2 ; vpsrld %xmm2, $16, %xmm4 ; vpsubd %xmm0, %xmm4, %xmm6 -; vcvtdq2ps %xmm4, %xmm8 -; vpsrld %xmm6, $1, %xmm10 -; vcvtdq2ps %xmm10, %xmm12 -; vaddps %xmm12, %xmm12, %xmm14 -; vaddps %xmm14, %xmm8, %xmm0 +; vcvtdq2ps %xmm4, %xmm0 +; vpsrld %xmm6, $1, %xmm2 +; vcvtdq2ps %xmm2, %xmm4 +; vaddps %xmm4, %xmm4, %xmm6 +; vaddps %xmm6, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -461,11 +461,11 @@ block0(v0: i32x4): ; vpslld $0x10, %xmm0, %xmm2 ; vpsrld $0x10, %xmm2, %xmm4 ; vpsubd %xmm4, %xmm0, %xmm6 -; vcvtdq2ps %xmm4, %xmm8 -; vpsrld $1, %xmm6, %xmm10 -; vcvtdq2ps %xmm10, %xmm12 -; vaddps %xmm12, %xmm12, %xmm14 -; vaddps %xmm8, %xmm14, %xmm0 +; vcvtdq2ps %xmm4, %xmm0 +; vpsrld $1, %xmm6, %xmm2 +; vcvtdq2ps %xmm2, %xmm4 +; vaddps %xmm4, %xmm4, %xmm6 +; vaddps %xmm0, %xmm6, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -533,10 +533,10 @@ block0(v0: f32x4): ; vcmpps $0, %xmm0, %xmm0, %xmm2 ; vandps %xmm0, %xmm2, %xmm4 ; vpxor %xmm2, %xmm4, %xmm6 -; vcvttps2dq %xmm4, %xmm8 -; vpand %xmm8, %xmm6, %xmm10 -; vpsrad %xmm10, $31, %xmm12 -; vpxor %xmm12, %xmm8, %xmm0 +; vcvttps2dq %xmm4, %xmm0 +; vpand %xmm0, %xmm6, %xmm2 +; vpsrad %xmm2, $31, %xmm4 +; vpxor %xmm4, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -549,10 +549,10 @@ block0(v0: f32x4): ; vcmpeqps %xmm0, %xmm0, %xmm2 ; vandps %xmm2, %xmm0, %xmm4 ; vpxor %xmm4, %xmm2, %xmm6 -; vcvttps2dq %xmm4, %xmm8 -; vpand %xmm6, %xmm8, %xmm10 -; vpsrad $0x1f, %xmm10, %xmm12 -; vpxor %xmm8, %xmm12, %xmm0 +; vcvttps2dq %xmm4, %xmm0 +; vpand %xmm6, %xmm0, %xmm2 +; vpsrad $0x1f, %xmm2, %xmm4 +; vpxor %xmm0, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/fma-call.clif b/cranelift/filetests/filetests/isa/x64/fma-call.clif index 0b92c3f44e79..3bb595a4fe26 100644 --- a/cranelift/filetests/filetests/isa/x64/fma-call.clif +++ b/cranelift/filetests/filetests/isa/x64/fma-call.clif @@ -77,17 +77,17 @@ block0(v0: f32x4, v1: f32x4, v2: f32x4): ; movdqu %xmm0, rsp(48 + virtual offset) ; movdqu rsp(0 + virtual offset), %xmm4 ; pshufd $1, %xmm4, %xmm0 -; movdqu rsp(16 + virtual offset), %xmm8 -; pshufd $1, %xmm8, %xmm1 -; movdqu rsp(32 + virtual offset), %xmm12 -; pshufd $1, %xmm12, %xmm2 +; movdqu rsp(16 + virtual offset), %xmm2 +; pshufd $1, %xmm2, %xmm1 +; movdqu rsp(32 + virtual offset), %xmm4 +; pshufd $1, %xmm4, %xmm2 ; load_ext_name %FmaF32+0, %r9 ; call *%r9 ; movdqu %xmm0, rsp(64 + virtual offset) ; movdqu rsp(0 + virtual offset), %xmm4 ; pshufd $2, %xmm4, %xmm0 -; movdqu rsp(16 + virtual offset), %xmm14 -; pshufd $2, %xmm14, %xmm1 +; movdqu rsp(16 + virtual offset), %xmm6 +; pshufd $2, %xmm6, %xmm1 ; movdqu rsp(32 + virtual offset), %xmm3 ; pshufd $2, %xmm3, %xmm2 ; load_ext_name %FmaF32+0, %r10 @@ -131,17 +131,17 @@ block0(v0: f32x4, v1: f32x4, v2: f32x4): ; movdqu %xmm0, 0x30(%rsp) ; movdqu (%rsp), %xmm4 ; pshufd $1, %xmm4, %xmm0 -; movdqu 0x10(%rsp), %xmm8 -; pshufd $1, %xmm8, %xmm1 -; movdqu 0x20(%rsp), %xmm12 -; pshufd $1, %xmm12, %xmm2 +; movdqu 0x10(%rsp), %xmm2 +; pshufd $1, %xmm2, %xmm1 +; movdqu 0x20(%rsp), %xmm4 +; pshufd $1, %xmm4, %xmm2 ; movabsq $0, %r9 ; reloc_external Abs8 %FmaF32 0 ; callq *%r9 ; movdqu %xmm0, 0x40(%rsp) ; movdqu (%rsp), %xmm4 ; pshufd $2, %xmm4, %xmm0 -; movdqu 0x10(%rsp), %xmm14 -; pshufd $2, %xmm14, %xmm1 +; movdqu 0x10(%rsp), %xmm6 +; pshufd $2, %xmm6, %xmm1 ; movdqu 0x20(%rsp), %xmm3 ; pshufd $2, %xmm3, %xmm2 ; movabsq $0, %r10 ; reloc_external Abs8 %FmaF32 0 @@ -196,9 +196,9 @@ block0(v0: f64x2, v1: f64x2, v2: f64x2): ; pshufd $238, %xmm2, %xmm2 ; load_ext_name %FmaF64+0, %r9 ; call *%r9 -; movdqa %xmm0, %xmm14 +; movdqa %xmm0, %xmm6 ; movdqu rsp(48 + virtual offset), %xmm0 -; movlhps %xmm0, %xmm14, %xmm0 +; movlhps %xmm0, %xmm6, %xmm0 ; addq %rsp, $64, %rsp ; movq %rbp, %rsp ; popq %rbp @@ -227,9 +227,9 @@ block0(v0: f64x2, v1: f64x2, v2: f64x2): ; pshufd $0xee, %xmm2, %xmm2 ; movabsq $0, %r9 ; reloc_external Abs8 %FmaF64 0 ; callq *%r9 -; movdqa %xmm0, %xmm14 +; movdqa %xmm0, %xmm6 ; movdqu 0x30(%rsp), %xmm0 -; movlhps %xmm14, %xmm0 +; movlhps %xmm6, %xmm0 ; addq $0x40, %rsp ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif b/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif index f8c390ba8a17..8cd260a3f536 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif @@ -919,10 +919,10 @@ block0(v0: i8x16, v1: i32): ; vpunpcklbw %xmm0, %xmm0, %xmm5 ; vpunpckhbw %xmm0, %xmm0, %xmm7 ; addl %r9d, $8, %r9d -; vmovd %r9d, %xmm11 -; vpsraw %xmm5, %xmm11, %xmm13 -; vpsraw %xmm7, %xmm11, %xmm15 -; vpacksswb %xmm13, %xmm15, %xmm0 +; vmovd %r9d, %xmm3 +; vpsraw %xmm5, %xmm3, %xmm5 +; vpsraw %xmm7, %xmm3, %xmm7 +; vpacksswb %xmm5, %xmm7, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -937,10 +937,10 @@ block0(v0: i8x16, v1: i32): ; vpunpcklbw %xmm0, %xmm0, %xmm5 ; vpunpckhbw %xmm0, %xmm0, %xmm7 ; addl $8, %r9d -; vmovd %r9d, %xmm11 -; vpsraw %xmm11, %xmm5, %xmm13 -; vpsraw %xmm11, %xmm7, %xmm15 -; vpacksswb %xmm15, %xmm13, %xmm0 +; vmovd %r9d, %xmm3 +; vpsraw %xmm3, %xmm5, %xmm5 +; vpsraw %xmm3, %xmm7, %xmm7 +; vpacksswb %xmm7, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -959,8 +959,8 @@ block0(v0: i8x16): ; vpunpcklbw %xmm0, %xmm0, %xmm2 ; vpunpckhbw %xmm0, %xmm0, %xmm4 ; vpsraw %xmm2, $11, %xmm6 -; vpsraw %xmm4, $11, %xmm8 -; vpacksswb %xmm6, %xmm8, %xmm0 +; vpsraw %xmm4, $11, %xmm0 +; vpacksswb %xmm6, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -973,8 +973,8 @@ block0(v0: i8x16): ; vpunpcklbw %xmm0, %xmm0, %xmm2 ; vpunpckhbw %xmm0, %xmm0, %xmm4 ; vpsraw $0xb, %xmm2, %xmm6 -; vpsraw $0xb, %xmm4, %xmm8 -; vpacksswb %xmm8, %xmm6, %xmm0 +; vpsraw $0xb, %xmm4, %xmm0 +; vpacksswb %xmm0, %xmm6, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -1350,10 +1350,10 @@ block0(v0: f64x2): ; uninit %xmm2 ; vxorpd %xmm2, %xmm2, %xmm4 ; vmaxpd %xmm0, %xmm4, %xmm6 -; vminpd %xmm6, const(0), %xmm8 -; vroundpd $3, %xmm8, %xmm10 -; vaddpd %xmm10, const(1), %xmm12 -; vshufps $136, %xmm12, %xmm4, %xmm0 +; vminpd %xmm6, const(0), %xmm0 +; vroundpd $3, %xmm0, %xmm2 +; vaddpd %xmm2, const(1), %xmm5 +; vshufps $136, %xmm5, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -1365,10 +1365,10 @@ block0(v0: f64x2): ; block1: ; offset 0x4 ; vxorpd %xmm2, %xmm2, %xmm4 ; vmaxpd %xmm4, %xmm0, %xmm6 -; vminpd 0x1c(%rip), %xmm6, %xmm8 -; vroundpd $3, %xmm8, %xmm10 -; vaddpd 0x1e(%rip), %xmm10, %xmm12 -; vshufps $0x88, %xmm4, %xmm12, %xmm0 +; vminpd 0x1c(%rip), %xmm6, %xmm0 +; vroundpd $3, %xmm0, %xmm2 +; vaddpd 0x1e(%rip), %xmm2, %xmm5 +; vshufps $0x88, %xmm4, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -1393,8 +1393,8 @@ block0(v0: i8x16, v1: i32): ; vpsllw %xmm0, %xmm5, %xmm7 ; lea const(0), %rsi ; shlq $4, %r10, %r10 -; vmovdqu 0(%rsi,%r10,1), %xmm13 -; vpand %xmm7, %xmm13, %xmm0 +; vmovdqu 0(%rsi,%r10,1), %xmm5 +; vpand %xmm7, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -1410,12 +1410,12 @@ block0(v0: i8x16, v1: i32): ; vpsllw %xmm5, %xmm0, %xmm7 ; leaq 0x15(%rip), %rsi ; shlq $4, %r10 -; vmovdqu (%rsi, %r10), %xmm13 -; vpand %xmm13, %xmm7, %xmm0 +; vmovdqu (%rsi, %r10), %xmm5 +; vpand %xmm5, %xmm7, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq -; addb %bh, %bh +; addb %al, (%rax) function %i8x16_shl_imm(i8x16) -> i8x16 { block0(v0: i8x16): diff --git a/cranelift/filetests/filetests/isa/x64/simd-bitselect.clif b/cranelift/filetests/filetests/isa/x64/simd-bitselect.clif index 50eb59680e30..6ffc922ee764 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-bitselect.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-bitselect.clif @@ -211,9 +211,9 @@ block0(v0: i8x16, v1: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqa %xmm0, %xmm8 +; movdqa %xmm0, %xmm2 ; movdqu const(0), %xmm0 -; movdqa %xmm8, %xmm4 +; movdqa %xmm2, %xmm4 ; pand %xmm4, %xmm0, %xmm4 ; pandn %xmm0, %xmm1, %xmm0 ; por %xmm0, %xmm4, %xmm0 @@ -226,9 +226,9 @@ block0(v0: i8x16, v1: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqa %xmm0, %xmm8 -; movdqu 0x1f(%rip), %xmm0 -; movdqa %xmm8, %xmm4 +; movdqa %xmm0, %xmm2 +; movdqu 0x20(%rip), %xmm0 +; movdqa %xmm2, %xmm4 ; pand %xmm0, %xmm4 ; pandn %xmm1, %xmm0 ; por %xmm4, %xmm0 @@ -240,6 +240,7 @@ block0(v0: i8x16, v1: i8x16): ; addb %al, (%rax) ; addb %al, (%rax) ; addb %al, (%rax) +; addb %al, (%rax) ; addb %dh, %al ; addb %al, (%rax) ; incl (%rax) diff --git a/cranelift/filetests/filetests/isa/x64/simd-bitwise-avx.clif b/cranelift/filetests/filetests/isa/x64/simd-bitwise-avx.clif index 8b540989c2b1..0de407a7abc7 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-bitwise-avx.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-bitwise-avx.clif @@ -42,8 +42,8 @@ block0(v0: i64): ; movl $-2147483648, %eax ; vmovd %eax, %xmm4 ; vandnps %xmm4, const(0), %xmm6 -; vandps %xmm4, 0(%rdi), %xmm8 -; vorps %xmm6, %xmm8, %xmm0 +; vandps %xmm4, 0(%rdi), %xmm0 +; vorps %xmm6, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -56,8 +56,8 @@ block0(v0: i64): ; movl $0x80000000, %eax ; vmovd %eax, %xmm4 ; vandnps 0x1b(%rip), %xmm4, %xmm6 -; vandps (%rdi), %xmm4, %xmm8 -; vorps %xmm8, %xmm6, %xmm0 +; vandps (%rdi), %xmm4, %xmm0 +; vorps %xmm0, %xmm6, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -75,6 +75,7 @@ block0(v0: i64): ; addb %al, (%rax) ; addb %al, (%rax) ; addb %al, (%rax) +; addb %al, (%rax) function %bor_f32x4(f32x4, f32x4) -> f32x4 { block0(v0: f32x4, v1: f32x4): diff --git a/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif b/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif index 817598710f4e..b1ed1466069f 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif @@ -337,8 +337,8 @@ block0(v0: i32): ; psllw %xmm0, %xmm5, %xmm0 ; lea const(0), %rsi ; shlq $4, %r10, %r10 -; movdqu 0(%rsi,%r10,1), %xmm13 -; pand %xmm0, %xmm13, %xmm0 +; movdqu 0(%rsi,%r10,1), %xmm5 +; pand %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -355,8 +355,8 @@ block0(v0: i32): ; psllw %xmm5, %xmm0 ; leaq 0x2d(%rip), %rsi ; shlq $4, %r10 -; movdqu (%rsi, %r10), %xmm13 -; pand %xmm13, %xmm0 +; movdqu (%rsi, %r10), %xmm5 +; pand %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -365,10 +365,9 @@ block0(v0: i32): ; addb %al, (%rax) ; addb %al, (%rax) ; addb %al, (%rax) -; addl %eax, (%rdx) -; addl 0x9080706(, %rax), %eax -; orb (%rbx), %cl -; orb $0xd, %al +; addb %al, (%rcx) +; addb (%rbx), %al +; addb $5, %al function %ishl_i8x16_imm(i8x16) -> i8x16 { block0(v0: i8x16): @@ -605,17 +604,17 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqu const(0), %xmm8 +; movdqu const(0), %xmm1 ; movq %rdi, %r9 ; andq %r9, $7, %r9 -; movdqa %xmm8, %xmm0 -; punpcklbw %xmm0, %xmm8, %xmm0 -; punpckhbw %xmm8, %xmm8, %xmm8 +; movdqa %xmm1, %xmm0 +; punpcklbw %xmm0, %xmm1, %xmm0 +; punpckhbw %xmm1, %xmm1, %xmm1 ; addl %r9d, $8, %r9d -; movd %r9d, %xmm11 -; psraw %xmm0, %xmm11, %xmm0 -; psraw %xmm8, %xmm11, %xmm8 -; packsswb %xmm0, %xmm8, %xmm0 +; movd %r9d, %xmm3 +; psraw %xmm0, %xmm3, %xmm0 +; psraw %xmm1, %xmm3, %xmm1 +; packsswb %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -625,23 +624,28 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqu 0x33(%rip), %xmm8 +; movdqu 0x34(%rip), %xmm1 ; movq %rdi, %r9 ; andq $7, %r9 -; movdqa %xmm8, %xmm0 -; punpcklbw %xmm8, %xmm0 -; punpckhbw %xmm8, %xmm8 +; movdqa %xmm1, %xmm0 +; punpcklbw %xmm1, %xmm0 +; punpckhbw %xmm1, %xmm1 ; addl $8, %r9d -; movd %r9d, %xmm11 -; psraw %xmm11, %xmm0 -; psraw %xmm11, %xmm8 -; packsswb %xmm8, %xmm0 +; movd %r9d, %xmm3 +; psraw %xmm3, %xmm0 +; psraw %xmm3, %xmm1 +; packsswb %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq -; addb %al, (%rcx) -; addb (%rbx), %al -; addb $5, %al +; addb %al, (%rax) +; addb %al, (%rax) +; addb %al, (%rax) +; addb %al, (%rax) +; addl %eax, (%rdx) +; addl 0x9080706(, %rax), %eax +; orb (%rbx), %cl +; orb $0xd, %al function %sshr_i8x16_imm(i8x16, i32) -> i8x16 { block0(v0: i8x16, v1: i32): @@ -655,10 +659,10 @@ block0(v0: i8x16, v1: i32): ; block0: ; movdqa %xmm0, %xmm7 ; punpcklbw %xmm7, %xmm0, %xmm7 -; movdqa %xmm7, %xmm8 +; movdqa %xmm7, %xmm1 ; movdqa %xmm0, %xmm7 ; punpckhbw %xmm7, %xmm0, %xmm7 -; movdqa %xmm8, %xmm0 +; movdqa %xmm1, %xmm0 ; psraw %xmm0, $11, %xmm0 ; psraw %xmm7, $11, %xmm7 ; packsswb %xmm0, %xmm7, %xmm0 @@ -673,10 +677,10 @@ block0(v0: i8x16, v1: i32): ; block1: ; offset 0x4 ; movdqa %xmm0, %xmm7 ; punpcklbw %xmm0, %xmm7 -; movdqa %xmm7, %xmm8 +; movdqa %xmm7, %xmm1 ; movdqa %xmm0, %xmm7 ; punpckhbw %xmm0, %xmm7 -; movdqa %xmm8, %xmm0 +; movdqa %xmm1, %xmm0 ; psraw $0xb, %xmm0 ; psraw $0xb, %xmm7 ; packsswb %xmm7, %xmm0 @@ -899,13 +903,13 @@ block0(v0: i64x2, v1: i32): ; movq %rdi, %rcx ; andq %rcx, $63, %rcx ; movq %rcx, %xmm5 -; movdqu const(0), %xmm8 -; psrlq %xmm8, %xmm5, %xmm8 -; movdqa %xmm0, %xmm11 -; psrlq %xmm11, %xmm5, %xmm11 -; movdqa %xmm8, %xmm0 -; pxor %xmm0, %xmm11, %xmm0 -; psubq %xmm0, %xmm8, %xmm0 +; movdqu const(0), %xmm1 +; psrlq %xmm1, %xmm5, %xmm1 +; movdqa %xmm0, %xmm3 +; psrlq %xmm3, %xmm5, %xmm3 +; movdqa %xmm1, %xmm0 +; pxor %xmm0, %xmm3, %xmm0 +; psubq %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -918,13 +922,13 @@ block0(v0: i64x2, v1: i32): ; movq %rdi, %rcx ; andq $0x3f, %rcx ; movq %rcx, %xmm5 -; movdqu 0x27(%rip), %xmm8 -; psrlq %xmm5, %xmm8 -; movdqa %xmm0, %xmm11 -; psrlq %xmm5, %xmm11 -; movdqa %xmm8, %xmm0 -; pxor %xmm11, %xmm0 -; psubq %xmm8, %xmm0 +; movdqu 0x28(%rip), %xmm1 +; psrlq %xmm5, %xmm1 +; movdqa %xmm0, %xmm3 +; psrlq %xmm5, %xmm3 +; movdqa %xmm1, %xmm0 +; pxor %xmm3, %xmm0 +; psubq %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -935,4 +939,9 @@ block0(v0: i64x2, v1: i32): ; addb %al, (%rax) ; addb %al, (%rax) ; addb %al, (%rax) +; addb %al, (%rax) +; addb %al, (%rax) +; addb $0, (%rax) +; addb %al, (%rax) +; addb %al, (%rax) diff --git a/cranelift/filetests/filetests/isa/x64/simd-cmp-avx.clif b/cranelift/filetests/filetests/isa/x64/simd-cmp-avx.clif index bccc8657cb9a..60bd126554ad 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-cmp-avx.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-cmp-avx.clif @@ -214,10 +214,10 @@ block0(v0: f32x4, v1: f32x4): ; vminps %xmm0, %xmm1, %xmm3 ; vminps %xmm1, %xmm0, %xmm5 ; vorps %xmm3, %xmm5, %xmm7 -; vcmpps $3, %xmm7, %xmm5, %xmm9 -; vorps %xmm7, %xmm9, %xmm11 -; vpsrld %xmm9, $10, %xmm13 -; vandnps %xmm13, %xmm11, %xmm0 +; vcmpps $3, %xmm7, %xmm5, %xmm1 +; vorps %xmm7, %xmm1, %xmm3 +; vpsrld %xmm1, $10, %xmm5 +; vandnps %xmm5, %xmm3, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -230,10 +230,10 @@ block0(v0: f32x4, v1: f32x4): ; vminps %xmm1, %xmm0, %xmm3 ; vminps %xmm0, %xmm1, %xmm5 ; vorps %xmm5, %xmm3, %xmm7 -; vcmpunordps %xmm5, %xmm7, %xmm9 -; vorps %xmm9, %xmm7, %xmm11 -; vpsrld $0xa, %xmm9, %xmm13 -; vandnps %xmm11, %xmm13, %xmm0 +; vcmpunordps %xmm5, %xmm7, %xmm1 +; vorps %xmm1, %xmm7, %xmm3 +; vpsrld $0xa, %xmm1, %xmm5 +; vandnps %xmm3, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -251,10 +251,10 @@ block0(v0: f64x2, v1: f64x2): ; vminpd %xmm0, %xmm1, %xmm3 ; vminpd %xmm1, %xmm0, %xmm5 ; vorpd %xmm3, %xmm5, %xmm7 -; vcmppd $3, %xmm3, %xmm5, %xmm9 -; vorpd %xmm7, %xmm9, %xmm11 -; vpsrlq %xmm9, $13, %xmm13 -; vandnpd %xmm13, %xmm11, %xmm0 +; vcmppd $3, %xmm3, %xmm5, %xmm1 +; vorpd %xmm7, %xmm1, %xmm3 +; vpsrlq %xmm1, $13, %xmm5 +; vandnpd %xmm5, %xmm3, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -267,10 +267,10 @@ block0(v0: f64x2, v1: f64x2): ; vminpd %xmm1, %xmm0, %xmm3 ; vminpd %xmm0, %xmm1, %xmm5 ; vorpd %xmm5, %xmm3, %xmm7 -; vcmpunordpd %xmm5, %xmm3, %xmm9 -; vorpd %xmm9, %xmm7, %xmm11 -; vpsrlq $0xd, %xmm9, %xmm13 -; vandnpd %xmm11, %xmm13, %xmm0 +; vcmpunordpd %xmm5, %xmm3, %xmm1 +; vorpd %xmm1, %xmm7, %xmm3 +; vpsrlq $0xd, %xmm1, %xmm5 +; vandnpd %xmm3, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -288,11 +288,11 @@ block0(v0: f32x4, v1: f32x4): ; vmaxps %xmm0, %xmm1, %xmm3 ; vmaxps %xmm1, %xmm0, %xmm5 ; vxorps %xmm3, %xmm5, %xmm7 -; vorps %xmm3, %xmm7, %xmm9 -; vsubps %xmm9, %xmm7, %xmm11 -; vcmpps $3, %xmm9, %xmm9, %xmm13 -; vpsrld %xmm13, $10, %xmm15 -; vandnps %xmm15, %xmm11, %xmm0 +; vorps %xmm3, %xmm7, %xmm1 +; vsubps %xmm1, %xmm7, %xmm3 +; vcmpps $3, %xmm1, %xmm1, %xmm5 +; vpsrld %xmm5, $10, %xmm7 +; vandnps %xmm7, %xmm3, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -305,11 +305,11 @@ block0(v0: f32x4, v1: f32x4): ; vmaxps %xmm1, %xmm0, %xmm3 ; vmaxps %xmm0, %xmm1, %xmm5 ; vxorps %xmm5, %xmm3, %xmm7 -; vorps %xmm7, %xmm3, %xmm9 -; vsubps %xmm7, %xmm9, %xmm11 -; vcmpunordps %xmm9, %xmm9, %xmm13 -; vpsrld $0xa, %xmm13, %xmm15 -; vandnps %xmm11, %xmm15, %xmm0 +; vorps %xmm7, %xmm3, %xmm1 +; vsubps %xmm7, %xmm1, %xmm3 +; vcmpunordps %xmm1, %xmm1, %xmm5 +; vpsrld $0xa, %xmm5, %xmm7 +; vandnps %xmm3, %xmm7, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -327,11 +327,11 @@ block0(v0: f64x2, v1: f64x2): ; vmaxpd %xmm0, %xmm1, %xmm3 ; vmaxpd %xmm1, %xmm0, %xmm5 ; vxorpd %xmm3, %xmm5, %xmm7 -; vorpd %xmm3, %xmm7, %xmm9 -; vsubpd %xmm9, %xmm7, %xmm11 -; vcmppd $3, %xmm9, %xmm9, %xmm13 -; vpsrlq %xmm13, $13, %xmm15 -; vandnpd %xmm15, %xmm11, %xmm0 +; vorpd %xmm3, %xmm7, %xmm1 +; vsubpd %xmm1, %xmm7, %xmm3 +; vcmppd $3, %xmm1, %xmm1, %xmm5 +; vpsrlq %xmm5, $13, %xmm7 +; vandnpd %xmm7, %xmm3, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -344,11 +344,11 @@ block0(v0: f64x2, v1: f64x2): ; vmaxpd %xmm1, %xmm0, %xmm3 ; vmaxpd %xmm0, %xmm1, %xmm5 ; vxorpd %xmm5, %xmm3, %xmm7 -; vorpd %xmm7, %xmm3, %xmm9 -; vsubpd %xmm7, %xmm9, %xmm11 -; vcmpunordpd %xmm9, %xmm9, %xmm13 -; vpsrlq $0xd, %xmm13, %xmm15 -; vandnpd %xmm11, %xmm15, %xmm0 +; vorpd %xmm7, %xmm3, %xmm1 +; vsubpd %xmm7, %xmm1, %xmm3 +; vcmpunordpd %xmm1, %xmm1, %xmm5 +; vpsrlq $0xd, %xmm5, %xmm7 +; vandnpd %xmm3, %xmm7, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/simd-comparison-legalize.clif b/cranelift/filetests/filetests/isa/x64/simd-comparison-legalize.clif index 805258582eb4..26f10fdcca1a 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-comparison-legalize.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-comparison-legalize.clif @@ -43,9 +43,9 @@ block0(v0: i32x4, v1: i32x4): ; block0: ; pmaxud %xmm0, %xmm1, %xmm0 ; pcmpeqd %xmm0, %xmm1, %xmm0 -; uninit %xmm8 -; pcmpeqd %xmm8, %xmm8, %xmm8 -; pxor %xmm0, %xmm8, %xmm0 +; uninit %xmm1 +; pcmpeqd %xmm1, %xmm1, %xmm1 +; pxor %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -57,8 +57,8 @@ block0(v0: i32x4, v1: i32x4): ; block1: ; offset 0x4 ; pmaxud %xmm1, %xmm0 ; pcmpeqd %xmm1, %xmm0 -; pcmpeqd %xmm8, %xmm8 -; pxor %xmm8, %xmm0 +; pcmpeqd %xmm1, %xmm1 +; pxor %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/simd-splat-avx.clif b/cranelift/filetests/filetests/isa/x64/simd-splat-avx.clif index c7a3a92af894..9e994d56593a 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-splat-avx.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-splat-avx.clif @@ -178,8 +178,8 @@ block0(v0: i64): ; uninit %xmm2 ; vpinsrb $0, %xmm2, 0(%rdi), %xmm4 ; uninit %xmm6 -; vpxor %xmm6, %xmm6, %xmm8 -; vpshufb %xmm4, %xmm8, %xmm0 +; vpxor %xmm6, %xmm6, %xmm0 +; vpshufb %xmm4, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -190,8 +190,8 @@ block0(v0: i64): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; vpinsrb $0, (%rdi), %xmm2, %xmm4 ; trap: heap_oob -; vpxor %xmm6, %xmm6, %xmm8 -; vpshufb %xmm8, %xmm4, %xmm0 +; vpxor %xmm6, %xmm6, %xmm0 +; vpshufb %xmm0, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/simd-widen-mul.clif b/cranelift/filetests/filetests/isa/x64/simd-widen-mul.clif index 4941dda84a70..4890d8bc4f75 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-widen-mul.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-widen-mul.clif @@ -18,8 +18,8 @@ block0(v0: i8x16, v1: i8x16): ; pmovsxbw %xmm6, %xmm0 ; movdqa %xmm1, %xmm6 ; palignr $8, %xmm6, %xmm1, %xmm6 -; pmovsxbw %xmm6, %xmm8 -; pmullw %xmm0, %xmm8, %xmm0 +; pmovsxbw %xmm6, %xmm1 +; pmullw %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -34,8 +34,8 @@ block0(v0: i8x16, v1: i8x16): ; pmovsxbw %xmm6, %xmm0 ; movdqa %xmm1, %xmm6 ; palignr $8, %xmm1, %xmm6 -; pmovsxbw %xmm6, %xmm8 -; pmullw %xmm8, %xmm0 +; pmovsxbw %xmm6, %xmm1 +; pmullw %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -223,14 +223,14 @@ block0(v0: i8x16, v1: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; uninit %xmm8 -; pxor %xmm8, %xmm8, %xmm8 -; punpckhbw %xmm0, %xmm8, %xmm0 -; uninit %xmm8 -; pxor %xmm8, %xmm8, %xmm8 -; movdqa %xmm1, %xmm11 -; punpckhbw %xmm11, %xmm8, %xmm11 -; pmullw %xmm0, %xmm11, %xmm0 +; uninit %xmm2 +; pxor %xmm2, %xmm2, %xmm2 +; punpckhbw %xmm0, %xmm2, %xmm0 +; uninit %xmm2 +; pxor %xmm2, %xmm2, %xmm2 +; movdqa %xmm1, %xmm3 +; punpckhbw %xmm3, %xmm2, %xmm3 +; pmullw %xmm0, %xmm3, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -240,12 +240,12 @@ block0(v0: i8x16, v1: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; pxor %xmm8, %xmm8 -; punpckhbw %xmm8, %xmm0 -; pxor %xmm8, %xmm8 -; movdqa %xmm1, %xmm11 -; punpckhbw %xmm8, %xmm11 -; pmullw %xmm11, %xmm0 +; pxor %xmm2, %xmm2 +; punpckhbw %xmm2, %xmm0 +; pxor %xmm2, %xmm2 +; movdqa %xmm1, %xmm3 +; punpckhbw %xmm2, %xmm3 +; pmullw %xmm3, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/wasm/x64-relaxed-simd-deterministic.wat b/cranelift/filetests/filetests/wasm/x64-relaxed-simd-deterministic.wat index 3d5a9fb3363b..381a74fe34c8 100644 --- a/cranelift/filetests/filetests/wasm/x64-relaxed-simd-deterministic.wat +++ b/cranelift/filetests/filetests/wasm/x64-relaxed-simd-deterministic.wat @@ -47,10 +47,10 @@ ;; vcmpps $0, %xmm0, %xmm0, %xmm3 ;; vandps %xmm0, %xmm3, %xmm5 ;; vpxor %xmm3, %xmm5, %xmm7 -;; vcvttps2dq %xmm5, %xmm9 -;; vpand %xmm9, %xmm7, %xmm11 -;; vpsrad %xmm11, $31, %xmm13 -;; vpxor %xmm13, %xmm9, %xmm0 +;; vcvttps2dq %xmm5, %xmm1 +;; vpand %xmm1, %xmm7, %xmm3 +;; vpsrad %xmm3, $31, %xmm5 +;; vpxor %xmm5, %xmm1, %xmm0 ;; jmp label1 ;; block1: ;; movq %rbp, %rsp @@ -65,19 +65,19 @@ ;; block0: ;; uninit %xmm3 ;; vxorps %xmm3, %xmm3, %xmm5 -;; vmaxps %xmm0, %xmm5, %xmm7 -;; vpcmpeqd %xmm5, %xmm5, %xmm9 -;; vpsrld %xmm9, $1, %xmm11 -;; vcvtdq2ps %xmm11, %xmm13 -;; vcvttps2dq %xmm7, %xmm15 -;; vsubps %xmm7, %xmm13, %xmm1 -;; vcmpps $2, %xmm13, %xmm1, %xmm3 +;; vmaxps %xmm0, %xmm5, %xmm0 +;; vpcmpeqd %xmm5, %xmm5, %xmm1 +;; vpsrld %xmm1, $1, %xmm3 +;; vcvtdq2ps %xmm3, %xmm5 +;; vcvttps2dq %xmm0, %xmm7 +;; vsubps %xmm0, %xmm5, %xmm1 +;; vcmpps $2, %xmm5, %xmm1, %xmm3 ;; vcvttps2dq %xmm1, %xmm5 -;; vpxor %xmm5, %xmm3, %xmm7 -;; uninit %xmm9 -;; vpxor %xmm9, %xmm9, %xmm11 -;; vpmaxsd %xmm7, %xmm11, %xmm13 -;; vpaddd %xmm13, %xmm15, %xmm0 +;; vpxor %xmm5, %xmm3, %xmm0 +;; uninit %xmm1 +;; vpxor %xmm1, %xmm1, %xmm3 +;; vpmaxsd %xmm0, %xmm3, %xmm5 +;; vpaddd %xmm5, %xmm7, %xmm0 ;; jmp label1 ;; block1: ;; movq %rbp, %rsp @@ -109,10 +109,10 @@ ;; uninit %xmm3 ;; vxorpd %xmm3, %xmm3, %xmm5 ;; vmaxpd %xmm0, %xmm5, %xmm7 -;; vminpd %xmm7, const(0), %xmm9 -;; vroundpd $3, %xmm9, %xmm11 -;; vaddpd %xmm11, const(1), %xmm13 -;; vshufps $136, %xmm13, %xmm5, %xmm0 +;; vminpd %xmm7, const(0), %xmm1 +;; vroundpd $3, %xmm1, %xmm3 +;; vaddpd %xmm3, const(1), %xmm6 +;; vshufps $136, %xmm6, %xmm5, %xmm0 ;; jmp label1 ;; block1: ;; movq %rbp, %rsp @@ -125,15 +125,15 @@ ;; movq %rsp, %rbp ;; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 } ;; block0: -;; vpmovsxbw %xmm0, %xmm12 -;; vpmovsxbw %xmm1, %xmm13 -;; vpmullw %xmm12, %xmm13, %xmm12 -;; vpalignr $8, %xmm0, %xmm0, %xmm11 -;; vpmovsxbw %xmm11, %xmm13 -;; vpalignr $8, %xmm1, %xmm1, %xmm11 -;; vpmovsxbw %xmm11, %xmm14 -;; vpmullw %xmm13, %xmm14, %xmm13 -;; vphaddw %xmm12, %xmm13, %xmm0 +;; vpmovsxbw %xmm0, %xmm4 +;; vpmovsxbw %xmm1, %xmm5 +;; vpmullw %xmm4, %xmm5, %xmm4 +;; vpalignr $8, %xmm0, %xmm0, %xmm3 +;; vpmovsxbw %xmm3, %xmm5 +;; vpalignr $8, %xmm1, %xmm1, %xmm3 +;; vpmovsxbw %xmm3, %xmm6 +;; vpmullw %xmm5, %xmm6, %xmm5 +;; vphaddw %xmm4, %xmm5, %xmm0 ;; jmp label1 ;; block1: ;; movq %rbp, %rsp @@ -146,17 +146,17 @@ ;; movq %rsp, %rbp ;; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 } ;; block0: -;; vpmovsxbw %xmm0, %xmm15 +;; vpmovsxbw %xmm0, %xmm7 ;; vpmovsxbw %xmm1, %xmm3 -;; vpmullw %xmm15, %xmm3, %xmm15 -;; vpalignr $8, %xmm0, %xmm0, %xmm14 -;; vpmovsxbw %xmm14, %xmm0 -;; vpalignr $8, %xmm1, %xmm1, %xmm14 -;; vpmovsxbw %xmm14, %xmm1 +;; vpmullw %xmm7, %xmm3, %xmm7 +;; vpalignr $8, %xmm0, %xmm0, %xmm6 +;; vpmovsxbw %xmm6, %xmm0 +;; vpalignr $8, %xmm1, %xmm1, %xmm6 +;; vpmovsxbw %xmm6, %xmm1 ;; vpmullw %xmm0, %xmm1, %xmm0 -;; vphaddw %xmm15, %xmm0, %xmm15 -;; vpmaddwd %xmm15, const(0), %xmm15 -;; vpaddd %xmm15, %xmm2, %xmm0 +;; vphaddw %xmm7, %xmm0, %xmm7 +;; vpmaddwd %xmm7, const(0), %xmm7 +;; vpaddd %xmm7, %xmm2, %xmm0 ;; jmp label1 ;; block1: ;; movq %rbp, %rsp diff --git a/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat b/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat index 39ebc2324b95..d3bbfd4441e2 100644 --- a/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat +++ b/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat @@ -58,20 +58,20 @@ ;; block0: ;; uninit %xmm7 ;; xorps %xmm7, %xmm7, %xmm7 -;; movdqa %xmm0, %xmm12 -;; maxps %xmm12, %xmm7, %xmm12 +;; movdqa %xmm0, %xmm4 +;; maxps %xmm4, %xmm7, %xmm4 ;; pcmpeqd %xmm7, %xmm7, %xmm7 ;; psrld %xmm7, $1, %xmm7 ;; cvtdq2ps %xmm7, %xmm1 -;; cvttps2dq %xmm12, %xmm15 -;; subps %xmm12, %xmm1, %xmm12 -;; cmpps $2, %xmm1, %xmm12, %xmm1 -;; cvttps2dq %xmm12, %xmm0 +;; cvttps2dq %xmm4, %xmm7 +;; subps %xmm4, %xmm1, %xmm4 +;; cmpps $2, %xmm1, %xmm4, %xmm1 +;; cvttps2dq %xmm4, %xmm0 ;; pxor %xmm0, %xmm1, %xmm0 -;; uninit %xmm10 -;; pxor %xmm10, %xmm10, %xmm10 -;; pmaxsd %xmm0, %xmm10, %xmm0 -;; paddd %xmm0, %xmm15, %xmm0 +;; uninit %xmm2 +;; pxor %xmm2, %xmm2, %xmm2 +;; pmaxsd %xmm0, %xmm2, %xmm0 +;; paddd %xmm0, %xmm7, %xmm0 ;; jmp label1 ;; block1: ;; movq %rbp, %rsp @@ -99,10 +99,9 @@ ;; block0: ;; uninit %xmm4 ;; xorpd %xmm4, %xmm4, %xmm4 -;; movdqa %xmm0, %xmm8 -;; maxpd %xmm8, %xmm4, %xmm8 -;; minpd %xmm8, const(0), %xmm8 -;; roundpd $3, %xmm8, %xmm0 +;; maxpd %xmm0, %xmm4, %xmm0 +;; minpd %xmm0, const(0), %xmm0 +;; roundpd $3, %xmm0, %xmm0 ;; addpd %xmm0, const(1), %xmm0 ;; shufps $136, %xmm0, %xmm4, %xmm0 ;; jmp label1 @@ -132,9 +131,10 @@ ;; movq %rsp, %rbp ;; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 } ;; block0: -;; movdqa %xmm0, %xmm8 -;; movdqa %xmm1, %xmm0 -;; pmaddubsw %xmm0, %xmm8, %xmm0 +;; movdqa %xmm1, %xmm3 +;; movdqa %xmm0, %xmm1 +;; movdqa %xmm3, %xmm0 +;; pmaddubsw %xmm0, %xmm1, %xmm0 ;; pmaddwd %xmm0, const(0), %xmm0 ;; paddd %xmm0, %xmm2, %xmm0 ;; jmp label1 From 5c1557d24e198aab3bfeffa091e77d7e9700cd3a Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 26 Sep 2023 10:55:45 -0700 Subject: [PATCH 012/199] Add docs and example for debugging with core dumps (#7087) * Add docs for debugging with core dumps * Fix reference to old style CLI flag * Add `no_run` to example that is only there to trap --- .gitignore | 1 + docs/SUMMARY.md | 3 + docs/examples-debugging-core-dumps.md | 90 ++++++++++++++++++++ docs/examples-debugging-native-debugger.md | 45 ++++++++++ docs/examples-debugging.md | 44 ++-------- docs/examples-rust-core-dumps.md | 19 +++++ examples/coredump.rs | 96 ++++++++++++++++++++++ 7 files changed, 261 insertions(+), 37 deletions(-) create mode 100644 docs/examples-debugging-core-dumps.md create mode 100644 docs/examples-debugging-native-debugger.md create mode 100644 docs/examples-rust-core-dumps.md create mode 100644 examples/coredump.rs diff --git a/.gitignore b/.gitignore index afb3ff15003f..d9c728065301 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ foo publish vendor examples/build +*.coredump diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 21c1061f2031..c9cb56991e01 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -3,6 +3,8 @@ - [Introduction](./introduction.md) - [Examples](./examples.md) - [Debugging WebAssembly](./examples-debugging.md) + - [Debugging with `gdb` and `lldb`](./examples-debugging-native-debugger.md) + - [Debugging with Core Dumps](./examples-debugging-core-dumps.md) - [Profiling WebAssembly](./examples-profiling.md) - [Profiling with Perf](./examples-profiling-perf.md) - [Profiling with VTune](./examples-profiling-vtune.md) @@ -15,6 +17,7 @@ - [WASI](./examples-rust-wasi.md) - [Linking Modules](./examples-rust-linking.md) - [Debugging](./examples-rust-debugging.md) + - [Core Dumps](./examples-rust-core-dumps.md) - [Using Multi-Value](./examples-rust-multi-value.md) - [Embedding in C](./examples-c-embed.md) - [Hello, World!](./examples-c-hello-world.md) diff --git a/docs/examples-debugging-core-dumps.md b/docs/examples-debugging-core-dumps.md new file mode 100644 index 000000000000..5f03f368aae9 --- /dev/null +++ b/docs/examples-debugging-core-dumps.md @@ -0,0 +1,90 @@ +# Debugging WebAssembly with Core Dumps + +Wasmtime can be configured to generate [the standard Wasm core dump +format][spec] whenever guest Wasm programs trap. These core dumps can then be +consumed by external tooling (such as [`wasmgdb`][wasmgdb]) for post-mortem analysis. + +This page focuses on generating and inspecting core dumps via the Wasmtime +command-line interface. For details on how to generate core dumps via the +`wasmtime` embedding API, see [Core Dumps in a Rust +Embedding](./examples-rust-core-dumps.md). + +First, we need to compile some code to Wasm that can trap. Consider the +following Rust code: + +```rust,no_run +// trap.rs + +fn main() { + foo(42); +} + +fn foo(x: u32) { + bar(x); +} + +fn bar(x: u32) { + baz(x); +} + +fn baz(x: u32) { + assert!(x != 42); +} +``` + +We can compile it to Wasm with the following command: + +```shell-session +$ rustc --target wasm32-wasi -o ./trap.wasm ./trap.rs +``` + +Next, we can run it in Wasmtime and capture a core dump when it traps: + +```shell-session +$ wasmtime -D coredump=./trap.coredump ./trap.wasm +thread 'main' panicked at /home/nick/scratch/trap.rs:14:5: +assertion failed: x != 42 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +Error: failed to run main module `/home/nick/scratch/trap.wasm` + +Caused by: + 0: core dumped at /home/nick/scratch/trap.coredump + 1: failed to invoke command default + 2: wasm coredump generated while executing store_name: + modules: + + instances: + Instance(store=1, index=1) + memories: + Memory(store=1, index=1) + globals: + Global(store=1, index=0) + backtrace: + error while executing at wasm backtrace: + 0: 0x5961 - !__rust_start_panic + 1: 0x562a - !rust_panic + 2: 0x555d - !std::panicking::rust_panic_with_hook::h58e7d0b3d70e95b6 + 3: 0x485d - !std::panicking::begin_panic_handler::{{closure}}::h1853004619879cfd + 4: 0x47bd - !std::sys_common::backtrace::__rust_end_short_backtrace::hed32bc5557405634 + 5: 0x4f02 - !rust_begin_unwind + 6: 0xac01 - !core::panicking::panic_fmt::h53ca5bf48b428895 + 7: 0xb1c5 - !core::panicking::panic::h62c2c2bb054da7e1 + 8: 0x661 - !trap::baz::h859f39b65389c077 + 9: 0x616 - !trap::bar::h7ad12f9c5b730d17 + 10: 0x60a - !trap::foo::ha69c95723611c1a0 + 11: 0x5fe - !trap::main::hdfcd9f2d150fc3dc + 12: 0x434 - !core::ops::function::FnOnce::call_once::h24336e950fb97d1e + 13: 0x40b - !std::sys_common::backtrace::__rust_begin_short_backtrace::h2b37384d2b1a57ff + 14: 0x4ec - !std::rt::lang_start::{{closure}}::he86eb1b6ac6d7501 + 15: 0x24f7 - !std::rt::lang_start_internal::h21f6a1d8f3633b54 + 16: 0x497 - !std::rt::lang_start::h7d256f21902ff32b + 17: 0x687 - !__main_void + 18: 0x3e6 - !_start + note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information +``` + +You now have a core dump at `./trap.coredump` that can be consumed by external +tooling to do post-mortem analysis of the failure. + +[spec]: https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md +[wasmgdb]: https://github.com/xtuc/wasm-coredump/blob/main/bin/wasmgdb/README.md diff --git a/docs/examples-debugging-native-debugger.md b/docs/examples-debugging-native-debugger.md new file mode 100644 index 000000000000..b61fe494fef3 --- /dev/null +++ b/docs/examples-debugging-native-debugger.md @@ -0,0 +1,45 @@ +# Debugging with `gdb` and `lldb` + +The following steps describe how to use `gdb` or `lldb` to debug both the Wasm +guest and the host (i.e. the Wasmtime CLI or your Wasmtime-embedding program) at +the same time: + +1. Compile your WebAssembly with debug info enabled, usually `-g`; for + example: + + ```sh + clang foo.c -g -o foo.wasm + ``` + +2. Run Wasmtime with the debug info enabled; this is `-D debug-info` from the + CLI and `Config::debug_info(true)` in an embedding (e.g. see [debugging in a + Rust embedding](./examples-rust-debugging.md)) + +3. Use a supported debugger: + + ```sh + lldb -- wasmtime run -D debug-info foo.wasm + ``` + ```sh + gdb --args wasmtime run -D debug-info foo.wasm + ``` + +If you run into trouble, the following discussions might help: + +- On MacOS with LLDB you may need to run: `settings set + plugin.jit-loader.gdb.enable on` + ([#1953](https://github.com/bytecodealliance/wasmtime/issues/1953)) + +- With LLDB, call `__vmctx.set()` to set the current context before calling any + dereference operators + ([#1482](https://github.com/bytecodealliance/wasmtime/issues/1482)): + ```sh + (lldb) p __vmctx->set() + (lldb) p *foo + ``` + +- The address of the start of instance memory can be found in `__vmctx->memory` + +- On Windows you may experience degraded WASM compilation throughput due to the + enablement of additional native heap checks when under the debugger by default. + You can set the environment variable `_NO_DEBUG_HEAP` to `1` to disable them. diff --git a/docs/examples-debugging.md b/docs/examples-debugging.md index c373e05c76de..b5301f05e6c7 100644 --- a/docs/examples-debugging.md +++ b/docs/examples-debugging.md @@ -1,41 +1,11 @@ # Debugging WebAssembly -The following steps describe a common way to debug a WebAssembly module in -Wasmtime: +Wasmtime currently provides the following support for debugging misbehaving +WebAssembly: -1. Compile your WebAssembly with debug info enabled, usually `-g`; for - example: +* We can [live debug and step through the guest Wasm and the host at the same + time with `gdb` or `lldb`.](./examples-debugging-native-debugger.md) - ```sh - clang foo.c -g -o foo.wasm - ``` - -2. Run Wasmtime with the debug info enabled; this is `-g` from the CLI and - `Config::debug_info(true)` in an embedding (e.g. see [debugging in a Rust - embedding](./examples-rust-debugging.md)) - -3. Use a supported debugger: - - ```sh - lldb -- wasmtime run -D debug-info foo.wasm - ``` - ```sh - gdb --args wasmtime run -D debug-info foo.wasm - ``` - -If you run into trouble, the following discussions might help: - -- On MacOS with LLDB you may need to run: `settings set - plugin.jit-loader.gdb.enable on` - ([#1953](https://github.com/bytecodealliance/wasmtime/issues/1953)) -- With LLDB, call `__vmctx.set()` to set the current context before calling any - dereference operators - ([#1482](https://github.com/bytecodealliance/wasmtime/issues/1482)): - ```sh - (lldb) p __vmctx->set() - (lldb) p *foo - ``` -- The address of the start of instance memory can be found in `__vmctx->memory` -- On Windows you may experience degraded WASM compilation throughput due to the - enablement of additional native heap checks when under the debugger by default. - You can set the environment variable `_NO_DEBUG_HEAP` to `1` to disable them. +* When a Wasm guest traps, we can [generate Wasm core + dumps](./examples-debugging-core-dumps.md), that can be consumed by other + tools for post-mortem analysis. diff --git a/docs/examples-rust-core-dumps.md b/docs/examples-rust-core-dumps.md new file mode 100644 index 000000000000..a1169fe58e1a --- /dev/null +++ b/docs/examples-rust-core-dumps.md @@ -0,0 +1,19 @@ +# Core Dumps + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/fib-debug/main.rs + +This examples shows how to configure capturing [core dumps] when a Wasm guest +traps that can then be passed to external tools (like [`wasmgdb`]) for +post-mortem analysis. + +[core dumps]: https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md +[`wasmgdb`]: https://github.com/xtuc/wasm-coredump/blob/main/bin/wasmgdb/README.md + +## `main.rs` + +```rust,ignore +{{#include ../examples/coredump.rs}} +``` diff --git a/examples/coredump.rs b/examples/coredump.rs new file mode 100644 index 000000000000..cd4405476696 --- /dev/null +++ b/examples/coredump.rs @@ -0,0 +1,96 @@ +//! An example of how to configure capturing core dumps when the guest Wasm +//! traps that can then be passed to external tools for post-mortem analysis. + +// You can execute this example with `cargo run --example coredump`. + +use anyhow::Result; +use wasmtime::*; + +fn main() -> Result<()> { + println!("Configure core dumps to be captured on trap."); + let mut config = Config::new(); + config.coredump_on_trap(true); + let engine = Engine::new(&config)?; + let mut store = Store::new(&engine, ()); + + println!("Define a Wasm module that will mutate local state and then trap."); + let module = Module::new( + store.engine(), + r#" + (module $trapper + (memory 10) + (global $g (mut i32) (i32.const 0)) + + (func (export "run") + call $a + ) + + (func $a + i32.const 0x1234 + i64.const 42 + i64.store + call $b + ) + + (func $b + i32.const 36 + global.set $g + call $c + ) + + (func $c + unreachable + ) + ) + "#, + )?; + + println!("Instantiate the module."); + let instance = Instance::new(&mut store, &module, &[])?; + + println!("Invoke its 'run' function."); + let run = instance + .get_func(&mut store, "run") + .expect("should have 'run' export"); + let args = &[]; + let results = &mut []; + let ok = run.call(&mut store, args, results); + + println!("Calling that function trapped."); + assert!(ok.is_err()); + let err = ok.unwrap_err(); + assert!(err.is::()); + + println!("Extract the captured core dump."); + let dump = err + .downcast_ref::() + .expect("should have an attached core dump, since we configured core dumps on"); + + println!( + "Number of memories in the core dump: {}", + dump.memories().len() + ); + for (i, mem) in dump.memories().iter().enumerate() { + if let Some(addr) = mem.data(&store).iter().position(|byte| *byte != 0) { + let val = mem.data(&store)[addr]; + println!(" First nonzero byte for memory {i}: {val} @ {addr:#x}"); + } else { + println!(" Memory {i} is all zeroes."); + } + } + + println!( + "Number of globals in the core dump: {}", + dump.globals().len() + ); + for (i, global) in dump.globals().iter().enumerate() { + let val = global.get(&mut store); + println!(" Global {i} = {val:?}"); + } + + println!("Serialize the core dump and write it to ./example.coredump"); + let serialized = dump.serialize(&mut store, "trapper.wasm"); + std::fs::write("./example.coredump", serialized)?; + + Ok(()) +} From 4a8eaa744c7ee5c31977465af24ee03bc55cdb57 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 26 Sep 2023 12:42:50 -0700 Subject: [PATCH 013/199] cranelift(x64): Swap operands to save AVX instruction encoding size (#7093) For `XmmRmiRVex`-format instructions, when the opcode is commutative, the first operand is one of xmm{0..7}, and the second operand is one of xmm{8..15}, then we can swap the operands to save a byte on instruction encoding. --- cranelift/codegen/src/isa/x64/inst/args.rs | 47 ++++++++++++ cranelift/codegen/src/isa/x64/inst/emit.rs | 15 ++++ .../codegen/src/isa/x64/inst/emit_tests.rs | 73 ++++++++++++++++++- 3 files changed, 134 insertions(+), 1 deletion(-) diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs index 34ec6be1f186..a9a393be1693 100644 --- a/cranelift/codegen/src/isa/x64/inst/args.rs +++ b/cranelift/codegen/src/isa/x64/inst/args.rs @@ -669,6 +669,12 @@ impl From for RegMemImm { } } +impl From for RegMemImm { + fn from(reg: Reg) -> Self { + RegMemImm::Reg { reg } + } +} + impl PrettyPrint for RegMemImm { fn pretty_print(&self, size: u8, allocs: &mut AllocationConsumer<'_>) -> String { match self { @@ -1761,6 +1767,47 @@ impl AvxOpcode { } } } + + /// Is the opcode known to be commutative? + /// + /// Note that this method is not exhaustive, and there may be commutative + /// opcodes that we don't recognize as commutative. + pub(crate) fn is_commutative(&self) -> bool { + match *self { + AvxOpcode::Vpaddb + | AvxOpcode::Vpaddw + | AvxOpcode::Vpaddd + | AvxOpcode::Vpaddq + | AvxOpcode::Vpaddsb + | AvxOpcode::Vpaddsw + | AvxOpcode::Vpaddusb + | AvxOpcode::Vpaddusw + | AvxOpcode::Vpand + | AvxOpcode::Vandps + | AvxOpcode::Vandpd + | AvxOpcode::Vpor + | AvxOpcode::Vorps + | AvxOpcode::Vorpd + | AvxOpcode::Vpxor + | AvxOpcode::Vxorps + | AvxOpcode::Vxorpd + | AvxOpcode::Vpmuldq + | AvxOpcode::Vpmuludq + | AvxOpcode::Vaddps + | AvxOpcode::Vaddpd + | AvxOpcode::Vmulps + | AvxOpcode::Vmulpd + | AvxOpcode::Vpcmpeqb + | AvxOpcode::Vpcmpeqw + | AvxOpcode::Vpcmpeqd + | AvxOpcode::Vpcmpeqq + | AvxOpcode::Vaddss + | AvxOpcode::Vaddsd + | AvxOpcode::Vmulss + | AvxOpcode::Vmulsd => true, + _ => false, + } + } } impl fmt::Display for AvxOpcode { diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index 853e24381a7c..0be0db116c2e 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -2285,6 +2285,20 @@ pub(crate) fn emit( let src1 = allocs.next(src1.to_reg()); let src2 = src2.clone().to_reg_mem_imm().with_allocs(allocs); + // When the opcode is commutative, src1 is xmm{0..7}, and src2 is + // xmm{8..15}, then we can swap the operands to save one byte on the + // instruction's encoding. + let (src1, src2) = match (src1, src2) { + (src1, RegMemImm::Reg { reg: src2 }) + if op.is_commutative() + && src1.to_real_reg().unwrap().hw_enc() < 8 + && src2.to_real_reg().unwrap().hw_enc() >= 8 => + { + (src2, RegMemImm::Reg { reg: src1 }) + } + (src1, src2) => (src1, src2), + }; + let src2 = match src2 { // For opcodes where one of the operands is an immediate the // encoding is a bit different, notably the usage of @@ -2319,6 +2333,7 @@ pub(crate) fn emit( } RegMemImm::Mem { addr } => RegisterOrAmode::Amode(addr.finalize(state, sink)), }; + let (prefix, map, opcode) = match op { AvxOpcode::Vminps => (LP::None, OM::_0F, 0x5D), AvxOpcode::Vminpd => (LP::_66, OM::_0F, 0x5D), diff --git a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs index 7144b0ab88de..d0050b8d4499 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs @@ -13,7 +13,7 @@ //! -- isa::x64::inst::emit_tests::test_x64_emit use super::*; -use crate::ir::UserExternalNameRef; +use crate::ir::{MemFlags, UserExternalNameRef}; use crate::isa::x64; use alloc::boxed::Box; use alloc::vec::Vec; @@ -5159,6 +5159,77 @@ fn test_x64_emit() { "roundpd $0, %xmm15, %xmm15", )); + // ======================================================== + // XmmRmiRVex + + // Standard instruction w/ XmmMemImm::Reg operand. + insns.push(( + Inst::XmmRmiRVex { + op: AvxOpcode::Vpmaxub, + dst: Writable::from_reg(Xmm::new(xmm13).unwrap()), + src1: Xmm::new(xmm1).unwrap(), + src2: XmmMemImm::new(xmm12.into()).unwrap(), + }, + "C44171DEEC", + "vpmaxub %xmm1, %xmm12, %xmm13", + )); + + // Standard instruction w/ XmmMemImm::Mem operand. + insns.push(( + Inst::XmmRmiRVex { + op: AvxOpcode::Vpmaxub, + dst: Writable::from_reg(Xmm::new(xmm13).unwrap()), + src1: Xmm::new(xmm1).unwrap(), + src2: XmmMemImm::new(RegMemImm::Mem { + addr: Amode::ImmReg { + simm32: 10, + base: rax, + flags: MemFlags::trusted(), + } + .into(), + }) + .unwrap(), + }, + "C571DE680A", + "vpmaxub %xmm1, 10(%rax), %xmm13", + )); + + // When there's an immediate. + insns.push(( + Inst::XmmRmiRVex { + op: AvxOpcode::Vpsrlw, + dst: Writable::from_reg(Xmm::new(xmm13).unwrap()), + src1: Xmm::new(xmm1).unwrap(), + src2: XmmMemImm::new(RegMemImm::Imm { simm32: 36 }).unwrap(), + }, + "C59171D124", + "vpsrlw %xmm1, $36, %xmm13", + )); + + // Certain commutative ops get their operands swapped to avoid relying on an + // extra prefix byte, when possible. Note that these two instructions encode + // to the same bytes, and are 4-byte encodings rather than 5-byte encodings. + insns.push(( + Inst::XmmRmiRVex { + op: AvxOpcode::Vmulsd, + dst: Writable::from_reg(Xmm::new(xmm13).unwrap()), + src1: Xmm::new(xmm1).unwrap(), + src2: XmmMemImm::new(xmm12.into()).unwrap(), + }, + "C51B59E9", + "vmulsd %xmm1, %xmm12, %xmm13", + )); + insns.push(( + Inst::XmmRmiRVex { + op: AvxOpcode::Vmulsd, + dst: Writable::from_reg(Xmm::new(xmm13).unwrap()), + src1: Xmm::new(xmm12).unwrap(), + src2: XmmMemImm::new(xmm1.into()).unwrap(), + }, + "C51B59E9", + "vmulsd %xmm12, %xmm1, %xmm13", + )); + // ======================================================== // XmmRmRImmVex insns.push(( From 5272e3abc99c3f1e7829e9fd2c9bf2b7fac17639 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 26 Sep 2023 12:44:33 -0700 Subject: [PATCH 014/199] Update `ittapi` crate (#7092) This update gets rid of a panic that occurs when using `--profile=vtune` from the command line. --- Cargo.lock | 8 ++++---- crates/jit/Cargo.toml | 10 +++++----- supply-chain/audits.toml | 10 ++++++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63a5aab2b99c..ed874c6aaa16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1646,9 +1646,9 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "ittapi" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e648c437172ce7d3ac35ca11a068755072054826fa455a916b43524fa4a62a7" +checksum = "41e0d0b7b3b53d92a7e8b80ede3400112a6b8b4c98d1f5b8b16bb787c780582c" dependencies = [ "anyhow", "ittapi-sys", @@ -1657,9 +1657,9 @@ dependencies = [ [[package]] name = "ittapi-sys" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9b32a4d23f72548178dde54f3c12c6b6a08598e25575c0d0fa5bd861e0dc1a5" +checksum = "f2f8763c96e54e6d6a0dccc2990d8b5e33e3313aaeae6185921a3f4c1614a77c" dependencies = [ "cc", ] diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml index 13fccaf9f769..56d8eb223e27 100644 --- a/crates/jit/Cargo.toml +++ b/crates/jit/Cargo.toml @@ -12,7 +12,9 @@ edition.workspace = true [dependencies] wasmtime-environ = { workspace = true } -wasmtime-jit-debug = { workspace = true, features = ["perf_jitdump"], optional = true } +wasmtime-jit-debug = { workspace = true, features = [ + "perf_jitdump", +], optional = true } wasmtime-runtime = { workspace = true } target-lexicon = { workspace = true } anyhow = { workspace = true } @@ -33,12 +35,10 @@ rustix = { workspace = true, features = ['thread'] } [target.'cfg(target_os = "windows")'.dependencies.windows-sys] workspace = true -features = [ - "Win32_System_Diagnostics_Debug", -] +features = ["Win32_System_Diagnostics_Debug"] [target.'cfg(all(target_arch = "x86_64", not(target_os = "android")))'.dependencies] -ittapi = { version = "0.3.3", optional = true } +ittapi = { version = "0.3.4", optional = true } [features] jitdump = ['wasmtime-jit-debug'] diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index ee4eb95a1245..dae6f3ab9aa0 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -1438,12 +1438,22 @@ criteria = "safe-to-deploy" delta = "0.4.1 -> 0.4.3" notes = "The Bytecode Alliance is the author of this crate." +[[audits.ittapi]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +version = "0.3.4" + [[audits.ittapi]] who = "Andrew Brown " criteria = "safe-to-deploy" delta = "0.3.1 -> 0.3.3" notes = "I am the author of this crate." +[[audits.ittapi-sys]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +version = "0.3.4" + [[audits.ittapi-sys]] who = "Andrew Brown " criteria = "safe-to-deploy" From 3cdc63bcc8033b22e50c4443fb349a6698ca59dd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 27 Sep 2023 09:36:00 -0500 Subject: [PATCH 015/199] Bump wasm-tools crates (#7094) * Bump wasm-tools crates Two major changes/reasons for this update: * Primarily pulling in support for semicolons-in-WIT files. Semicolons are not currently required, though, so I'll follow-up later with actual semicolons. * The syntax for parsing `(if ...)` was fixed in `wast`. Previously it did not require `(then ...)` but this is required by the spec. New spec tests require this as well. This breaks existing text format tests which don't use `(then ...)` inside of an `(if ...)`. Most tests were updated by hand but `embenchen_*` tests were updated by running through the old parser to produce non-s-expression using code. * Fix an example `*.wat` --- Cargo.lock | 46 +- Cargo.toml | 18 +- .../wasm/wasmtests/embenchen_fannkuch.wat | 28821 +++++++--------- cranelift/wasm/wasmtests/embenchen_fasta.wat | 28501 +++++++-------- cranelift/wasm/wasmtests/embenchen_ifs.wat | 27188 ++++++--------- cranelift/wasm/wasmtests/embenchen_primes.wat | 26443 ++++++-------- cranelift/wasm/wasmtests/rust_fannkuch.wat | 4210 +-- examples/fuel.wat | 2 +- supply-chain/imports.lock | 70 + tests/all/cli_tests/print-arguments.wat | 6 +- tests/all/cli_tests/print_env.wat | 6 +- tests/all/component_model/resources.rs | 6 +- tests/all/memory.rs | 2 +- .../misc_testsuite/component-model/fused.wast | 200 +- .../component-model/resources.wast | 96 +- .../many_table_gets_lead_to_gc.wast | 2 +- 16 files changed, 48724 insertions(+), 66893 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed874c6aaa16..9887f11ac7e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3153,18 +3153,18 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-encoder" -version = "0.33.1" +version = "0.33.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39de0723a53d3c8f54bed106cfbc0d06b3e4d945c5c5022115a61e3b29183ae" +checksum = "34180c89672b3e4825c3a8db4b61a674f1447afd5fe2445b2d22c3d8b6ea086c" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fab01638cbecc57afec7b53ce0e28620b44d7ae1dea53120c96dd08486c07ce" +checksum = "577508d8a45bc54ad97efe77c95ba57bb10e7e5c5bac9c31295ce88b8045cd7d" dependencies = [ "anyhow", "indexmap 2.0.0", @@ -3177,9 +3177,9 @@ dependencies = [ [[package]] name = "wasm-mutate" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730c1644f14f3dfa52d8c63bb26c6f3fe89ba80430f1ce29b0388e04825ec514" +checksum = "f22ff62504d0e55a4e4c32cdf4e65f9c925ba0bc9904e141394eb2ad2b8f319d" dependencies = [ "egg", "log", @@ -3191,9 +3191,9 @@ dependencies = [ [[package]] name = "wasm-smith" -version = "0.12.17" +version = "0.12.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "154bb82cb9f17e5c0773e192e800094583767f752077d5116be376de747539eb" +checksum = "7499466e905b4e8d0cc1720c1bfddf1e5040f6fa42efd4f43edd1b88dbe9ce9b" dependencies = [ "arbitrary", "flagset", @@ -3243,9 +3243,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.113.1" +version = "0.113.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a128cea7b8516703ab41b10a0b1aa9ba18d0454cd3792341489947ddeee268db" +checksum = "1fd0d44fab0bd78404e352f3399324eef76516a4580b52bc9031c60f064e98f3" dependencies = [ "indexmap 2.0.0", "semver", @@ -3262,9 +3262,9 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.2.66" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2e5e818f88cee5311e9a5df15cba0a8f772978baf3109af97004bce6e8e3c6" +checksum = "f6615a5587149e753bf4b93f90fa3c3f41c88597a7a2da72879afcabeda9648f" dependencies = [ "anyhow", "wasmparser", @@ -3425,7 +3425,7 @@ dependencies = [ "wasmtime-wasi-nn", "wasmtime-wasi-threads", "wasmtime-wast", - "wast 65.0.1", + "wast 65.0.2", "wat", "windows-sys", ] @@ -3803,7 +3803,7 @@ dependencies = [ "anyhow", "log", "wasmtime", - "wast 65.0.1", + "wast 65.0.2", ] [[package]] @@ -3846,9 +3846,9 @@ dependencies = [ [[package]] name = "wast" -version = "65.0.1" +version = "65.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd8c1cbadf94a0b0d1071c581d3cfea1b7ed5192c79808dd15406e508dd0afb" +checksum = "a55a88724cf8c2c0ebbf32c8e8f4ac0d6aa7ba6d73a1cfd94b254aa8f894317e" dependencies = [ "leb128", "memchr", @@ -3858,11 +3858,11 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.73" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3209e35eeaf483714f4c6be93f4a03e69aad5f304e3fa66afa7cb90fe1c8051f" +checksum = "d83e1a8d86d008adc7bafa5cf4332d448699a08fcf2a715a71fbb75e2c5ca188" dependencies = [ - "wast 65.0.1", + "wast 65.0.2", ] [[package]] @@ -4178,9 +4178,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af872ef43ecb73cc49c7bd2dd19ef9117168e183c78cf70000dca0e14b6a5473" +checksum = "0477d36188eeba8e8f2579220ec385086f188de99453c6689814d4dad6a4c22c" dependencies = [ "anyhow", "bitflags 2.3.3", @@ -4196,9 +4196,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dcd022610436a1873e60bfdd9b407763f2404adf7d1cb57912c7ae4059e57a5" +checksum = "4419bc240aa8c421e44a0c180e33891d5556b8b2327f432265a2c382cca9d294" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index 5c823d258df9..4ce888943c6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -209,15 +209,15 @@ is-terminal = "0.4.0" wit-bindgen = { version = "0.11.0", default-features = false } # wasm-tools family: -wasmparser = "0.113.1" -wat = "1.0.73" -wast = "65.0.1" -wasmprinter = "0.2.66" -wasm-encoder = "0.33.1" -wasm-smith = "0.12.17" -wasm-mutate = "0.2.34" -wit-parser = "0.11.1" -wit-component = "0.14.2" +wasmparser = "0.113.2" +wat = "1.0.74" +wast = "65.0.2" +wasmprinter = "0.2.67" +wasm-encoder = "0.33.2" +wasm-smith = "0.12.18" +wasm-mutate = "0.2.35" +wit-parser = "0.11.2" +wit-component = "0.14.3" # Non-Bytecode Alliance maintained dependencies: # -------------------------- diff --git a/cranelift/wasm/wasmtests/embenchen_fannkuch.wat b/cranelift/wasm/wasmtests/embenchen_fannkuch.wat index c61b5e6e2fc7..391fdcd55b71 100644 --- a/cranelift/wasm/wasmtests/embenchen_fannkuch.wat +++ b/cranelift/wasm/wasmtests/embenchen_fannkuch.wat @@ -1,16725 +1,12180 @@ (module - (type $0 (func (param i32 i32 i32) (result i32))) - (type $1 (func (param i32) (result i32))) - (type $2 (func (param i32))) - (type $3 (func (result i32))) - (type $4 (func (param i32 i32) (result i32))) - (type $5 (func (param i32 i32))) - (type $6 (func)) - (type $7 (func (param i32 i32 i32 i32 i32) (result i32))) - (type $8 (func (param i32 i32 i32))) - (type $9 (func (param i64 i32) (result i32))) - (type $10 (func (param i32 i32 i32 i32 i32))) - (type $11 (func (param f64 i32) (result f64))) - (type $12 (func (param i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory $16 2048 2048)) - (data (i32.const 1024) "\04\04\00\00\05") - (data (i32.const 1040) "\01") - (data (i32.const 1064) "\01\00\00\00\02\00\00\00<\10\00\00\00\04") - (data (i32.const 1088) "\01") - (data (i32.const 1103) "\n\ff\ff\ff\ff") - (data (i32.const 1140) "error: %d\n\00Pfannkuchen(%d) = %d.\n\00%d\00\11\00\n\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\t\00\00\00\00\0b") - (data (i32.const 1209) "\11\00\0f\n\11\11\11\03\n\07\00\01\13\t\0b\0b\00\00\t\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") - (data (i32.const 1258) "\0b") - (data (i32.const 1267) "\11\00\n\n\11\11\11\00\n\00\00\02\00\t\0b\00\00\00\t\00\0b\00\00\0b") - (data (i32.const 1316) "\0c") - (data (i32.const 1328) "\0c\00\00\00\00\0c\00\00\00\00\t\0c\00\00\00\00\00\0c\00\00\0c") - (data (i32.const 1374) "\0e") - (data (i32.const 1386) "\0d\00\00\00\04\0d\00\00\00\00\t\0e\00\00\00\00\00\0e\00\00\0e") - (data (i32.const 1432) "\10") - (data (i32.const 1444) "\0f\00\00\00\00\0f\00\00\00\00\t\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") - (data (i32.const 1499) "\12\00\00\00\12\12\12\00\00\00\00\00\00\t") - (data (i32.const 1548) "\0b") - (data (i32.const 1560) "\n\00\00\00\00\n\00\00\00\00\t\0b\00\00\00\00\00\0b\00\00\0b") - (data (i32.const 1606) "\0c") - (data (i32.const 1618) "\0c\00\00\00\00\0c\00\00\00\00\t\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\"\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e\'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\t\n\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\\]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") - (import "env" "table" (table $timport$17 8 8 funcref)) - (elem (global.get $gimport$19) $45 $9 $46 $14 $10 $15 $47 $16) - (import "env" "DYNAMICTOP_PTR" (global $gimport$0 i32)) - (import "env" "STACKTOP" (global $gimport$1 i32)) - (import "env" "STACK_MAX" (global $gimport$2 i32)) - (import "env" "memoryBase" (global $gimport$18 i32)) - (import "env" "tableBase" (global $gimport$19 i32)) - (import "env" "abort" (func $fimport$3 (param i32))) - (import "env" "enlargeMemory" (func $fimport$4 (result i32))) - (import "env" "getTotalMemory" (func $fimport$5 (result i32))) - (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (result i32))) - (import "env" "_pthread_cleanup_pop" (func $fimport$7 (param i32))) - (import "env" "___syscall6" (func $fimport$8 (param i32 i32) (result i32))) - (import "env" "_pthread_cleanup_push" (func $fimport$9 (param i32 i32))) - (import "env" "_abort" (func $fimport$10)) - (import "env" "___setErrNo" (func $fimport$11 (param i32))) - (import "env" "_emscripten_memcpy_big" (func $fimport$12 (param i32 i32 i32) (result i32))) - (import "env" "___syscall54" (func $fimport$13 (param i32 i32) (result i32))) - (import "env" "___syscall140" (func $fimport$14 (param i32 i32) (result i32))) - (import "env" "___syscall146" (func $fimport$15 (param i32 i32) (result i32))) - (global $global$0 (mut i32) (global.get $gimport$0)) - (global $global$1 (mut i32) (global.get $gimport$1)) - (global $global$2 (mut i32) (global.get $gimport$2)) - (global $global$3 (mut i32) (i32.const 0)) - (global $global$4 (mut i32) (i32.const 0)) - (global $global$5 (mut i32) (i32.const 0)) - (export "_sbrk" (func $38)) - (export "_free" (func $36)) - (export "_main" (func $8)) - (export "_pthread_self" (func $41)) - (export "_memset" (func $39)) - (export "_malloc" (func $35)) - (export "_memcpy" (func $40)) - (export "___errno_location" (func $12)) - (export "runPostSets" (func $37)) - (export "stackAlloc" (func $0)) - (export "stackSave" (func $1)) - (export "stackRestore" (func $2)) - (export "establishStackSpace" (func $3)) - (export "setThrew" (func $4)) - (export "setTempRet0" (func $5)) - (export "getTempRet0" (func $6)) - (export "dynCall_ii" (func $42)) - (export "dynCall_iiii" (func $43)) - (export "dynCall_vi" (func $44)) - (func $0 (; 13 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (block $label$1 (result i32) - (local.set $1 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (local.get $0) - ) - ) - (global.set $global$1 - (i32.and - (i32.add - (global.get $global$1) - (i32.const 15) - ) - (i32.const -16) - ) - ) - (local.get $1) + (type $0 (;0;) (func (param i32 i32 i32) (result i32))) + (type $1 (;1;) (func (param i32) (result i32))) + (type $2 (;2;) (func (param i32))) + (type $3 (;3;) (func (result i32))) + (type $4 (;4;) (func (param i32 i32) (result i32))) + (type $5 (;5;) (func (param i32 i32))) + (type $6 (;6;) (func)) + (type $7 (;7;) (func (param i32 i32 i32 i32 i32) (result i32))) + (type $8 (;8;) (func (param i32 i32 i32))) + (type $9 (;9;) (func (param i64 i32) (result i32))) + (type $10 (;10;) (func (param i32 i32 i32 i32 i32))) + (type $11 (;11;) (func (param f64 i32) (result f64))) + (type $12 (;12;) (func (param i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory $16 (;0;) 2048 2048)) + (import "env" "table" (table $timport$17 (;0;) 8 8 funcref)) + (import "env" "DYNAMICTOP_PTR" (global $gimport$0 (;0;) i32)) + (import "env" "STACKTOP" (global $gimport$1 (;1;) i32)) + (import "env" "STACK_MAX" (global $gimport$2 (;2;) i32)) + (import "env" "memoryBase" (global $gimport$18 (;3;) i32)) + (import "env" "tableBase" (global $gimport$19 (;4;) i32)) + (import "env" "abort" (func $fimport$3 (;0;) (type $2))) + (import "env" "enlargeMemory" (func $fimport$4 (;1;) (type $3))) + (import "env" "getTotalMemory" (func $fimport$5 (;2;) (type $3))) + (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (;3;) (type $3))) + (import "env" "_pthread_cleanup_pop" (func $fimport$7 (;4;) (type $2))) + (import "env" "___syscall6" (func $fimport$8 (;5;) (type $4))) + (import "env" "_pthread_cleanup_push" (func $fimport$9 (;6;) (type $5))) + (import "env" "_abort" (func $fimport$10 (;7;) (type $6))) + (import "env" "___setErrNo" (func $fimport$11 (;8;) (type $2))) + (import "env" "_emscripten_memcpy_big" (func $fimport$12 (;9;) (type $0))) + (import "env" "___syscall54" (func $fimport$13 (;10;) (type $4))) + (import "env" "___syscall140" (func $fimport$14 (;11;) (type $4))) + (import "env" "___syscall146" (func $fimport$15 (;12;) (type $4))) + (func $0 (;13;) (type $1) (param $0 i32) (result i32) + (local $1 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $1 + global.get $global$1 + local.get $0 + i32.add + global.set $global$1 + global.get $global$1 + i32.const 15 + i32.add + i32.const -16 + i32.and + global.set $global$1 + local.get $1 + end ) - ) - (func $1 (; 14 ;) (type $3) (result i32) - (global.get $global$1) - ) - (func $2 (; 15 ;) (type $2) (param $0 i32) - (global.set $global$1 - (local.get $0) + (func $1 (;14;) (type $3) (result i32) + global.get $global$1 ) - ) - (func $3 (; 16 ;) (type $5) (param $0 i32) (param $1 i32) - (block $label$1 - (global.set $global$1 - (local.get $0) - ) - (global.set $global$2 - (local.get $1) - ) + (func $2 (;15;) (type $2) (param $0 i32) + local.get $0 + global.set $global$1 ) - ) - (func $4 (; 17 ;) (type $5) (param $0 i32) (param $1 i32) - (if - (i32.eqz - (global.get $global$3) - ) - (block - (global.set $global$3 - (local.get $0) - ) - (global.set $global$4 - (local.get $1) - ) - ) + (func $3 (;16;) (type $5) (param $0 i32) (param $1 i32) + block $label$1 ;; label = @1 + local.get $0 + global.set $global$1 + local.get $1 + global.set $global$2 + end ) - ) - (func $5 (; 18 ;) (type $2) (param $0 i32) - (global.set $global$5 - (local.get $0) + (func $4 (;17;) (type $5) (param $0 i32) (param $1 i32) + global.get $global$3 + i32.eqz + if ;; label = @1 + block ;; label = @2 + local.get $0 + global.set $global$3 + local.get $1 + global.set $global$4 + end + end ) - ) - (func $6 (; 19 ;) (type $3) (result i32) - (global.get $global$5) - ) - (func $7 (; 20 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 i32) - (local $17 i32) - (block $label$1 (result i32) - (local.set $3 - (call $35 - (local.tee $15 - (i32.shl - (local.tee $4 - (i32.load offset=4 - (local.get $0) - ) - ) - (i32.const 2) - ) - ) - ) - ) - (local.set $6 - (call $35 - (local.get $15) - ) - ) - (local.set $10 - (call $35 - (local.get $15) - ) - ) - (if - (local.tee $2 - (i32.gt_s - (local.get $4) - (i32.const 0) - ) - ) - (block - (local.set $1 - (i32.const 0) - ) - (loop $label$3 - (i32.store - (i32.add - (local.get $3) - (i32.shl - (local.get $1) - (i32.const 2) - ) - ) - (local.get $1) - ) - (br_if $label$3 - (i32.ne - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.get $4) - ) - ) - ) - (i32.store - (i32.add - (local.get $3) - (i32.shl - (local.tee $0 - (i32.load - (local.get $0) - ) - ) - (i32.const 2) - ) - ) - (local.tee $11 - (i32.add - (local.get $4) - (i32.const -1) - ) - ) - ) - (i32.store - (local.tee $14 - (i32.add - (local.get $3) - (i32.shl - (local.get $11) - (i32.const 2) - ) - ) - ) - (local.get $0) - ) - (if - (local.get $2) - (block - (local.set $0 - (i32.const 0) - ) - (local.set $1 - (local.get $4) - ) - (loop $label$5 - (block $label$6 - (if - (i32.gt_s - (local.get $1) - (i32.const 1) - ) - (loop $label$8 - (i32.store - (i32.add - (local.get $10) - (i32.shl - (local.tee $2 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i32.const 2) - ) - ) - (local.get $1) - ) - (if - (i32.gt_s - (local.get $2) - (i32.const 1) - ) - (block - (local.set $1 - (local.get $2) - ) - (br $label$8) - ) - (local.set $2 - (i32.const 1) - ) - ) - ) - (local.set $2 - (local.get $1) - ) - ) - (if - (local.tee $7 - (i32.load - (local.get $3) - ) - ) - (if - (i32.ne - (i32.load - (local.get $14) - ) - (local.get $11) - ) - (block - (drop - (call $40 - (local.get $6) - (local.get $3) - (local.get $15) - ) - ) - (local.set $8 - (i32.const 0) - ) - (local.set $9 - (i32.load - (local.get $6) - ) - ) - (loop $label$14 - (if - (i32.gt_s - (local.tee $1 - (i32.add - (local.get $9) - (i32.const -1) - ) - ) - (i32.const 1) - ) - (block - (local.set $5 - (i32.const 1) - ) - (loop $label$16 - (local.set $17 - (i32.load - (local.tee $12 - (i32.add - (local.get $6) - (i32.shl - (local.get $5) - (i32.const 2) - ) - ) - ) - ) - ) - (i32.store - (local.get $12) - (i32.load - (local.tee $12 - (i32.add - (local.get $6) - (i32.shl - (local.get $1) - (i32.const 2) - ) - ) - ) - ) - ) - (i32.store - (local.get $12) - (local.get $17) - ) - (br_if $label$16 - (i32.lt_s - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - ) - ) - ) - (local.set $5 - (i32.add - (local.get $8) - (i32.const 1) - ) - ) - (local.set $1 - (i32.load - (local.tee $12 - (i32.add - (local.get $6) - (i32.shl - (local.get $9) - (i32.const 2) - ) - ) - ) - ) - ) - (i32.store - (local.get $12) - (local.get $9) - ) - (if - (local.get $1) - (block - (local.set $8 - (local.get $5) - ) - (local.set $9 - (local.get $1) - ) - (br $label$14) - ) - ) - ) - (if - (i32.le_s - (local.get $0) - (local.get $8) - ) - (local.set $0 - (local.get $5) - ) - ) - ) - ) - ) - (if - (i32.lt_s - (local.get $2) - (local.get $11) - ) - (local.set $1 - (local.get $2) - ) - (block - (local.set $1 - (i32.const 31) - ) - (br $label$6) - ) - ) - (loop $label$21 - (if - (i32.gt_s - (local.get $1) - (i32.const 0) - ) - (block - (local.set $2 - (i32.const 0) - ) - (loop $label$23 - (i32.store - (i32.add - (local.get $3) - (i32.shl - (local.get $2) - (i32.const 2) - ) - ) - (i32.load - (i32.add - (local.get $3) - (i32.shl - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 2) - ) - ) - ) - ) - (br_if $label$23 - (i32.lt_s - (local.get $2) - (local.get $1) - ) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - (local.set $2 - (i32.const 0) - ) - ) - (i32.store - (i32.add - (local.get $3) - (i32.shl - (local.get $2) - (i32.const 2) - ) - ) - (local.get $7) - ) - (local.set $5 - (i32.load - (local.tee $2 - (i32.add - (local.get $10) - (i32.shl - (local.get $1) - (i32.const 2) - ) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $5) - (i32.const -1) - ) - ) - (br_if $label$5 - (i32.gt_s - (local.get $5) - (i32.const 1) - ) - ) - (if - (i32.lt_s - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.get $11) - ) - (block - (local.set $7 - (i32.load - (local.get $3) - ) - ) - (br $label$21) - ) - (block - (local.set $1 - (i32.const 31) - ) - (br $label$6) - ) - ) - ) - ) - ) - (if - (i32.eq - (local.get $1) - (i32.const 31) - ) - (block - (call $36 - (local.get $3) - ) - (call $36 - (local.get $6) - ) - (call $36 - (local.get $10) - ) - (return - (local.get $0) - ) - ) - ) - ) - (block - (local.set $16 - (local.get $14) - ) - (local.set $13 - (local.get $11) - ) - ) - ) - ) - (block - (i32.store - (i32.add - (local.get $3) - (i32.shl - (local.tee $0 - (i32.load - (local.get $0) - ) - ) - (i32.const 2) - ) - ) - (local.tee $13 - (i32.add - (local.get $4) - (i32.const -1) - ) - ) - ) - (i32.store - (local.tee $16 - (i32.add - (local.get $3) - (i32.shl - (local.get $13) - (i32.const 2) - ) - ) - ) - (local.get $0) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - (local.set $1 - (local.get $4) - ) - (loop $label$30 - (block $label$31 - (if - (i32.gt_s - (local.get $1) - (i32.const 1) - ) - (loop $label$33 - (i32.store - (i32.add - (local.get $10) - (i32.shl - (local.tee $2 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i32.const 2) - ) - ) - (local.get $1) - ) - (if - (i32.gt_s - (local.get $2) - (i32.const 1) - ) - (block - (local.set $1 - (local.get $2) - ) - (br $label$33) - ) - (local.set $2 - (i32.const 1) - ) - ) - ) - (local.set $2 - (local.get $1) - ) - ) - (if - (local.tee $9 - (i32.load - (local.get $3) - ) - ) - (if - (i32.ne - (i32.load - (local.get $16) - ) - (local.get $13) - ) - (block - (local.set $5 - (i32.const 0) - ) - (local.set $8 - (i32.load - (local.get $6) - ) - ) - (loop $label$39 - (if - (i32.gt_s - (local.tee $1 - (i32.add - (local.get $8) - (i32.const -1) - ) - ) - (i32.const 1) - ) - (block - (local.set $4 - (i32.const 1) - ) - (loop $label$41 - (local.set $14 - (i32.load - (local.tee $7 - (i32.add - (local.get $6) - (i32.shl - (local.get $4) - (i32.const 2) - ) - ) - ) - ) - ) - (i32.store - (local.get $7) - (i32.load - (local.tee $7 - (i32.add - (local.get $6) - (i32.shl - (local.get $1) - (i32.const 2) - ) - ) - ) - ) - ) - (i32.store - (local.get $7) - (local.get $14) - ) - (br_if $label$41 - (i32.lt_s - (local.tee $4 - (i32.add - (local.get $4) - (i32.const 1) - ) - ) - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - ) - ) - ) - (local.set $4 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - (local.set $1 - (i32.load - (local.tee $7 - (i32.add - (local.get $6) - (i32.shl - (local.get $8) - (i32.const 2) - ) - ) - ) - ) - ) - (i32.store - (local.get $7) - (local.get $8) - ) - (if - (local.get $1) - (block - (local.set $5 - (local.get $4) - ) - (local.set $8 - (local.get $1) - ) - (br $label$39) - ) - ) - ) - (if - (i32.le_s - (local.get $0) - (local.get $5) - ) - (local.set $0 - (local.get $4) - ) - ) - ) - ) - ) - (if - (i32.lt_s - (local.get $2) - (local.get $13) - ) - (local.set $1 - (local.get $2) - ) - (block - (local.set $1 - (i32.const 31) - ) - (br $label$31) - ) - ) - (loop $label$46 - (if - (i32.gt_s - (local.get $1) - (i32.const 0) - ) - (block - (local.set $2 - (i32.const 0) - ) - (loop $label$48 - (i32.store - (i32.add - (local.get $3) - (i32.shl - (local.get $2) - (i32.const 2) - ) - ) - (i32.load - (i32.add - (local.get $3) - (i32.shl - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 2) - ) - ) - ) - ) - (br_if $label$48 - (i32.lt_s - (local.get $2) - (local.get $1) - ) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - (local.set $2 - (i32.const 0) - ) - ) - (i32.store - (i32.add - (local.get $3) - (i32.shl - (local.get $2) - (i32.const 2) - ) - ) - (local.get $9) - ) - (local.set $4 - (i32.load - (local.tee $2 - (i32.add - (local.get $10) - (i32.shl - (local.get $1) - (i32.const 2) - ) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $4) - (i32.const -1) - ) - ) - (br_if $label$30 - (i32.gt_s - (local.get $4) - (i32.const 1) - ) - ) - (if - (i32.lt_s - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.get $13) - ) - (block - (local.set $9 - (i32.load - (local.get $3) - ) - ) - (br $label$46) - ) - (block - (local.set $1 - (i32.const 31) - ) - (br $label$31) - ) - ) - ) - ) - ) - (if - (i32.eq - (local.get $1) - (i32.const 31) - ) - (block - (call $36 - (local.get $3) - ) - (call $36 - (local.get $6) - ) - (call $36 - (local.get $10) - ) - (return - (local.get $0) - ) - ) - ) - (i32.const 0) + (func $5 (;18;) (type $2) (param $0 i32) + local.get $0 + global.set $global$5 ) - ) - (func $8 (; 21 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (block $label$1 (result i32) - (local.set $5 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 32) - ) - ) - (local.set $7 - (i32.add - (local.get $5) - (i32.const 16) - ) - ) - (local.set $10 - (i32.add - (local.get $5) - (i32.const 8) - ) - ) - (local.set $2 - (local.get $5) - ) - (block $label$2 - (block $label$3 - (br_if $label$3 - (i32.le_s - (local.get $0) - (i32.const 1) - ) - ) - (block $label$4 - (block $label$5 - (block $label$6 - (block $label$7 - (block $label$8 - (block $label$9 - (block $label$10 - (br_table $label$5 $label$10 $label$8 $label$9 $label$7 $label$6 $label$4 - (i32.sub - (local.tee $0 - (i32.load8_s - (i32.load offset=4 - (local.get $1) - ) - ) - ) - (i32.const 48) - ) - ) - ) - (local.set $3 - (i32.const 9) - ) - (br $label$2) - ) - (br $label$3) - ) - (local.set $3 - (i32.const 10) - ) - (br $label$2) - ) - (local.set $3 - (i32.const 11) - ) - (br $label$2) - ) - (local.set $3 - (i32.const 12) - ) - (br $label$2) - ) - (global.set $global$1 - (local.get $5) - ) - (return - (i32.const 0) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $0) - (i32.const -48) - ) - ) - (drop - (call $33 - (i32.const 1140) - (local.get $2) - ) - ) - (global.set $global$1 - (local.get $5) - ) - (return - (i32.const -1) - ) - ) - (local.set $3 - (i32.const 11) - ) - ) - (local.set $6 - (i32.add - (local.get $3) - (i32.const -1) - ) - ) - (local.set $2 - (i32.const 0) - ) - (local.set $0 - (i32.const 0) - ) - (loop $label$11 - (i32.store - (local.tee $1 - (call $35 - (i32.const 12) - ) - ) - (local.get $0) - ) - (i32.store offset=4 - (local.get $1) - (local.get $3) - ) - (i32.store offset=8 - (local.get $1) - (local.get $2) - ) - (if - (i32.ne - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.get $6) - ) - (block - (local.set $2 - (local.get $1) - ) - (br $label$11) - ) - ) - ) - (local.set $4 - (call $35 - (local.tee $0 - (i32.shl - (local.get $3) - (i32.const 2) - ) - ) - ) - ) - (local.set $8 - (call $35 - (local.get $0) - ) - ) - (local.set $0 - (i32.const 0) - ) - (loop $label$13 - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - (local.get $0) - ) - (br_if $label$13 - (i32.ne - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.get $3) - ) - ) - ) - (local.set $0 - (local.get $3) - ) - (local.set $6 - (i32.const 30) - ) - (loop $label$14 - (block $label$15 - (local.set $2 - (i32.const 0) - ) - (loop $label$16 - (i32.store - (local.get $10) - (i32.add - (i32.load - (i32.add - (local.get $4) - (i32.shl - (local.get $2) - (i32.const 2) - ) - ) - ) - (i32.const 1) - ) - ) - (drop - (call $33 - (i32.const 1174) - (local.get $10) - ) - ) - (br_if $label$16 - (i32.ne - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (local.get $3) - ) - ) - ) - (drop - (call $34 - (i32.const 10) - ) - ) - (if - (i32.gt_s - (local.get $0) - (i32.const 1) - ) - (loop $label$18 - (i32.store - (i32.add - (local.get $8) - (i32.shl - (local.tee $2 - (i32.add - (local.get $0) - (i32.const -1) - ) - ) - (i32.const 2) - ) - ) - (local.get $0) - ) - (if - (i32.gt_s - (local.get $2) - (i32.const 1) - ) - (block - (local.set $0 - (local.get $2) - ) - (br $label$18) - ) - (local.set $0 - (i32.const 1) - ) - ) - ) - (br_if $label$15 - (i32.eq - (local.get $0) - (local.get $3) - ) - ) - ) - (local.set $6 - (i32.add - (local.get $6) - (i32.const -1) - ) - ) - (loop $label$22 - (block $label$23 - (local.set $9 - (i32.load - (local.get $4) - ) - ) - (if - (i32.gt_s - (local.get $0) - (i32.const 0) - ) - (block - (local.set $2 - (i32.const 0) - ) - (loop $label$25 - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $2) - (i32.const 2) - ) - ) - (i32.load - (i32.add - (local.get $4) - (i32.shl - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 2) - ) - ) - ) - ) - (br_if $label$25 - (i32.lt_s - (local.get $2) - (local.get $0) - ) - ) - (local.set $2 - (local.get $0) - ) - ) - ) - (local.set $2 - (i32.const 0) - ) - ) - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $2) - (i32.const 2) - ) - ) - (local.get $9) - ) - (local.set $2 - (i32.load - (local.tee $9 - (i32.add - (local.get $8) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - ) - ) - ) - (i32.store - (local.get $9) - (i32.add - (local.get $2) - (i32.const -1) - ) - ) - (br_if $label$23 - (i32.gt_s - (local.get $2) - (i32.const 1) - ) - ) - (br_if $label$22 - (i32.ne - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.get $3) - ) - ) - (br $label$15) - ) - ) - (br_if $label$14 - (local.get $6) - ) - ) - ) - (call $36 - (local.get $4) - ) - (call $36 - (local.get $8) - ) - (if - (local.get $1) - (block - (local.set $0 - (i32.const 0) - ) - (loop $label$28 - (if - (i32.lt_s - (local.get $0) - (local.tee $2 - (call $7 - (local.get $1) - ) - ) - ) - (local.set $0 - (local.get $2) - ) - ) - (local.set $2 - (i32.load offset=8 - (local.get $1) - ) - ) - (call $36 - (local.get $1) - ) - (if - (local.get $2) - (block - (local.set $1 - (local.get $2) - ) - (br $label$28) - ) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - ) - (i32.store - (local.get $7) - (local.get $3) - ) - (i32.store offset=4 - (local.get $7) - (local.get $0) - ) - (drop - (call $33 - (i32.const 1151) - (local.get $7) - ) - ) - (global.set $global$1 - (local.get $5) - ) - (i32.const 0) + (func $6 (;19;) (type $3) (result i32) + global.get $global$5 ) - ) - (func $9 (; 22 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store - (local.tee $2 - (local.get $1) - ) - (i32.load offset=60 - (local.get $0) - ) - ) - (local.set $0 - (call $11 - (call $fimport$8 - (i32.const 6) - (local.get $2) - ) - ) - ) - (global.set $global$1 - (local.get $1) - ) - (local.get $0) + (func $7 (;20;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + i32.load offset=4 + local.tee $4 + i32.const 2 + i32.shl + local.tee $15 + call $35 + local.set $3 + local.get $15 + call $35 + local.set $6 + local.get $15 + call $35 + local.set $10 + local.get $4 + i32.const 0 + i32.gt_s + local.tee $2 + if ;; label = @2 + block ;; label = @3 + i32.const 0 + local.set $1 + loop $label$3 ;; label = @4 + local.get $3 + local.get $1 + i32.const 2 + i32.shl + i32.add + local.get $1 + i32.store + local.get $1 + i32.const 1 + i32.add + local.tee $1 + local.get $4 + i32.ne + br_if 0 (;@4;) + end + local.get $3 + local.get $0 + i32.load + local.tee $0 + i32.const 2 + i32.shl + i32.add + local.get $4 + i32.const -1 + i32.add + local.tee $11 + i32.store + local.get $3 + local.get $11 + i32.const 2 + i32.shl + i32.add + local.tee $14 + local.get $0 + i32.store + local.get $2 + if ;; label = @4 + block ;; label = @5 + i32.const 0 + local.set $0 + local.get $4 + local.set $1 + loop $label$5 ;; label = @6 + block $label$6 ;; label = @7 + local.get $1 + i32.const 1 + i32.gt_s + if ;; label = @8 + loop $label$8 ;; label = @9 + local.get $10 + local.get $1 + i32.const -1 + i32.add + local.tee $2 + i32.const 2 + i32.shl + i32.add + local.get $1 + i32.store + local.get $2 + i32.const 1 + i32.gt_s + if ;; label = @10 + block ;; label = @11 + local.get $2 + local.set $1 + br 2 (;@9;) + end + else + i32.const 1 + local.set $2 + end + end + else + local.get $1 + local.set $2 + end + local.get $3 + i32.load + local.tee $7 + if ;; label = @8 + local.get $14 + i32.load + local.get $11 + i32.ne + if ;; label = @9 + block ;; label = @10 + local.get $6 + local.get $3 + local.get $15 + call $40 + drop + i32.const 0 + local.set $8 + local.get $6 + i32.load + local.set $9 + loop $label$14 ;; label = @11 + local.get $9 + i32.const -1 + i32.add + local.tee $1 + i32.const 1 + i32.gt_s + if ;; label = @12 + block ;; label = @13 + i32.const 1 + local.set $5 + loop $label$16 ;; label = @14 + local.get $6 + local.get $5 + i32.const 2 + i32.shl + i32.add + local.tee $12 + i32.load + local.set $17 + local.get $12 + local.get $6 + local.get $1 + i32.const 2 + i32.shl + i32.add + local.tee $12 + i32.load + i32.store + local.get $12 + local.get $17 + i32.store + local.get $5 + i32.const 1 + i32.add + local.tee $5 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + i32.lt_s + br_if 0 (;@14;) + end + end + end + local.get $8 + i32.const 1 + i32.add + local.set $5 + local.get $6 + local.get $9 + i32.const 2 + i32.shl + i32.add + local.tee $12 + i32.load + local.set $1 + local.get $12 + local.get $9 + i32.store + local.get $1 + if ;; label = @12 + block ;; label = @13 + local.get $5 + local.set $8 + local.get $1 + local.set $9 + br 2 (;@11;) + end + end + end + local.get $0 + local.get $8 + i32.le_s + if ;; label = @11 + local.get $5 + local.set $0 + end + end + end + end + local.get $2 + local.get $11 + i32.lt_s + if ;; label = @8 + local.get $2 + local.set $1 + else + block ;; label = @9 + i32.const 31 + local.set $1 + br 2 (;@7;) + end + end + loop $label$21 ;; label = @8 + local.get $1 + i32.const 0 + i32.gt_s + if ;; label = @9 + block ;; label = @10 + i32.const 0 + local.set $2 + loop $label$23 ;; label = @11 + local.get $3 + local.get $2 + i32.const 2 + i32.shl + i32.add + local.get $3 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 2 + i32.shl + i32.add + i32.load + i32.store + local.get $2 + local.get $1 + i32.lt_s + br_if 0 (;@11;) + local.get $1 + local.set $2 + end + end + else + i32.const 0 + local.set $2 + end + local.get $3 + local.get $2 + i32.const 2 + i32.shl + i32.add + local.get $7 + i32.store + local.get $10 + local.get $1 + i32.const 2 + i32.shl + i32.add + local.tee $2 + i32.load + local.set $5 + local.get $2 + local.get $5 + i32.const -1 + i32.add + i32.store + local.get $5 + i32.const 1 + i32.gt_s + br_if 2 (;@6;) + local.get $1 + i32.const 1 + i32.add + local.tee $1 + local.get $11 + i32.lt_s + if ;; label = @9 + block ;; label = @10 + local.get $3 + i32.load + local.set $7 + br 2 (;@8;) + end + else + block ;; label = @10 + i32.const 31 + local.set $1 + br 3 (;@7;) + end + end + end + end + end + local.get $1 + i32.const 31 + i32.eq + if ;; label = @6 + block ;; label = @7 + local.get $3 + call $36 + local.get $6 + call $36 + local.get $10 + call $36 + local.get $0 + return + end + end + end + else + block ;; label = @5 + local.get $14 + local.set $16 + local.get $11 + local.set $13 + end + end + end + else + block ;; label = @3 + local.get $3 + local.get $0 + i32.load + local.tee $0 + i32.const 2 + i32.shl + i32.add + local.get $4 + i32.const -1 + i32.add + local.tee $13 + i32.store + local.get $3 + local.get $13 + i32.const 2 + i32.shl + i32.add + local.tee $16 + local.get $0 + i32.store + end + end + i32.const 0 + local.set $0 + local.get $4 + local.set $1 + loop $label$30 ;; label = @2 + block $label$31 ;; label = @3 + local.get $1 + i32.const 1 + i32.gt_s + if ;; label = @4 + loop $label$33 ;; label = @5 + local.get $10 + local.get $1 + i32.const -1 + i32.add + local.tee $2 + i32.const 2 + i32.shl + i32.add + local.get $1 + i32.store + local.get $2 + i32.const 1 + i32.gt_s + if ;; label = @6 + block ;; label = @7 + local.get $2 + local.set $1 + br 2 (;@5;) + end + else + i32.const 1 + local.set $2 + end + end + else + local.get $1 + local.set $2 + end + local.get $3 + i32.load + local.tee $9 + if ;; label = @4 + local.get $16 + i32.load + local.get $13 + i32.ne + if ;; label = @5 + block ;; label = @6 + i32.const 0 + local.set $5 + local.get $6 + i32.load + local.set $8 + loop $label$39 ;; label = @7 + local.get $8 + i32.const -1 + i32.add + local.tee $1 + i32.const 1 + i32.gt_s + if ;; label = @8 + block ;; label = @9 + i32.const 1 + local.set $4 + loop $label$41 ;; label = @10 + local.get $6 + local.get $4 + i32.const 2 + i32.shl + i32.add + local.tee $7 + i32.load + local.set $14 + local.get $7 + local.get $6 + local.get $1 + i32.const 2 + i32.shl + i32.add + local.tee $7 + i32.load + i32.store + local.get $7 + local.get $14 + i32.store + local.get $4 + i32.const 1 + i32.add + local.tee $4 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + i32.lt_s + br_if 0 (;@10;) + end + end + end + local.get $5 + i32.const 1 + i32.add + local.set $4 + local.get $6 + local.get $8 + i32.const 2 + i32.shl + i32.add + local.tee $7 + i32.load + local.set $1 + local.get $7 + local.get $8 + i32.store + local.get $1 + if ;; label = @8 + block ;; label = @9 + local.get $4 + local.set $5 + local.get $1 + local.set $8 + br 2 (;@7;) + end + end + end + local.get $0 + local.get $5 + i32.le_s + if ;; label = @7 + local.get $4 + local.set $0 + end + end + end + end + local.get $2 + local.get $13 + i32.lt_s + if ;; label = @4 + local.get $2 + local.set $1 + else + block ;; label = @5 + i32.const 31 + local.set $1 + br 2 (;@3;) + end + end + loop $label$46 ;; label = @4 + local.get $1 + i32.const 0 + i32.gt_s + if ;; label = @5 + block ;; label = @6 + i32.const 0 + local.set $2 + loop $label$48 ;; label = @7 + local.get $3 + local.get $2 + i32.const 2 + i32.shl + i32.add + local.get $3 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 2 + i32.shl + i32.add + i32.load + i32.store + local.get $2 + local.get $1 + i32.lt_s + br_if 0 (;@7;) + local.get $1 + local.set $2 + end + end + else + i32.const 0 + local.set $2 + end + local.get $3 + local.get $2 + i32.const 2 + i32.shl + i32.add + local.get $9 + i32.store + local.get $10 + local.get $1 + i32.const 2 + i32.shl + i32.add + local.tee $2 + i32.load + local.set $4 + local.get $2 + local.get $4 + i32.const -1 + i32.add + i32.store + local.get $4 + i32.const 1 + i32.gt_s + br_if 2 (;@2;) + local.get $1 + i32.const 1 + i32.add + local.tee $1 + local.get $13 + i32.lt_s + if ;; label = @5 + block ;; label = @6 + local.get $3 + i32.load + local.set $9 + br 2 (;@4;) + end + else + block ;; label = @6 + i32.const 31 + local.set $1 + br 3 (;@3;) + end + end + end + end + end + local.get $1 + i32.const 31 + i32.eq + if ;; label = @2 + block ;; label = @3 + local.get $3 + call $36 + local.get $6 + call $36 + local.get $10 + call $36 + local.get $0 + return + end + end + i32.const 0 + end ) - ) - (func $10 (; 23 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 32) - ) - ) - (i32.store - (local.tee $3 - (local.get $4) - ) - (i32.load offset=60 - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.const 0) - ) - (i32.store offset=8 - (local.get $3) - (local.get $1) - ) - (i32.store offset=12 - (local.get $3) - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - (i32.store offset=16 - (local.get $3) - (local.get $2) - ) - (local.set $0 - (if (result i32) - (i32.lt_s - (call $11 - (call $fimport$14 - (i32.const 140) - (local.get $3) - ) - ) - (i32.const 0) - ) - (block (result i32) - (i32.store - (local.get $0) - (i32.const -1) - ) - (i32.const -1) - ) - (i32.load - (local.get $0) - ) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $0) + (func $8 (;21;) (type $4) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $5 + global.get $global$1 + i32.const 32 + i32.add + global.set $global$1 + local.get $5 + i32.const 16 + i32.add + local.set $7 + local.get $5 + i32.const 8 + i32.add + local.set $10 + local.get $5 + local.set $2 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $0 + i32.const 1 + i32.le_s + br_if 0 (;@3;) + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + block $label$6 ;; label = @6 + block $label$7 ;; label = @7 + block $label$8 ;; label = @8 + block $label$9 ;; label = @9 + block $label$10 ;; label = @10 + local.get $1 + i32.load offset=4 + i32.load8_s + local.tee $0 + i32.const 48 + i32.sub + br_table 5 (;@5;) 0 (;@10;) 2 (;@8;) 1 (;@9;) 3 (;@7;) 4 (;@6;) 6 (;@4;) + end + i32.const 9 + local.set $3 + br 7 (;@2;) + end + br 5 (;@3;) + end + i32.const 10 + local.set $3 + br 5 (;@2;) + end + i32.const 11 + local.set $3 + br 4 (;@2;) + end + i32.const 12 + local.set $3 + br 3 (;@2;) + end + local.get $5 + global.set $global$1 + i32.const 0 + return + end + local.get $2 + local.get $0 + i32.const -48 + i32.add + i32.store + i32.const 1140 + local.get $2 + call $33 + drop + local.get $5 + global.set $global$1 + i32.const -1 + return + end + i32.const 11 + local.set $3 + end + local.get $3 + i32.const -1 + i32.add + local.set $6 + i32.const 0 + local.set $2 + i32.const 0 + local.set $0 + loop $label$11 ;; label = @2 + i32.const 12 + call $35 + local.tee $1 + local.get $0 + i32.store + local.get $1 + local.get $3 + i32.store offset=4 + local.get $1 + local.get $2 + i32.store offset=8 + local.get $0 + i32.const 1 + i32.add + local.tee $0 + local.get $6 + i32.ne + if ;; label = @3 + block ;; label = @4 + local.get $1 + local.set $2 + br 2 (;@2;) + end + end + end + local.get $3 + i32.const 2 + i32.shl + local.tee $0 + call $35 + local.set $4 + local.get $0 + call $35 + local.set $8 + i32.const 0 + local.set $0 + loop $label$13 ;; label = @2 + local.get $4 + local.get $0 + i32.const 2 + i32.shl + i32.add + local.get $0 + i32.store + local.get $0 + i32.const 1 + i32.add + local.tee $0 + local.get $3 + i32.ne + br_if 0 (;@2;) + end + local.get $3 + local.set $0 + i32.const 30 + local.set $6 + loop $label$14 ;; label = @2 + block $label$15 ;; label = @3 + i32.const 0 + local.set $2 + loop $label$16 ;; label = @4 + local.get $10 + local.get $4 + local.get $2 + i32.const 2 + i32.shl + i32.add + i32.load + i32.const 1 + i32.add + i32.store + i32.const 1174 + local.get $10 + call $33 + drop + local.get $2 + i32.const 1 + i32.add + local.tee $2 + local.get $3 + i32.ne + br_if 0 (;@4;) + end + i32.const 10 + call $34 + drop + local.get $0 + i32.const 1 + i32.gt_s + if ;; label = @4 + loop $label$18 ;; label = @5 + local.get $8 + local.get $0 + i32.const -1 + i32.add + local.tee $2 + i32.const 2 + i32.shl + i32.add + local.get $0 + i32.store + local.get $2 + i32.const 1 + i32.gt_s + if ;; label = @6 + block ;; label = @7 + local.get $2 + local.set $0 + br 2 (;@5;) + end + else + i32.const 1 + local.set $0 + end + end + else + local.get $0 + local.get $3 + i32.eq + br_if 1 (;@3;) + end + local.get $6 + i32.const -1 + i32.add + local.set $6 + loop $label$22 ;; label = @4 + block $label$23 ;; label = @5 + local.get $4 + i32.load + local.set $9 + local.get $0 + i32.const 0 + i32.gt_s + if ;; label = @6 + block ;; label = @7 + i32.const 0 + local.set $2 + loop $label$25 ;; label = @8 + local.get $4 + local.get $2 + i32.const 2 + i32.shl + i32.add + local.get $4 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 2 + i32.shl + i32.add + i32.load + i32.store + local.get $2 + local.get $0 + i32.lt_s + br_if 0 (;@8;) + local.get $0 + local.set $2 + end + end + else + i32.const 0 + local.set $2 + end + local.get $4 + local.get $2 + i32.const 2 + i32.shl + i32.add + local.get $9 + i32.store + local.get $8 + local.get $0 + i32.const 2 + i32.shl + i32.add + local.tee $9 + i32.load + local.set $2 + local.get $9 + local.get $2 + i32.const -1 + i32.add + i32.store + local.get $2 + i32.const 1 + i32.gt_s + br_if 0 (;@5;) + local.get $0 + i32.const 1 + i32.add + local.tee $0 + local.get $3 + i32.ne + br_if 1 (;@4;) + br 2 (;@3;) + end + end + local.get $6 + br_if 1 (;@2;) + end + end + local.get $4 + call $36 + local.get $8 + call $36 + local.get $1 + if ;; label = @2 + block ;; label = @3 + i32.const 0 + local.set $0 + loop $label$28 ;; label = @4 + local.get $0 + local.get $1 + call $7 + local.tee $2 + i32.lt_s + if ;; label = @5 + local.get $2 + local.set $0 + end + local.get $1 + i32.load offset=8 + local.set $2 + local.get $1 + call $36 + local.get $2 + if ;; label = @5 + block ;; label = @6 + local.get $2 + local.set $1 + br 2 (;@4;) + end + end + end + end + else + i32.const 0 + local.set $0 + end + local.get $7 + local.get $3 + i32.store + local.get $7 + local.get $0 + i32.store offset=4 + i32.const 1151 + local.get $7 + call $33 + drop + local.get $5 + global.set $global$1 + i32.const 0 + end ) - ) - (func $11 (; 24 ;) (type $1) (param $0 i32) (result i32) - (if (result i32) - (i32.gt_u - (local.get $0) - (i32.const -4096) - ) - (block (result i32) - (i32.store - (call $12) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - (local.get $0) + (func $9 (;22;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $1 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $1 + local.tee $2 + local.get $0 + i32.load offset=60 + i32.store + i32.const 6 + local.get $2 + call $fimport$8 + call $11 + local.set $0 + local.get $1 + global.set $global$1 + local.get $0 + end ) - ) - (func $12 (; 25 ;) (type $3) (result i32) - (i32.const 3648) - ) - (func $13 (; 26 ;) (type $2) (param $0 i32) - (nop) - ) - (func $14 (; 27 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 80) - ) - ) - (local.set $3 - (local.get $4) - ) - (local.set $5 - (i32.add - (local.get $4) - (i32.const 12) - ) - ) - (i32.store offset=36 - (local.get $0) - (i32.const 3) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 64) - ) - ) - (block - (i32.store - (local.get $3) - (i32.load offset=60 - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.const 21505) - ) - (i32.store offset=8 - (local.get $3) - (local.get $5) - ) - (if - (call $fimport$13 - (i32.const 54) - (local.get $3) - ) - (i32.store8 offset=75 - (local.get $0) - (i32.const -1) - ) - ) - ) - ) - (local.set $0 - (call $15 - (local.get $0) - (local.get $1) - (local.get $2) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $0) + (func $10 (;23;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 32 + i32.add + global.set $global$1 + local.get $4 + local.tee $3 + local.get $0 + i32.load offset=60 + i32.store + local.get $3 + i32.const 0 + i32.store offset=4 + local.get $3 + local.get $1 + i32.store offset=8 + local.get $3 + local.get $4 + i32.const 20 + i32.add + local.tee $0 + i32.store offset=12 + local.get $3 + local.get $2 + i32.store offset=16 + i32.const 140 + local.get $3 + call $fimport$14 + call $11 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $0 + i32.const -1 + i32.store + i32.const -1 + end + else + local.get $0 + i32.load + end + local.set $0 + local.get $4 + global.set $global$1 + local.get $0 + end ) - ) - (func $15 (; 28 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (block $label$1 (result i32) - (local.set $8 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 48) - ) - ) - (local.set $9 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - (local.set $10 - (local.get $8) - ) - (i32.store - (local.tee $3 - (i32.add - (local.get $8) - (i32.const 32) - ) - ) - (local.tee $4 - (i32.load - (local.tee $6 - (i32.add - (local.get $0) - (i32.const 28) - ) - ) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (local.tee $5 - (i32.sub - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - (local.get $4) - ) - ) - ) - (i32.store offset=8 - (local.get $3) - (local.get $1) - ) - (i32.store offset=12 - (local.get $3) - (local.get $2) - ) - (local.set $13 - (i32.add - (local.get $0) - (i32.const 60) - ) - ) - (local.set $14 - (i32.add - (local.get $0) - (i32.const 44) - ) - ) - (local.set $1 - (local.get $3) - ) - (local.set $4 - (i32.const 2) - ) - (local.set $12 - (i32.add - (local.get $5) - (local.get $2) - ) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (loop $label$5 - (if - (i32.load - (i32.const 3604) - ) - (block - (call $fimport$9 - (i32.const 1) - (local.get $0) - ) - (i32.store - (local.get $10) - (i32.load - (local.get $13) - ) - ) - (i32.store offset=4 - (local.get $10) - (local.get $1) - ) - (i32.store offset=8 - (local.get $10) - (local.get $4) - ) - (local.set $3 - (call $11 - (call $fimport$15 - (i32.const 146) - (local.get $10) - ) - ) - ) - (call $fimport$7 - (i32.const 0) - ) - ) - (block - (i32.store - (local.get $9) - (i32.load - (local.get $13) - ) - ) - (i32.store offset=4 - (local.get $9) - (local.get $1) - ) - (i32.store offset=8 - (local.get $9) - (local.get $4) - ) - (local.set $3 - (call $11 - (call $fimport$15 - (i32.const 146) - (local.get $9) - ) - ) - ) - ) - ) - (br_if $label$4 - (i32.eq - (local.get $12) - (local.get $3) - ) - ) - (br_if $label$3 - (i32.lt_s - (local.get $3) - (i32.const 0) - ) - ) - (local.set $5 - (if (result i32) - (i32.gt_u - (local.get $3) - (local.tee $5 - (i32.load offset=4 - (local.get $1) - ) - ) - ) - (block (result i32) - (i32.store - (local.get $6) - (local.tee $7 - (i32.load - (local.get $14) - ) - ) - ) - (i32.store - (local.get $11) - (local.get $7) - ) - (local.set $7 - (i32.load offset=12 - (local.get $1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (local.set $4 - (i32.add - (local.get $4) - (i32.const -1) - ) - ) - (i32.sub - (local.get $3) - (local.get $5) - ) - ) - (if (result i32) - (i32.eq - (local.get $4) - (i32.const 2) - ) - (block (result i32) - (i32.store - (local.get $6) - (i32.add - (i32.load - (local.get $6) - ) - (local.get $3) - ) - ) - (local.set $7 - (local.get $5) - ) - (local.set $4 - (i32.const 2) - ) - (local.get $3) - ) - (block (result i32) - (local.set $7 - (local.get $5) - ) - (local.get $3) - ) - ) - ) - ) - (i32.store - (local.get $1) - (i32.add - (i32.load - (local.get $1) - ) - (local.get $5) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.sub - (local.get $7) - (local.get $5) - ) - ) - (local.set $12 - (i32.sub - (local.get $12) - (local.get $3) - ) - ) - (br $label$5) - ) - ) - (i32.store offset=16 - (local.get $0) - (i32.add - (local.tee $1 - (i32.load - (local.get $14) - ) - ) - (i32.load offset=48 - (local.get $0) - ) - ) - ) - (i32.store - (local.get $6) - (local.get $1) - ) - (i32.store - (local.get $11) - (local.get $1) - ) - (br $label$2) - ) - (i32.store offset=16 - (local.get $0) - (i32.const 0) - ) - (i32.store - (local.get $6) - (i32.const 0) - ) - (i32.store - (local.get $11) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (local.set $2 - (if (result i32) - (i32.eq - (local.get $4) - (i32.const 2) - ) - (i32.const 0) - (i32.sub - (local.get $2) - (i32.load offset=4 - (local.get $1) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $8) - ) - (local.get $2) + (func $11 (;24;) (type $1) (param $0 i32) (result i32) + local.get $0 + i32.const -4096 + i32.gt_u + if (result i32) ;; label = @1 + block (result i32) ;; label = @2 + call $12 + i32.const 0 + local.get $0 + i32.sub + i32.store + i32.const -1 + end + else + local.get $0 + end ) - ) - (func $16 (; 29 ;) (type $2) (param $0 i32) - (if - (i32.eqz - (i32.load offset=68 - (local.get $0) - ) - ) - (call $13 - (local.get $0) - ) + (func $12 (;25;) (type $3) (result i32) + i32.const 3648 ) - ) - (func $17 (; 30 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $5 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (if - (i32.and - (local.tee $4 - (i32.ne - (local.get $2) - (i32.const 0) - ) - ) - (i32.ne - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.const 0) - ) - ) - (block - (local.set $4 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (local.set $3 - (local.get $2) - ) - (local.set $2 - (local.get $0) - ) - (loop $label$6 - (if - (i32.eq - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.get $4) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (br $label$3) - ) - ) - (br_if $label$6 - (i32.and - (local.tee $0 - (i32.ne - (local.tee $3 - (i32.add - (local.get $3) - (i32.const -1) - ) - ) - (i32.const 0) - ) - ) - (i32.ne - (i32.and - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 3) - ) - (i32.const 0) - ) - ) - ) - (br $label$4) - ) - ) - (block - (local.set $3 - (local.get $2) - ) - (local.set $2 - (local.get $0) - ) - (local.set $0 - (local.get $4) - ) - ) - ) - ) - (if - (local.get $0) - (block - (local.set $0 - (local.get $3) - ) - (br $label$3) - ) - (local.set $0 - (i32.const 0) - ) - ) - (br $label$2) - ) - (if - (i32.ne - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.tee $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $3 - (i32.mul - (local.get $5) - (i32.const 16843009) - ) - ) - (block $label$12 - (block $label$13 - (br_if $label$13 - (i32.le_u - (local.get $0) - (i32.const 3) - ) - ) - (loop $label$14 - (if - (i32.eqz - (i32.and - (i32.xor - (i32.and - (local.tee $4 - (i32.xor - (i32.load - (local.get $2) - ) - (local.get $3) - ) - ) - (i32.const -2139062144) - ) - (i32.const -2139062144) - ) - (i32.add - (local.get $4) - (i32.const -16843009) - ) - ) - ) - (block - (local.set $2 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - (br_if $label$14 - (i32.gt_u - (local.tee $0 - (i32.add - (local.get $0) - (i32.const -4) - ) - ) - (i32.const 3) - ) - ) - (br $label$13) - ) - ) - ) - (br $label$12) - ) - (if - (i32.eqz - (local.get $0) - ) - (block - (local.set $0 - (i32.const 0) - ) - (br $label$2) - ) - ) - ) - (loop $label$17 - (br_if $label$2 - (i32.eq - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.get $1) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (local.set $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (br_if $label$17 - (local.tee $0 - (i32.add - (local.get $0) - (i32.const -1) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - ) - ) - ) - ) - (if (result i32) - (local.get $0) - (local.get $2) - (i32.const 0) - ) + (func $13 (;26;) (type $2) (param $0 i32) + nop ) - ) - (func $18 (; 31 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 224) - ) - ) - (local.set $5 - (i32.add - (local.get $4) - (i32.const 136) - ) - ) - (i64.store align=4 - (local.tee $3 - (i32.add - (local.get $4) - (i32.const 80) - ) - ) - (i64.const 0) - ) - (i64.store offset=8 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=16 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=24 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=32 align=4 - (local.get $3) - (i64.const 0) - ) - (i32.store - (local.tee $6 - (i32.add - (local.get $4) - (i32.const 120) - ) - ) - (i32.load - (local.get $2) - ) - ) - (if - (i32.lt_s - (call $19 - (i32.const 0) - (local.get $1) - (local.get $6) - (local.tee $2 - (local.get $4) - ) - (local.get $3) - ) - (i32.const 0) - ) - (local.set $1 - (i32.const -1) - ) - (block - (local.set $12 - (if (result i32) - (i32.gt_s - (i32.load offset=76 - (local.get $0) - ) - (i32.const -1) - ) - (call $20 - (local.get $0) - ) - (i32.const 0) - ) - ) - (local.set $7 - (i32.load - (local.get $0) - ) - ) - (if - (i32.lt_s - (i32.load8_s offset=74 - (local.get $0) - ) - (i32.const 1) - ) - (i32.store - (local.get $0) - (i32.and - (local.get $7) - (i32.const -33) - ) - ) - ) - (if - (i32.load - (local.tee $8 - (i32.add - (local.get $0) - (i32.const 48) - ) - ) - ) - (local.set $1 - (call $19 - (local.get $0) - (local.get $1) - (local.get $6) - (local.get $2) - (local.get $3) - ) - ) - (block - (local.set $10 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 44) - ) - ) - ) - ) - (i32.store - (local.get $9) - (local.get $5) - ) - (i32.store - (local.tee $13 - (i32.add - (local.get $0) - (i32.const 28) - ) - ) - (local.get $5) - ) - (i32.store - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - (local.get $5) - ) - (i32.store - (local.get $8) - (i32.const 80) - ) - (i32.store - (local.tee $14 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - (i32.add - (local.get $5) - (i32.const 80) - ) - ) - (local.set $1 - (call $19 - (local.get $0) - (local.get $1) - (local.get $6) - (local.get $2) - (local.get $3) - ) - ) - (if - (local.get $10) - (block - (drop - (call_indirect (type $0) - (local.get $0) - (i32.const 0) - (i32.const 0) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $0) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $11) - ) - ) - (local.set $1 - (i32.const -1) - ) - ) - (i32.store - (local.get $9) - (local.get $10) - ) - (i32.store - (local.get $8) - (i32.const 0) - ) - (i32.store - (local.get $14) - (i32.const 0) - ) - (i32.store - (local.get $13) - (i32.const 0) - ) - (i32.store - (local.get $11) - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (local.get $0) - (i32.or - (local.tee $2 - (i32.load - (local.get $0) - ) - ) - (i32.and - (local.get $7) - (i32.const 32) - ) - ) - ) - (if - (local.get $12) - (call $13 - (local.get $0) - ) - ) - (if - (i32.and - (local.get $2) - (i32.const 32) - ) - (local.set $1 - (i32.const -1) - ) - ) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $1) + (func $14 (;27;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 80 + i32.add + global.set $global$1 + local.get $4 + local.set $3 + local.get $4 + i32.const 12 + i32.add + local.set $5 + local.get $0 + i32.const 3 + i32.store offset=36 + local.get $0 + i32.load + i32.const 64 + i32.and + i32.eqz + if ;; label = @2 + block ;; label = @3 + local.get $3 + local.get $0 + i32.load offset=60 + i32.store + local.get $3 + i32.const 21505 + i32.store offset=4 + local.get $3 + local.get $5 + i32.store offset=8 + i32.const 54 + local.get $3 + call $fimport$13 + if ;; label = @4 + local.get $0 + i32.const -1 + i32.store8 offset=75 + end + end + end + local.get $0 + local.get $1 + local.get $2 + call $15 + local.set $0 + local.get $4 + global.set $global$1 + local.get $0 + end ) - ) - (func $19 (; 32 ;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 i32) - (local $17 i32) - (local $18 i32) - (local $19 i32) - (local $20 i32) - (local $21 i32) - (local $22 i32) - (local $23 i32) - (local $24 i32) - (local $25 i32) - (local $26 i32) - (local $27 i32) - (local $28 i32) - (local $29 i32) - (local $30 i32) - (local $31 i32) - (local $32 i32) - (local $33 i32) - (local $34 i32) - (local $35 i32) - (local $36 i32) - (local $37 i32) - (local $38 i32) - (local $39 i32) - (local $40 i32) - (local $41 i32) - (local $42 i32) - (local $43 i32) - (local $44 i32) - (local $45 i32) - (local $46 i32) - (local $47 i32) - (local $48 i32) - (local $49 i32) - (local $50 i64) - (local $51 i64) - (local $52 f64) - (local $53 f64) - (block $label$1 (result i32) - (local.set $23 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 624) - ) - ) - (local.set $20 - (i32.add - (local.get $23) - (i32.const 16) - ) - ) - (local.set $16 - (local.get $23) - ) - (local.set $36 - (i32.add - (local.get $23) - (i32.const 528) - ) - ) - (local.set $30 - (i32.ne - (local.get $0) - (i32.const 0) - ) - ) - (local.set $38 - (local.tee $21 - (i32.add - (local.tee $17 - (i32.add - (local.get $23) - (i32.const 536) - ) - ) - (i32.const 40) - ) - ) - ) - (local.set $39 - (i32.add - (local.get $17) - (i32.const 39) - ) - ) - (local.set $42 - (i32.add - (local.tee $37 - (i32.add - (local.get $23) - (i32.const 8) - ) - ) - (i32.const 4) - ) - ) - (local.set $43 - (i32.sub - (i32.const 0) - (local.tee $27 - (local.tee $19 - (i32.add - (local.get $23) - (i32.const 588) - ) - ) - ) - ) - ) - (local.set $33 - (i32.add - (local.tee $17 - (i32.add - (local.get $23) - (i32.const 576) - ) - ) - (i32.const 12) - ) - ) - (local.set $40 - (i32.add - (local.get $17) - (i32.const 11) - ) - ) - (local.set $44 - (i32.sub - (local.tee $28 - (local.get $33) - ) - (local.get $27) - ) - ) - (local.set $45 - (i32.sub - (i32.const -2) - (local.get $27) - ) - ) - (local.set $46 - (i32.add - (local.get $28) - (i32.const 2) - ) - ) - (local.set $48 - (i32.add - (local.tee $47 - (i32.add - (local.get $23) - (i32.const 24) - ) - ) - (i32.const 288) - ) - ) - (local.set $41 - (local.tee $31 - (i32.add - (local.get $19) - (i32.const 9) - ) - ) - ) - (local.set $34 - (i32.add - (local.get $19) - (i32.const 8) - ) - ) - (local.set $15 - (i32.const 0) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $17 - (i32.const 0) - ) - (block $label$2 - (block $label$3 - (loop $label$4 - (block $label$5 - (if - (i32.gt_s - (local.get $15) - (i32.const -1) - ) - (local.set $15 - (if (result i32) - (i32.gt_s - (local.get $10) - (i32.sub - (i32.const 2147483647) - (local.get $15) - ) - ) - (block (result i32) - (i32.store - (call $12) - (i32.const 75) - ) - (i32.const -1) - ) - (i32.add - (local.get $10) - (local.get $15) - ) - ) - ) - ) - (br_if $label$3 - (i32.eqz - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.get $1) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (local.set $11 - (local.get $1) - ) - (block $label$9 - (block $label$10 - (loop $label$11 - (block $label$12 - (block $label$13 - (block $label$14 - (block $label$15 - (br_table $label$14 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$15 $label$13 - (i32.sub - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 0) - ) - ) - ) - (local.set $5 - (local.get $11) - ) - (br $label$10) - ) - (local.set $5 - (local.get $11) - ) - (br $label$12) - ) - (local.set $5 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (br $label$11) - ) - ) - (br $label$9) - ) - (loop $label$16 - (br_if $label$9 - (i32.ne - (i32.load8_s offset=1 - (local.get $5) - ) - (i32.const 37) - ) - ) - (local.set $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - (br_if $label$16 - (i32.eq - (i32.load8_s - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 2) - ) - ) - ) - (i32.const 37) - ) - ) - ) - ) - (local.set $10 - (i32.sub - (local.get $11) - (local.get $1) - ) - ) - (if - (local.get $30) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (local.get $10) - (local.get $0) - ) - ) - ) - ) - (if - (local.get $10) - (block - (local.set $1 - (local.get $5) - ) - (br $label$4) - ) - ) - (local.set $10 - (if (result i32) - (i32.lt_u - (local.tee $9 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $10 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block (result i32) - (local.set $10 - (i32.add - (local.get $5) - (i32.const 3) - ) - ) - (if - (local.tee $12 - (i32.eq - (i32.load8_s offset=2 - (local.get $5) - ) - (i32.const 36) - ) - ) - (local.set $11 - (local.get $10) - ) - ) - (if - (local.get $12) - (local.set $17 - (i32.const 1) - ) - ) - (local.set $5 - (i32.load8_s - (local.get $11) - ) - ) - (if - (i32.eqz - (local.get $12) - ) - (local.set $9 - (i32.const -1) - ) - ) - (local.get $17) - ) - (block (result i32) - (local.set $5 - (local.get $10) - ) - (local.set $9 - (i32.const -1) - ) - (local.get $17) - ) - ) - ) - (block $label$25 - (if - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (i32.const 32) - ) - (block - (local.set $17 - (i32.const 0) - ) - (loop $label$27 - (br_if $label$25 - (i32.eqz - (i32.and - (i32.shl - (i32.const 1) - (local.get $12) - ) - (i32.const 75913) - ) - ) - ) - (local.set $17 - (i32.or - (i32.shl - (i32.const 1) - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (local.get $17) - ) - ) - (br_if $label$27 - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (i32.const 32) - ) - ) - ) - ) - (local.set $17 - (i32.const 0) - ) - ) - ) - (block $label$29 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 42) - ) - (block - (local.set $11 - (block $label$31 (result i32) - (block $label$32 - (br_if $label$32 - (i32.ge_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - (br_if $label$32 - (i32.ne - (i32.load8_s offset=2 - (local.get $11) - ) - (i32.const 36) - ) - ) - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $12) - (i32.const 2) - ) - ) - (i32.const 10) - ) - (local.set $8 - (i32.const 1) - ) - (local.set $10 - (i32.wrap_i64 - (i64.load - (i32.add - (local.get $3) - (i32.shl - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -48) - ) - (i32.const 3) - ) - ) - ) - ) - ) - (br $label$31 - (i32.add - (local.get $11) - (i32.const 3) - ) - ) - ) - (if - (local.get $10) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $12 - (local.get $17) - ) - (local.set $17 - (i32.const 0) - ) - (local.set $11 - (local.get $7) - ) - (local.set $10 - (i32.const 0) - ) - (br $label$29) - ) - ) - (local.set $10 - (i32.load - (local.tee $11 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (local.set $8 - (i32.const 0) - ) - (local.get $7) - ) - ) - (local.set $12 - (i32.or - (local.get $17) - (i32.const 8192) - ) - ) - (local.set $7 - (i32.sub - (i32.const 0) - (local.get $10) - ) - ) - (local.set $5 - (i32.load8_s - (local.get $11) - ) - ) - (if - (i32.eqz - (local.tee $6 - (i32.lt_s - (local.get $10) - (i32.const 0) - ) - ) - ) - (local.set $12 - (local.get $17) - ) - ) - (local.set $17 - (local.get $8) - ) - (if - (local.get $6) - (local.set $10 - (local.get $7) - ) - ) - ) - (if - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block - (local.set $7 - (i32.const 0) - ) - (local.set $5 - (local.get $12) - ) - (loop $label$39 - (local.set $7 - (i32.add - (i32.mul - (local.get $7) - (i32.const 10) - ) - (local.get $5) - ) - ) - (br_if $label$39 - (i32.lt_u - (local.tee $5 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $12 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - ) - (if - (i32.lt_s - (local.get $7) - (i32.const 0) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - (block - (local.set $5 - (local.get $12) - ) - (local.set $12 - (local.get $17) - ) - (local.set $17 - (local.get $10) - ) - (local.set $10 - (local.get $7) - ) - ) - ) - ) - (block - (local.set $12 - (local.get $17) - ) - (local.set $17 - (local.get $10) - ) - (local.set $10 - (i32.const 0) - ) - ) - ) - ) - ) - (block $label$43 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 46) - ) - (block - (if - (i32.ne - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 42) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block - (local.set $11 - (local.get $7) - ) - (local.set $7 - (i32.const 0) - ) - ) - (block - (local.set $5 - (i32.const 0) - ) - (local.set $11 - (local.get $7) - ) - (br $label$43) - ) - ) - (loop $label$48 - (local.set $5 - (i32.add - (i32.mul - (local.get $7) - (i32.const 10) - ) - (local.get $5) - ) - ) - (br_if $label$43 - (i32.ge_u - (local.tee $8 - (i32.add - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - (local.set $7 - (local.get $5) - ) - (local.set $5 - (local.get $8) - ) - (br $label$48) - ) - ) - ) - (if - (i32.lt_u - (local.tee $5 - (i32.add - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 2) - ) - ) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (if - (i32.eq - (i32.load8_s offset=3 - (local.get $11) - ) - (i32.const 36) - ) - (block - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $5) - (i32.const 2) - ) - ) - (i32.const 10) - ) - (local.set $5 - (i32.wrap_i64 - (i64.load - (i32.add - (local.get $3) - (i32.shl - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -48) - ) - (i32.const 3) - ) - ) - ) - ) - ) - (local.set $11 - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (br $label$43) - ) - ) - ) - (if - (local.get $17) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $11 - (if (result i32) - (local.get $30) - (block (result i32) - (local.set $5 - (i32.load - (local.tee $11 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (local.get $7) - ) - (block (result i32) - (local.set $5 - (i32.const 0) - ) - (local.get $7) - ) - ) - ) - ) - (local.set $5 - (i32.const -1) - ) - ) - ) - (local.set $7 - (local.get $11) - ) - (local.set $8 - (i32.const 0) - ) - (loop $label$55 - (if - (i32.gt_u - (local.tee $6 - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -65) - ) - ) - (i32.const 57) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $11 - (i32.add - (local.get $7) - (i32.const 1) - ) - ) - (if - (i32.lt_u - (i32.add - (local.tee $6 - (i32.and - (local.tee $13 - (i32.load8_s - (i32.add - (i32.add - (i32.mul - (local.get $8) - (i32.const 58) - ) - (i32.const 1177) - ) - (local.get $6) - ) - ) - ) - (i32.const 255) - ) - ) - (i32.const -1) - ) - (i32.const 8) - ) - (block - (local.set $7 - (local.get $11) - ) - (local.set $8 - (local.get $6) - ) - (br $label$55) - ) - ) - ) - (if - (i32.eqz - (i32.shr_s - (i32.shl - (local.get $13) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $14 - (i32.gt_s - (local.get $9) - (i32.const -1) - ) - ) - (block $label$59 - (block $label$60 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $13) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 19) - ) - (if - (local.get $14) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - (br $label$60) - ) - (block - (if - (local.get $14) - (block - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $9) - (i32.const 2) - ) - ) - (local.get $6) - ) - (i64.store - (local.get $16) - (i64.load - (i32.add - (local.get $3) - (i32.shl - (local.get $9) - (i32.const 3) - ) - ) - ) - ) - (br $label$60) - ) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $15 - (i32.const 0) - ) - (br $label$5) - ) - ) - (call $22 - (local.get $16) - (local.get $6) - (local.get $2) - ) - ) - ) - (br $label$59) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - ) - ) - (local.set $9 - (i32.and - (local.tee $7 - (i32.load8_s - (local.get $7) - ) - ) - (i32.const -33) - ) - ) - (if - (i32.eqz - (i32.and - (i32.ne - (local.get $8) - (i32.const 0) - ) - (i32.eq - (i32.and - (local.get $7) - (i32.const 15) - ) - (i32.const 3) - ) - ) - ) - (local.set $9 - (local.get $7) - ) - ) - (local.set $7 - (i32.and - (local.get $12) - (i32.const -65537) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 8192) - ) - (local.set $12 - (local.get $7) - ) - ) - (block $label$70 - (block $label$71 - (block $label$72 - (block $label$73 - (block $label$74 - (block $label$75 - (block $label$76 - (block $label$77 - (block $label$78 - (block $label$79 - (block $label$80 - (block $label$81 - (block $label$82 - (block $label$83 - (block $label$84 - (block $label$85 - (block $label$86 - (block $label$87 - (block $label$88 - (block $label$89 - (br_table $label$78 $label$77 $label$80 $label$77 $label$78 $label$78 $label$78 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$79 $label$77 $label$77 $label$77 $label$77 $label$87 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$78 $label$77 $label$83 $label$85 $label$78 $label$78 $label$78 $label$77 $label$85 $label$77 $label$77 $label$77 $label$82 $label$89 $label$86 $label$88 $label$77 $label$77 $label$81 $label$77 $label$84 $label$77 $label$77 $label$87 $label$77 - (i32.sub - (local.get $9) - (i32.const 65) - ) - ) - ) - (block $label$90 - (block $label$91 - (block $label$92 - (block $label$93 - (block $label$94 - (block $label$95 - (block $label$96 - (block $label$97 - (br_table $label$97 $label$96 $label$95 $label$94 $label$93 $label$90 $label$92 $label$91 $label$90 - (i32.sub - (i32.shr_s - (i32.shl - (i32.and - (local.get $8) - (i32.const 255) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 0) - ) - ) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i64.store - (i32.load - (local.get $16) - ) - (i64.extend_i32_s - (local.get $15) - ) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store16 - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store8 - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i64.store - (i32.load - (local.get $16) - ) - (i64.extend_i32_s - (local.get $15) - ) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $12 - (i32.or - (local.get $12) - (i32.const 8) - ) - ) - (if - (i32.le_u - (local.get $5) - (i32.const 8) - ) - (local.set $5 - (i32.const 8) - ) - ) - (local.set $9 - (i32.const 120) - ) - (br $label$76) - ) - (br $label$76) - ) - (if - (i64.eq - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (local.set $7 - (local.get $21) - ) - (block - (local.set $1 - (local.get $21) - ) - (loop $label$101 - (i64.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i64.or - (i64.and - (local.get $50) - (i64.const 7) - ) - (i64.const 48) - ) - ) - (br_if $label$101 - (i64.ne - (local.tee $50 - (i64.shr_u - (local.get $50) - (i64.const 3) - ) - ) - (i64.const 0) - ) - ) - (local.set $7 - (local.get $1) - ) - ) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 8) - ) - (block - (local.set $8 - (i32.add - (local.tee $1 - (i32.sub - (local.get $38) - (local.get $7) - ) - ) - (i32.const 1) - ) - ) - (if - (i32.le_s - (local.get $5) - (local.get $1) - ) - (local.set $5 - (local.get $8) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1657) - ) - (br $label$71) - ) - (block - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1657) - ) - (br $label$71) - ) - ) - ) - (if - (i64.lt_s - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (block - (i64.store - (local.get $16) - (local.tee $50 - (i64.sub - (i64.const 0) - (local.get $50) - ) - ) - ) - (local.set $6 - (i32.const 1) - ) - (local.set $8 - (i32.const 1657) - ) - (br $label$75) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 2048) - ) - (block - (local.set $6 - (i32.const 1) - ) - (local.set $8 - (i32.const 1658) - ) - (br $label$75) - ) - (block - (local.set $6 - (local.tee $1 - (i32.and - (local.get $12) - (i32.const 1) - ) - ) - ) - (local.set $8 - (if (result i32) - (local.get $1) - (i32.const 1659) - (i32.const 1657) - ) - ) - (br $label$75) - ) - ) - ) - (local.set $50 - (i64.load - (local.get $16) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1657) - ) - (br $label$75) - ) - (i64.store8 - (local.get $39) - (i64.load - (local.get $16) - ) - ) - (local.set $1 - (local.get $39) - ) - (local.set $12 - (local.get $7) - ) - (local.set $7 - (i32.const 1) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1657) - ) - (local.set $5 - (local.get $21) - ) - (br $label$70) - ) - (local.set $1 - (call $24 - (i32.load - (call $12) - ) - ) - ) - (br $label$74) - ) - (if - (i32.eqz - (local.tee $1 - (i32.load - (local.get $16) - ) - ) - ) - (local.set $1 - (i32.const 1667) - ) - ) - (br $label$74) - ) - (i64.store32 - (local.get $37) - (i64.load - (local.get $16) - ) - ) - (i32.store - (local.get $42) - (i32.const 0) - ) - (i32.store - (local.get $16) - (local.get $37) - ) - (local.set $7 - (local.get $37) - ) - (local.set $6 - (i32.const -1) - ) - (br $label$73) - ) - (local.set $7 - (i32.load - (local.get $16) - ) - ) - (if - (local.get $5) - (block - (local.set $6 - (local.get $5) - ) - (br $label$73) - ) - (block - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (i32.const 0) - (local.get $12) - ) - (local.set $1 - (i32.const 0) - ) - (br $label$72) - ) - ) - ) - (local.set $52 - (f64.load - (local.get $16) - ) - ) - (i32.store - (local.get $20) - (i32.const 0) - ) - (local.set $26 - (if (result i32) - (i64.lt_s - (i64.reinterpret_f64 - (local.get $52) - ) - (i64.const 0) - ) - (block (result i32) - (local.set $24 - (i32.const 1) - ) - (local.set $52 - (f64.neg - (local.get $52) - ) - ) - (i32.const 1674) - ) - (block (result i32) - (local.set $1 - (i32.and - (local.get $12) - (i32.const 1) - ) - ) - (if (result i32) - (i32.and - (local.get $12) - (i32.const 2048) - ) - (block (result i32) - (local.set $24 - (i32.const 1) - ) - (i32.const 1677) - ) - (block (result i32) - (local.set $24 - (local.get $1) - ) - (if (result i32) - (local.get $1) - (i32.const 1680) - (i32.const 1675) - ) - ) - ) - ) - ) - ) - (block $label$119 - (if - (i64.lt_u - (i64.and - (i64.reinterpret_f64 - (local.get $52) - ) - (i64.const 9218868437227405312) - ) - (i64.const 9218868437227405312) - ) - (block - (if - (local.tee $1 - (f64.ne - (local.tee $52 - (f64.mul - (call $27 - (local.get $52) - (local.get $20) - ) - (f64.const 2) - ) - ) - (f64.const 0) - ) - ) - (i32.store - (local.get $20) - (i32.add - (i32.load - (local.get $20) - ) - (i32.const -1) - ) - ) - ) - (if - (i32.eq - (local.tee $22 - (i32.or - (local.get $9) - (i32.const 32) - ) - ) - (i32.const 97) - ) - (block - (local.set $1 - (i32.add - (local.get $26) - (i32.const 9) - ) - ) - (if - (local.tee $6 - (i32.and - (local.get $9) - (i32.const 32) - ) - ) - (local.set $26 - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.or - (i32.gt_u - (local.get $5) - (i32.const 11) - ) - (i32.eqz - (local.tee $1 - (i32.sub - (i32.const 12) - (local.get $5) - ) - ) - ) - ) - ) - (block - (local.set $53 - (f64.const 8) - ) - (loop $label$125 - (local.set $53 - (f64.mul - (local.get $53) - (f64.const 16) - ) - ) - (br_if $label$125 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - (local.set $52 - (if (result f64) - (i32.eq - (i32.load8_s - (local.get $26) - ) - (i32.const 45) - ) - (f64.neg - (f64.add - (local.get $53) - (f64.sub - (f64.neg - (local.get $52) - ) - (local.get $53) - ) - ) - ) - (f64.sub - (f64.add - (local.get $52) - (local.get $53) - ) - (local.get $53) - ) - ) - ) - ) - ) - (local.set $1 - (i32.sub - (i32.const 0) - (local.tee $7 - (i32.load - (local.get $20) - ) - ) - ) - ) - (if - (i32.eq - (local.tee $1 - (call $23 - (i64.extend_i32_s - (if (result i32) - (i32.lt_s - (local.get $7) - (i32.const 0) - ) - (local.get $1) - (local.get $7) - ) - ) - (local.get $33) - ) - ) - (local.get $33) - ) - (block - (i32.store8 - (local.get $40) - (i32.const 48) - ) - (local.set $1 - (local.get $40) - ) - ) - ) - (local.set $13 - (i32.or - (local.get $24) - (i32.const 2) - ) - ) - (i32.store8 - (i32.add - (local.get $1) - (i32.const -1) - ) - (i32.add - (i32.and - (i32.shr_s - (local.get $7) - (i32.const 31) - ) - (i32.const 2) - ) - (i32.const 43) - ) - ) - (i32.store8 - (local.tee $8 - (i32.add - (local.get $1) - (i32.const -2) - ) - ) - (i32.add - (local.get $9) - (i32.const 15) - ) - ) - (local.set $9 - (i32.lt_s - (local.get $5) - (i32.const 1) - ) - ) - (local.set $14 - (i32.eqz - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - ) - (local.set $1 - (local.get $19) - ) - (loop $label$131 - (i32.store8 - (local.get $1) - (i32.or - (i32.load8_u - (i32.add - (local.tee $7 - (i32.trunc_f64_s - (local.get $52) - ) - ) - (i32.const 1641) - ) - ) - (local.get $6) - ) - ) - (local.set $52 - (f64.mul - (f64.sub - (local.get $52) - (f64.convert_i32_s - (local.get $7) - ) - ) - (f64.const 16) - ) - ) - (local.set $1 - (block $label$132 (result i32) - (if (result i32) - (i32.eq - (i32.sub - (local.tee $7 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.get $27) - ) - (i32.const 1) - ) - (block (result i32) - (drop - (br_if $label$132 - (local.get $7) - (i32.and - (local.get $14) - (i32.and - (local.get $9) - (f64.eq - (local.get $52) - (f64.const 0) - ) - ) - ) - ) - ) - (i32.store8 - (local.get $7) - (i32.const 46) - ) - (i32.add - (local.get $1) - (i32.const 2) - ) - ) - (local.get $7) - ) - ) - ) - (br_if $label$131 - (f64.ne - (local.get $52) - (f64.const 0) - ) - ) - ) - (local.set $6 - (i32.sub - (i32.add - (local.get $46) - (local.get $5) - ) - (local.tee $7 - (local.get $8) - ) - ) - ) - (local.set $9 - (i32.add - (i32.sub - (local.get $44) - (local.get $7) - ) - (local.get $1) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $5 - (i32.add - (if (result i32) - (i32.and - (i32.ne - (local.get $5) - (i32.const 0) - ) - (i32.lt_s - (i32.add - (local.get $45) - (local.get $1) - ) - (local.get $5) - ) - ) - (local.get $6) - (local.tee $6 - (local.get $9) - ) - ) - (local.get $13) - ) - ) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $26) - (local.get $13) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (local.set $1 - (i32.sub - (local.get $1) - (local.get $27) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $19) - (local.get $1) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (i32.sub - (local.get $6) - (i32.add - (local.get $1) - (local.tee $1 - (i32.sub - (local.get $28) - (local.get $7) - ) - ) - ) - ) - (i32.const 0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $8) - (local.get $1) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $5) - (local.get $10) - ) - (local.set $10 - (local.get $5) - ) - ) - (br $label$119) - ) - ) - (if - (local.get $1) - (block - (i32.store - (local.get $20) - (local.tee $6 - (i32.add - (i32.load - (local.get $20) - ) - (i32.const -28) - ) - ) - ) - (local.set $52 - (f64.mul - (local.get $52) - (f64.const 268435456) - ) - ) - ) - (local.set $6 - (i32.load - (local.get $20) - ) - ) - ) - (local.set $8 - (local.tee $7 - (if (result i32) - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (local.get $47) - (local.get $48) - ) - ) - ) - (loop $label$145 - (i32.store - (local.get $8) - (local.tee $1 - (i32.trunc_f64_s - (local.get $52) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (br_if $label$145 - (f64.ne - (local.tee $52 - (f64.mul - (f64.sub - (local.get $52) - (f64.convert_i32_u - (local.get $1) - ) - ) - (f64.const 1e9) - ) - ) - (f64.const 0) - ) - ) - ) - (if - (i32.gt_s - (local.get $6) - (i32.const 0) - ) - (block - (local.set $1 - (local.get $7) - ) - (loop $label$147 - (local.set $14 - (if (result i32) - (i32.gt_s - (local.get $6) - (i32.const 29) - ) - (i32.const 29) - (local.get $6) - ) - ) - (block $label$150 - (if - (i32.ge_u - (local.tee $6 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - (local.get $1) - ) - (block - (local.set $50 - (i64.extend_i32_u - (local.get $14) - ) - ) - (local.set $13 - (i32.const 0) - ) - (loop $label$152 - (i64.store32 - (local.get $6) - (i64.rem_u - (local.tee $51 - (i64.add - (i64.shl - (i64.extend_i32_u - (i32.load - (local.get $6) - ) - ) - (local.get $50) - ) - (i64.extend_i32_u - (local.get $13) - ) - ) - ) - (i64.const 1000000000) - ) - ) - (local.set $13 - (i32.wrap_i64 - (i64.div_u - (local.get $51) - (i64.const 1000000000) - ) - ) - ) - (br_if $label$152 - (i32.ge_u - (local.tee $6 - (i32.add - (local.get $6) - (i32.const -4) - ) - ) - (local.get $1) - ) - ) - ) - (br_if $label$150 - (i32.eqz - (local.get $13) - ) - ) - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -4) - ) - ) - (local.get $13) - ) - ) - ) - ) - (loop $label$153 - (if - (i32.gt_u - (local.get $8) - (local.get $1) - ) - (if - (i32.eqz - (i32.load - (local.tee $6 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - ) - (block - (local.set $8 - (local.get $6) - ) - (br $label$153) - ) - ) - ) - ) - (i32.store - (local.get $20) - (local.tee $6 - (i32.sub - (i32.load - (local.get $20) - ) - (local.get $14) - ) - ) - ) - (br_if $label$147 - (i32.gt_s - (local.get $6) - (i32.const 0) - ) - ) - ) - ) - (local.set $1 - (local.get $7) - ) - ) - (local.set $18 - (if (result i32) - (i32.lt_s - (local.get $5) - (i32.const 0) - ) - (i32.const 6) - (local.get $5) - ) - ) - (if - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (block - (local.set $14 - (i32.add - (i32.div_s - (i32.add - (local.get $18) - (i32.const 25) - ) - (i32.const 9) - ) - (i32.const 1) - ) - ) - (local.set $25 - (i32.eq - (local.get $22) - (i32.const 102) - ) - ) - (local.set $5 - (local.get $8) - ) - (loop $label$160 - (if - (i32.gt_s - (local.tee $13 - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (i32.const 9) - ) - (local.set $13 - (i32.const 9) - ) - ) - (block $label$162 - (if - (i32.lt_u - (local.get $1) - (local.get $5) - ) - (block - (local.set $29 - (i32.add - (i32.shl - (i32.const 1) - (local.get $13) - ) - (i32.const -1) - ) - ) - (local.set $35 - (i32.shr_u - (i32.const 1000000000) - (local.get $13) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (local.get $1) - ) - (loop $label$164 - (i32.store - (local.get $8) - (i32.add - (i32.shr_u - (local.tee $32 - (i32.load - (local.get $8) - ) - ) - (local.get $13) - ) - (local.get $6) - ) - ) - (local.set $6 - (i32.mul - (i32.and - (local.get $32) - (local.get $29) - ) - (local.get $35) - ) - ) - (br_if $label$164 - (i32.lt_u - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (local.get $5) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (local.get $8) - ) - ) - (br_if $label$162 - (i32.eqz - (local.get $6) - ) - ) - (i32.store - (local.get $5) - (local.get $6) - ) - (local.set $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - ) - (block - (local.set $8 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (local.get $8) - ) - ) - ) - ) - ) - (local.set $6 - (i32.add - (local.tee $8 - (if (result i32) - (local.get $25) - (local.get $7) - (local.get $1) - ) - ) - (i32.shl - (local.get $14) - (i32.const 2) - ) - ) - ) - (if - (i32.gt_s - (i32.shr_s - (i32.sub - (local.get $5) - (local.get $8) - ) - (i32.const 2) - ) - (local.get $14) - ) - (local.set $5 - (local.get $6) - ) - ) - (i32.store - (local.get $20) - (local.tee $6 - (i32.add - (i32.load - (local.get $20) - ) - (local.get $13) - ) - ) - ) - (br_if $label$160 - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - ) - (local.set $13 - (local.get $5) - ) - ) - ) - (local.set $13 - (local.get $8) - ) - ) - (local.set $25 - (local.get $7) - ) - (block $label$172 - (if - (i32.lt_u - (local.get $1) - (local.get $13) - ) - (block - (local.set $5 - (i32.mul - (i32.shr_s - (i32.sub - (local.get $25) - (local.get $1) - ) - (i32.const 2) - ) - (i32.const 9) - ) - ) - (br_if $label$172 - (i32.lt_u - (local.tee $6 - (i32.load - (local.get $1) - ) - ) - (i32.const 10) - ) - ) - (local.set $8 - (i32.const 10) - ) - (loop $label$174 - (local.set $5 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$174 - (i32.ge_u - (local.get $6) - (local.tee $8 - (i32.mul - (local.get $8) - (i32.const 10) - ) - ) - ) - ) - ) - ) - (local.set $5 - (i32.const 0) - ) - ) - ) - (local.set $29 - (i32.eq - (local.get $22) - (i32.const 103) - ) - ) - (local.set $35 - (i32.ne - (local.get $18) - (i32.const 0) - ) - ) - (if - (i32.lt_s - (local.tee $8 - (i32.add - (i32.sub - (local.get $18) - (if (result i32) - (i32.ne - (local.get $22) - (i32.const 102) - ) - (local.get $5) - (i32.const 0) - ) - ) - (i32.shr_s - (i32.shl - (i32.and - (local.get $35) - (local.get $29) - ) - (i32.const 31) - ) - (i32.const 31) - ) - ) - ) - (i32.add - (i32.mul - (i32.shr_s - (i32.sub - (local.get $13) - (local.get $25) - ) - (i32.const 2) - ) - (i32.const 9) - ) - (i32.const -9) - ) - ) - (block - (if - (i32.lt_s - (local.tee $8 - (i32.add - (i32.rem_s - (local.tee $14 - (i32.add - (local.get $8) - (i32.const 9216) - ) - ) - (i32.const 9) - ) - (i32.const 1) - ) - ) - (i32.const 9) - ) - (block - (local.set $6 - (i32.const 10) - ) - (loop $label$180 - (local.set $6 - (i32.mul - (local.get $6) - (i32.const 10) - ) - ) - (br_if $label$180 - (i32.ne - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 1) - ) - ) - (i32.const 9) - ) - ) - ) - ) - (local.set $6 - (i32.const 10) - ) - ) - (local.set $14 - (i32.rem_u - (local.tee $22 - (i32.load - (local.tee $8 - (i32.add - (i32.add - (local.get $7) - (i32.const 4) - ) - (i32.shl - (i32.add - (i32.div_s - (local.get $14) - (i32.const 9) - ) - (i32.const -1024) - ) - (i32.const 2) - ) - ) - ) - ) - ) - (local.get $6) - ) - ) - (block $label$182 - (if - (i32.eqz - (i32.and - (local.tee $32 - (i32.eq - (i32.add - (local.get $8) - (i32.const 4) - ) - (local.get $13) - ) - ) - (i32.eqz - (local.get $14) - ) - ) - ) - (block - (local.set $52 - (if (result f64) - (i32.lt_u - (local.get $14) - (local.tee $49 - (i32.div_s - (local.get $6) - (i32.const 2) - ) - ) - ) - (f64.const 0.5) - (if (result f64) - (i32.and - (local.get $32) - (i32.eq - (local.get $14) - (local.get $49) - ) - ) - (f64.const 1) - (f64.const 1.5) - ) - ) - ) - (local.set $53 - (if (result f64) - (i32.and - (i32.div_u - (local.get $22) - (local.get $6) - ) - (i32.const 1) - ) - (f64.const 9007199254740994) - (f64.const 9007199254740992) - ) - ) - (block $label$190 - (if - (local.get $24) - (block - (br_if $label$190 - (i32.ne - (i32.load8_s - (local.get $26) - ) - (i32.const 45) - ) - ) - (local.set $53 - (f64.neg - (local.get $53) - ) - ) - (local.set $52 - (f64.neg - (local.get $52) - ) - ) - ) - ) - ) - (i32.store - (local.get $8) - (local.tee $14 - (i32.sub - (local.get $22) - (local.get $14) - ) - ) - ) - (br_if $label$182 - (f64.eq - (f64.add - (local.get $53) - (local.get $52) - ) - (local.get $53) - ) - ) - (i32.store - (local.get $8) - (local.tee $5 - (i32.add - (local.get $14) - (local.get $6) - ) - ) - ) - (if - (i32.gt_u - (local.get $5) - (i32.const 999999999) - ) - (loop $label$193 - (i32.store - (local.get $8) - (i32.const 0) - ) - (if - (i32.lt_u - (local.tee $8 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - (local.get $1) - ) - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -4) - ) - ) - (i32.const 0) - ) - ) - (i32.store - (local.get $8) - (local.tee $5 - (i32.add - (i32.load - (local.get $8) - ) - (i32.const 1) - ) - ) - ) - (br_if $label$193 - (i32.gt_u - (local.get $5) - (i32.const 999999999) - ) - ) - ) - ) - (local.set $5 - (i32.mul - (i32.shr_s - (i32.sub - (local.get $25) - (local.get $1) - ) - (i32.const 2) - ) - (i32.const 9) - ) - ) - (br_if $label$182 - (i32.lt_u - (local.tee $14 - (i32.load - (local.get $1) - ) - ) - (i32.const 10) - ) - ) - (local.set $6 - (i32.const 10) - ) - (loop $label$195 - (local.set $5 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$195 - (i32.ge_u - (local.get $14) - (local.tee $6 - (i32.mul - (local.get $6) - (i32.const 10) - ) - ) - ) - ) - ) - ) - ) - ) - (local.set $14 - (local.get $1) - ) - (local.set $6 - (local.get $5) - ) - (if - (i32.le_u - (local.get $13) - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - ) - (local.set $8 - (local.get $13) - ) - ) - ) - (block - (local.set $14 - (local.get $1) - ) - (local.set $6 - (local.get $5) - ) - (local.set $8 - (local.get $13) - ) - ) - ) - (local.set $32 - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (loop $label$198 - (block $label$199 - (if - (i32.le_u - (local.get $8) - (local.get $14) - ) - (block - (local.set $22 - (i32.const 0) - ) - (br $label$199) - ) - ) - (if - (i32.load - (local.tee $1 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - (local.set $22 - (i32.const 1) - ) - (block - (local.set $8 - (local.get $1) - ) - (br $label$198) - ) - ) - ) - ) - (block $label$203 - (if - (local.get $29) - (block - (local.set $1 - (if (result i32) - (i32.and - (i32.gt_s - (local.tee $1 - (i32.add - (i32.xor - (i32.and - (local.get $35) - (i32.const 1) - ) - (i32.const 1) - ) - (local.get $18) - ) - ) - (local.get $6) - ) - (i32.gt_s - (local.get $6) - (i32.const -5) - ) - ) - (block (result i32) - (local.set $5 - (i32.add - (local.get $9) - (i32.const -1) - ) - ) - (i32.sub - (i32.add - (local.get $1) - (i32.const -1) - ) - (local.get $6) - ) - ) - (block (result i32) - (local.set $5 - (i32.add - (local.get $9) - (i32.const -2) - ) - ) - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - (br_if $label$203 - (local.tee $13 - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - ) - (block $label$207 - (if - (local.get $22) - (block - (if - (i32.eqz - (local.tee $18 - (i32.load - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - ) - (block - (local.set $9 - (i32.const 9) - ) - (br $label$207) - ) - ) - (if - (i32.rem_u - (local.get $18) - (i32.const 10) - ) - (block - (local.set $9 - (i32.const 0) - ) - (br $label$207) - ) - (block - (local.set $13 - (i32.const 10) - ) - (local.set $9 - (i32.const 0) - ) - ) - ) - (loop $label$212 - (local.set $9 - (i32.add - (local.get $9) - (i32.const 1) - ) - ) - (br_if $label$212 - (i32.eqz - (i32.rem_u - (local.get $18) - (local.tee $13 - (i32.mul - (local.get $13) - (i32.const 10) - ) - ) - ) - ) - ) - ) - ) - (local.set $9 - (i32.const 9) - ) - ) - ) - (local.set $18 - (i32.add - (i32.mul - (i32.shr_s - (i32.sub - (local.get $8) - (local.get $25) - ) - (i32.const 2) - ) - (i32.const 9) - ) - (i32.const -9) - ) - ) - (if - (i32.eq - (i32.or - (local.get $5) - (i32.const 32) - ) - (i32.const 102) - ) - (block - (local.set $13 - (i32.const 0) - ) - (if - (i32.ge_s - (local.get $1) - (if (result i32) - (i32.lt_s - (local.tee $9 - (i32.sub - (local.get $18) - (local.get $9) - ) - ) - (i32.const 0) - ) - (local.tee $9 - (i32.const 0) - ) - (local.get $9) - ) - ) - (local.set $1 - (local.get $9) - ) - ) - ) - (block - (local.set $13 - (i32.const 0) - ) - (if - (i32.ge_s - (local.get $1) - (if (result i32) - (i32.lt_s - (local.tee $9 - (i32.sub - (i32.add - (local.get $18) - (local.get $6) - ) - (local.get $9) - ) - ) - (i32.const 0) - ) - (local.tee $9 - (i32.const 0) - ) - (local.get $9) - ) - ) - (local.set $1 - (local.get $9) - ) - ) - ) - ) - ) - (block - (local.set $13 - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - (local.set $1 - (local.get $18) - ) - (local.set $5 - (local.get $9) - ) - ) - ) - ) - (if - (local.tee $25 - (i32.eq - (i32.or - (local.get $5) - (i32.const 32) - ) - (i32.const 102) - ) - ) - (block - (local.set $9 - (i32.const 0) - ) - (if - (i32.le_s - (local.get $6) - (i32.const 0) - ) - (local.set $6 - (i32.const 0) - ) - ) - ) - (block - (if - (i32.lt_s - (i32.sub - (local.get $28) - (local.tee $9 - (call $23 - (i64.extend_i32_s - (if (result i32) - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (local.get $32) - (local.get $6) - ) - ) - (local.get $33) - ) - ) - ) - (i32.const 2) - ) - (loop $label$229 - (i32.store8 - (local.tee $9 - (i32.add - (local.get $9) - (i32.const -1) - ) - ) - (i32.const 48) - ) - (br_if $label$229 - (i32.lt_s - (i32.sub - (local.get $28) - (local.get $9) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.store8 - (i32.add - (local.get $9) - (i32.const -1) - ) - (i32.add - (i32.and - (i32.shr_s - (local.get $6) - (i32.const 31) - ) - (i32.const 2) - ) - (i32.const 43) - ) - ) - (i32.store8 - (local.tee $6 - (i32.add - (local.get $9) - (i32.const -2) - ) - ) - (local.get $5) - ) - (local.set $9 - (local.get $6) - ) - (local.set $6 - (i32.sub - (local.get $28) - (local.get $6) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $18 - (i32.add - (i32.add - (i32.add - (i32.add - (local.get $24) - (i32.const 1) - ) - (local.get $1) - ) - (i32.ne - (local.tee $29 - (i32.or - (local.get $1) - (local.get $13) - ) - ) - (i32.const 0) - ) - ) - (local.get $6) - ) - ) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $26) - (local.get $24) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $18) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (block $label$231 - (if - (local.get $25) - (block - (local.set $6 - (local.tee $9 - (if (result i32) - (i32.gt_u - (local.get $14) - (local.get $7) - ) - (local.get $7) - (local.get $14) - ) - ) - ) - (loop $label$235 - (local.set $5 - (call $23 - (i64.extend_i32_u - (i32.load - (local.get $6) - ) - ) - (local.get $31) - ) - ) - (block $label$236 - (if - (i32.eq - (local.get $6) - (local.get $9) - ) - (block - (br_if $label$236 - (i32.ne - (local.get $5) - (local.get $31) - ) - ) - (i32.store8 - (local.get $34) - (i32.const 48) - ) - (local.set $5 - (local.get $34) - ) - ) - (block - (br_if $label$236 - (i32.le_u - (local.get $5) - (local.get $19) - ) - ) - (drop - (call $39 - (local.get $19) - (i32.const 48) - (i32.sub - (local.get $5) - (local.get $27) - ) - ) - ) - (loop $label$239 - (br_if $label$239 - (i32.gt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $5) - (i32.sub - (local.get $41) - (local.get $5) - ) - (local.get $0) - ) - ) - ) - (if - (i32.le_u - (local.tee $5 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - (local.get $7) - ) - (block - (local.set $6 - (local.get $5) - ) - (br $label$235) - ) - ) - ) - (block $label$242 - (if - (local.get $29) - (block - (br_if $label$242 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (i32.const 1709) - (i32.const 1) - (local.get $0) - ) - ) - ) - ) - ) - (if - (i32.and - (i32.gt_s - (local.get $1) - (i32.const 0) - ) - (i32.lt_u - (local.get $5) - (local.get $8) - ) - ) - (loop $label$245 - (if - (i32.gt_u - (local.tee $7 - (call $23 - (i64.extend_i32_u - (i32.load - (local.get $5) - ) - ) - (local.get $31) - ) - ) - (local.get $19) - ) - (block - (drop - (call $39 - (local.get $19) - (i32.const 48) - (i32.sub - (local.get $7) - (local.get $27) - ) - ) - ) - (loop $label$247 - (br_if $label$247 - (i32.gt_u - (local.tee $7 - (i32.add - (local.get $7) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $7) - (if (result i32) - (i32.gt_s - (local.get $1) - (i32.const 9) - ) - (i32.const 9) - (local.get $1) - ) - (local.get $0) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $1) - (i32.const -9) - ) - ) - (if - (i32.and - (i32.gt_s - (local.get $1) - (i32.const 9) - ) - (i32.lt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - (local.get $8) - ) - ) - (block - (local.set $1 - (local.get $7) - ) - (br $label$245) - ) - (local.set $1 - (local.get $7) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (i32.add - (local.get $1) - (i32.const 9) - ) - (i32.const 9) - (i32.const 0) - ) - ) - (block - (local.set $5 - (i32.add - (local.get $14) - (i32.const 4) - ) - ) - (if - (i32.eqz - (local.get $22) - ) - (local.set $8 - (local.get $5) - ) - ) - (if - (i32.gt_s - (local.get $1) - (i32.const -1) - ) - (block - (local.set $13 - (i32.eqz - (local.get $13) - ) - ) - (local.set $7 - (local.get $14) - ) - (local.set $5 - (local.get $1) - ) - (loop $label$256 - (if - (i32.eq - (local.tee $1 - (call $23 - (i64.extend_i32_u - (i32.load - (local.get $7) - ) - ) - (local.get $31) - ) - ) - (local.get $31) - ) - (block - (i32.store8 - (local.get $34) - (i32.const 48) - ) - (local.set $1 - (local.get $34) - ) - ) - ) - (block $label$258 - (if - (i32.eq - (local.get $7) - (local.get $14) - ) - (block - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (i32.const 1) - (local.get $0) - ) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (br_if $label$258 - (i32.and - (local.get $13) - (i32.lt_s - (local.get $5) - (i32.const 1) - ) - ) - ) - (br_if $label$258 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (i32.const 1709) - (i32.const 1) - (local.get $0) - ) - ) - ) - (block - (br_if $label$258 - (i32.le_u - (local.get $1) - (local.get $19) - ) - ) - (drop - (call $39 - (local.get $19) - (i32.const 48) - (i32.add - (local.get $1) - (local.get $43) - ) - ) - ) - (loop $label$262 - (br_if $label$262 - (i32.gt_u - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - ) - (local.set $6 - (i32.sub - (local.get $41) - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (if (result i32) - (i32.gt_s - (local.get $5) - (local.get $6) - ) - (local.get $6) - (local.get $5) - ) - (local.get $0) - ) - ) - ) - (br_if $label$256 - (i32.and - (i32.lt_u - (local.tee $7 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (local.get $8) - ) - (i32.gt_s - (local.tee $5 - (i32.sub - (local.get $5) - (local.get $6) - ) - ) - (i32.const -1) - ) - ) - ) - (local.set $1 - (local.get $5) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (i32.add - (local.get $1) - (i32.const 18) - ) - (i32.const 18) - (i32.const 0) - ) - (br_if $label$231 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $9) - (i32.sub - (local.get $28) - (local.get $9) - ) - (local.get $0) - ) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $18) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $18) - (local.get $10) - ) - (local.set $10 - (local.get $18) - ) - ) - ) - (block - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $8 - (i32.add - (if (result i32) - (local.tee $6 - (i32.or - (f64.ne - (local.get $52) - (local.get $52) - ) - (i32.const 0) - ) - ) - (local.tee $24 - (i32.const 0) - ) - (local.get $24) - ) - (i32.const 3) - ) - ) - (local.get $7) - ) - (if - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 32) - ) - ) - (block - (drop - (call $21 - (local.get $26) - (local.get $24) - (local.get $0) - ) - ) - (local.set $1 - (i32.load - (local.get $0) - ) - ) - ) - ) - (local.set $7 - (if (result i32) - (local.tee $5 - (i32.ne - (i32.and - (local.get $9) - (i32.const 32) - ) - (i32.const 0) - ) - ) - (i32.const 1693) - (i32.const 1697) - ) - ) - (local.set $5 - (if (result i32) - (local.get $5) - (i32.const 1701) - (i32.const 1705) - ) - ) - (if - (i32.eqz - (local.get $6) - ) - (local.set $5 - (local.get $7) - ) - ) - (if - (i32.eqz - (i32.and - (local.get $1) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $5) - (i32.const 3) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $8) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $8) - (local.get $10) - ) - (local.set $10 - (local.get $8) - ) - ) - ) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $7 - (local.get $5) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1657) - ) - (local.set $5 - (local.get $21) - ) - (br $label$70) - ) - (local.set $7 - (i32.and - (local.get $9) - (i32.const 32) - ) - ) - (local.set $7 - (if (result i32) - (i64.eq - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (block (result i32) - (local.set $50 - (i64.const 0) - ) - (local.get $21) - ) - (block (result i32) - (local.set $1 - (local.get $21) - ) - (loop $label$280 - (i32.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i32.or - (i32.load8_u - (i32.add - (i32.and - (i32.wrap_i64 - (local.get $50) - ) - (i32.const 15) - ) - (i32.const 1641) - ) - ) - (local.get $7) - ) - ) - (br_if $label$280 - (i64.ne - (local.tee $50 - (i64.shr_u - (local.get $50) - (i64.const 4) - ) - ) - (i64.const 0) - ) - ) - ) - (local.set $50 - (i64.load - (local.get $16) - ) - ) - (local.get $1) - ) - ) - ) - (local.set $8 - (i32.add - (i32.shr_s - (local.get $9) - (i32.const 4) - ) - (i32.const 1657) - ) - ) - (if - (local.tee $1 - (i32.or - (i32.eqz - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - (i64.eq - (local.get $50) - (i64.const 0) - ) - ) - ) - (local.set $8 - (i32.const 1657) - ) - ) - (local.set $6 - (if (result i32) - (local.get $1) - (i32.const 0) - (i32.const 2) - ) - ) - (br $label$71) - ) - (local.set $7 - (call $23 - (local.get $50) - (local.get $21) - ) - ) - (br $label$71) - ) - (local.set $14 - (i32.eqz - (local.tee $13 - (call $17 - (local.get $1) - (i32.const 0) - (local.get $5) - ) - ) - ) - ) - (local.set $8 - (i32.sub - (local.get $13) - (local.get $1) - ) - ) - (local.set $9 - (i32.add - (local.get $1) - (local.get $5) - ) - ) - (local.set $12 - (local.get $7) - ) - (local.set $7 - (if (result i32) - (local.get $14) - (local.get $5) - (local.get $8) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1657) - ) - (local.set $5 - (if (result i32) - (local.get $14) - (local.get $9) - (local.get $13) - ) - ) - (br $label$70) - ) - (local.set $1 - (i32.const 0) - ) - (local.set $5 - (i32.const 0) - ) - (local.set $8 - (local.get $7) - ) - (loop $label$288 - (block $label$289 - (br_if $label$289 - (i32.eqz - (local.tee $9 - (i32.load - (local.get $8) - ) - ) - ) - ) - (br_if $label$289 - (i32.or - (i32.lt_s - (local.tee $5 - (call $26 - (local.get $36) - (local.get $9) - ) - ) - (i32.const 0) - ) - (i32.gt_u - (local.get $5) - (i32.sub - (local.get $6) - (local.get $1) - ) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (br_if $label$288 - (i32.gt_u - (local.get $6) - (local.tee $1 - (i32.add - (local.get $5) - (local.get $1) - ) - ) - ) - ) - ) - ) - (if - (i32.lt_s - (local.get $5) - (i32.const 0) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $1) - (local.get $12) - ) - (if - (local.get $1) - (block - (local.set $5 - (i32.const 0) - ) - (loop $label$292 - (br_if $label$72 - (i32.eqz - (local.tee $8 - (i32.load - (local.get $7) - ) - ) - ) - ) - (br_if $label$72 - (i32.gt_s - (local.tee $5 - (i32.add - (local.tee $8 - (call $26 - (local.get $36) - (local.get $8) - ) - ) - (local.get $5) - ) - ) - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $36) - (local.get $8) - (local.get $0) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (br_if $label$292 - (i32.lt_u - (local.get $5) - (local.get $1) - ) - ) - (br $label$72) - ) - ) - (block - (local.set $1 - (i32.const 0) - ) - (br $label$72) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $1) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.le_s - (local.get $10) - (local.get $1) - ) - (local.set $10 - (local.get $1) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $1 - (i32.and - (local.get $12) - (i32.const -65537) - ) - ) - (if - (i32.gt_s - (local.get $5) - (i32.const -1) - ) - (local.set $12 - (local.get $1) - ) - ) - (local.set $5 - (if (result i32) - (i32.or - (local.get $5) - (local.tee $9 - (i64.ne - (i64.load - (local.get $16) - ) - (i64.const 0) - ) - ) - ) - (block (result i32) - (local.set $1 - (local.get $7) - ) - (if - (i32.gt_s - (local.get $5) - (local.tee $7 - (i32.add - (i32.xor - (i32.and - (local.get $9) - (i32.const 1) - ) - (i32.const 1) - ) - (i32.sub - (local.get $38) - (local.get $7) - ) - ) - ) - ) - (local.set $7 - (local.get $5) - ) - ) - (local.get $21) - ) - (block (result i32) - (local.set $1 - (local.get $21) - ) - (local.set $7 - (i32.const 0) - ) - (local.get $21) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (if (result i32) - (i32.lt_s - (local.get $10) - (local.tee $5 - (i32.add - (if (result i32) - (i32.lt_s - (local.get $7) - (local.tee $9 - (i32.sub - (local.get $5) - (local.get $1) - ) - ) - ) - (local.tee $7 - (local.get $9) - ) - (local.get $7) - ) - (local.get $6) - ) - ) - ) - (local.tee $10 - (local.get $5) - ) - (local.get $10) - ) - (local.get $5) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $8) - (local.get $6) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $7) - (local.get $9) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (local.get $9) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - ) - (br $label$2) - ) - (if - (i32.eqz - (local.get $0) - ) - (if - (local.get $17) - (block - (local.set $0 - (i32.const 1) - ) - (loop $label$308 - (if - (local.tee $1 - (i32.load - (i32.add - (local.get $4) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - ) - ) - (block - (call $22 - (i32.add - (local.get $3) - (i32.shl - (local.get $0) - (i32.const 3) - ) - ) - (local.get $1) - (local.get $2) - ) - (br_if $label$308 - (i32.lt_s - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.const 10) - ) - ) - (local.set $15 - (i32.const 1) - ) - (br $label$2) - ) - ) - ) - (loop $label$310 - (if - (i32.load - (i32.add - (local.get $4) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$2) - ) - ) - (br_if $label$310 - (i32.lt_s - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.const 10) - ) - ) - (local.set $15 - (i32.const 1) - ) - ) - ) - (local.set $15 - (i32.const 0) - ) - ) - ) - ) - (global.set $global$1 - (local.get $23) - ) - (local.get $15) + (func $15 (;28;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $8 + global.get $global$1 + i32.const 48 + i32.add + global.set $global$1 + local.get $8 + i32.const 16 + i32.add + local.set $9 + local.get $8 + local.set $10 + local.get $8 + i32.const 32 + i32.add + local.tee $3 + local.get $0 + i32.const 28 + i32.add + local.tee $6 + i32.load + local.tee $4 + i32.store + local.get $3 + local.get $0 + i32.const 20 + i32.add + local.tee $11 + i32.load + local.get $4 + i32.sub + local.tee $5 + i32.store offset=4 + local.get $3 + local.get $1 + i32.store offset=8 + local.get $3 + local.get $2 + i32.store offset=12 + local.get $0 + i32.const 60 + i32.add + local.set $13 + local.get $0 + i32.const 44 + i32.add + local.set $14 + local.get $3 + local.set $1 + i32.const 2 + local.set $4 + local.get $5 + local.get $2 + i32.add + local.set $12 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + loop $label$5 ;; label = @5 + i32.const 3604 + i32.load + if ;; label = @6 + block ;; label = @7 + i32.const 1 + local.get $0 + call $fimport$9 + local.get $10 + local.get $13 + i32.load + i32.store + local.get $10 + local.get $1 + i32.store offset=4 + local.get $10 + local.get $4 + i32.store offset=8 + i32.const 146 + local.get $10 + call $fimport$15 + call $11 + local.set $3 + i32.const 0 + call $fimport$7 + end + else + block ;; label = @7 + local.get $9 + local.get $13 + i32.load + i32.store + local.get $9 + local.get $1 + i32.store offset=4 + local.get $9 + local.get $4 + i32.store offset=8 + i32.const 146 + local.get $9 + call $fimport$15 + call $11 + local.set $3 + end + end + local.get $12 + local.get $3 + i32.eq + br_if 1 (;@4;) + local.get $3 + i32.const 0 + i32.lt_s + br_if 2 (;@3;) + local.get $3 + local.get $1 + i32.load offset=4 + local.tee $5 + i32.gt_u + if (result i32) ;; label = @6 + block (result i32) ;; label = @7 + local.get $6 + local.get $14 + i32.load + local.tee $7 + i32.store + local.get $11 + local.get $7 + i32.store + local.get $1 + i32.load offset=12 + local.set $7 + local.get $1 + i32.const 8 + i32.add + local.set $1 + local.get $4 + i32.const -1 + i32.add + local.set $4 + local.get $3 + local.get $5 + i32.sub + end + else + local.get $4 + i32.const 2 + i32.eq + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + local.get $6 + local.get $6 + i32.load + local.get $3 + i32.add + i32.store + local.get $5 + local.set $7 + i32.const 2 + local.set $4 + local.get $3 + end + else + block (result i32) ;; label = @8 + local.get $5 + local.set $7 + local.get $3 + end + end + end + local.set $5 + local.get $1 + local.get $1 + i32.load + local.get $5 + i32.add + i32.store + local.get $1 + local.get $7 + local.get $5 + i32.sub + i32.store offset=4 + local.get $12 + local.get $3 + i32.sub + local.set $12 + br 0 (;@5;) + end + end + local.get $0 + local.get $14 + i32.load + local.tee $1 + local.get $0 + i32.load offset=48 + i32.add + i32.store offset=16 + local.get $6 + local.get $1 + i32.store + local.get $11 + local.get $1 + i32.store + br 1 (;@2;) + end + local.get $0 + i32.const 0 + i32.store offset=16 + local.get $6 + i32.const 0 + i32.store + local.get $11 + i32.const 0 + i32.store + local.get $0 + local.get $0 + i32.load + i32.const 32 + i32.or + i32.store + local.get $4 + i32.const 2 + i32.eq + if (result i32) ;; label = @3 + i32.const 0 + else + local.get $2 + local.get $1 + i32.load offset=4 + i32.sub + end + local.set $2 + end + local.get $8 + global.set $global$1 + local.get $2 + end ) - ) - (func $20 (; 33 ;) (type $1) (param $0 i32) (result i32) - (i32.const 0) - ) - (func $21 (; 34 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (block $label$1 (result i32) - (block $label$2 - (block $label$3 - (br_if $label$3 - (local.tee $3 - (i32.load - (local.tee $4 - (i32.add - (local.get $2) - (i32.const 16) - ) - ) - ) - ) - ) - (if - (call $30 - (local.get $2) - ) - (local.set $3 - (i32.const 0) - ) - (block - (local.set $3 - (i32.load - (local.get $4) - ) - ) - (br $label$3) - ) - ) - (br $label$2) - ) - (if - (i32.lt_u - (i32.sub - (local.get $3) - (local.tee $4 - (i32.load - (local.tee $5 - (i32.add - (local.get $2) - (i32.const 20) - ) - ) - ) - ) - ) - (local.get $1) - ) - (block - (local.set $3 - (call_indirect (type $0) - (local.get $2) - (local.get $0) - (local.get $1) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $2) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - ) - (br $label$2) - ) - ) - (local.set $2 - (block $label$7 (result i32) - (if (result i32) - (i32.gt_s - (i32.load8_s offset=75 - (local.get $2) - ) - (i32.const -1) - ) - (block (result i32) - (local.set $3 - (local.get $1) - ) - (loop $label$9 - (drop - (br_if $label$7 - (i32.const 0) - (i32.eqz - (local.get $3) - ) - ) - ) - (if - (i32.ne - (i32.load8_s - (i32.add - (local.get $0) - (local.tee $6 - (i32.add - (local.get $3) - (i32.const -1) - ) - ) - ) - ) - (i32.const 10) - ) - (block - (local.set $3 - (local.get $6) - ) - (br $label$9) - ) - ) - ) - (br_if $label$2 - (i32.lt_u - (call_indirect (type $0) - (local.get $2) - (local.get $0) - (local.get $3) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $2) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - (local.get $3) - ) - ) - (local.set $4 - (i32.load - (local.get $5) - ) - ) - (local.set $1 - (i32.sub - (local.get $1) - (local.get $3) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (local.get $3) - ) - ) - (local.get $3) - ) - (i32.const 0) - ) - ) - ) - (drop - (call $40 - (local.get $4) - (local.get $0) - (local.get $1) - ) - ) - (i32.store - (local.get $5) - (i32.add - (i32.load - (local.get $5) - ) - (local.get $1) - ) - ) - (local.set $3 - (i32.add - (local.get $2) - (local.get $1) - ) - ) - ) - (local.get $3) + (func $16 (;29;) (type $2) (param $0 i32) + local.get $0 + i32.load offset=68 + i32.eqz + if ;; label = @1 + local.get $0 + call $13 + end ) - ) - (func $22 (; 35 ;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i64) - (local $5 f64) - (block $label$1 - (if - (i32.le_u - (local.get $1) - (i32.const 20) - ) - (block $label$3 - (block $label$4 - (block $label$5 - (block $label$6 - (block $label$7 - (block $label$8 - (block $label$9 - (block $label$10 - (block $label$11 - (block $label$12 - (block $label$13 - (br_table $label$13 $label$12 $label$11 $label$10 $label$9 $label$8 $label$7 $label$6 $label$5 $label$4 $label$3 - (i32.sub - (local.get $1) - (i32.const 9) - ) - ) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i32.store - (local.get $0) - (local.get $3) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (local.get $3) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (local.get $3) - ) - ) - (br $label$1) - ) - (local.set $4 - (i64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (i64.store - (local.get $0) - (local.get $4) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (i32.shr_s - (i32.shl - (i32.and - (local.get $3) - (i32.const 65535) - ) - (i32.const 16) - ) - (i32.const 16) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (i32.and - (local.get $3) - (i32.const 65535) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (i32.shr_s - (i32.shl - (i32.and - (local.get $3) - (i32.const 255) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (i32.and - (local.get $3) - (i32.const 255) - ) - ) - ) - (br $label$1) - ) - (local.set $5 - (f64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (f64.store - (local.get $0) - (local.get $5) - ) - (br $label$1) - ) - (local.set $5 - (f64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (f64.store - (local.get $0) - (local.get $5) - ) - ) - ) + (func $17 (;30;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + local.get $1 + i32.const 255 + i32.and + local.set $5 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + local.get $2 + i32.const 0 + i32.ne + local.tee $4 + local.get $0 + i32.const 3 + i32.and + i32.const 0 + i32.ne + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $1 + i32.const 255 + i32.and + local.set $4 + local.get $2 + local.set $3 + local.get $0 + local.set $2 + loop $label$6 ;; label = @7 + local.get $2 + i32.load8_s + local.get $4 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $3 + local.set $0 + br 6 (;@3;) + end + end + local.get $3 + i32.const -1 + i32.add + local.tee $3 + i32.const 0 + i32.ne + local.tee $0 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 3 + i32.and + i32.const 0 + i32.ne + i32.and + br_if 0 (;@7;) + br 3 (;@4;) + end + end + else + block ;; label = @6 + local.get $2 + local.set $3 + local.get $0 + local.set $2 + local.get $4 + local.set $0 + end + end + end + local.get $0 + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.set $0 + br 2 (;@3;) + end + else + i32.const 0 + local.set $0 + end + br 1 (;@2;) + end + local.get $2 + i32.load8_s + local.get $1 + i32.const 255 + i32.and + local.tee $1 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.ne + if ;; label = @3 + block ;; label = @4 + local.get $5 + i32.const 16843009 + i32.mul + local.set $3 + block $label$12 ;; label = @5 + block $label$13 ;; label = @6 + local.get $0 + i32.const 3 + i32.le_u + br_if 0 (;@6;) + loop $label$14 ;; label = @7 + local.get $2 + i32.load + local.get $3 + i32.xor + local.tee $4 + i32.const -2139062144 + i32.and + i32.const -2139062144 + i32.xor + local.get $4 + i32.const -16843009 + i32.add + i32.and + i32.eqz + if ;; label = @8 + block ;; label = @9 + local.get $2 + i32.const 4 + i32.add + local.set $2 + local.get $0 + i32.const -4 + i32.add + local.tee $0 + i32.const 3 + i32.gt_u + br_if 2 (;@7;) + br 3 (;@6;) + end + end + end + br 1 (;@5;) + end + local.get $0 + i32.eqz + if ;; label = @6 + block ;; label = @7 + i32.const 0 + local.set $0 + br 5 (;@2;) + end + end + end + loop $label$17 ;; label = @5 + local.get $2 + i32.load8_s + local.get $1 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eq + br_if 3 (;@2;) + local.get $2 + i32.const 1 + i32.add + local.set $2 + local.get $0 + i32.const -1 + i32.add + local.tee $0 + br_if 0 (;@5;) + i32.const 0 + local.set $0 + end + end + end + end + local.get $0 + if (result i32) ;; label = @2 + local.get $2 + else + i32.const 0 + end + end ) - ) - (func $23 (; 36 ;) (type $9) (param $0 i64) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i64) - (block $label$1 (result i32) - (local.set $2 - (i32.wrap_i64 - (local.get $0) - ) - ) - (if - (i64.gt_u - (local.get $0) - (i64.const 4294967295) - ) - (block - (loop $label$3 - (i64.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i64.or - (i64.rem_u - (local.get $0) - (i64.const 10) - ) - (i64.const 48) - ) - ) - (local.set $4 - (i64.div_u - (local.get $0) - (i64.const 10) - ) - ) - (if - (i64.gt_u - (local.get $0) - (i64.const 42949672959) - ) - (block - (local.set $0 - (local.get $4) - ) - (br $label$3) - ) - ) - ) - (local.set $2 - (i32.wrap_i64 - (local.get $4) - ) - ) - ) - ) - (if - (local.get $2) - (loop $label$6 - (i32.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i32.or - (i32.rem_u - (local.get $2) - (i32.const 10) - ) - (i32.const 48) - ) - ) - (local.set $3 - (i32.div_u - (local.get $2) - (i32.const 10) - ) - ) - (if - (i32.ge_u - (local.get $2) - (i32.const 10) - ) - (block - (local.set $2 - (local.get $3) - ) - (br $label$6) - ) - ) - ) - ) - (local.get $1) + (func $18 (;31;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 224 + i32.add + global.set $global$1 + local.get $4 + i32.const 136 + i32.add + local.set $5 + local.get $4 + i32.const 80 + i32.add + local.tee $3 + i64.const 0 + i64.store align=4 + local.get $3 + i64.const 0 + i64.store offset=8 align=4 + local.get $3 + i64.const 0 + i64.store offset=16 align=4 + local.get $3 + i64.const 0 + i64.store offset=24 align=4 + local.get $3 + i64.const 0 + i64.store offset=32 align=4 + local.get $4 + i32.const 120 + i32.add + local.tee $6 + local.get $2 + i32.load + i32.store + i32.const 0 + local.get $1 + local.get $6 + local.get $4 + local.tee $2 + local.get $3 + call $19 + i32.const 0 + i32.lt_s + if ;; label = @2 + i32.const -1 + local.set $1 + else + block ;; label = @3 + local.get $0 + i32.load offset=76 + i32.const -1 + i32.gt_s + if (result i32) ;; label = @4 + local.get $0 + call $20 + else + i32.const 0 + end + local.set $12 + local.get $0 + i32.load + local.set $7 + local.get $0 + i32.load8_s offset=74 + i32.const 1 + i32.lt_s + if ;; label = @4 + local.get $0 + local.get $7 + i32.const -33 + i32.and + i32.store + end + local.get $0 + i32.const 48 + i32.add + local.tee $8 + i32.load + if ;; label = @4 + local.get $0 + local.get $1 + local.get $6 + local.get $2 + local.get $3 + call $19 + local.set $1 + else + block ;; label = @5 + local.get $0 + i32.const 44 + i32.add + local.tee $9 + i32.load + local.set $10 + local.get $9 + local.get $5 + i32.store + local.get $0 + i32.const 28 + i32.add + local.tee $13 + local.get $5 + i32.store + local.get $0 + i32.const 20 + i32.add + local.tee $11 + local.get $5 + i32.store + local.get $8 + i32.const 80 + i32.store + local.get $0 + i32.const 16 + i32.add + local.tee $14 + local.get $5 + i32.const 80 + i32.add + i32.store + local.get $0 + local.get $1 + local.get $6 + local.get $2 + local.get $3 + call $19 + local.set $1 + local.get $10 + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 0 + i32.const 0 + local.get $0 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + drop + local.get $11 + i32.load + i32.eqz + if ;; label = @8 + i32.const -1 + local.set $1 + end + local.get $9 + local.get $10 + i32.store + local.get $8 + i32.const 0 + i32.store + local.get $14 + i32.const 0 + i32.store + local.get $13 + i32.const 0 + i32.store + local.get $11 + i32.const 0 + i32.store + end + end + end + end + local.get $0 + local.get $0 + i32.load + local.tee $2 + local.get $7 + i32.const 32 + i32.and + i32.or + i32.store + local.get $12 + if ;; label = @4 + local.get $0 + call $13 + end + local.get $2 + i32.const 32 + i32.and + if ;; label = @4 + i32.const -1 + local.set $1 + end + end + end + local.get $4 + global.set $global$1 + local.get $1 + end ) - ) - (func $24 (; 37 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.const 0) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (loop $label$5 - (br_if $label$4 - (i32.eq - (i32.load8_u - (i32.add - (local.get $1) - (i32.const 1711) - ) - ) - (local.get $0) - ) - ) - (br_if $label$5 - (i32.ne - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (i32.const 87) - ) - ) - (local.set $1 - (i32.const 87) - ) - (local.set $0 - (i32.const 1799) - ) - (br $label$3) - ) - ) - (if - (local.get $1) - (block - (local.set $0 - (i32.const 1799) - ) - (br $label$3) - ) - (local.set $0 - (i32.const 1799) - ) - ) - (br $label$2) - ) - (loop $label$8 - (local.set $2 - (local.get $0) - ) - (loop $label$9 - (local.set $0 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (if - (i32.load8_s - (local.get $2) - ) - (block - (local.set $2 - (local.get $0) - ) - (br $label$9) - ) - ) - ) - (br_if $label$8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - ) - (local.get $0) + (func $19 (;32;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) + (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) (local $22 i32) (local $23 i32) (local $24 i32) (local $25 i32) (local $26 i32) (local $27 i32) (local $28 i32) (local $29 i32) (local $30 i32) (local $31 i32) (local $32 i32) (local $33 i32) (local $34 i32) (local $35 i32) (local $36 i32) (local $37 i32) (local $38 i32) (local $39 i32) (local $40 i32) (local $41 i32) (local $42 i32) (local $43 i32) (local $44 i32) (local $45 i32) (local $46 i32) (local $47 i32) (local $48 i32) (local $49 i32) (local $50 i64) (local $51 i64) (local $52 f64) (local $53 f64) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $23 + global.get $global$1 + i32.const 624 + i32.add + global.set $global$1 + local.get $23 + i32.const 16 + i32.add + local.set $20 + local.get $23 + local.set $16 + local.get $23 + i32.const 528 + i32.add + local.set $36 + local.get $0 + i32.const 0 + i32.ne + local.set $30 + local.get $23 + i32.const 536 + i32.add + local.tee $17 + i32.const 40 + i32.add + local.tee $21 + local.set $38 + local.get $17 + i32.const 39 + i32.add + local.set $39 + local.get $23 + i32.const 8 + i32.add + local.tee $37 + i32.const 4 + i32.add + local.set $42 + i32.const 0 + local.get $23 + i32.const 588 + i32.add + local.tee $19 + local.tee $27 + i32.sub + local.set $43 + local.get $23 + i32.const 576 + i32.add + local.tee $17 + i32.const 12 + i32.add + local.set $33 + local.get $17 + i32.const 11 + i32.add + local.set $40 + local.get $33 + local.tee $28 + local.get $27 + i32.sub + local.set $44 + i32.const -2 + local.get $27 + i32.sub + local.set $45 + local.get $28 + i32.const 2 + i32.add + local.set $46 + local.get $23 + i32.const 24 + i32.add + local.tee $47 + i32.const 288 + i32.add + local.set $48 + local.get $19 + i32.const 9 + i32.add + local.tee $31 + local.set $41 + local.get $19 + i32.const 8 + i32.add + local.set $34 + i32.const 0 + local.set $15 + i32.const 0 + local.set $10 + i32.const 0 + local.set $17 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + loop $label$4 ;; label = @4 + block $label$5 ;; label = @5 + local.get $15 + i32.const -1 + i32.gt_s + if ;; label = @6 + local.get $10 + i32.const 2147483647 + local.get $15 + i32.sub + i32.gt_s + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + call $12 + i32.const 75 + i32.store + i32.const -1 + end + else + local.get $10 + local.get $15 + i32.add + end + local.set $15 + end + local.get $1 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eqz + br_if 2 (;@3;) + local.get $1 + local.set $11 + block $label$9 ;; label = @6 + block $label$10 ;; label = @7 + loop $label$11 ;; label = @8 + block $label$12 ;; label = @9 + block $label$13 ;; label = @10 + block $label$14 ;; label = @11 + block $label$15 ;; label = @12 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 0 + i32.sub + br_table 1 (;@11;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 0 (;@12;) 2 (;@10;) + end + local.get $11 + local.set $5 + br 4 (;@7;) + end + local.get $11 + local.set $5 + br 1 (;@9;) + end + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.set $5 + br 1 (;@8;) + end + end + br 1 (;@6;) + end + loop $label$16 ;; label = @7 + local.get $5 + i32.load8_s offset=1 + i32.const 37 + i32.ne + br_if 1 (;@6;) + local.get $11 + i32.const 1 + i32.add + local.set $11 + local.get $5 + i32.const 2 + i32.add + local.tee $5 + i32.load8_s + i32.const 37 + i32.eq + br_if 0 (;@7;) + end + end + local.get $11 + local.get $1 + i32.sub + local.set $10 + local.get $30 + if ;; label = @6 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @7 + local.get $1 + local.get $10 + local.get $0 + call $21 + drop + end + end + local.get $10 + if ;; label = @6 + block ;; label = @7 + local.get $5 + local.set $1 + br 3 (;@4;) + end + end + local.get $5 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $10 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $9 + i32.const 10 + i32.lt_u + if (result i32) ;; label = @6 + block (result i32) ;; label = @7 + local.get $5 + i32.const 3 + i32.add + local.set $10 + local.get $5 + i32.load8_s offset=2 + i32.const 36 + i32.eq + local.tee $12 + if ;; label = @8 + local.get $10 + local.set $11 + end + local.get $12 + if ;; label = @8 + i32.const 1 + local.set $17 + end + local.get $11 + i32.load8_s + local.set $5 + local.get $12 + i32.eqz + if ;; label = @8 + i32.const -1 + local.set $9 + end + local.get $17 + end + else + block (result i32) ;; label = @7 + local.get $10 + local.set $5 + i32.const -1 + local.set $9 + local.get $17 + end + end + local.set $10 + block $label$25 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + local.tee $12 + i32.const 32 + i32.lt_u + if ;; label = @7 + block ;; label = @8 + i32.const 0 + local.set $17 + loop $label$27 ;; label = @9 + i32.const 1 + local.get $12 + i32.shl + i32.const 75913 + i32.and + i32.eqz + br_if 3 (;@6;) + i32.const 1 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + i32.shl + local.get $17 + i32.or + local.set $17 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + local.tee $12 + i32.const 32 + i32.lt_u + br_if 0 (;@9;) + end + end + else + i32.const 0 + local.set $17 + end + end + block $label$29 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 42 + i32.eq + if ;; label = @7 + block ;; label = @8 + block $label$31 (result i32) ;; label = @9 + block $label$32 ;; label = @10 + local.get $11 + i32.const 1 + i32.add + local.tee $7 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $12 + i32.const 10 + i32.ge_u + br_if 0 (;@10;) + local.get $11 + i32.load8_s offset=2 + i32.const 36 + i32.ne + br_if 0 (;@10;) + local.get $4 + local.get $12 + i32.const 2 + i32.shl + i32.add + i32.const 10 + i32.store + i32.const 1 + local.set $8 + local.get $3 + local.get $7 + i32.load8_s + i32.const -48 + i32.add + i32.const 3 + i32.shl + i32.add + i64.load + i32.wrap_i64 + local.set $10 + local.get $11 + i32.const 3 + i32.add + br 1 (;@9;) + end + local.get $10 + if ;; label = @10 + block ;; label = @11 + i32.const -1 + local.set $15 + br 6 (;@5;) + end + end + local.get $30 + i32.eqz + if ;; label = @10 + block ;; label = @11 + local.get $17 + local.set $12 + i32.const 0 + local.set $17 + local.get $7 + local.set $11 + i32.const 0 + local.set $10 + br 5 (;@6;) + end + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $11 + i32.load + local.set $10 + local.get $2 + local.get $11 + i32.const 4 + i32.add + i32.store + i32.const 0 + local.set $8 + local.get $7 + end + local.set $11 + local.get $17 + i32.const 8192 + i32.or + local.set $12 + i32.const 0 + local.get $10 + i32.sub + local.set $7 + local.get $11 + i32.load8_s + local.set $5 + local.get $10 + i32.const 0 + i32.lt_s + local.tee $6 + i32.eqz + if ;; label = @9 + local.get $17 + local.set $12 + end + local.get $8 + local.set $17 + local.get $6 + if ;; label = @9 + local.get $7 + local.set $10 + end + end + else + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $12 + i32.const 10 + i32.lt_u + if ;; label = @8 + block ;; label = @9 + i32.const 0 + local.set $7 + local.get $12 + local.set $5 + loop $label$39 ;; label = @10 + local.get $7 + i32.const 10 + i32.mul + local.get $5 + i32.add + local.set $7 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $12 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + br_if 0 (;@10;) + end + local.get $7 + i32.const 0 + i32.lt_s + if ;; label = @10 + block ;; label = @11 + i32.const -1 + local.set $15 + br 6 (;@5;) + end + else + block ;; label = @11 + local.get $12 + local.set $5 + local.get $17 + local.set $12 + local.get $10 + local.set $17 + local.get $7 + local.set $10 + end + end + end + else + block ;; label = @9 + local.get $17 + local.set $12 + local.get $10 + local.set $17 + i32.const 0 + local.set $10 + end + end + end + end + block $label$43 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 46 + i32.eq + if ;; label = @7 + block ;; label = @8 + local.get $11 + i32.const 1 + i32.add + local.tee $7 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 42 + i32.ne + if ;; label = @9 + block ;; label = @10 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + if ;; label = @11 + block ;; label = @12 + local.get $7 + local.set $11 + i32.const 0 + local.set $7 + end + else + block ;; label = @12 + i32.const 0 + local.set $5 + local.get $7 + local.set $11 + br 6 (;@6;) + end + end + loop $label$48 ;; label = @11 + local.get $7 + i32.const 10 + i32.mul + local.get $5 + i32.add + local.set $5 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + i32.const -48 + i32.add + local.tee $8 + i32.const 10 + i32.ge_u + br_if 5 (;@6;) + local.get $5 + local.set $7 + local.get $8 + local.set $5 + br 0 (;@11;) + end + end + end + local.get $11 + i32.const 2 + i32.add + local.tee $7 + i32.load8_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + if ;; label = @9 + local.get $11 + i32.load8_s offset=3 + i32.const 36 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.get $5 + i32.const 2 + i32.shl + i32.add + i32.const 10 + i32.store + local.get $3 + local.get $7 + i32.load8_s + i32.const -48 + i32.add + i32.const 3 + i32.shl + i32.add + i64.load + i32.wrap_i64 + local.set $5 + local.get $11 + i32.const 4 + i32.add + local.set $11 + br 5 (;@6;) + end + end + end + local.get $17 + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + end + local.get $30 + if (result i32) ;; label = @9 + block (result i32) ;; label = @10 + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $11 + i32.load + local.set $5 + local.get $2 + local.get $11 + i32.const 4 + i32.add + i32.store + local.get $7 + end + else + block (result i32) ;; label = @10 + i32.const 0 + local.set $5 + local.get $7 + end + end + local.set $11 + end + else + i32.const -1 + local.set $5 + end + end + local.get $11 + local.set $7 + i32.const 0 + local.set $8 + loop $label$55 ;; label = @6 + local.get $7 + i32.load8_s + i32.const -65 + i32.add + local.tee $6 + i32.const 57 + i32.gt_u + if ;; label = @7 + block ;; label = @8 + i32.const -1 + local.set $15 + br 3 (;@5;) + end + end + local.get $7 + i32.const 1 + i32.add + local.set $11 + local.get $8 + i32.const 58 + i32.mul + i32.const 1177 + i32.add + local.get $6 + i32.add + i32.load8_s + local.tee $13 + i32.const 255 + i32.and + local.tee $6 + i32.const -1 + i32.add + i32.const 8 + i32.lt_u + if ;; label = @7 + block ;; label = @8 + local.get $11 + local.set $7 + local.get $6 + local.set $8 + br 2 (;@6;) + end + end + end + local.get $13 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eqz + if ;; label = @6 + block ;; label = @7 + i32.const -1 + local.set $15 + br 2 (;@5;) + end + end + local.get $9 + i32.const -1 + i32.gt_s + local.set $14 + block $label$59 ;; label = @6 + block $label$60 ;; label = @7 + local.get $13 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 19 + i32.eq + if ;; label = @8 + local.get $14 + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + else + br 2 (;@7;) + end + else + block ;; label = @9 + local.get $14 + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.get $9 + i32.const 2 + i32.shl + i32.add + local.get $6 + i32.store + local.get $16 + local.get $3 + local.get $9 + i32.const 3 + i32.shl + i32.add + i64.load + i64.store + br 4 (;@7;) + end + end + local.get $30 + i32.eqz + if ;; label = @10 + block ;; label = @11 + i32.const 0 + local.set $15 + br 6 (;@5;) + end + end + local.get $16 + local.get $6 + local.get $2 + call $22 + end + end + br 1 (;@6;) + end + local.get $30 + i32.eqz + if ;; label = @7 + block ;; label = @8 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 4 (;@4;) + end + end + end + local.get $7 + i32.load8_s + local.tee $7 + i32.const -33 + i32.and + local.set $9 + local.get $8 + i32.const 0 + i32.ne + local.get $7 + i32.const 15 + i32.and + i32.const 3 + i32.eq + i32.and + i32.eqz + if ;; label = @6 + local.get $7 + local.set $9 + end + local.get $12 + i32.const -65537 + i32.and + local.set $7 + local.get $12 + i32.const 8192 + i32.and + if ;; label = @6 + local.get $7 + local.set $12 + end + block $label$70 ;; label = @6 + block $label$71 ;; label = @7 + block $label$72 ;; label = @8 + block $label$73 ;; label = @9 + block $label$74 ;; label = @10 + block $label$75 ;; label = @11 + block $label$76 ;; label = @12 + block $label$77 ;; label = @13 + block $label$78 ;; label = @14 + block $label$79 ;; label = @15 + block $label$80 ;; label = @16 + block $label$81 ;; label = @17 + block $label$82 ;; label = @18 + block $label$83 ;; label = @19 + block $label$84 ;; label = @20 + block $label$85 ;; label = @21 + block $label$86 ;; label = @22 + block $label$87 ;; label = @23 + block $label$88 ;; label = @24 + block $label$89 ;; label = @25 + local.get $9 + i32.const 65 + i32.sub + br_table 11 (;@14;) 12 (;@13;) 9 (;@16;) 12 (;@13;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 10 (;@15;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 11 (;@14;) 12 (;@13;) 6 (;@19;) 4 (;@21;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 4 (;@21;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 7 (;@18;) 0 (;@25;) 3 (;@22;) 1 (;@24;) 12 (;@13;) 12 (;@13;) 8 (;@17;) 12 (;@13;) 5 (;@20;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) + end + block $label$90 ;; label = @25 + block $label$91 ;; label = @26 + block $label$92 ;; label = @27 + block $label$93 ;; label = @28 + block $label$94 ;; label = @29 + block $label$95 ;; label = @30 + block $label$96 ;; label = @31 + block $label$97 ;; label = @32 + local.get $8 + i32.const 255 + i32.and + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 0 + i32.sub + br_table 0 (;@32;) 1 (;@31;) 2 (;@30;) 3 (;@29;) 4 (;@28;) 7 (;@25;) 5 (;@27;) 6 (;@26;) 7 (;@25;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 27 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 26 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i64.extend_i32_s + i64.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 25 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store16 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 24 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store8 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 23 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 22 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i64.extend_i32_s + i64.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 21 (;@4;) + end + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 20 (;@4;) + end + local.get $12 + i32.const 8 + i32.or + local.set $12 + local.get $5 + i32.const 8 + i32.le_u + if ;; label = @24 + i32.const 8 + local.set $5 + end + i32.const 120 + local.set $9 + br 11 (;@12;) + end + br 10 (;@12;) + end + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.eq + if ;; label = @22 + local.get $21 + local.set $7 + else + block ;; label = @23 + local.get $21 + local.set $1 + loop $label$101 ;; label = @24 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $50 + i64.const 7 + i64.and + i64.const 48 + i64.or + i64.store8 + local.get $50 + i64.const 3 + i64.shr_u + local.tee $50 + i64.const 0 + i64.ne + br_if 0 (;@24;) + local.get $1 + local.set $7 + end + end + end + local.get $12 + i32.const 8 + i32.and + if ;; label = @22 + block ;; label = @23 + local.get $38 + local.get $7 + i32.sub + local.tee $1 + i32.const 1 + i32.add + local.set $8 + local.get $5 + local.get $1 + i32.le_s + if ;; label = @24 + local.get $8 + local.set $5 + end + i32.const 0 + local.set $6 + i32.const 1657 + local.set $8 + br 16 (;@7;) + end + else + block ;; label = @23 + i32.const 0 + local.set $6 + i32.const 1657 + local.set $8 + br 16 (;@7;) + end + end + end + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.lt_s + if ;; label = @21 + block ;; label = @22 + local.get $16 + i64.const 0 + local.get $50 + i64.sub + local.tee $50 + i64.store + i32.const 1 + local.set $6 + i32.const 1657 + local.set $8 + br 11 (;@11;) + end + end + local.get $12 + i32.const 2048 + i32.and + if ;; label = @21 + block ;; label = @22 + i32.const 1 + local.set $6 + i32.const 1658 + local.set $8 + br 11 (;@11;) + end + else + block ;; label = @22 + local.get $12 + i32.const 1 + i32.and + local.tee $1 + local.set $6 + local.get $1 + if (result i32) ;; label = @23 + i32.const 1659 + else + i32.const 1657 + end + local.set $8 + br 11 (;@11;) + end + end + end + local.get $16 + i64.load + local.set $50 + i32.const 0 + local.set $6 + i32.const 1657 + local.set $8 + br 8 (;@11;) + end + local.get $39 + local.get $16 + i64.load + i64.store8 + local.get $39 + local.set $1 + local.get $7 + local.set $12 + i32.const 1 + local.set $7 + i32.const 0 + local.set $6 + i32.const 1657 + local.set $8 + local.get $21 + local.set $5 + br 12 (;@6;) + end + call $12 + i32.load + call $24 + local.set $1 + br 7 (;@10;) + end + local.get $16 + i32.load + local.tee $1 + i32.eqz + if ;; label = @17 + i32.const 1667 + local.set $1 + end + br 6 (;@10;) + end + local.get $37 + local.get $16 + i64.load + i64.store32 + local.get $42 + i32.const 0 + i32.store + local.get $16 + local.get $37 + i32.store + local.get $37 + local.set $7 + i32.const -1 + local.set $6 + br 6 (;@9;) + end + local.get $16 + i32.load + local.set $7 + local.get $5 + if ;; label = @15 + block ;; label = @16 + local.get $5 + local.set $6 + br 7 (;@9;) + end + else + block ;; label = @16 + local.get $0 + i32.const 32 + local.get $10 + i32.const 0 + local.get $12 + call $25 + i32.const 0 + local.set $1 + br 8 (;@8;) + end + end + end + local.get $16 + f64.load + local.set $52 + local.get $20 + i32.const 0 + i32.store + local.get $52 + i64.reinterpret_f64 + i64.const 0 + i64.lt_s + if (result i32) ;; label = @14 + block (result i32) ;; label = @15 + i32.const 1 + local.set $24 + local.get $52 + f64.neg + local.set $52 + i32.const 1674 + end + else + block (result i32) ;; label = @15 + local.get $12 + i32.const 1 + i32.and + local.set $1 + local.get $12 + i32.const 2048 + i32.and + if (result i32) ;; label = @16 + block (result i32) ;; label = @17 + i32.const 1 + local.set $24 + i32.const 1677 + end + else + block (result i32) ;; label = @17 + local.get $1 + local.set $24 + local.get $1 + if (result i32) ;; label = @18 + i32.const 1680 + else + i32.const 1675 + end + end + end + end + end + local.set $26 + block $label$119 ;; label = @14 + local.get $52 + i64.reinterpret_f64 + i64.const 9218868437227405312 + i64.and + i64.const 9218868437227405312 + i64.lt_u + if ;; label = @15 + block ;; label = @16 + local.get $52 + local.get $20 + call $27 + f64.const 0x1p+1 (;=2;) + f64.mul + local.tee $52 + f64.const 0x0p+0 (;=0;) + f64.ne + local.tee $1 + if ;; label = @17 + local.get $20 + local.get $20 + i32.load + i32.const -1 + i32.add + i32.store + end + local.get $9 + i32.const 32 + i32.or + local.tee $22 + i32.const 97 + i32.eq + if ;; label = @17 + block ;; label = @18 + local.get $26 + i32.const 9 + i32.add + local.set $1 + local.get $9 + i32.const 32 + i32.and + local.tee $6 + if ;; label = @19 + local.get $1 + local.set $26 + end + local.get $5 + i32.const 11 + i32.gt_u + i32.const 12 + local.get $5 + i32.sub + local.tee $1 + i32.eqz + i32.or + i32.eqz + if ;; label = @19 + block ;; label = @20 + f64.const 0x1p+3 (;=8;) + local.set $53 + loop $label$125 ;; label = @21 + local.get $53 + f64.const 0x1p+4 (;=16;) + f64.mul + local.set $53 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + br_if 0 (;@21;) + end + local.get $26 + i32.load8_s + i32.const 45 + i32.eq + if (result f64) ;; label = @21 + local.get $53 + local.get $52 + f64.neg + local.get $53 + f64.sub + f64.add + f64.neg + else + local.get $52 + local.get $53 + f64.add + local.get $53 + f64.sub + end + local.set $52 + end + end + i32.const 0 + local.get $20 + i32.load + local.tee $7 + i32.sub + local.set $1 + local.get $7 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @19 + local.get $1 + else + local.get $7 + end + i64.extend_i32_s + local.get $33 + call $23 + local.tee $1 + local.get $33 + i32.eq + if ;; label = @19 + block ;; label = @20 + local.get $40 + i32.const 48 + i32.store8 + local.get $40 + local.set $1 + end + end + local.get $24 + i32.const 2 + i32.or + local.set $13 + local.get $1 + i32.const -1 + i32.add + local.get $7 + i32.const 31 + i32.shr_s + i32.const 2 + i32.and + i32.const 43 + i32.add + i32.store8 + local.get $1 + i32.const -2 + i32.add + local.tee $8 + local.get $9 + i32.const 15 + i32.add + i32.store8 + local.get $5 + i32.const 1 + i32.lt_s + local.set $9 + local.get $12 + i32.const 8 + i32.and + i32.eqz + local.set $14 + local.get $19 + local.set $1 + loop $label$131 ;; label = @19 + local.get $1 + local.get $52 + i32.trunc_f64_s + local.tee $7 + i32.const 1641 + i32.add + i32.load8_u + local.get $6 + i32.or + i32.store8 + local.get $52 + local.get $7 + f64.convert_i32_s + f64.sub + f64.const 0x1p+4 (;=16;) + f64.mul + local.set $52 + block $label$132 (result i32) ;; label = @20 + local.get $1 + i32.const 1 + i32.add + local.tee $7 + local.get $27 + i32.sub + i32.const 1 + i32.eq + if (result i32) ;; label = @21 + block (result i32) ;; label = @22 + local.get $7 + local.get $14 + local.get $9 + local.get $52 + f64.const 0x0p+0 (;=0;) + f64.eq + i32.and + i32.and + br_if 2 (;@20;) + drop + local.get $7 + i32.const 46 + i32.store8 + local.get $1 + i32.const 2 + i32.add + end + else + local.get $7 + end + end + local.set $1 + local.get $52 + f64.const 0x0p+0 (;=0;) + f64.ne + br_if 0 (;@19;) + end + local.get $46 + local.get $5 + i32.add + local.get $8 + local.tee $7 + i32.sub + local.set $6 + local.get $44 + local.get $7 + i32.sub + local.get $1 + i32.add + local.set $9 + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + i32.const 0 + i32.ne + local.get $45 + local.get $1 + i32.add + local.get $5 + i32.lt_s + i32.and + if (result i32) ;; label = @19 + local.get $6 + else + local.get $9 + local.tee $6 + end + local.get $13 + i32.add + local.tee $5 + local.get $12 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $26 + local.get $13 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $5 + local.get $12 + i32.const 65536 + i32.xor + call $25 + local.get $1 + local.get $27 + i32.sub + local.set $1 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $19 + local.get $1 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $6 + local.get $1 + local.get $28 + local.get $7 + i32.sub + local.tee $1 + i32.add + i32.sub + i32.const 0 + i32.const 0 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $8 + local.get $1 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $5 + local.get $10 + i32.ge_s + if ;; label = @19 + local.get $5 + local.set $10 + end + br 4 (;@14;) + end + end + local.get $1 + if ;; label = @17 + block ;; label = @18 + local.get $20 + local.get $20 + i32.load + i32.const -28 + i32.add + local.tee $6 + i32.store + local.get $52 + f64.const 0x1p+28 (;=268435456;) + f64.mul + local.set $52 + end + else + local.get $20 + i32.load + local.set $6 + end + local.get $6 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @17 + local.get $47 + else + local.get $48 + end + local.tee $7 + local.set $8 + loop $label$145 ;; label = @17 + local.get $8 + local.get $52 + i32.trunc_f64_s + local.tee $1 + i32.store + local.get $8 + i32.const 4 + i32.add + local.set $8 + local.get $52 + local.get $1 + f64.convert_i32_u + f64.sub + f64.const 0x1.dcd65p+29 (;=1000000000;) + f64.mul + local.tee $52 + f64.const 0x0p+0 (;=0;) + f64.ne + br_if 0 (;@17;) + end + local.get $6 + i32.const 0 + i32.gt_s + if ;; label = @17 + block ;; label = @18 + local.get $7 + local.set $1 + loop $label$147 ;; label = @19 + local.get $6 + i32.const 29 + i32.gt_s + if (result i32) ;; label = @20 + i32.const 29 + else + local.get $6 + end + local.set $14 + block $label$150 ;; label = @20 + local.get $8 + i32.const -4 + i32.add + local.tee $6 + local.get $1 + i32.ge_u + if ;; label = @21 + block ;; label = @22 + local.get $14 + i64.extend_i32_u + local.set $50 + i32.const 0 + local.set $13 + loop $label$152 ;; label = @23 + local.get $6 + local.get $6 + i32.load + i64.extend_i32_u + local.get $50 + i64.shl + local.get $13 + i64.extend_i32_u + i64.add + local.tee $51 + i64.const 1000000000 + i64.rem_u + i64.store32 + local.get $51 + i64.const 1000000000 + i64.div_u + i32.wrap_i64 + local.set $13 + local.get $6 + i32.const -4 + i32.add + local.tee $6 + local.get $1 + i32.ge_u + br_if 0 (;@23;) + end + local.get $13 + i32.eqz + br_if 2 (;@20;) + local.get $1 + i32.const -4 + i32.add + local.tee $1 + local.get $13 + i32.store + end + end + end + loop $label$153 ;; label = @20 + local.get $8 + local.get $1 + i32.gt_u + if ;; label = @21 + local.get $8 + i32.const -4 + i32.add + local.tee $6 + i32.load + i32.eqz + if ;; label = @22 + block ;; label = @23 + local.get $6 + local.set $8 + br 3 (;@20;) + end + end + end + end + local.get $20 + local.get $20 + i32.load + local.get $14 + i32.sub + local.tee $6 + i32.store + local.get $6 + i32.const 0 + i32.gt_s + br_if 0 (;@19;) + end + end + else + local.get $7 + local.set $1 + end + local.get $5 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @17 + i32.const 6 + else + local.get $5 + end + local.set $18 + local.get $6 + i32.const 0 + i32.lt_s + if ;; label = @17 + block ;; label = @18 + local.get $18 + i32.const 25 + i32.add + i32.const 9 + i32.div_s + i32.const 1 + i32.add + local.set $14 + local.get $22 + i32.const 102 + i32.eq + local.set $25 + local.get $8 + local.set $5 + loop $label$160 ;; label = @19 + i32.const 0 + local.get $6 + i32.sub + local.tee $13 + i32.const 9 + i32.gt_s + if ;; label = @20 + i32.const 9 + local.set $13 + end + block $label$162 ;; label = @20 + local.get $1 + local.get $5 + i32.lt_u + if ;; label = @21 + block ;; label = @22 + i32.const 1 + local.get $13 + i32.shl + i32.const -1 + i32.add + local.set $29 + i32.const 1000000000 + local.get $13 + i32.shr_u + local.set $35 + i32.const 0 + local.set $6 + local.get $1 + local.set $8 + loop $label$164 ;; label = @23 + local.get $8 + local.get $8 + i32.load + local.tee $32 + local.get $13 + i32.shr_u + local.get $6 + i32.add + i32.store + local.get $32 + local.get $29 + i32.and + local.get $35 + i32.mul + local.set $6 + local.get $8 + i32.const 4 + i32.add + local.tee $8 + local.get $5 + i32.lt_u + br_if 0 (;@23;) + end + local.get $1 + i32.const 4 + i32.add + local.set $8 + local.get $1 + i32.load + i32.eqz + if ;; label = @23 + local.get $8 + local.set $1 + end + local.get $6 + i32.eqz + br_if 2 (;@20;) + local.get $5 + local.get $6 + i32.store + local.get $5 + i32.const 4 + i32.add + local.set $5 + end + else + block ;; label = @22 + local.get $1 + i32.const 4 + i32.add + local.set $8 + local.get $1 + i32.load + i32.eqz + if ;; label = @23 + local.get $8 + local.set $1 + end + end + end + end + local.get $25 + if (result i32) ;; label = @20 + local.get $7 + else + local.get $1 + end + local.tee $8 + local.get $14 + i32.const 2 + i32.shl + i32.add + local.set $6 + local.get $5 + local.get $8 + i32.sub + i32.const 2 + i32.shr_s + local.get $14 + i32.gt_s + if ;; label = @20 + local.get $6 + local.set $5 + end + local.get $20 + local.get $20 + i32.load + local.get $13 + i32.add + local.tee $6 + i32.store + local.get $6 + i32.const 0 + i32.lt_s + br_if 0 (;@19;) + local.get $5 + local.set $13 + end + end + else + local.get $8 + local.set $13 + end + local.get $7 + local.set $25 + block $label$172 ;; label = @17 + local.get $1 + local.get $13 + i32.lt_u + if ;; label = @18 + block ;; label = @19 + local.get $25 + local.get $1 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + local.set $5 + local.get $1 + i32.load + local.tee $6 + i32.const 10 + i32.lt_u + br_if 2 (;@17;) + i32.const 10 + local.set $8 + loop $label$174 ;; label = @20 + local.get $5 + i32.const 1 + i32.add + local.set $5 + local.get $6 + local.get $8 + i32.const 10 + i32.mul + local.tee $8 + i32.ge_u + br_if 0 (;@20;) + end + end + else + i32.const 0 + local.set $5 + end + end + local.get $22 + i32.const 103 + i32.eq + local.set $29 + local.get $18 + i32.const 0 + i32.ne + local.set $35 + local.get $18 + local.get $22 + i32.const 102 + i32.ne + if (result i32) ;; label = @17 + local.get $5 + else + i32.const 0 + end + i32.sub + local.get $35 + local.get $29 + i32.and + i32.const 31 + i32.shl + i32.const 31 + i32.shr_s + i32.add + local.tee $8 + local.get $13 + local.get $25 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + i32.const -9 + i32.add + i32.lt_s + if ;; label = @17 + block ;; label = @18 + local.get $8 + i32.const 9216 + i32.add + local.tee $14 + i32.const 9 + i32.rem_s + i32.const 1 + i32.add + local.tee $8 + i32.const 9 + i32.lt_s + if ;; label = @19 + block ;; label = @20 + i32.const 10 + local.set $6 + loop $label$180 ;; label = @21 + local.get $6 + i32.const 10 + i32.mul + local.set $6 + local.get $8 + i32.const 1 + i32.add + local.tee $8 + i32.const 9 + i32.ne + br_if 0 (;@21;) + end + end + else + i32.const 10 + local.set $6 + end + local.get $7 + i32.const 4 + i32.add + local.get $14 + i32.const 9 + i32.div_s + i32.const -1024 + i32.add + i32.const 2 + i32.shl + i32.add + local.tee $8 + i32.load + local.tee $22 + local.get $6 + i32.rem_u + local.set $14 + block $label$182 ;; label = @19 + local.get $8 + i32.const 4 + i32.add + local.get $13 + i32.eq + local.tee $32 + local.get $14 + i32.eqz + i32.and + i32.eqz + if ;; label = @20 + block ;; label = @21 + local.get $14 + local.get $6 + i32.const 2 + i32.div_s + local.tee $49 + i32.lt_u + if (result f64) ;; label = @22 + f64.const 0x1p-1 (;=0.5;) + else + local.get $32 + local.get $14 + local.get $49 + i32.eq + i32.and + if (result f64) ;; label = @23 + f64.const 0x1p+0 (;=1;) + else + f64.const 0x1.8p+0 (;=1.5;) + end + end + local.set $52 + local.get $22 + local.get $6 + i32.div_u + i32.const 1 + i32.and + if (result f64) ;; label = @22 + f64.const 0x1.0000000000001p+53 (;=9007199254740994;) + else + f64.const 0x1p+53 (;=9007199254740992;) + end + local.set $53 + block $label$190 ;; label = @22 + local.get $24 + if ;; label = @23 + block ;; label = @24 + local.get $26 + i32.load8_s + i32.const 45 + i32.ne + br_if 2 (;@22;) + local.get $53 + f64.neg + local.set $53 + local.get $52 + f64.neg + local.set $52 + end + end + end + local.get $8 + local.get $22 + local.get $14 + i32.sub + local.tee $14 + i32.store + local.get $53 + local.get $52 + f64.add + local.get $53 + f64.eq + br_if 2 (;@19;) + local.get $8 + local.get $14 + local.get $6 + i32.add + local.tee $5 + i32.store + local.get $5 + i32.const 999999999 + i32.gt_u + if ;; label = @22 + loop $label$193 ;; label = @23 + local.get $8 + i32.const 0 + i32.store + local.get $8 + i32.const -4 + i32.add + local.tee $8 + local.get $1 + i32.lt_u + if ;; label = @24 + local.get $1 + i32.const -4 + i32.add + local.tee $1 + i32.const 0 + i32.store + end + local.get $8 + local.get $8 + i32.load + i32.const 1 + i32.add + local.tee $5 + i32.store + local.get $5 + i32.const 999999999 + i32.gt_u + br_if 0 (;@23;) + end + end + local.get $25 + local.get $1 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + local.set $5 + local.get $1 + i32.load + local.tee $14 + i32.const 10 + i32.lt_u + br_if 2 (;@19;) + i32.const 10 + local.set $6 + loop $label$195 ;; label = @22 + local.get $5 + i32.const 1 + i32.add + local.set $5 + local.get $14 + local.get $6 + i32.const 10 + i32.mul + local.tee $6 + i32.ge_u + br_if 0 (;@22;) + end + end + end + end + local.get $1 + local.set $14 + local.get $5 + local.set $6 + local.get $13 + local.get $8 + i32.const 4 + i32.add + local.tee $8 + i32.le_u + if ;; label = @19 + local.get $13 + local.set $8 + end + end + else + block ;; label = @18 + local.get $1 + local.set $14 + local.get $5 + local.set $6 + local.get $13 + local.set $8 + end + end + i32.const 0 + local.get $6 + i32.sub + local.set $32 + loop $label$198 ;; label = @17 + block $label$199 ;; label = @18 + local.get $8 + local.get $14 + i32.le_u + if ;; label = @19 + block ;; label = @20 + i32.const 0 + local.set $22 + br 2 (;@18;) + end + end + local.get $8 + i32.const -4 + i32.add + local.tee $1 + i32.load + if ;; label = @19 + i32.const 1 + local.set $22 + else + block ;; label = @20 + local.get $1 + local.set $8 + br 3 (;@17;) + end + end + end + end + block $label$203 ;; label = @17 + local.get $29 + if ;; label = @18 + block ;; label = @19 + local.get $35 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $18 + i32.add + local.tee $1 + local.get $6 + i32.gt_s + local.get $6 + i32.const -5 + i32.gt_s + i32.and + if (result i32) ;; label = @20 + block (result i32) ;; label = @21 + local.get $9 + i32.const -1 + i32.add + local.set $5 + local.get $1 + i32.const -1 + i32.add + local.get $6 + i32.sub + end + else + block (result i32) ;; label = @21 + local.get $9 + i32.const -2 + i32.add + local.set $5 + local.get $1 + i32.const -1 + i32.add + end + end + local.set $1 + local.get $12 + i32.const 8 + i32.and + local.tee $13 + br_if 2 (;@17;) + block $label$207 ;; label = @20 + local.get $22 + if ;; label = @21 + block ;; label = @22 + local.get $8 + i32.const -4 + i32.add + i32.load + local.tee $18 + i32.eqz + if ;; label = @23 + block ;; label = @24 + i32.const 9 + local.set $9 + br 4 (;@20;) + end + end + local.get $18 + i32.const 10 + i32.rem_u + if ;; label = @23 + block ;; label = @24 + i32.const 0 + local.set $9 + br 4 (;@20;) + end + else + block ;; label = @24 + i32.const 10 + local.set $13 + i32.const 0 + local.set $9 + end + end + loop $label$212 ;; label = @23 + local.get $9 + i32.const 1 + i32.add + local.set $9 + local.get $18 + local.get $13 + i32.const 10 + i32.mul + local.tee $13 + i32.rem_u + i32.eqz + br_if 0 (;@23;) + end + end + else + i32.const 9 + local.set $9 + end + end + local.get $8 + local.get $25 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + i32.const -9 + i32.add + local.set $18 + local.get $5 + i32.const 32 + i32.or + i32.const 102 + i32.eq + if ;; label = @20 + block ;; label = @21 + i32.const 0 + local.set $13 + local.get $1 + local.get $18 + local.get $9 + i32.sub + local.tee $9 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @22 + i32.const 0 + local.tee $9 + else + local.get $9 + end + i32.ge_s + if ;; label = @22 + local.get $9 + local.set $1 + end + end + else + block ;; label = @21 + i32.const 0 + local.set $13 + local.get $1 + local.get $18 + local.get $6 + i32.add + local.get $9 + i32.sub + local.tee $9 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @22 + i32.const 0 + local.tee $9 + else + local.get $9 + end + i32.ge_s + if ;; label = @22 + local.get $9 + local.set $1 + end + end + end + end + else + block ;; label = @19 + local.get $12 + i32.const 8 + i32.and + local.set $13 + local.get $18 + local.set $1 + local.get $9 + local.set $5 + end + end + end + local.get $5 + i32.const 32 + i32.or + i32.const 102 + i32.eq + local.tee $25 + if ;; label = @17 + block ;; label = @18 + i32.const 0 + local.set $9 + local.get $6 + i32.const 0 + i32.le_s + if ;; label = @19 + i32.const 0 + local.set $6 + end + end + else + block ;; label = @18 + local.get $28 + local.get $6 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @19 + local.get $32 + else + local.get $6 + end + i64.extend_i32_s + local.get $33 + call $23 + local.tee $9 + i32.sub + i32.const 2 + i32.lt_s + if ;; label = @19 + loop $label$229 ;; label = @20 + local.get $9 + i32.const -1 + i32.add + local.tee $9 + i32.const 48 + i32.store8 + local.get $28 + local.get $9 + i32.sub + i32.const 2 + i32.lt_s + br_if 0 (;@20;) + end + end + local.get $9 + i32.const -1 + i32.add + local.get $6 + i32.const 31 + i32.shr_s + i32.const 2 + i32.and + i32.const 43 + i32.add + i32.store8 + local.get $9 + i32.const -2 + i32.add + local.tee $6 + local.get $5 + i32.store8 + local.get $6 + local.set $9 + local.get $28 + local.get $6 + i32.sub + local.set $6 + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $24 + i32.const 1 + i32.add + local.get $1 + i32.add + local.get $1 + local.get $13 + i32.or + local.tee $29 + i32.const 0 + i32.ne + i32.add + local.get $6 + i32.add + local.tee $18 + local.get $12 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + local.get $26 + local.get $24 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $18 + local.get $12 + i32.const 65536 + i32.xor + call $25 + block $label$231 ;; label = @17 + local.get $25 + if ;; label = @18 + block ;; label = @19 + local.get $14 + local.get $7 + i32.gt_u + if (result i32) ;; label = @20 + local.get $7 + else + local.get $14 + end + local.tee $9 + local.set $6 + loop $label$235 ;; label = @20 + local.get $6 + i32.load + i64.extend_i32_u + local.get $31 + call $23 + local.set $5 + block $label$236 ;; label = @21 + local.get $6 + local.get $9 + i32.eq + if ;; label = @22 + block ;; label = @23 + local.get $5 + local.get $31 + i32.ne + br_if 2 (;@21;) + local.get $34 + i32.const 48 + i32.store8 + local.get $34 + local.set $5 + end + else + block ;; label = @23 + local.get $5 + local.get $19 + i32.le_u + br_if 2 (;@21;) + local.get $19 + i32.const 48 + local.get $5 + local.get $27 + i32.sub + call $39 + drop + loop $label$239 ;; label = @24 + local.get $5 + i32.const -1 + i32.add + local.tee $5 + local.get $19 + i32.gt_u + br_if 0 (;@24;) + end + end + end + end + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @21 + local.get $5 + local.get $41 + local.get $5 + i32.sub + local.get $0 + call $21 + drop + end + local.get $6 + i32.const 4 + i32.add + local.tee $5 + local.get $7 + i32.le_u + if ;; label = @21 + block ;; label = @22 + local.get $5 + local.set $6 + br 2 (;@20;) + end + end + end + block $label$242 ;; label = @20 + local.get $29 + if ;; label = @21 + block ;; label = @22 + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@20;) + i32.const 1709 + i32.const 1 + local.get $0 + call $21 + drop + end + end + end + local.get $1 + i32.const 0 + i32.gt_s + local.get $5 + local.get $8 + i32.lt_u + i32.and + if ;; label = @20 + loop $label$245 ;; label = @21 + local.get $5 + i32.load + i64.extend_i32_u + local.get $31 + call $23 + local.tee $7 + local.get $19 + i32.gt_u + if ;; label = @22 + block ;; label = @23 + local.get $19 + i32.const 48 + local.get $7 + local.get $27 + i32.sub + call $39 + drop + loop $label$247 ;; label = @24 + local.get $7 + i32.const -1 + i32.add + local.tee $7 + local.get $19 + i32.gt_u + br_if 0 (;@24;) + end + end + end + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @22 + local.get $7 + local.get $1 + i32.const 9 + i32.gt_s + if (result i32) ;; label = @23 + i32.const 9 + else + local.get $1 + end + local.get $0 + call $21 + drop + end + local.get $1 + i32.const -9 + i32.add + local.set $7 + local.get $1 + i32.const 9 + i32.gt_s + local.get $5 + i32.const 4 + i32.add + local.tee $5 + local.get $8 + i32.lt_u + i32.and + if ;; label = @22 + block ;; label = @23 + local.get $7 + local.set $1 + br 2 (;@21;) + end + else + local.get $7 + local.set $1 + end + end + end + local.get $0 + i32.const 48 + local.get $1 + i32.const 9 + i32.add + i32.const 9 + i32.const 0 + call $25 + end + else + block ;; label = @19 + local.get $14 + i32.const 4 + i32.add + local.set $5 + local.get $22 + i32.eqz + if ;; label = @20 + local.get $5 + local.set $8 + end + local.get $1 + i32.const -1 + i32.gt_s + if ;; label = @20 + block ;; label = @21 + local.get $13 + i32.eqz + local.set $13 + local.get $14 + local.set $7 + local.get $1 + local.set $5 + loop $label$256 ;; label = @22 + local.get $7 + i32.load + i64.extend_i32_u + local.get $31 + call $23 + local.tee $1 + local.get $31 + i32.eq + if ;; label = @23 + block ;; label = @24 + local.get $34 + i32.const 48 + i32.store8 + local.get $34 + local.set $1 + end + end + block $label$258 ;; label = @23 + local.get $7 + local.get $14 + i32.eq + if ;; label = @24 + block ;; label = @25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @26 + local.get $1 + i32.const 1 + local.get $0 + call $21 + drop + end + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $13 + local.get $5 + i32.const 1 + i32.lt_s + i32.and + br_if 2 (;@23;) + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@23;) + i32.const 1709 + i32.const 1 + local.get $0 + call $21 + drop + end + else + block ;; label = @25 + local.get $1 + local.get $19 + i32.le_u + br_if 2 (;@23;) + local.get $19 + i32.const 48 + local.get $1 + local.get $43 + i32.add + call $39 + drop + loop $label$262 ;; label = @26 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $19 + i32.gt_u + br_if 0 (;@26;) + end + end + end + end + local.get $41 + local.get $1 + i32.sub + local.set $6 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @23 + local.get $1 + local.get $5 + local.get $6 + i32.gt_s + if (result i32) ;; label = @24 + local.get $6 + else + local.get $5 + end + local.get $0 + call $21 + drop + end + local.get $7 + i32.const 4 + i32.add + local.tee $7 + local.get $8 + i32.lt_u + local.get $5 + local.get $6 + i32.sub + local.tee $5 + i32.const -1 + i32.gt_s + i32.and + br_if 0 (;@22;) + local.get $5 + local.set $1 + end + end + end + local.get $0 + i32.const 48 + local.get $1 + i32.const 18 + i32.add + i32.const 18 + i32.const 0 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@17;) + local.get $9 + local.get $28 + local.get $9 + i32.sub + local.get $0 + call $21 + drop + end + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $18 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $18 + local.get $10 + i32.ge_s + if ;; label = @17 + local.get $18 + local.set $10 + end + end + else + block ;; label = @16 + local.get $0 + i32.const 32 + local.get $10 + local.get $52 + local.get $52 + f64.ne + i32.const 0 + i32.or + local.tee $6 + if (result i32) ;; label = @17 + i32.const 0 + local.tee $24 + else + local.get $24 + end + i32.const 3 + i32.add + local.tee $8 + local.get $7 + call $25 + local.get $0 + i32.load + local.tee $1 + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + block ;; label = @18 + local.get $26 + local.get $24 + local.get $0 + call $21 + drop + local.get $0 + i32.load + local.set $1 + end + end + local.get $9 + i32.const 32 + i32.and + i32.const 0 + i32.ne + local.tee $5 + if (result i32) ;; label = @17 + i32.const 1693 + else + i32.const 1697 + end + local.set $7 + local.get $5 + if (result i32) ;; label = @17 + i32.const 1701 + else + i32.const 1705 + end + local.set $5 + local.get $6 + i32.eqz + if ;; label = @17 + local.get $7 + local.set $5 + end + local.get $1 + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + local.get $5 + i32.const 3 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $8 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $8 + local.get $10 + i32.ge_s + if ;; label = @17 + local.get $8 + local.set $10 + end + end + end + end + local.get $11 + local.set $1 + br 9 (;@4;) + end + local.get $5 + local.set $7 + i32.const 0 + local.set $6 + i32.const 1657 + local.set $8 + local.get $21 + local.set $5 + br 6 (;@6;) + end + local.get $9 + i32.const 32 + i32.and + local.set $7 + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.eq + if (result i32) ;; label = @12 + block (result i32) ;; label = @13 + i64.const 0 + local.set $50 + local.get $21 + end + else + block (result i32) ;; label = @13 + local.get $21 + local.set $1 + loop $label$280 ;; label = @14 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $50 + i32.wrap_i64 + i32.const 15 + i32.and + i32.const 1641 + i32.add + i32.load8_u + local.get $7 + i32.or + i32.store8 + local.get $50 + i64.const 4 + i64.shr_u + local.tee $50 + i64.const 0 + i64.ne + br_if 0 (;@14;) + end + local.get $16 + i64.load + local.set $50 + local.get $1 + end + end + local.set $7 + local.get $9 + i32.const 4 + i32.shr_s + i32.const 1657 + i32.add + local.set $8 + local.get $12 + i32.const 8 + i32.and + i32.eqz + local.get $50 + i64.const 0 + i64.eq + i32.or + local.tee $1 + if ;; label = @12 + i32.const 1657 + local.set $8 + end + local.get $1 + if (result i32) ;; label = @12 + i32.const 0 + else + i32.const 2 + end + local.set $6 + br 4 (;@7;) + end + local.get $50 + local.get $21 + call $23 + local.set $7 + br 3 (;@7;) + end + local.get $1 + i32.const 0 + local.get $5 + call $17 + local.tee $13 + i32.eqz + local.set $14 + local.get $13 + local.get $1 + i32.sub + local.set $8 + local.get $1 + local.get $5 + i32.add + local.set $9 + local.get $7 + local.set $12 + local.get $14 + if (result i32) ;; label = @10 + local.get $5 + else + local.get $8 + end + local.set $7 + i32.const 0 + local.set $6 + i32.const 1657 + local.set $8 + local.get $14 + if (result i32) ;; label = @10 + local.get $9 + else + local.get $13 + end + local.set $5 + br 3 (;@6;) + end + i32.const 0 + local.set $1 + i32.const 0 + local.set $5 + local.get $7 + local.set $8 + loop $label$288 ;; label = @9 + block $label$289 ;; label = @10 + local.get $8 + i32.load + local.tee $9 + i32.eqz + br_if 0 (;@10;) + local.get $36 + local.get $9 + call $26 + local.tee $5 + i32.const 0 + i32.lt_s + local.get $5 + local.get $6 + local.get $1 + i32.sub + i32.gt_u + i32.or + br_if 0 (;@10;) + local.get $8 + i32.const 4 + i32.add + local.set $8 + local.get $6 + local.get $5 + local.get $1 + i32.add + local.tee $1 + i32.gt_u + br_if 1 (;@9;) + end + end + local.get $5 + i32.const 0 + i32.lt_s + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $1 + local.get $12 + call $25 + local.get $1 + if ;; label = @9 + block ;; label = @10 + i32.const 0 + local.set $5 + loop $label$292 ;; label = @11 + local.get $7 + i32.load + local.tee $8 + i32.eqz + br_if 3 (;@8;) + local.get $36 + local.get $8 + call $26 + local.tee $8 + local.get $5 + i32.add + local.tee $5 + local.get $1 + i32.gt_s + br_if 3 (;@8;) + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @12 + local.get $36 + local.get $8 + local.get $0 + call $21 + drop + end + local.get $7 + i32.const 4 + i32.add + local.set $7 + local.get $5 + local.get $1 + i32.lt_u + br_if 0 (;@11;) + br 3 (;@8;) + end + end + else + block ;; label = @10 + i32.const 0 + local.set $1 + br 2 (;@8;) + end + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $1 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $10 + local.get $1 + i32.le_s + if ;; label = @8 + local.get $1 + local.set $10 + end + local.get $11 + local.set $1 + br 3 (;@4;) + end + local.get $12 + i32.const -65537 + i32.and + local.set $1 + local.get $5 + i32.const -1 + i32.gt_s + if ;; label = @7 + local.get $1 + local.set $12 + end + local.get $5 + local.get $16 + i64.load + i64.const 0 + i64.ne + local.tee $9 + i32.or + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + local.get $7 + local.set $1 + local.get $5 + local.get $9 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $38 + local.get $7 + i32.sub + i32.add + local.tee $7 + i32.gt_s + if ;; label = @9 + local.get $5 + local.set $7 + end + local.get $21 + end + else + block (result i32) ;; label = @8 + local.get $21 + local.set $1 + i32.const 0 + local.set $7 + local.get $21 + end + end + local.set $5 + end + local.get $0 + i32.const 32 + local.get $10 + local.get $7 + local.get $5 + local.get $1 + i32.sub + local.tee $9 + i32.lt_s + if (result i32) ;; label = @6 + local.get $9 + local.tee $7 + else + local.get $7 + end + local.get $6 + i32.add + local.tee $5 + i32.lt_s + if (result i32) ;; label = @6 + local.get $5 + local.tee $10 + else + local.get $10 + end + local.get $5 + local.get $12 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @6 + local.get $8 + local.get $6 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $5 + local.get $12 + i32.const 65536 + i32.xor + call $25 + local.get $0 + i32.const 48 + local.get $7 + local.get $9 + i32.const 0 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @6 + local.get $1 + local.get $9 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $11 + local.set $1 + br 1 (;@4;) + end + end + br 1 (;@2;) + end + local.get $0 + i32.eqz + if ;; label = @3 + local.get $17 + if ;; label = @4 + block ;; label = @5 + i32.const 1 + local.set $0 + loop $label$308 ;; label = @6 + local.get $4 + local.get $0 + i32.const 2 + i32.shl + i32.add + i32.load + local.tee $1 + if ;; label = @7 + block ;; label = @8 + local.get $3 + local.get $0 + i32.const 3 + i32.shl + i32.add + local.get $1 + local.get $2 + call $22 + local.get $0 + i32.const 1 + i32.add + local.tee $0 + i32.const 10 + i32.lt_s + br_if 2 (;@6;) + i32.const 1 + local.set $15 + br 6 (;@2;) + end + end + end + loop $label$310 ;; label = @6 + local.get $4 + local.get $0 + i32.const 2 + i32.shl + i32.add + i32.load + if ;; label = @7 + block ;; label = @8 + i32.const -1 + local.set $15 + br 6 (;@2;) + end + end + local.get $0 + i32.const 1 + i32.add + local.tee $0 + i32.const 10 + i32.lt_s + br_if 0 (;@6;) + i32.const 1 + local.set $15 + end + end + else + i32.const 0 + local.set $15 + end + end + end + local.get $23 + global.set $global$1 + local.get $15 + end ) - ) - (func $25 (; 38 ;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (block $label$1 - (local.set $7 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 256) - ) - ) - (local.set $6 - (local.get $7) - ) - (block $label$2 - (if - (i32.and - (i32.gt_s - (local.get $2) - (local.get $3) - ) - (i32.eqz - (i32.and - (local.get $4) - (i32.const 73728) - ) - ) - ) - (block - (drop - (call $39 - (local.get $6) - (local.get $1) - (if (result i32) - (i32.gt_u - (local.tee $5 - (i32.sub - (local.get $2) - (local.get $3) - ) - ) - (i32.const 256) - ) - (i32.const 256) - (local.get $5) - ) - ) - ) - (local.set $4 - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 32) - ) - ) - ) - (if - (i32.gt_u - (local.get $5) - (i32.const 255) - ) - (block - (loop $label$7 - (if - (local.get $4) - (block - (drop - (call $21 - (local.get $6) - (i32.const 256) - (local.get $0) - ) - ) - (local.set $1 - (i32.load - (local.get $0) - ) - ) - ) - ) - (local.set $4 - (i32.eqz - (i32.and - (local.get $1) - (i32.const 32) - ) - ) - ) - (br_if $label$7 - (i32.gt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const -256) - ) - ) - (i32.const 255) - ) - ) - ) - (br_if $label$2 - (i32.eqz - (local.get $4) - ) - ) - (local.set $5 - (i32.and - (i32.sub - (local.get $2) - (local.get $3) - ) - (i32.const 255) - ) - ) - ) - (br_if $label$2 - (i32.eqz - (local.get $4) - ) - ) - ) - (drop - (call $21 - (local.get $6) - (local.get $5) - (local.get $0) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $7) - ) + (func $20 (;33;) (type $1) (param $0 i32) (result i32) + i32.const 0 ) - ) - (func $26 (; 39 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (if (result i32) - (local.get $0) - (call $29 - (local.get $0) - (local.get $1) - (i32.const 0) - ) - (i32.const 0) + (func $21 (;34;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) + block $label$1 (result i32) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $2 + i32.const 16 + i32.add + local.tee $4 + i32.load + local.tee $3 + br_if 0 (;@3;) + local.get $2 + call $30 + if ;; label = @4 + i32.const 0 + local.set $3 + else + block ;; label = @5 + local.get $4 + i32.load + local.set $3 + br 2 (;@3;) + end + end + br 1 (;@2;) + end + local.get $3 + local.get $2 + i32.const 20 + i32.add + local.tee $5 + i32.load + local.tee $4 + i32.sub + local.get $1 + i32.lt_u + if ;; label = @3 + block ;; label = @4 + local.get $2 + local.get $0 + local.get $1 + local.get $2 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + local.set $3 + br 2 (;@2;) + end + end + block $label$7 (result i32) ;; label = @3 + local.get $2 + i32.load8_s offset=75 + i32.const -1 + i32.gt_s + if (result i32) ;; label = @4 + block (result i32) ;; label = @5 + local.get $1 + local.set $3 + loop $label$9 ;; label = @6 + i32.const 0 + local.get $3 + i32.eqz + br_if 3 (;@3;) + drop + local.get $0 + local.get $3 + i32.const -1 + i32.add + local.tee $6 + i32.add + i32.load8_s + i32.const 10 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $6 + local.set $3 + br 2 (;@6;) + end + end + end + local.get $2 + local.get $0 + local.get $3 + local.get $2 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + local.get $3 + i32.lt_u + br_if 3 (;@2;) + local.get $5 + i32.load + local.set $4 + local.get $1 + local.get $3 + i32.sub + local.set $1 + local.get $0 + local.get $3 + i32.add + local.set $0 + local.get $3 + end + else + i32.const 0 + end + end + local.set $2 + local.get $4 + local.get $0 + local.get $1 + call $40 + drop + local.get $5 + local.get $5 + i32.load + local.get $1 + i32.add + i32.store + local.get $2 + local.get $1 + i32.add + local.set $3 + end + local.get $3 + end ) - ) - (func $27 (; 40 ;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (call $28 - (local.get $0) - (local.get $1) + (func $22 (;35;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) (local $4 i64) (local $5 f64) + block $label$1 ;; label = @1 + local.get $1 + i32.const 20 + i32.le_u + if ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + block $label$6 ;; label = @6 + block $label$7 ;; label = @7 + block $label$8 ;; label = @8 + block $label$9 ;; label = @9 + block $label$10 ;; label = @10 + block $label$11 ;; label = @11 + block $label$12 ;; label = @12 + block $label$13 ;; label = @13 + local.get $1 + i32.const 9 + i32.sub + br_table 0 (;@13;) 1 (;@12;) 2 (;@11;) 3 (;@10;) 4 (;@9;) 5 (;@8;) 6 (;@7;) 7 (;@6;) 8 (;@5;) 9 (;@4;) 10 (;@3;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.store + br 11 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i64.extend_i32_s + i64.store + br 10 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i64.extend_i32_u + i64.store + br 9 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + i64.load + local.set $4 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $4 + i64.store + br 8 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 65535 + i32.and + i32.const 16 + i32.shl + i32.const 16 + i32.shr_s + i64.extend_i32_s + i64.store + br 7 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 65535 + i32.and + i64.extend_i32_u + i64.store + br 6 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 255 + i32.and + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i64.extend_i32_s + i64.store + br 5 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 255 + i32.and + i64.extend_i32_u + i64.store + br 4 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + f64.load + local.set $5 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $5 + f64.store + br 3 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + f64.load + local.set $5 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $5 + f64.store + end + end + end ) - ) - (func $28 (; 41 ;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (local $2 i64) - (local $3 i64) - (block $label$1 (result f64) - (block $label$2 - (block $label$3 - (block $label$4 - (block $label$5 - (br_table $label$5 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$4 $label$3 - (i32.sub - (i32.shr_s - (i32.shl - (i32.and - (i32.and - (i32.wrap_i64 - (local.tee $3 - (i64.shr_u - (local.tee $2 - (i64.reinterpret_f64 - (local.get $0) - ) - ) - (i64.const 52) - ) - ) - ) - (i32.const 65535) - ) - (i32.const 2047) - ) - (i32.const 16) - ) - (i32.const 16) - ) - (i32.const 0) - ) - ) - ) - (i32.store - (local.get $1) - (if (result i32) - (f64.ne - (local.get $0) - (f64.const 0) - ) - (block (result i32) - (local.set $0 - (call $28 - (f64.mul - (local.get $0) - (f64.const 18446744073709551615) - ) - (local.get $1) - ) - ) - (i32.add - (i32.load - (local.get $1) - ) - (i32.const -64) - ) - ) - (i32.const 0) - ) - ) - (br $label$2) - ) - (br $label$2) - ) - (i32.store - (local.get $1) - (i32.add - (i32.and - (i32.wrap_i64 - (local.get $3) - ) - (i32.const 2047) - ) - (i32.const -1022) - ) - ) - (local.set $0 - (f64.reinterpret_i64 - (i64.or - (i64.and - (local.get $2) - (i64.const -9218868437227405313) - ) - (i64.const 4602678819172646912) - ) - ) - ) - ) - (local.get $0) + (func $23 (;36;) (type $9) (param $0 i64) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i64) + block $label$1 (result i32) ;; label = @1 + local.get $0 + i32.wrap_i64 + local.set $2 + local.get $0 + i64.const 4294967295 + i64.gt_u + if ;; label = @2 + block ;; label = @3 + loop $label$3 ;; label = @4 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $0 + i64.const 10 + i64.rem_u + i64.const 48 + i64.or + i64.store8 + local.get $0 + i64.const 10 + i64.div_u + local.set $4 + local.get $0 + i64.const 42949672959 + i64.gt_u + if ;; label = @5 + block ;; label = @6 + local.get $4 + local.set $0 + br 2 (;@4;) + end + end + end + local.get $4 + i32.wrap_i64 + local.set $2 + end + end + local.get $2 + if ;; label = @2 + loop $label$6 ;; label = @3 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $2 + i32.const 10 + i32.rem_u + i32.const 48 + i32.or + i32.store8 + local.get $2 + i32.const 10 + i32.div_u + local.set $3 + local.get $2 + i32.const 10 + i32.ge_u + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.set $2 + br 2 (;@3;) + end + end + end + end + local.get $1 + end ) - ) - (func $29 (; 42 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (block $label$1 (result i32) - (if (result i32) - (local.get $0) - (block (result i32) - (if - (i32.lt_u - (local.get $1) - (i32.const 128) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (br $label$1 - (i32.const 1) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (i32.const 2048) - ) - (block - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 192) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (br $label$1 - (i32.const 2) - ) - ) - ) - (if - (i32.or - (i32.lt_u - (local.get $1) - (i32.const 55296) - ) - (i32.eq - (i32.and - (local.get $1) - (i32.const -8192) - ) - (i32.const 57344) - ) - ) - (block - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 12) - ) - (i32.const 224) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=2 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (br $label$1 - (i32.const 3) - ) - ) - ) - (if (result i32) - (i32.lt_u - (i32.add - (local.get $1) - (i32.const -65536) - ) - (i32.const 1048576) - ) - (block (result i32) - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 18) - ) - (i32.const 240) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 12) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=2 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=3 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.const 4) - ) - (block (result i32) - (i32.store - (call $12) - (i32.const 84) - ) - (i32.const -1) - ) - ) - ) - (i32.const 1) - ) + (func $24 (;37;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + i32.const 0 + local.set $1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + loop $label$5 ;; label = @5 + local.get $1 + i32.const 1711 + i32.add + i32.load8_u + local.get $0 + i32.eq + br_if 1 (;@4;) + local.get $1 + i32.const 1 + i32.add + local.tee $1 + i32.const 87 + i32.ne + br_if 0 (;@5;) + i32.const 87 + local.set $1 + i32.const 1799 + local.set $0 + br 2 (;@3;) + end + end + local.get $1 + if ;; label = @4 + block ;; label = @5 + i32.const 1799 + local.set $0 + br 2 (;@3;) + end + else + i32.const 1799 + local.set $0 + end + br 1 (;@2;) + end + loop $label$8 ;; label = @3 + local.get $0 + local.set $2 + loop $label$9 ;; label = @4 + local.get $2 + i32.const 1 + i32.add + local.set $0 + local.get $2 + i32.load8_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.set $2 + br 2 (;@4;) + end + end + end + local.get $1 + i32.const -1 + i32.add + local.tee $1 + br_if 0 (;@3;) + end + end + local.get $0 + end ) - ) - (func $30 (; 43 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.load8_s - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 74) - ) - ) - ) - ) - (i32.store8 - (local.get $2) - (i32.or - (i32.add - (local.get $1) - (i32.const 255) - ) - (local.get $1) - ) - ) - (local.tee $0 - (if (result i32) - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 8) - ) - (block (result i32) - (i32.store - (local.get $0) - (i32.or - (local.get $1) - (i32.const 32) - ) - ) - (i32.const -1) - ) - (block (result i32) - (i32.store offset=8 - (local.get $0) - (i32.const 0) - ) - (i32.store offset=4 - (local.get $0) - (i32.const 0) - ) - (i32.store offset=28 - (local.get $0) - (local.tee $1 - (i32.load offset=44 - (local.get $0) - ) - ) - ) - (i32.store offset=20 - (local.get $0) - (local.get $1) - ) - (i32.store offset=16 - (local.get $0) - (i32.add - (local.get $1) - (i32.load offset=48 - (local.get $0) - ) - ) - ) - (i32.const 0) - ) - ) - ) + (func $25 (;38;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) + (local $5 i32) (local $6 i32) (local $7 i32) + block $label$1 ;; label = @1 + global.get $global$1 + local.set $7 + global.get $global$1 + i32.const 256 + i32.add + global.set $global$1 + local.get $7 + local.set $6 + block $label$2 ;; label = @2 + local.get $2 + local.get $3 + i32.gt_s + local.get $4 + i32.const 73728 + i32.and + i32.eqz + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $6 + local.get $1 + local.get $2 + local.get $3 + i32.sub + local.tee $5 + i32.const 256 + i32.gt_u + if (result i32) ;; label = @5 + i32.const 256 + else + local.get $5 + end + call $39 + drop + local.get $0 + i32.load + local.tee $1 + i32.const 32 + i32.and + i32.eqz + local.set $4 + local.get $5 + i32.const 255 + i32.gt_u + if ;; label = @5 + block ;; label = @6 + loop $label$7 ;; label = @7 + local.get $4 + if ;; label = @8 + block ;; label = @9 + local.get $6 + i32.const 256 + local.get $0 + call $21 + drop + local.get $0 + i32.load + local.set $1 + end + end + local.get $1 + i32.const 32 + i32.and + i32.eqz + local.set $4 + local.get $5 + i32.const -256 + i32.add + local.tee $5 + i32.const 255 + i32.gt_u + br_if 0 (;@7;) + end + local.get $4 + i32.eqz + br_if 4 (;@2;) + local.get $2 + local.get $3 + i32.sub + i32.const 255 + i32.and + local.set $5 + end + else + local.get $4 + i32.eqz + br_if 3 (;@2;) + end + local.get $6 + local.get $5 + local.get $0 + call $21 + drop + end + end + end + local.get $7 + global.set $global$1 + end ) - ) - (func $31 (; 44 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (block $label$1 (result i32) - (local.set $3 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store8 - (local.tee $4 - (local.get $3) - ) - (local.tee $7 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - ) - (block $label$2 - (block $label$3 - (br_if $label$3 - (local.tee $5 - (i32.load - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - ) - (if - (call $30 - (local.get $0) - ) - (local.set $1 - (i32.const -1) - ) - (block - (local.set $5 - (i32.load - (local.get $2) - ) - ) - (br $label$3) - ) - ) - (br $label$2) - ) - (if - (i32.lt_u - (local.tee $6 - (i32.load - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (local.get $5) - ) - (if - (i32.ne - (local.tee $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (i32.load8_s offset=75 - (local.get $0) - ) - ) - (block - (i32.store - (local.get $2) - (i32.add - (local.get $6) - (i32.const 1) - ) - ) - (i32.store8 - (local.get $6) - (local.get $7) - ) - (br $label$2) - ) - ) - ) - (local.set $1 - (if (result i32) - (i32.eq - (call_indirect (type $0) - (local.get $0) - (local.get $4) - (i32.const 1) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $0) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - (i32.const 1) - ) - (i32.load8_u - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (global.set $global$1 - (local.get $3) - ) - (local.get $1) + (func $26 (;39;) (type $4) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + if (result i32) ;; label = @1 + local.get $0 + local.get $1 + i32.const 0 + call $29 + else + i32.const 0 + end ) - ) - (func $32 (; 45 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (block $label$1 (result i32) - (block $label$2 - (block $label$3 - (br_if $label$3 - (i32.lt_s - (i32.load offset=76 - (local.get $1) - ) - (i32.const 0) - ) - ) - (br_if $label$3 - (i32.eqz - (call $20 - (local.get $1) - ) - ) - ) - (local.set $0 - (block $label$4 (result i32) - (block $label$5 - (br_if $label$5 - (i32.eq - (i32.load8_s offset=75 - (local.get $1) - ) - (local.get $0) - ) - ) - (br_if $label$5 - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $3 - (i32.add - (local.get $1) - (i32.const 20) - ) - ) - ) - ) - (i32.load offset=16 - (local.get $1) - ) - ) - ) - (i32.store - (local.get $3) - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.store8 - (local.get $2) - (local.get $0) - ) - (br $label$4 - (i32.and - (local.get $0) - (i32.const 255) - ) - ) - ) - (call $31 - (local.get $1) - (local.get $0) - ) - ) - ) - (call $13 - (local.get $1) - ) - (br $label$2) - ) - (if - (i32.ne - (i32.load8_s offset=75 - (local.get $1) - ) - (local.get $0) - ) - (if - (i32.lt_u - (local.tee $2 - (i32.load - (local.tee $3 - (i32.add - (local.get $1) - (i32.const 20) - ) - ) - ) - ) - (i32.load offset=16 - (local.get $1) - ) - ) - (block - (i32.store - (local.get $3) - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.store8 - (local.get $2) - (local.get $0) - ) - (local.set $0 - (i32.and - (local.get $0) - (i32.const 255) - ) - ) - (br $label$2) - ) - ) - ) - (local.set $0 - (call $31 - (local.get $1) - (local.get $0) - ) - ) - ) - (local.get $0) + (func $27 (;40;) (type $11) (param $0 f64) (param $1 i32) (result f64) + local.get $0 + local.get $1 + call $28 ) - ) - (func $33 (; 46 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (block $label$1 (result i32) - (local.set $2 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store - (local.tee $3 - (local.get $2) - ) - (local.get $1) - ) - (local.set $0 - (call $18 - (i32.load - (i32.const 1024) - ) - (local.get $0) - (local.get $3) - ) - ) - (global.set $global$1 - (local.get $2) - ) - (local.get $0) + (func $28 (;41;) (type $11) (param $0 f64) (param $1 i32) (result f64) + (local $2 i64) (local $3 i64) + block $label$1 (result f64) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + local.get $0 + i64.reinterpret_f64 + local.tee $2 + i64.const 52 + i64.shr_u + local.tee $3 + i32.wrap_i64 + i32.const 65535 + i32.and + i32.const 2047 + i32.and + i32.const 16 + i32.shl + i32.const 16 + i32.shr_s + i32.const 0 + i32.sub + br_table 0 (;@5;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 1 (;@4;) 2 (;@3;) + end + local.get $1 + local.get $0 + f64.const 0x0p+0 (;=0;) + f64.ne + if (result i32) ;; label = @5 + block (result i32) ;; label = @6 + local.get $0 + f64.const 0x1p+64 (;=18446744073709552000;) + f64.mul + local.get $1 + call $28 + local.set $0 + local.get $1 + i32.load + i32.const -64 + i32.add + end + else + i32.const 0 + end + i32.store + br 2 (;@2;) + end + br 1 (;@2;) + end + local.get $1 + local.get $3 + i32.wrap_i64 + i32.const 2047 + i32.and + i32.const -1022 + i32.add + i32.store + local.get $2 + i64.const -9218868437227405313 + i64.and + i64.const 4602678819172646912 + i64.or + f64.reinterpret_i64 + local.set $0 + end + local.get $0 + end ) - ) - (func $34 (; 47 ;) (type $1) (param $0 i32) (result i32) - (call $32 - (local.get $0) - (i32.load - (i32.const 1024) - ) + (func $29 (;42;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $1 + i32.const 128 + i32.lt_u + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.store8 + i32.const 1 + br 4 (;@1;) + end + end + local.get $1 + i32.const 2048 + i32.lt_u + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 192 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + i32.const 2 + br 4 (;@1;) + end + end + local.get $1 + i32.const 55296 + i32.lt_u + local.get $1 + i32.const -8192 + i32.and + i32.const 57344 + i32.eq + i32.or + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.const 12 + i32.shr_u + i32.const 224 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=2 + i32.const 3 + br 4 (;@1;) + end + end + local.get $1 + i32.const -65536 + i32.add + i32.const 1048576 + i32.lt_u + if (result i32) ;; label = @4 + block (result i32) ;; label = @5 + local.get $0 + local.get $1 + i32.const 18 + i32.shr_u + i32.const 240 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 12 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=2 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=3 + i32.const 4 + end + else + block (result i32) ;; label = @5 + call $12 + i32.const 84 + i32.store + i32.const -1 + end + end + end + else + i32.const 1 + end + end ) - ) - (func $35 (; 48 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 i32) - (local $17 i32) - (local $18 i32) - (local $19 i32) - (local $20 i32) - (local $21 i32) - (block $label$1 (result i32) - (local.set $14 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (local.set $18 - (local.get $14) - ) - (block $label$2 - (if - (i32.lt_u - (local.get $0) - (i32.const 245) - ) - (block - (local.set $3 - (i32.and - (i32.add - (local.get $0) - (i32.const 11) - ) - (i32.const -8) - ) - ) - (if - (i32.and - (local.tee $0 - (i32.shr_u - (local.tee $8 - (i32.load - (i32.const 3652) - ) - ) - (local.tee $2 - (i32.shr_u - (if (result i32) - (i32.lt_u - (local.get $0) - (i32.const 11) - ) - (local.tee $3 - (i32.const 16) - ) - (local.get $3) - ) - (i32.const 3) - ) - ) - ) - ) - (i32.const 3) - ) - (block - (local.set $4 - (i32.load - (local.tee $1 - (i32.add - (local.tee $7 - (i32.load - (local.tee $3 - (i32.add - (local.tee $2 - (i32.add - (i32.shl - (i32.shl - (local.tee $5 - (i32.add - (i32.xor - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 1) - ) - (local.get $2) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $4) - ) - (i32.store - (i32.const 3652) - (i32.and - (local.get $8) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $5) - ) - (i32.const -1) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 12) - ) - ) - ) - (local.get $7) - ) - (block - (i32.store - (local.get $0) - (local.get $2) - ) - (i32.store - (local.get $3) - (local.get $4) - ) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=4 - (local.get $7) - (i32.or - (local.tee $0 - (i32.shl - (local.get $5) - (i32.const 3) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $7) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (local.get $1) - ) - ) - ) - (if - (i32.gt_u - (local.get $3) - (local.tee $16 - (i32.load - (i32.const 3660) - ) - ) - ) - (block - (if - (local.get $0) - (block - (local.set $5 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.tee $0 - (i32.and - (i32.shl - (local.get $0) - (local.get $2) - ) - (i32.or - (local.tee $0 - (i32.shl - (i32.const 2) - (local.get $2) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (local.set $12 - (i32.load - (local.tee $5 - (i32.add - (local.tee $9 - (i32.load - (local.tee $2 - (i32.add - (local.tee $4 - (i32.add - (i32.shl - (i32.shl - (local.tee $11 - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $0) - (local.get $5) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $5) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.eq - (local.get $4) - (local.get $12) - ) - (i32.store - (i32.const 3652) - (local.tee $7 - (i32.and - (local.get $8) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $11) - ) - (i32.const -1) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $12) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $12) - (i32.const 12) - ) - ) - ) - (local.get $9) - ) - (block - (i32.store - (local.get $0) - (local.get $4) - ) - (i32.store - (local.get $2) - (local.get $12) - ) - (local.set $7 - (local.get $8) - ) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=4 - (local.get $9) - (i32.or - (local.get $3) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.tee $4 - (i32.add - (local.get $9) - (local.get $3) - ) - ) - (i32.or - (local.tee $11 - (i32.sub - (i32.shl - (local.get $11) - (i32.const 3) - ) - (local.get $3) - ) - ) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $4) - (local.get $11) - ) - (local.get $11) - ) - (if - (local.get $16) - (block - (local.set $9 - (i32.load - (i32.const 3672) - ) - ) - (local.set $2 - (i32.add - (i32.shl - (i32.shl - (local.tee $0 - (i32.shr_u - (local.get $16) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - (if - (i32.and - (local.get $7) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $3 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (local.set $6 - (local.get $3) - ) - (local.set $1 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 3652) - (i32.or - (local.get $7) - (local.get $0) - ) - ) - (local.set $6 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (local.set $1 - (local.get $2) - ) - ) - ) - (i32.store - (local.get $6) - (local.get $9) - ) - (i32.store offset=12 - (local.get $1) - (local.get $9) - ) - (i32.store offset=8 - (local.get $9) - (local.get $1) - ) - (i32.store offset=12 - (local.get $9) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 3660) - (local.get $11) - ) - (i32.store - (i32.const 3672) - (local.get $4) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (local.get $5) - ) - ) - ) - (if - (local.tee $6 - (i32.load - (i32.const 3656) - ) - ) - (block - (local.set $2 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.get $6) - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (local.set $9 - (i32.sub - (i32.and - (i32.load offset=4 - (local.tee $2 - (i32.load - (i32.add - (i32.shl - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $0) - (local.get $2) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $2) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - ) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.set $1 - (local.get $2) - ) - (loop $label$25 - (block $label$26 - (if - (i32.eqz - (local.tee $0 - (i32.load offset=16 - (local.get $1) - ) - ) - ) - (br_if $label$26 - (i32.eqz - (local.tee $0 - (i32.load offset=20 - (local.get $1) - ) - ) - ) - ) - ) - (if - (local.tee $7 - (i32.lt_u - (local.tee $1 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.get $9) - ) - ) - (local.set $9 - (local.get $1) - ) - ) - (local.set $1 - (local.get $0) - ) - (if - (local.get $7) - (local.set $2 - (local.get $0) - ) - ) - (br $label$25) - ) - ) - (if - (i32.lt_u - (local.get $2) - (local.tee $12 - (i32.load - (i32.const 3668) - ) - ) - ) - (call $fimport$10) - ) - (if - (i32.ge_u - (local.get $2) - (local.tee $13 - (i32.add - (local.get $2) - (local.get $3) - ) - ) - ) - (call $fimport$10) - ) - (local.set $15 - (i32.load offset=24 - (local.get $2) - ) - ) - (block $label$32 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $2) - ) - ) - (local.get $2) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 20) - ) - ) - ) - ) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 16) - ) - ) - ) - ) - ) - (block - (local.set $4 - (i32.const 0) - ) - (br $label$32) - ) - ) - ) - (loop $label$36 - (if - (local.tee $7 - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (local.set $1 - (local.get $11) - ) - (br $label$36) - ) - ) - (if - (local.tee $7 - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (local.set $1 - (local.get $11) - ) - (br $label$36) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $12) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $4 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $11 - (i32.load offset=8 - (local.get $2) - ) - ) - (local.get $12) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 12) - ) - ) - ) - (local.get $2) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $2) - ) - (block - (i32.store - (local.get $7) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $11) - ) - (local.set $4 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (block $label$46 - (if - (local.get $15) - (block - (if - (i32.eq - (local.get $2) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $2) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $4) - ) - (if - (i32.eqz - (local.get $4) - ) - (block - (i32.store - (i32.const 3656) - (i32.and - (local.get $6) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$46) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $15) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $15) - (i32.const 16) - ) - ) - ) - (local.get $2) - ) - (i32.store - (local.get $0) - (local.get $4) - ) - (i32.store offset=20 - (local.get $15) - (local.get $4) - ) - ) - (br_if $label$46 - (i32.eqz - (local.get $4) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $4) - (local.tee $0 - (i32.load - (i32.const 3668) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $4) - (local.get $15) - ) - (if - (local.tee $1 - (i32.load offset=16 - (local.get $2) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $0) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $4) - (local.get $1) - ) - (i32.store offset=24 - (local.get $1) - (local.get $4) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=20 - (local.get $2) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $4) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $4) - ) - ) - ) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $9) - (i32.const 16) - ) - (block - (i32.store offset=4 - (local.get $2) - (i32.or - (local.tee $0 - (i32.add - (local.get $9) - (local.get $3) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $2) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - (block - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $3) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.get $13) - (i32.or - (local.get $9) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $13) - (local.get $9) - ) - (local.get $9) - ) - (if - (local.get $16) - (block - (local.set $7 - (i32.load - (i32.const 3672) - ) - ) - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.tee $0 - (i32.shr_u - (local.get $16) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - (if - (i32.and - (local.get $8) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (local.set $10 - (local.get $1) - ) - (local.set $5 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 3652) - (i32.or - (local.get $8) - (local.get $0) - ) - ) - (local.set $10 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $5 - (local.get $3) - ) - ) - ) - (i32.store - (local.get $10) - (local.get $7) - ) - (i32.store offset=12 - (local.get $5) - (local.get $7) - ) - (i32.store offset=8 - (local.get $7) - (local.get $5) - ) - (i32.store offset=12 - (local.get $7) - (local.get $3) - ) - ) - ) - (i32.store - (i32.const 3660) - (local.get $9) - ) - (i32.store - (i32.const 3672) - (local.get $13) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - (local.set $0 - (local.get $3) - ) - ) - ) - (local.set $0 - (local.get $3) - ) - ) - ) - (if - (i32.gt_u - (local.get $0) - (i32.const -65) - ) - (local.set $0 - (i32.const -1) - ) - (block - (local.set $7 - (i32.and - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 11) - ) - ) - (i32.const -8) - ) - ) - (if - (local.tee $5 - (i32.load - (i32.const 3656) - ) - ) - (block - (local.set $17 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $0) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $7) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $7) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $3 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $3) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (local.set $3 - (i32.sub - (i32.const 0) - (local.get $7) - ) - ) - (block $label$78 - (block $label$79 - (block $label$80 - (if - (local.tee $1 - (i32.load - (i32.add - (i32.shl - (local.get $17) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - ) - (block - (local.set $0 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $17) - (i32.const 1) - ) - ) - ) - (local.set $4 - (i32.const 0) - ) - (local.set $10 - (i32.shl - (local.get $7) - (if (result i32) - (i32.eq - (local.get $17) - (i32.const 31) - ) - (i32.const 0) - (local.get $0) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - (loop $label$84 - (if - (i32.lt_u - (local.tee $6 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $1) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.get $3) - ) - (if - (local.get $6) - (block - (local.set $3 - (local.get $6) - ) - (local.set $0 - (local.get $1) - ) - ) - (block - (local.set $3 - (i32.const 0) - ) - (local.set $0 - (local.get $1) - ) - (br $label$79) - ) - ) - ) - (local.set $1 - (if (result i32) - (i32.or - (i32.eqz - (local.tee $19 - (i32.load offset=20 - (local.get $1) - ) - ) - ) - (i32.eq - (local.get $19) - (local.tee $6 - (i32.load - (i32.add - (i32.add - (local.get $1) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $10) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - (local.get $4) - (local.get $19) - ) - ) - (local.set $10 - (i32.shl - (local.get $10) - (i32.xor - (i32.and - (local.tee $4 - (i32.eqz - (local.get $6) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (if - (local.get $4) - (block - (local.set $4 - (local.get $1) - ) - (local.set $1 - (local.get $0) - ) - (br $label$80) - ) - (block - (local.set $4 - (local.get $1) - ) - (local.set $1 - (local.get $6) - ) - (br $label$84) - ) - ) - ) - ) - (block - (local.set $4 - (i32.const 0) - ) - (local.set $1 - (i32.const 0) - ) - ) - ) - ) - (br_if $label$79 - (local.tee $0 - (if (result i32) - (i32.and - (i32.eqz - (local.get $4) - ) - (i32.eqz - (local.get $1) - ) - ) - (block (result i32) - (if - (i32.eqz - (local.tee $0 - (i32.and - (local.get $5) - (i32.or - (local.tee $0 - (i32.shl - (i32.const 2) - (local.get $17) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (br $label$2) - ) - ) - (local.set $10 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.get $0) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (i32.load - (i32.add - (i32.shl - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $0) - (local.get $10) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $10) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - ) - (local.get $4) - ) - ) - ) - (local.set $4 - (local.get $1) - ) - (br $label$78) - ) - (loop $label$96 - (if - (local.tee $10 - (i32.lt_u - (local.tee $4 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.get $3) - ) - ) - (local.set $3 - (local.get $4) - ) - ) - (if - (local.get $10) - (local.set $1 - (local.get $0) - ) - ) - (if - (local.tee $4 - (i32.load offset=16 - (local.get $0) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (br $label$96) - ) - ) - (br_if $label$96 - (local.tee $0 - (i32.load offset=20 - (local.get $0) - ) - ) - ) - (local.set $4 - (local.get $1) - ) - ) - ) - (if - (local.get $4) - (if - (i32.lt_u - (local.get $3) - (i32.sub - (i32.load - (i32.const 3660) - ) - (local.get $7) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (local.tee $12 - (i32.load - (i32.const 3668) - ) - ) - ) - (call $fimport$10) - ) - (if - (i32.ge_u - (local.get $4) - (local.tee $6 - (i32.add - (local.get $4) - (local.get $7) - ) - ) - ) - (call $fimport$10) - ) - (local.set $10 - (i32.load offset=24 - (local.get $4) - ) - ) - (block $label$104 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $4) - ) - ) - (local.get $4) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - ) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - ) - (block - (local.set $13 - (i32.const 0) - ) - (br $label$104) - ) - ) - ) - (loop $label$108 - (if - (local.tee $11 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $11) - ) - (local.set $1 - (local.get $9) - ) - (br $label$108) - ) - ) - (if - (local.tee $11 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $11) - ) - (local.set $1 - (local.get $9) - ) - (br $label$108) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $12) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $13 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $9 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.get $12) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $11 - (i32.add - (local.get $9) - (i32.const 12) - ) - ) - ) - (local.get $4) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (i32.store - (local.get $11) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $9) - ) - (local.set $13 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (block $label$118 - (if - (local.get $10) - (block - (if - (i32.eq - (local.get $4) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $4) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $13) - ) - (if - (i32.eqz - (local.get $13) - ) - (block - (i32.store - (i32.const 3656) - (local.tee $2 - (i32.and - (local.get $5) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - ) - (br $label$118) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $10) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $10) - (i32.const 16) - ) - ) - ) - (local.get $4) - ) - (i32.store - (local.get $0) - (local.get $13) - ) - (i32.store offset=20 - (local.get $10) - (local.get $13) - ) - ) - (if - (i32.eqz - (local.get $13) - ) - (block - (local.set $2 - (local.get $5) - ) - (br $label$118) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $13) - (local.tee $0 - (i32.load - (i32.const 3668) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $13) - (local.get $10) - ) - (if - (local.tee $1 - (i32.load offset=16 - (local.get $4) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $0) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $13) - (local.get $1) - ) - (i32.store offset=24 - (local.get $1) - (local.get $13) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=20 - (local.get $4) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $13) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $13) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (block $label$136 - (if - (i32.lt_u - (local.get $3) - (i32.const 16) - ) - (block - (i32.store offset=4 - (local.get $4) - (i32.or - (local.tee $0 - (i32.add - (local.get $3) - (local.get $7) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $4) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - (block - (i32.store offset=4 - (local.get $4) - (i32.or - (local.get $7) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $3) - ) - (local.get $3) - ) - (local.set $0 - (i32.shr_u - (local.get $3) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $3) - (i32.const 256) - ) - (block - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.get $0) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - (if - (i32.and - (local.tee $1 - (i32.load - (i32.const 3652) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (local.set $16 - (local.get $1) - ) - (local.set $8 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 3652) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (local.set $16 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $8 - (local.get $3) - ) - ) - ) - (i32.store - (local.get $16) - (local.get $6) - ) - (i32.store offset=12 - (local.get $8) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $8) - ) - (i32.store offset=12 - (local.get $6) - (local.get $3) - ) - (br $label$136) - ) - ) - (local.set $1 - (i32.add - (i32.shl - (local.tee $5 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $3) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $3) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $3) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $5 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $5) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - (i32.store offset=28 - (local.get $6) - (local.get $5) - ) - (i32.store offset=4 - (local.tee $0 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.get $2) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $5) - ) - ) - ) - ) - (block - (i32.store - (i32.const 3656) - (i32.or - (local.get $2) - (local.get $0) - ) - ) - (i32.store - (local.get $1) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $1) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$136) - ) - ) - (local.set $0 - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $5) - (i32.const 1) - ) - ) - ) - (local.set $5 - (i32.shl - (local.get $3) - (if (result i32) - (i32.eq - (local.get $5) - (i32.const 31) - ) - (i32.const 0) - (local.get $1) - ) - ) - ) - (block $label$151 - (block $label$152 - (block $label$153 - (loop $label$154 - (br_if $label$152 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.set $2 - (i32.shl - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$153 - (i32.eqz - (local.tee $1 - (i32.load - (local.tee $5 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $5) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $5 - (local.get $2) - ) - (local.set $0 - (local.get $1) - ) - (br $label$154) - ) - ) - (if - (i32.lt_u - (local.get $5) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $5) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $0) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$136) - ) - ) - (br $label$151) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $3 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $1 - (i32.load - (i32.const 3668) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $1) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $6) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $2) - ) - (i32.store offset=12 - (local.get $6) - (local.get $0) - ) - (i32.store offset=24 - (local.get $6) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $4) - (i32.const 8) - ) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - ) - ) - ) - ) - (if - (i32.ge_u - (local.tee $1 - (i32.load - (i32.const 3660) - ) - ) - (local.get $0) - ) - (block - (local.set $2 - (i32.load - (i32.const 3672) - ) - ) - (if - (i32.gt_u - (local.tee $3 - (i32.sub - (local.get $1) - (local.get $0) - ) - ) - (i32.const 15) - ) - (block - (i32.store - (i32.const 3672) - (local.tee $1 - (i32.add - (local.get $2) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 3660) - (local.get $3) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $1) - (local.get $3) - ) - (local.get $3) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - ) - (block - (i32.store - (i32.const 3660) - (i32.const 0) - ) - (i32.store - (i32.const 3672) - (i32.const 0) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $1) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $2) - (local.get $1) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.gt_u - (local.tee $10 - (i32.load - (i32.const 3664) - ) - ) - (local.get $0) - ) - (block - (i32.store - (i32.const 3664) - (local.tee $3 - (i32.sub - (local.get $10) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 3676) - (local.tee $1 - (i32.add - (local.tee $2 - (i32.load - (i32.const 3676) - ) - ) - (local.get $0) - ) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.le_u - (local.tee $6 - (i32.and - (local.tee $8 - (i32.add - (local.tee $1 - (if (result i32) - (i32.load - (i32.const 4124) - ) - (i32.load - (i32.const 4132) - ) - (block (result i32) - (i32.store - (i32.const 4132) - (i32.const 4096) - ) - (i32.store - (i32.const 4128) - (i32.const 4096) - ) - (i32.store - (i32.const 4136) - (i32.const -1) - ) - (i32.store - (i32.const 4140) - (i32.const -1) - ) - (i32.store - (i32.const 4144) - (i32.const 0) - ) - (i32.store - (i32.const 4096) - (i32.const 0) - ) - (i32.store - (local.get $18) - (local.tee $1 - (i32.xor - (i32.and - (local.get $18) - (i32.const -16) - ) - (i32.const 1431655768) - ) - ) - ) - (i32.store - (i32.const 4124) - (local.get $1) - ) - (i32.const 4096) - ) - ) - ) - (local.tee $13 - (i32.add - (local.get $0) - (i32.const 47) - ) - ) - ) - ) - (local.tee $4 - (i32.sub - (i32.const 0) - (local.get $1) - ) - ) - ) - ) - (local.get $0) - ) - (block - (global.set $global$1 - (local.get $14) - ) - (return - (i32.const 0) - ) - ) - ) - (if - (local.tee $2 - (i32.load - (i32.const 4092) - ) - ) - (if - (i32.or - (i32.le_u - (local.tee $1 - (i32.add - (local.tee $3 - (i32.load - (i32.const 4084) - ) - ) - (local.get $6) - ) - ) - (local.get $3) - ) - (i32.gt_u - (local.get $1) - (local.get $2) - ) - ) - (block - (global.set $global$1 - (local.get $14) - ) - (return - (i32.const 0) - ) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $0) - (i32.const 48) - ) - ) - (block $label$171 - (block $label$172 - (if - (i32.eqz - (i32.and - (i32.load - (i32.const 4096) - ) - (i32.const 4) - ) - ) - (block - (block $label$174 - (block $label$175 - (block $label$176 - (br_if $label$176 - (i32.eqz - (local.tee $3 - (i32.load - (i32.const 3676) - ) - ) - ) - ) - (local.set $2 - (i32.const 4100) - ) - (loop $label$177 - (block $label$178 - (if - (i32.le_u - (local.tee $1 - (i32.load - (local.get $2) - ) - ) - (local.get $3) - ) - (br_if $label$178 - (i32.gt_u - (i32.add - (local.get $1) - (i32.load - (local.tee $5 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - ) - ) - (local.get $3) - ) - ) - ) - (br_if $label$176 - (i32.eqz - (local.tee $1 - (i32.load offset=8 - (local.get $2) - ) - ) - ) - ) - (local.set $2 - (local.get $1) - ) - (br $label$177) - ) - ) - (if - (i32.lt_u - (local.tee $3 - (i32.and - (i32.sub - (local.get $8) - (local.get $10) - ) - (local.get $4) - ) - ) - (i32.const 2147483647) - ) - (if - (i32.eq - (local.tee $1 - (call $38 - (local.get $3) - ) - ) - (i32.add - (i32.load - (local.get $2) - ) - (i32.load - (local.get $5) - ) - ) - ) - (br_if $label$172 - (i32.ne - (local.get $1) - (i32.const -1) - ) - ) - (block - (local.set $2 - (local.get $1) - ) - (local.set $1 - (local.get $3) - ) - (br $label$175) - ) - ) - ) - (br $label$174) - ) - (if - (i32.ne - (local.tee $1 - (call $38 - (i32.const 0) - ) - ) - (i32.const -1) - ) - (block - (local.set $2 - (i32.sub - (i32.and - (i32.add - (local.tee $5 - (i32.add - (local.tee $2 - (i32.load - (i32.const 4128) - ) - ) - (i32.const -1) - ) - ) - (local.tee $3 - (local.get $1) - ) - ) - (i32.sub - (i32.const 0) - (local.get $2) - ) - ) - (local.get $3) - ) - ) - (local.set $4 - (i32.add - (local.tee $3 - (i32.add - (if (result i32) - (i32.and - (local.get $5) - (local.get $3) - ) - (local.get $2) - (i32.const 0) - ) - (local.get $6) - ) - ) - (local.tee $5 - (i32.load - (i32.const 4084) - ) - ) - ) - ) - (if - (i32.and - (i32.gt_u - (local.get $3) - (local.get $0) - ) - (i32.lt_u - (local.get $3) - (i32.const 2147483647) - ) - ) - (block - (if - (local.tee $2 - (i32.load - (i32.const 4092) - ) - ) - (br_if $label$174 - (i32.or - (i32.le_u - (local.get $4) - (local.get $5) - ) - (i32.gt_u - (local.get $4) - (local.get $2) - ) - ) - ) - ) - (br_if $label$172 - (i32.eq - (local.tee $2 - (call $38 - (local.get $3) - ) - ) - (local.get $1) - ) - ) - (local.set $1 - (local.get $3) - ) - (br $label$175) - ) - ) - ) - ) - (br $label$174) - ) - (local.set $5 - (i32.sub - (i32.const 0) - (local.get $1) - ) - ) - (if - (i32.and - (i32.gt_u - (local.get $7) - (local.get $1) - ) - (i32.and - (i32.lt_u - (local.get $1) - (i32.const 2147483647) - ) - (i32.ne - (local.get $2) - (i32.const -1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $3 - (i32.and - (i32.add - (i32.sub - (local.get $13) - (local.get $1) - ) - (local.tee $3 - (i32.load - (i32.const 4132) - ) - ) - ) - (i32.sub - (i32.const 0) - (local.get $3) - ) - ) - ) - (i32.const 2147483647) - ) - (if - (i32.eq - (call $38 - (local.get $3) - ) - (i32.const -1) - ) - (block - (drop - (call $38 - (local.get $5) - ) - ) - (br $label$174) - ) - (local.set $3 - (i32.add - (local.get $3) - (local.get $1) - ) - ) - ) - (local.set $3 - (local.get $1) - ) - ) - (local.set $3 - (local.get $1) - ) - ) - (if - (i32.ne - (local.get $2) - (i32.const -1) - ) - (block - (local.set $1 - (local.get $2) - ) - (br $label$172) - ) - ) - ) - (i32.store - (i32.const 4096) - (i32.or - (i32.load - (i32.const 4096) - ) - (i32.const 4) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $6) - (i32.const 2147483647) - ) - (if - (i32.and - (i32.lt_u - (local.tee $1 - (call $38 - (local.get $6) - ) - ) - (local.tee $3 - (call $38 - (i32.const 0) - ) - ) - ) - (i32.and - (i32.ne - (local.get $1) - (i32.const -1) - ) - (i32.ne - (local.get $3) - (i32.const -1) - ) - ) - ) - (br_if $label$172 - (i32.gt_u - (local.tee $3 - (i32.sub - (local.get $3) - (local.get $1) - ) - ) - (i32.add - (local.get $0) - (i32.const 40) - ) - ) - ) - ) - ) - (br $label$171) - ) - (i32.store - (i32.const 4084) - (local.tee $2 - (i32.add - (i32.load - (i32.const 4084) - ) - (local.get $3) - ) - ) - ) - (if - (i32.gt_u - (local.get $2) - (i32.load - (i32.const 4088) - ) - ) - (i32.store - (i32.const 4088) - (local.get $2) - ) - ) - (block $label$198 - (if - (local.tee $8 - (i32.load - (i32.const 3676) - ) - ) - (block - (local.set $2 - (i32.const 4100) - ) - (block $label$200 - (block $label$201 - (loop $label$202 - (br_if $label$201 - (i32.eq - (local.get $1) - (i32.add - (local.tee $4 - (i32.load - (local.get $2) - ) - ) - (local.tee $5 - (i32.load - (local.tee $7 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - ) - ) - ) - ) - ) - (br_if $label$202 - (local.tee $2 - (i32.load offset=8 - (local.get $2) - ) - ) - ) - ) - (br $label$200) - ) - (if - (i32.eqz - (i32.and - (i32.load offset=12 - (local.get $2) - ) - (i32.const 8) - ) - ) - (if - (i32.and - (i32.lt_u - (local.get $8) - (local.get $1) - ) - (i32.ge_u - (local.get $8) - (local.get $4) - ) - ) - (block - (i32.store - (local.get $7) - (i32.add - (local.get $5) - (local.get $3) - ) - ) - (local.set $5 - (i32.load - (i32.const 3664) - ) - ) - (local.set $1 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $2 - (i32.add - (local.get $8) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 3676) - (local.tee $2 - (i32.add - (local.get $8) - (if (result i32) - (i32.and - (local.get $2) - (i32.const 7) - ) - (local.get $1) - (local.tee $1 - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 3664) - (local.tee $1 - (i32.add - (i32.sub - (local.get $3) - (local.get $1) - ) - (local.get $5) - ) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $2) - (local.get $1) - ) - (i32.const 40) - ) - (i32.store - (i32.const 3680) - (i32.load - (i32.const 4140) - ) - ) - (br $label$198) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.tee $2 - (i32.load - (i32.const 3668) - ) - ) - ) - (block - (i32.store - (i32.const 3668) - (local.get $1) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - (local.set $10 - (i32.add - (local.get $1) - (local.get $3) - ) - ) - (local.set $5 - (i32.const 4100) - ) - (block $label$208 - (block $label$209 - (loop $label$210 - (br_if $label$209 - (i32.eq - (i32.load - (local.get $5) - ) - (local.get $10) - ) - ) - (br_if $label$210 - (local.tee $5 - (i32.load offset=8 - (local.get $5) - ) - ) - ) - (local.set $5 - (i32.const 4100) - ) - ) - (br $label$208) - ) - (if - (i32.and - (i32.load offset=12 - (local.get $5) - ) - (i32.const 8) - ) - (local.set $5 - (i32.const 4100) - ) - (block - (i32.store - (local.get $5) - (local.get $1) - ) - (i32.store - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - (i32.add - (i32.load - (local.get $5) - ) - (local.get $3) - ) - ) - (local.set $7 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $4 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $3 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $5 - (i32.add - (local.get $10) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $6 - (i32.add - (local.tee $13 - (i32.add - (local.get $1) - (if (result i32) - (i32.and - (local.get $4) - (i32.const 7) - ) - (local.get $7) - (i32.const 0) - ) - ) - ) - (local.get $0) - ) - ) - (local.set $7 - (i32.sub - (i32.sub - (local.tee $4 - (i32.add - (local.get $10) - (if (result i32) - (i32.and - (local.get $5) - (i32.const 7) - ) - (local.get $3) - (i32.const 0) - ) - ) - ) - (local.get $13) - ) - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $13) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (block $label$217 - (if - (i32.eq - (local.get $4) - (local.get $8) - ) - (block - (i32.store - (i32.const 3664) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3664) - ) - (local.get $7) - ) - ) - ) - (i32.store - (i32.const 3676) - (local.get $6) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - ) - (block - (if - (i32.eq - (local.get $4) - (i32.load - (i32.const 3672) - ) - ) - (block - (i32.store - (i32.const 3660) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3660) - ) - (local.get $7) - ) - ) - ) - (i32.store - (i32.const 3672) - (local.get $6) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $0) - ) - (local.get $0) - ) - (br $label$217) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (local.tee $0 - (if (result i32) - (i32.eq - (i32.and - (local.tee $0 - (i32.load offset=4 - (local.get $4) - ) - ) - (i32.const 3) - ) - (i32.const 1) - ) - (block (result i32) - (local.set $11 - (i32.and - (local.get $0) - (i32.const -8) - ) - ) - (local.set $1 - (i32.shr_u - (local.get $0) - (i32.const 3) - ) - ) - (block $label$222 - (if - (i32.lt_u - (local.get $0) - (i32.const 256) - ) - (block - (local.set $5 - (i32.load offset=12 - (local.get $4) - ) - ) - (block $label$224 - (if - (i32.ne - (local.tee $3 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.tee $0 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $3) - (local.get $2) - ) - (call $fimport$10) - ) - (br_if $label$224 - (i32.eq - (i32.load offset=12 - (local.get $3) - ) - (local.get $4) - ) - ) - (call $fimport$10) - ) - ) - ) - (if - (i32.eq - (local.get $5) - (local.get $3) - ) - (block - (i32.store - (i32.const 3652) - (i32.and - (i32.load - (i32.const 3652) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$222) - ) - ) - (block $label$228 - (if - (i32.eq - (local.get $5) - (local.get $0) - ) - (local.set $20 - (i32.add - (local.get $5) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $5) - (local.get $2) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $5) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (local.set $20 - (local.get $0) - ) - (br $label$228) - ) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=12 - (local.get $3) - (local.get $5) - ) - (i32.store - (local.get $20) - (local.get $3) - ) - ) - (block - (local.set $8 - (i32.load offset=24 - (local.get $4) - ) - ) - (block $label$234 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $4) - ) - ) - (local.get $4) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.tee $3 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load - (local.get $3) - ) - ) - (local.set $1 - (local.get $3) - ) - (block - (local.set $12 - (i32.const 0) - ) - (br $label$234) - ) - ) - ) - (loop $label$239 - (if - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $5) - ) - (br $label$239) - ) - ) - (if - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $5) - ) - (br $label$239) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $2) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $12 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.get $2) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $3 - (i32.add - (local.get $5) - (i32.const 12) - ) - ) - ) - (local.get $4) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (i32.store - (local.get $3) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $5) - ) - (local.set $12 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (br_if $label$222 - (i32.eqz - (local.get $8) - ) - ) - (block $label$249 - (if - (i32.eq - (local.get $4) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $4) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $12) - ) - (br_if $label$249 - (local.get $12) - ) - (i32.store - (i32.const 3656) - (i32.and - (i32.load - (i32.const 3656) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$222) - ) - (block - (if - (i32.lt_u - (local.get $8) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - ) - (local.get $4) - ) - (i32.store - (local.get $0) - (local.get $12) - ) - (i32.store offset=20 - (local.get $8) - (local.get $12) - ) - ) - (br_if $label$222 - (i32.eqz - (local.get $12) - ) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $12) - (local.tee $1 - (i32.load - (i32.const 3668) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $12) - (local.get $8) - ) - (if - (local.tee $3 - (i32.load - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $3) - (local.get $1) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $12) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $12) - ) - ) - ) - ) - (br_if $label$222 - (i32.eqz - (local.tee $0 - (i32.load offset=4 - (local.get $0) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $12) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $12) - ) - ) - ) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $11) - (local.get $7) - ) - ) - (i32.add - (local.get $4) - (local.get $11) - ) - ) - (local.get $4) - ) - ) - (i32.const 4) - ) - ) - (i32.and - (i32.load - (local.get $0) - ) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $7) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $7) - ) - (local.get $7) - ) - (local.set $0 - (i32.shr_u - (local.get $7) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $7) - (i32.const 256) - ) - (block - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.get $0) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - (block $label$263 - (if - (i32.and - (local.tee $1 - (i32.load - (i32.const 3652) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (block - (if - (i32.ge_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3668) - ) - ) - (block - (local.set $21 - (local.get $1) - ) - (local.set $9 - (local.get $0) - ) - (br $label$263) - ) - ) - (call $fimport$10) - ) - (block - (i32.store - (i32.const 3652) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (local.set $21 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $9 - (local.get $3) - ) - ) - ) - ) - (i32.store - (local.get $21) - (local.get $6) - ) - (i32.store offset=12 - (local.get $9) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $9) - ) - (i32.store offset=12 - (local.get $6) - (local.get $3) - ) - (br $label$217) - ) - ) - (local.set $3 - (i32.add - (i32.shl - (local.tee $2 - (block $label$267 (result i32) - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $7) - (i32.const 8) - ) - ) - (block (result i32) - (drop - (br_if $label$267 - (i32.const 31) - (i32.gt_u - (local.get $7) - (i32.const 16777215) - ) - ) - ) - (i32.or - (i32.and - (i32.shr_u - (local.get $7) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $3 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $3) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - (i32.store offset=28 - (local.get $6) - (local.get $2) - ) - (i32.store offset=4 - (local.tee $0 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (i32.const 3656) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $2) - ) - ) - ) - ) - (block - (i32.store - (i32.const 3656) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $3) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$217) - ) - ) - (local.set $0 - (i32.load - (local.get $3) - ) - ) - (local.set $1 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $2) - (i32.const 1) - ) - ) - ) - (local.set $2 - (i32.shl - (local.get $7) - (if (result i32) - (i32.eq - (local.get $2) - (i32.const 31) - ) - (i32.const 0) - (local.get $1) - ) - ) - ) - (block $label$273 - (block $label$274 - (block $label$275 - (loop $label$276 - (br_if $label$274 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.set $3 - (i32.shl - (local.get $2) - (i32.const 1) - ) - ) - (br_if $label$275 - (i32.eqz - (local.tee $1 - (i32.load - (local.tee $2 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $2) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $2 - (local.get $3) - ) - (local.set $0 - (local.get $1) - ) - (br $label$276) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $2) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $0) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$217) - ) - ) - (br $label$273) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $3 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $1 - (i32.load - (i32.const 3668) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $1) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $6) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $2) - ) - (i32.store offset=12 - (local.get $6) - (local.get $0) - ) - (i32.store offset=24 - (local.get $6) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $13) - (i32.const 8) - ) - ) - ) - ) - ) - (loop $label$281 - (block $label$282 - (if - (i32.le_u - (local.tee $2 - (i32.load - (local.get $5) - ) - ) - (local.get $8) - ) - (br_if $label$282 - (i32.gt_u - (local.tee $13 - (i32.add - (local.get $2) - (i32.load offset=4 - (local.get $5) - ) - ) - ) - (local.get $8) - ) - ) - ) - (local.set $5 - (i32.load offset=8 - (local.get $5) - ) - ) - (br $label$281) - ) - ) - (local.set $2 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $5 - (i32.add - (local.tee $7 - (i32.add - (local.get $13) - (i32.const -47) - ) - ) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $10 - (i32.add - (local.tee $7 - (if (result i32) - (i32.lt_u - (local.tee $2 - (i32.add - (local.get $7) - (if (result i32) - (i32.and - (local.get $5) - (i32.const 7) - ) - (local.get $2) - (i32.const 0) - ) - ) - ) - (local.tee $12 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - ) - (local.get $8) - (local.get $2) - ) - ) - (i32.const 8) - ) - ) - (local.set $5 - (i32.add - (local.get $7) - (i32.const 24) - ) - ) - (local.set $9 - (i32.add - (local.get $3) - (i32.const -40) - ) - ) - (local.set $2 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $4 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 3676) - (local.tee $4 - (i32.add - (local.get $1) - (if (result i32) - (i32.and - (local.get $4) - (i32.const 7) - ) - (local.get $2) - (local.tee $2 - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 3664) - (local.tee $2 - (i32.sub - (local.get $9) - (local.get $2) - ) - ) - ) - (i32.store offset=4 - (local.get $4) - (i32.or - (local.get $2) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $4) - (local.get $2) - ) - (i32.const 40) - ) - (i32.store - (i32.const 3680) - (i32.load - (i32.const 4140) - ) - ) - (i32.store - (local.tee $2 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (i32.const 27) - ) - (i64.store align=4 - (local.get $10) - (i64.load align=4 - (i32.const 4100) - ) - ) - (i64.store offset=8 align=4 - (local.get $10) - (i64.load align=4 - (i32.const 4108) - ) - ) - (i32.store - (i32.const 4100) - (local.get $1) - ) - (i32.store - (i32.const 4104) - (local.get $3) - ) - (i32.store - (i32.const 4112) - (i32.const 0) - ) - (i32.store - (i32.const 4108) - (local.get $10) - ) - (local.set $1 - (local.get $5) - ) - (loop $label$290 - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i32.const 7) - ) - (br_if $label$290 - (i32.lt_u - (i32.add - (local.get $1) - (i32.const 4) - ) - (local.get $13) - ) - ) - ) - (if - (i32.ne - (local.get $7) - (local.get $8) - ) - (block - (i32.store - (local.get $2) - (i32.and - (i32.load - (local.get $2) - ) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $8) - (i32.or - (local.tee $4 - (i32.sub - (local.get $7) - (local.get $8) - ) - ) - (i32.const 1) - ) - ) - (i32.store - (local.get $7) - (local.get $4) - ) - (local.set $1 - (i32.shr_u - (local.get $4) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $4) - (i32.const 256) - ) - (block - (local.set $2 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - (if - (i32.and - (local.tee $3 - (i32.load - (i32.const 3652) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.load - (local.tee $3 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (local.set $15 - (local.get $3) - ) - (local.set $11 - (local.get $1) - ) - ) - ) - (block - (i32.store - (i32.const 3652) - (i32.or - (local.get $3) - (local.get $1) - ) - ) - (local.set $15 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (local.set $11 - (local.get $2) - ) - ) - ) - (i32.store - (local.get $15) - (local.get $8) - ) - (i32.store offset=12 - (local.get $11) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $11) - ) - (i32.store offset=12 - (local.get $8) - (local.get $2) - ) - (br $label$198) - ) - ) - (local.set $2 - (i32.add - (i32.shl - (local.tee $5 - (if (result i32) - (local.tee $1 - (i32.shr_u - (local.get $4) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $4) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $4) - (i32.add - (local.tee $1 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $3 - (i32.shl - (local.get $1) - (local.tee $2 - (i32.and - (i32.shr_u - (i32.add - (local.get $1) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $2) - ) - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $3 - (i32.shl - (local.get $3) - (local.get $1) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $3) - (local.get $1) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $1) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - (i32.store offset=28 - (local.get $8) - (local.get $5) - ) - (i32.store offset=20 - (local.get $8) - (i32.const 0) - ) - (i32.store - (local.get $12) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.tee $3 - (i32.load - (i32.const 3656) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $5) - ) - ) - ) - ) - (block - (i32.store - (i32.const 3656) - (i32.or - (local.get $3) - (local.get $1) - ) - ) - (i32.store - (local.get $2) - (local.get $8) - ) - (i32.store offset=24 - (local.get $8) - (local.get $2) - ) - (i32.store offset=12 - (local.get $8) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $8) - ) - (br $label$198) - ) - ) - (local.set $1 - (i32.load - (local.get $2) - ) - ) - (local.set $3 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $5) - (i32.const 1) - ) - ) - ) - (local.set $5 - (i32.shl - (local.get $4) - (if (result i32) - (i32.eq - (local.get $5) - (i32.const 31) - ) - (i32.const 0) - (local.get $3) - ) - ) - ) - (block $label$304 - (block $label$305 - (block $label$306 - (loop $label$307 - (br_if $label$305 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $1) - ) - (i32.const -8) - ) - (local.get $4) - ) - ) - (local.set $2 - (i32.shl - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$306 - (i32.eqz - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (i32.add - (local.get $1) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $5) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $5 - (local.get $2) - ) - (local.set $1 - (local.get $3) - ) - (br $label$307) - ) - ) - (if - (i32.lt_u - (local.get $5) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $5) - (local.get $8) - ) - (i32.store offset=24 - (local.get $8) - (local.get $1) - ) - (i32.store offset=12 - (local.get $8) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $8) - ) - (br $label$198) - ) - ) - (br $label$304) - ) - (if - (i32.and - (i32.ge_u - (local.tee $5 - (i32.load - (local.tee $2 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - ) - (local.tee $3 - (i32.load - (i32.const 3668) - ) - ) - ) - (i32.ge_u - (local.get $1) - (local.get $3) - ) - ) - (block - (i32.store offset=12 - (local.get $5) - (local.get $8) - ) - (i32.store - (local.get $2) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $5) - ) - (i32.store offset=12 - (local.get $8) - (local.get $1) - ) - (i32.store offset=24 - (local.get $8) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - ) - (block - (if - (i32.or - (i32.eqz - (local.tee $2 - (i32.load - (i32.const 3668) - ) - ) - ) - (i32.lt_u - (local.get $1) - (local.get $2) - ) - ) - (i32.store - (i32.const 3668) - (local.get $1) - ) - ) - (i32.store - (i32.const 4100) - (local.get $1) - ) - (i32.store - (i32.const 4104) - (local.get $3) - ) - (i32.store - (i32.const 4112) - (i32.const 0) - ) - (i32.store - (i32.const 3688) - (i32.load - (i32.const 4124) - ) - ) - (i32.store - (i32.const 3684) - (i32.const -1) - ) - (local.set $2 - (i32.const 0) - ) - (loop $label$314 - (i32.store offset=12 - (local.tee $5 - (i32.add - (i32.shl - (i32.shl - (local.get $2) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - (local.get $5) - ) - (i32.store offset=8 - (local.get $5) - (local.get $5) - ) - (br_if $label$314 - (i32.ne - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 32) - ) - ) - ) - (local.set $5 - (i32.add - (local.get $3) - (i32.const -40) - ) - ) - (local.set $3 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $2 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 3676) - (local.tee $3 - (i32.add - (local.get $1) - (local.tee $1 - (if (result i32) - (i32.and - (local.get $2) - (i32.const 7) - ) - (local.get $3) - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 3664) - (local.tee $1 - (i32.sub - (local.get $5) - (local.get $1) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $3) - (local.get $1) - ) - (i32.const 40) - ) - (i32.store - (i32.const 3680) - (i32.load - (i32.const 4140) - ) - ) - ) - ) - ) - (if - (i32.gt_u - (local.tee $1 - (i32.load - (i32.const 3664) - ) - ) - (local.get $0) - ) - (block - (i32.store - (i32.const 3664) - (local.tee $3 - (i32.sub - (local.get $1) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 3676) - (local.tee $1 - (i32.add - (local.tee $2 - (i32.load - (i32.const 3676) - ) - ) - (local.get $0) - ) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - ) - (i32.store - (call $12) - (i32.const 12) - ) - (global.set $global$1 - (local.get $14) - ) - (i32.const 0) + (func $30 (;43;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + i32.const 74 + i32.add + local.tee $2 + i32.load8_s + local.set $1 + local.get $2 + local.get $1 + i32.const 255 + i32.add + local.get $1 + i32.or + i32.store8 + local.get $0 + i32.load + local.tee $1 + i32.const 8 + i32.and + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $0 + local.get $1 + i32.const 32 + i32.or + i32.store + i32.const -1 + end + else + block (result i32) ;; label = @3 + local.get $0 + i32.const 0 + i32.store offset=8 + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + local.get $0 + i32.load offset=44 + local.tee $1 + i32.store offset=28 + local.get $0 + local.get $1 + i32.store offset=20 + local.get $0 + local.get $1 + local.get $0 + i32.load offset=48 + i32.add + i32.store offset=16 + i32.const 0 + end + end + local.tee $0 + end ) - ) - (func $36 (; 49 ;) (type $2) (param $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (block $label$1 - (if - (i32.eqz - (local.get $0) - ) - (return) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.add - (local.get $0) - (i32.const -8) - ) - ) - (local.tee $11 - (i32.load - (i32.const 3668) - ) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (local.tee $8 - (i32.and - (local.tee $0 - (i32.load - (i32.add - (local.get $0) - (i32.const -4) - ) - ) - ) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (call $fimport$10) - ) - (local.set $6 - (i32.add - (local.get $1) - (local.tee $4 - (i32.and - (local.get $0) - (i32.const -8) - ) - ) - ) - ) - (block $label$5 - (if - (i32.and - (local.get $0) - (i32.const 1) - ) - (block - (local.set $3 - (local.get $1) - ) - (local.set $2 - (local.get $4) - ) - ) - (block - (if - (i32.eqz - (local.get $8) - ) - (return) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.add - (local.get $1) - (i32.sub - (i32.const 0) - (local.tee $8 - (i32.load - (local.get $1) - ) - ) - ) - ) - ) - (local.get $11) - ) - (call $fimport$10) - ) - (local.set $1 - (i32.add - (local.get $8) - (local.get $4) - ) - ) - (if - (i32.eq - (local.get $0) - (i32.load - (i32.const 3672) - ) - ) - (block - (if - (i32.ne - (i32.and - (local.tee $3 - (i32.load - (local.tee $2 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - ) - ) - (i32.const 3) - ) - (i32.const 3) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (i32.store - (i32.const 3660) - (local.get $1) - ) - (i32.store - (local.get $2) - (i32.and - (local.get $3) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $0) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $0) - (local.get $1) - ) - (local.get $1) - ) - (return) - ) - ) - (local.set $10 - (i32.shr_u - (local.get $8) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $8) - (i32.const 256) - ) - (block - (local.set $3 - (i32.load offset=12 - (local.get $0) - ) - ) - (if - (i32.ne - (local.tee $4 - (i32.load offset=8 - (local.get $0) - ) - ) - (local.tee $2 - (i32.add - (i32.shl - (i32.shl - (local.get $10) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (local.get $11) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load offset=12 - (local.get $4) - ) - (local.get $0) - ) - (call $fimport$10) - ) - ) - ) - (if - (i32.eq - (local.get $3) - (local.get $4) - ) - (block - (i32.store - (i32.const 3652) - (i32.and - (i32.load - (i32.const 3652) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $10) - ) - (i32.const -1) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (if - (i32.eq - (local.get $3) - (local.get $2) - ) - (local.set $5 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $3) - (local.get $11) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $2 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - (local.get $0) - ) - (local.set $5 - (local.get $2) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=12 - (local.get $4) - (local.get $3) - ) - (i32.store - (local.get $5) - (local.get $4) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (local.set $12 - (i32.load offset=24 - (local.get $0) - ) - ) - (block $label$22 - (if - (i32.eq - (local.tee $4 - (i32.load offset=12 - (local.get $0) - ) - ) - (local.get $0) - ) - (block - (if - (local.tee $4 - (i32.load - (local.tee $8 - (i32.add - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - (local.set $5 - (local.get $8) - ) - (if - (i32.eqz - (local.tee $4 - (i32.load - (local.get $5) - ) - ) - ) - (block - (local.set $7 - (i32.const 0) - ) - (br $label$22) - ) - ) - ) - (loop $label$27 - (if - (local.tee $10 - (i32.load - (local.tee $8 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $4 - (local.get $10) - ) - (local.set $5 - (local.get $8) - ) - (br $label$27) - ) - ) - (if - (local.tee $10 - (i32.load - (local.tee $8 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $4 - (local.get $10) - ) - (local.set $5 - (local.get $8) - ) - (br $label$27) - ) - ) - ) - (if - (i32.lt_u - (local.get $5) - (local.get $11) - ) - (call $fimport$10) - (block - (i32.store - (local.get $5) - (i32.const 0) - ) - (local.set $7 - (local.get $4) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.load offset=8 - (local.get $0) - ) - ) - (local.get $11) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $8 - (i32.add - (local.get $5) - (i32.const 12) - ) - ) - ) - (local.get $0) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $10 - (i32.add - (local.get $4) - (i32.const 8) - ) - ) - ) - (local.get $0) - ) - (block - (i32.store - (local.get $8) - (local.get $4) - ) - (i32.store - (local.get $10) - (local.get $5) - ) - (local.set $7 - (local.get $4) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (if - (local.get $12) - (block - (if - (i32.eq - (local.get $0) - (i32.load - (local.tee $5 - (i32.add - (i32.shl - (local.tee $4 - (i32.load offset=28 - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - ) - ) - (block - (i32.store - (local.get $5) - (local.get $7) - ) - (if - (i32.eqz - (local.get $7) - ) - (block - (i32.store - (i32.const 3656) - (i32.and - (i32.load - (i32.const 3656) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $12) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $4 - (i32.add - (local.get $12) - (i32.const 16) - ) - ) - ) - (local.get $0) - ) - (i32.store - (local.get $4) - (local.get $7) - ) - (i32.store offset=20 - (local.get $12) - (local.get $7) - ) - ) - (if - (i32.eqz - (local.get $7) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $7) - (local.tee $5 - (i32.load - (i32.const 3668) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $7) - (local.get $12) - ) - (if - (local.tee $4 - (i32.load - (local.tee $8 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $4) - (local.get $5) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $7) - (local.get $4) - ) - (i32.store offset=24 - (local.get $4) - (local.get $7) - ) - ) - ) - ) - (if - (local.tee $4 - (i32.load offset=4 - (local.get $8) - ) - ) - (if - (i32.lt_u - (local.get $4) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $7) - (local.get $4) - ) - (i32.store offset=24 - (local.get $4) - (local.get $7) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - ) - ) - ) - (if - (i32.ge_u - (local.get $3) - (local.get $6) - ) - (call $fimport$10) - ) - (if - (i32.eqz - (i32.and - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - ) - ) - (i32.const 1) - ) - ) - (call $fimport$10) - ) - (if - (i32.and - (local.get $0) - (i32.const 2) - ) - (block - (i32.store - (local.get $1) - (i32.and - (local.get $0) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $2) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $2) - ) - (local.get $2) - ) - ) - (block - (if - (i32.eq - (local.get $6) - (i32.load - (i32.const 3676) - ) - ) - (block - (i32.store - (i32.const 3664) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3664) - ) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 3676) - (local.get $3) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (if - (i32.ne - (local.get $3) - (i32.load - (i32.const 3672) - ) - ) - (return) - ) - (i32.store - (i32.const 3672) - (i32.const 0) - ) - (i32.store - (i32.const 3660) - (i32.const 0) - ) - (return) - ) - ) - (if - (i32.eq - (local.get $6) - (i32.load - (i32.const 3672) - ) - ) - (block - (i32.store - (i32.const 3660) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3660) - ) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 3672) - (local.get $3) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $0) - ) - (local.get $0) - ) - (return) - ) - ) - (local.set $5 - (i32.add - (i32.and - (local.get $0) - (i32.const -8) - ) - (local.get $2) - ) - ) - (local.set $4 - (i32.shr_u - (local.get $0) - (i32.const 3) - ) - ) - (block $label$61 - (if - (i32.lt_u - (local.get $0) - (i32.const 256) - ) - (block - (local.set $2 - (i32.load offset=12 - (local.get $6) - ) - ) - (if - (i32.ne - (local.tee $1 - (i32.load offset=8 - (local.get $6) - ) - ) - (local.tee $0 - (i32.add - (i32.shl - (i32.shl - (local.get $4) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $1) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load offset=12 - (local.get $1) - ) - (local.get $6) - ) - (call $fimport$10) - ) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $1) - ) - (block - (i32.store - (i32.const 3652) - (i32.and - (i32.load - (i32.const 3652) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (br $label$61) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $0) - ) - (local.set $14 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - (local.get $6) - ) - (local.set $14 - (local.get $0) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=12 - (local.get $1) - (local.get $2) - ) - (i32.store - (local.get $14) - (local.get $1) - ) - ) - (block - (local.set $7 - (i32.load offset=24 - (local.get $6) - ) - ) - (block $label$73 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $6) - ) - ) - (local.get $6) - ) - (block - (if - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.tee $2 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - (local.set $2 - (local.get $1) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.get $2) - ) - ) - ) - (block - (local.set $9 - (i32.const 0) - ) - (br $label$73) - ) - ) - ) - (loop $label$78 - (if - (local.tee $4 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (local.set $2 - (local.get $1) - ) - (br $label$78) - ) - ) - (if - (local.tee $4 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (local.set $2 - (local.get $1) - ) - (br $label$78) - ) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $2) - (i32.const 0) - ) - (local.set $9 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $2 - (i32.load offset=8 - (local.get $6) - ) - ) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 12) - ) - ) - ) - (local.get $6) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $4 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $6) - ) - (block - (i32.store - (local.get $1) - (local.get $0) - ) - (i32.store - (local.get $4) - (local.get $2) - ) - (local.set $9 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (if - (local.get $7) - (block - (if - (i32.eq - (local.get $6) - (i32.load - (local.tee $2 - (i32.add - (i32.shl - (local.tee $0 - (i32.load offset=28 - (local.get $6) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - ) - ) - (block - (i32.store - (local.get $2) - (local.get $9) - ) - (if - (i32.eqz - (local.get $9) - ) - (block - (i32.store - (i32.const 3656) - (i32.and - (i32.load - (i32.const 3656) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $0) - ) - (i32.const -1) - ) - ) - ) - (br $label$61) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $7) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $7) - (i32.const 16) - ) - ) - ) - (local.get $6) - ) - (i32.store - (local.get $0) - (local.get $9) - ) - (i32.store offset=20 - (local.get $7) - (local.get $9) - ) - ) - (br_if $label$61 - (i32.eqz - (local.get $9) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $9) - (local.tee $2 - (i32.load - (i32.const 3668) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $9) - (local.get $7) - ) - (if - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $0) - (local.get $2) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $9) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $9) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=4 - (local.get $1) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $9) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $9) - ) - ) - ) - ) - ) - ) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $5) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $5) - ) - (local.get $5) - ) - (if - (i32.eq - (local.get $3) - (i32.load - (i32.const 3672) - ) - ) - (block - (i32.store - (i32.const 3660) - (local.get $5) - ) - (return) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - ) - (local.set $1 - (i32.shr_u - (local.get $2) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.const 256) - ) - (block - (local.set $0 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3692) - ) - ) - (if - (i32.and - (local.tee $2 - (i32.load - (i32.const 3652) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.load - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (local.set $15 - (local.get $2) - ) - (local.set $13 - (local.get $1) - ) - ) - ) - (block - (i32.store - (i32.const 3652) - (i32.or - (local.get $2) - (local.get $1) - ) - ) - (local.set $15 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - (local.set $13 - (local.get $0) - ) - ) - ) - (i32.store - (local.get $15) - (local.get $3) - ) - (i32.store offset=12 - (local.get $13) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $13) - ) - (i32.store offset=12 - (local.get $3) - (local.get $0) - ) - (return) - ) - ) - (local.set $0 - (i32.add - (i32.shl - (local.tee $1 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $2) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $2) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $2) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $4 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $0) - ) - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $0 - (i32.shl - (local.get $1) - (local.get $4) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $0) - (local.get $1) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 3956) - ) - ) - (i32.store offset=28 - (local.get $3) - (local.get $1) - ) - (i32.store offset=20 - (local.get $3) - (i32.const 0) - ) - (i32.store offset=16 - (local.get $3) - (i32.const 0) - ) - (block $label$113 - (if - (i32.and - (local.tee $4 - (i32.load - (i32.const 3656) - ) - ) - (local.tee $5 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (block - (local.set $0 - (i32.load - (local.get $0) - ) - ) - (local.set $4 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $1) - (i32.const 1) - ) - ) - ) - (local.set $1 - (i32.shl - (local.get $2) - (if (result i32) - (i32.eq - (local.get $1) - (i32.const 31) - ) - (i32.const 0) - (local.get $4) - ) - ) - ) - (block $label$117 - (block $label$118 - (block $label$119 - (loop $label$120 - (br_if $label$118 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $2) - ) - ) - (local.set $4 - (i32.shl - (local.get $1) - (i32.const 1) - ) - ) - (br_if $label$119 - (i32.eqz - (local.tee $5 - (i32.load - (local.tee $1 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $1) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $1 - (local.get $4) - ) - (local.set $0 - (local.get $5) - ) - (br $label$120) - ) - ) - (if - (i32.lt_u - (local.get $1) - (i32.load - (i32.const 3668) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $0) - ) - (i32.store offset=12 - (local.get $3) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $3) - ) - (br $label$113) - ) - ) - (br $label$117) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $4 - (i32.load - (i32.const 3668) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $4) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $3) - ) - (i32.store - (local.get $1) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $2) - ) - (i32.store offset=12 - (local.get $3) - (local.get $0) - ) - (i32.store offset=24 - (local.get $3) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - (block - (i32.store - (i32.const 3656) - (i32.or - (local.get $4) - (local.get $5) - ) - ) - (i32.store - (local.get $0) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $0) - ) - (i32.store offset=12 - (local.get $3) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $3) - ) - ) - ) - ) - (i32.store - (i32.const 3684) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3684) - ) - (i32.const -1) - ) - ) - ) - (if - (local.get $0) - (return) - (local.set $0 - (i32.const 4108) - ) - ) - (loop $label$128 - (local.set $0 - (i32.add - (local.tee $2 - (i32.load - (local.get $0) - ) - ) - (i32.const 8) - ) - ) - (br_if $label$128 - (local.get $2) - ) - ) - (i32.store - (i32.const 3684) - (i32.const -1) - ) + (func $31 (;44;) (type $4) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $3 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $3 + local.tee $4 + local.get $1 + i32.const 255 + i32.and + local.tee $7 + i32.store8 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $0 + i32.const 16 + i32.add + local.tee $2 + i32.load + local.tee $5 + br_if 0 (;@3;) + local.get $0 + call $30 + if ;; label = @4 + i32.const -1 + local.set $1 + else + block ;; label = @5 + local.get $2 + i32.load + local.set $5 + br 2 (;@3;) + end + end + br 1 (;@2;) + end + local.get $0 + i32.const 20 + i32.add + local.tee $2 + i32.load + local.tee $6 + local.get $5 + i32.lt_u + if ;; label = @3 + local.get $1 + i32.const 255 + i32.and + local.tee $1 + local.get $0 + i32.load8_s offset=75 + i32.ne + if ;; label = @4 + block ;; label = @5 + local.get $2 + local.get $6 + i32.const 1 + i32.add + i32.store + local.get $6 + local.get $7 + i32.store8 + br 3 (;@2;) + end + end + end + local.get $0 + local.get $4 + i32.const 1 + local.get $0 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + i32.const 1 + i32.eq + if (result i32) ;; label = @3 + local.get $4 + i32.load8_u + else + i32.const -1 + end + local.set $1 + end + local.get $3 + global.set $global$1 + local.get $1 + end ) - ) - (func $37 (; 50 ;) (type $6) - (nop) - ) - (func $38 (; 51 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.add - (local.tee $2 - (i32.load - (global.get $global$0) - ) - ) - (local.tee $0 - (i32.and - (i32.add - (local.get $0) - (i32.const 15) - ) - (i32.const -16) - ) - ) - ) - ) - (if - (i32.or - (i32.and - (i32.gt_s - (local.get $0) - (i32.const 0) - ) - (i32.lt_s - (local.get $1) - (local.get $2) - ) - ) - (i32.lt_s - (local.get $1) - (i32.const 0) - ) - ) - (block - (drop - (call $fimport$6) - ) - (call $fimport$11 - (i32.const 12) - ) - (return - (i32.const -1) - ) - ) - ) - (i32.store - (global.get $global$0) - (local.get $1) - ) - (if - (i32.gt_s - (local.get $1) - (call $fimport$5) - ) - (if - (i32.eqz - (call $fimport$4) - ) - (block - (call $fimport$11 - (i32.const 12) - ) - (i32.store - (global.get $global$0) - (local.get $2) - ) - (return - (i32.const -1) - ) - ) - ) - ) - (local.get $2) + (func $32 (;45;) (type $4) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) + block $label$1 (result i32) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $1 + i32.load offset=76 + i32.const 0 + i32.lt_s + br_if 0 (;@3;) + local.get $1 + call $20 + i32.eqz + br_if 0 (;@3;) + block $label$4 (result i32) ;; label = @4 + block $label$5 ;; label = @5 + local.get $1 + i32.load8_s offset=75 + local.get $0 + i32.eq + br_if 0 (;@5;) + local.get $1 + i32.const 20 + i32.add + local.tee $3 + i32.load + local.tee $2 + local.get $1 + i32.load offset=16 + i32.ge_u + br_if 0 (;@5;) + local.get $3 + local.get $2 + i32.const 1 + i32.add + i32.store + local.get $2 + local.get $0 + i32.store8 + local.get $0 + i32.const 255 + i32.and + br 1 (;@4;) + end + local.get $1 + local.get $0 + call $31 + end + local.set $0 + local.get $1 + call $13 + br 1 (;@2;) + end + local.get $1 + i32.load8_s offset=75 + local.get $0 + i32.ne + if ;; label = @3 + local.get $1 + i32.const 20 + i32.add + local.tee $3 + i32.load + local.tee $2 + local.get $1 + i32.load offset=16 + i32.lt_u + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.get $2 + i32.const 1 + i32.add + i32.store + local.get $2 + local.get $0 + i32.store8 + local.get $0 + i32.const 255 + i32.and + local.set $0 + br 3 (;@2;) + end + end + end + local.get $1 + local.get $0 + call $31 + local.set $0 + end + local.get $0 + end ) - ) - (func $39 (; 52 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $4 - (i32.add - (local.get $0) - (local.get $2) - ) - ) - (if - (i32.ge_s - (local.get $2) - (i32.const 20) - ) - (block - (local.set $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (if - (local.tee $3 - (i32.and - (local.get $0) - (i32.const 3) - ) - ) - (block - (local.set $3 - (i32.sub - (i32.add - (local.get $0) - (i32.const 4) - ) - (local.get $3) - ) - ) - (loop $label$4 - (if - (i32.lt_s - (local.get $0) - (local.get $3) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (br $label$4) - ) - ) - ) - ) - ) - (local.set $3 - (i32.or - (i32.or - (i32.or - (local.get $1) - (i32.shl - (local.get $1) - (i32.const 8) - ) - ) - (i32.shl - (local.get $1) - (i32.const 16) - ) - ) - (i32.shl - (local.get $1) - (i32.const 24) - ) - ) - ) - (local.set $5 - (i32.and - (local.get $4) - (i32.const -4) - ) - ) - (loop $label$6 - (if - (i32.lt_s - (local.get $0) - (local.get $5) - ) - (block - (i32.store - (local.get $0) - (local.get $3) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - (br $label$6) - ) - ) - ) - ) - ) - (loop $label$8 - (if - (i32.lt_s - (local.get $0) - (local.get $4) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (br $label$8) - ) - ) - ) - (i32.sub - (local.get $0) - (local.get $2) - ) + (func $33 (;46;) (type $4) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $2 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $2 + local.tee $3 + local.get $1 + i32.store + i32.const 1024 + i32.load + local.get $0 + local.get $3 + call $18 + local.set $0 + local.get $2 + global.set $global$1 + local.get $0 + end ) - ) - (func $40 (; 53 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (block $label$1 (result i32) - (if - (i32.ge_s - (local.get $2) - (i32.const 4096) - ) - (return - (call $fimport$12 - (local.get $0) - (local.get $1) - (local.get $2) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (if - (i32.eq - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.and - (local.get $1) - (i32.const 3) - ) - ) - (block - (loop $label$4 - (if - (i32.and - (local.get $0) - (i32.const 3) - ) - (block - (if - (i32.eqz - (local.get $2) - ) - (return - (local.get $3) - ) - ) - (i32.store8 - (local.get $0) - (i32.load8_s - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 1) - ) - ) - (br $label$4) - ) - ) - ) - (loop $label$7 - (if - (i32.ge_s - (local.get $2) - (i32.const 4) - ) - (block - (i32.store - (local.get $0) - (i32.load - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 4) - ) - ) - (br $label$7) - ) - ) - ) - ) - ) - (loop $label$9 - (if - (i32.gt_s - (local.get $2) - (i32.const 0) - ) - (block - (i32.store8 - (local.get $0) - (i32.load8_s - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 1) - ) - ) - (br $label$9) - ) - ) - ) - (local.get $3) + (func $34 (;47;) (type $1) (param $0 i32) (result i32) + local.get $0 + i32.const 1024 + i32.load + call $32 ) - ) - (func $41 (; 54 ;) (type $3) (result i32) - (i32.const 0) - ) - (func $42 (; 55 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (call_indirect (type $1) - (local.get $1) - (i32.add - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 0) - ) + (func $35 (;48;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $14 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $14 + local.set $18 + block $label$2 ;; label = @2 + local.get $0 + i32.const 245 + i32.lt_u + if ;; label = @3 + block ;; label = @4 + local.get $0 + i32.const 11 + i32.add + i32.const -8 + i32.and + local.set $3 + i32.const 3652 + i32.load + local.tee $8 + local.get $0 + i32.const 11 + i32.lt_u + if (result i32) ;; label = @5 + i32.const 16 + local.tee $3 + else + local.get $3 + end + i32.const 3 + i32.shr_u + local.tee $2 + i32.shr_u + local.tee $0 + i32.const 3 + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $0 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $2 + i32.add + local.tee $5 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.tee $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $7 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.set $4 + local.get $2 + local.get $4 + i32.eq + if ;; label = @7 + i32.const 3652 + local.get $8 + i32.const 1 + local.get $5 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + else + block ;; label = @8 + local.get $4 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $4 + i32.const 12 + i32.add + local.tee $0 + i32.load + local.get $7 + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $0 + local.get $2 + i32.store + local.get $3 + local.get $4 + i32.store + end + else + call $fimport$10 + end + end + end + local.get $7 + local.get $5 + i32.const 3 + i32.shl + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $7 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + local.get $14 + global.set $global$1 + local.get $1 + return + end + end + local.get $3 + i32.const 3660 + i32.load + local.tee $16 + i32.gt_u + if ;; label = @5 + block ;; label = @6 + local.get $0 + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.get $2 + i32.shl + i32.const 2 + local.get $2 + i32.shl + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.or + i32.and + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $5 + local.get $0 + local.get $5 + i32.shr_u + local.tee $2 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $5 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + i32.add + local.tee $11 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.tee $4 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $9 + i32.const 8 + i32.add + local.tee $5 + i32.load + local.set $12 + local.get $4 + local.get $12 + i32.eq + if ;; label = @9 + i32.const 3652 + local.get $8 + i32.const 1 + local.get $11 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $7 + i32.store + else + block ;; label = @10 + local.get $12 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$10 + end + local.get $12 + i32.const 12 + i32.add + local.tee $0 + i32.load + local.get $9 + i32.eq + if ;; label = @11 + block ;; label = @12 + local.get $0 + local.get $4 + i32.store + local.get $2 + local.get $12 + i32.store + local.get $8 + local.set $7 + end + else + call $fimport$10 + end + end + end + local.get $9 + local.get $3 + i32.const 3 + i32.or + i32.store offset=4 + local.get $9 + local.get $3 + i32.add + local.tee $4 + local.get $11 + i32.const 3 + i32.shl + local.get $3 + i32.sub + local.tee $11 + i32.const 1 + i32.or + i32.store offset=4 + local.get $4 + local.get $11 + i32.add + local.get $11 + i32.store + local.get $16 + if ;; label = @9 + block ;; label = @10 + i32.const 3672 + i32.load + local.set $9 + local.get $16 + i32.const 3 + i32.shr_u + local.tee $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.set $2 + local.get $7 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @11 + local.get $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $0 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @12 + call $fimport$10 + else + block ;; label = @13 + local.get $3 + local.set $6 + local.get $0 + local.set $1 + end + end + else + block ;; label = @12 + i32.const 3652 + local.get $7 + local.get $0 + i32.or + i32.store + local.get $2 + i32.const 8 + i32.add + local.set $6 + local.get $2 + local.set $1 + end + end + local.get $6 + local.get $9 + i32.store + local.get $1 + local.get $9 + i32.store offset=12 + local.get $9 + local.get $1 + i32.store offset=8 + local.get $9 + local.get $2 + i32.store offset=12 + end + end + i32.const 3660 + local.get $11 + i32.store + i32.const 3672 + local.get $4 + i32.store + local.get $14 + global.set $global$1 + local.get $5 + return + end + end + i32.const 3656 + i32.load + local.tee $6 + if ;; label = @7 + block ;; label = @8 + local.get $6 + i32.const 0 + local.get $6 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $2 + local.get $0 + local.get $2 + i32.shr_u + local.tee $1 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $2 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 3956 + i32.add + i32.load + local.tee $2 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.sub + local.set $9 + local.get $2 + local.set $1 + loop $label$25 ;; label = @9 + block $label$26 ;; label = @10 + local.get $1 + i32.load offset=16 + local.tee $0 + i32.eqz + if ;; label = @11 + local.get $1 + i32.load offset=20 + local.tee $0 + i32.eqz + br_if 1 (;@10;) + end + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.sub + local.tee $1 + local.get $9 + i32.lt_u + local.tee $7 + if ;; label = @11 + local.get $1 + local.set $9 + end + local.get $0 + local.set $1 + local.get $7 + if ;; label = @11 + local.get $0 + local.set $2 + end + br 1 (;@9;) + end + end + local.get $2 + i32.const 3668 + i32.load + local.tee $12 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $2 + local.get $2 + local.get $3 + i32.add + local.tee $13 + i32.ge_u + if ;; label = @9 + call $fimport$10 + end + local.get $2 + i32.load offset=24 + local.set $15 + block $label$32 ;; label = @9 + local.get $2 + i32.load offset=12 + local.tee $0 + local.get $2 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $2 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @12 + local.get $2 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @13 + block ;; label = @14 + i32.const 0 + local.set $4 + br 5 (;@9;) + end + end + end + loop $label$36 ;; label = @12 + local.get $0 + i32.const 20 + i32.add + local.tee $11 + i32.load + local.tee $7 + if ;; label = @13 + block ;; label = @14 + local.get $7 + local.set $0 + local.get $11 + local.set $1 + br 2 (;@12;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $11 + i32.load + local.tee $7 + if ;; label = @13 + block ;; label = @14 + local.get $7 + local.set $0 + local.get $11 + local.set $1 + br 2 (;@12;) + end + end + end + local.get $1 + local.get $12 + i32.lt_u + if ;; label = @12 + call $fimport$10 + else + block ;; label = @13 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $4 + end + end + end + else + block ;; label = @11 + local.get $2 + i32.load offset=8 + local.tee $11 + local.get $12 + i32.lt_u + if ;; label = @12 + call $fimport$10 + end + local.get $11 + i32.const 12 + i32.add + local.tee $7 + i32.load + local.get $2 + i32.ne + if ;; label = @12 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $2 + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $7 + local.get $0 + i32.store + local.get $1 + local.get $11 + i32.store + local.get $0 + local.set $4 + end + else + call $fimport$10 + end + end + end + end + block $label$46 ;; label = @9 + local.get $15 + if ;; label = @10 + block ;; label = @11 + local.get $2 + local.get $2 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 3956 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $0 + local.get $4 + i32.store + local.get $4 + i32.eqz + if ;; label = @14 + block ;; label = @15 + i32.const 3656 + local.get $6 + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 6 (;@9;) + end + end + end + else + block ;; label = @13 + local.get $15 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$10 + end + local.get $15 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $2 + i32.eq + if ;; label = @14 + local.get $0 + local.get $4 + i32.store + else + local.get $15 + local.get $4 + i32.store offset=20 + end + local.get $4 + i32.eqz + br_if 4 (;@9;) + end + end + local.get $4 + i32.const 3668 + i32.load + local.tee $0 + i32.lt_u + if ;; label = @12 + call $fimport$10 + end + local.get $4 + local.get $15 + i32.store offset=24 + local.get $2 + i32.load offset=16 + local.tee $1 + if ;; label = @12 + local.get $1 + local.get $0 + i32.lt_u + if ;; label = @13 + call $fimport$10 + else + block ;; label = @14 + local.get $4 + local.get $1 + i32.store offset=16 + local.get $1 + local.get $4 + i32.store offset=24 + end + end + end + local.get $2 + i32.load offset=20 + local.tee $0 + if ;; label = @12 + local.get $0 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @13 + call $fimport$10 + else + block ;; label = @14 + local.get $4 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $4 + i32.store offset=24 + end + end + end + end + end + end + local.get $9 + i32.const 16 + i32.lt_u + if ;; label = @9 + block ;; label = @10 + local.get $2 + local.get $9 + local.get $3 + i32.add + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + else + block ;; label = @10 + local.get $2 + local.get $3 + i32.const 3 + i32.or + i32.store offset=4 + local.get $13 + local.get $9 + i32.const 1 + i32.or + i32.store offset=4 + local.get $13 + local.get $9 + i32.add + local.get $9 + i32.store + local.get $16 + if ;; label = @11 + block ;; label = @12 + i32.const 3672 + i32.load + local.set $7 + local.get $16 + i32.const 3 + i32.shr_u + local.tee $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.set $3 + local.get $8 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @13 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$10 + else + block ;; label = @15 + local.get $1 + local.set $10 + local.get $0 + local.set $5 + end + end + else + block ;; label = @14 + i32.const 3652 + local.get $8 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $10 + local.get $3 + local.set $5 + end + end + local.get $10 + local.get $7 + i32.store + local.get $5 + local.get $7 + i32.store offset=12 + local.get $7 + local.get $5 + i32.store offset=8 + local.get $7 + local.get $3 + i32.store offset=12 + end + end + i32.const 3660 + local.get $9 + i32.store + i32.const 3672 + local.get $13 + i32.store + end + end + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + else + local.get $3 + local.set $0 + end + end + else + local.get $3 + local.set $0 + end + end + else + local.get $0 + i32.const -65 + i32.gt_u + if ;; label = @4 + i32.const -1 + local.set $0 + else + block ;; label = @5 + local.get $0 + i32.const 11 + i32.add + local.tee $0 + i32.const -8 + i32.and + local.set $7 + i32.const 3656 + i32.load + local.tee $5 + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @8 + local.get $7 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @9 + i32.const 31 + else + local.get $7 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $3 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $3 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.set $17 + i32.const 0 + local.get $7 + i32.sub + local.set $3 + block $label$78 ;; label = @8 + block $label$79 ;; label = @9 + block $label$80 ;; label = @10 + local.get $17 + i32.const 2 + i32.shl + i32.const 3956 + i32.add + i32.load + local.tee $1 + if ;; label = @11 + block ;; label = @12 + i32.const 25 + local.get $17 + i32.const 1 + i32.shr_u + i32.sub + local.set $0 + i32.const 0 + local.set $4 + local.get $7 + local.get $17 + i32.const 31 + i32.eq + if (result i32) ;; label = @13 + i32.const 0 + else + local.get $0 + end + i32.shl + local.set $10 + i32.const 0 + local.set $0 + loop $label$84 ;; label = @13 + local.get $1 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.sub + local.tee $6 + local.get $3 + i32.lt_u + if ;; label = @14 + local.get $6 + if ;; label = @15 + block ;; label = @16 + local.get $6 + local.set $3 + local.get $1 + local.set $0 + end + else + block ;; label = @16 + i32.const 0 + local.set $3 + local.get $1 + local.set $0 + br 7 (;@9;) + end + end + end + local.get $1 + i32.load offset=20 + local.tee $19 + i32.eqz + local.get $19 + local.get $1 + i32.const 16 + i32.add + local.get $10 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + i32.load + local.tee $6 + i32.eq + i32.or + if (result i32) ;; label = @14 + local.get $4 + else + local.get $19 + end + local.set $1 + local.get $10 + local.get $6 + i32.eqz + local.tee $4 + i32.const 1 + i32.and + i32.const 1 + i32.xor + i32.shl + local.set $10 + local.get $4 + if ;; label = @14 + block ;; label = @15 + local.get $1 + local.set $4 + local.get $0 + local.set $1 + br 5 (;@10;) + end + else + block ;; label = @15 + local.get $1 + local.set $4 + local.get $6 + local.set $1 + br 2 (;@13;) + end + end + end + end + else + block ;; label = @12 + i32.const 0 + local.set $4 + i32.const 0 + local.set $1 + end + end + end + local.get $4 + i32.eqz + local.get $1 + i32.eqz + i32.and + if (result i32) ;; label = @10 + block (result i32) ;; label = @11 + local.get $5 + i32.const 2 + local.get $17 + i32.shl + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.or + i32.and + local.tee $0 + i32.eqz + if ;; label = @12 + block ;; label = @13 + local.get $7 + local.set $0 + br 11 (;@2;) + end + end + local.get $0 + i32.const 0 + local.get $0 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $10 + local.get $0 + local.get $10 + i32.shr_u + local.tee $4 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $10 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 3956 + i32.add + i32.load + end + else + local.get $4 + end + local.tee $0 + br_if 0 (;@9;) + local.get $1 + local.set $4 + br 1 (;@8;) + end + loop $label$96 ;; label = @9 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.sub + local.tee $4 + local.get $3 + i32.lt_u + local.tee $10 + if ;; label = @10 + local.get $4 + local.set $3 + end + local.get $10 + if ;; label = @10 + local.get $0 + local.set $1 + end + local.get $0 + i32.load offset=16 + local.tee $4 + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.set $0 + br 2 (;@9;) + end + end + local.get $0 + i32.load offset=20 + local.tee $0 + br_if 0 (;@9;) + local.get $1 + local.set $4 + end + end + local.get $4 + if ;; label = @8 + local.get $3 + i32.const 3660 + i32.load + local.get $7 + i32.sub + i32.lt_u + if ;; label = @9 + block ;; label = @10 + local.get $4 + i32.const 3668 + i32.load + local.tee $12 + i32.lt_u + if ;; label = @11 + call $fimport$10 + end + local.get $4 + local.get $4 + local.get $7 + i32.add + local.tee $6 + i32.ge_u + if ;; label = @11 + call $fimport$10 + end + local.get $4 + i32.load offset=24 + local.set $10 + block $label$104 ;; label = @11 + local.get $4 + i32.load offset=12 + local.tee $0 + local.get $4 + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $4 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @14 + local.get $4 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @15 + block ;; label = @16 + i32.const 0 + local.set $13 + br 5 (;@11;) + end + end + end + loop $label$108 ;; label = @14 + local.get $0 + i32.const 20 + i32.add + local.tee $9 + i32.load + local.tee $11 + if ;; label = @15 + block ;; label = @16 + local.get $11 + local.set $0 + local.get $9 + local.set $1 + br 2 (;@14;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $9 + i32.load + local.tee $11 + if ;; label = @15 + block ;; label = @16 + local.get $11 + local.set $0 + local.get $9 + local.set $1 + br 2 (;@14;) + end + end + end + local.get $1 + local.get $12 + i32.lt_u + if ;; label = @14 + call $fimport$10 + else + block ;; label = @15 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $13 + end + end + end + else + block ;; label = @13 + local.get $4 + i32.load offset=8 + local.tee $9 + local.get $12 + i32.lt_u + if ;; label = @14 + call $fimport$10 + end + local.get $9 + i32.const 12 + i32.add + local.tee $11 + i32.load + local.get $4 + i32.ne + if ;; label = @14 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $4 + i32.eq + if ;; label = @14 + block ;; label = @15 + local.get $11 + local.get $0 + i32.store + local.get $1 + local.get $9 + i32.store + local.get $0 + local.set $13 + end + else + call $fimport$10 + end + end + end + end + block $label$118 ;; label = @11 + local.get $10 + if ;; label = @12 + block ;; label = @13 + local.get $4 + local.get $4 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 3956 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @14 + block ;; label = @15 + local.get $0 + local.get $13 + i32.store + local.get $13 + i32.eqz + if ;; label = @16 + block ;; label = @17 + i32.const 3656 + local.get $5 + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $2 + i32.store + br 6 (;@11;) + end + end + end + else + block ;; label = @15 + local.get $10 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @16 + call $fimport$10 + end + local.get $10 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @16 + local.get $0 + local.get $13 + i32.store + else + local.get $10 + local.get $13 + i32.store offset=20 + end + local.get $13 + i32.eqz + if ;; label = @16 + block ;; label = @17 + local.get $5 + local.set $2 + br 6 (;@11;) + end + end + end + end + local.get $13 + i32.const 3668 + i32.load + local.tee $0 + i32.lt_u + if ;; label = @14 + call $fimport$10 + end + local.get $13 + local.get $10 + i32.store offset=24 + local.get $4 + i32.load offset=16 + local.tee $1 + if ;; label = @14 + local.get $1 + local.get $0 + i32.lt_u + if ;; label = @15 + call $fimport$10 + else + block ;; label = @16 + local.get $13 + local.get $1 + i32.store offset=16 + local.get $1 + local.get $13 + i32.store offset=24 + end + end + end + local.get $4 + i32.load offset=20 + local.tee $0 + if ;; label = @14 + local.get $0 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @15 + call $fimport$10 + else + block ;; label = @16 + local.get $13 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $13 + i32.store offset=24 + local.get $5 + local.set $2 + end + end + else + local.get $5 + local.set $2 + end + end + else + local.get $5 + local.set $2 + end + end + block $label$136 ;; label = @11 + local.get $3 + i32.const 16 + i32.lt_u + if ;; label = @12 + block ;; label = @13 + local.get $4 + local.get $3 + local.get $7 + i32.add + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $4 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + else + block ;; label = @13 + local.get $4 + local.get $7 + i32.const 3 + i32.or + i32.store offset=4 + local.get $6 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $3 + i32.add + local.get $3 + i32.store + local.get $3 + i32.const 3 + i32.shr_u + local.set $0 + local.get $3 + i32.const 256 + i32.lt_u + if ;; label = @14 + block ;; label = @15 + local.get $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.set $3 + i32.const 3652 + i32.load + local.tee $1 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @16 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @17 + call $fimport$10 + else + block ;; label = @18 + local.get $1 + local.set $16 + local.get $0 + local.set $8 + end + end + else + block ;; label = @17 + i32.const 3652 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $16 + local.get $3 + local.set $8 + end + end + local.get $16 + local.get $6 + i32.store + local.get $8 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $8 + i32.store offset=8 + local.get $6 + local.get $3 + i32.store offset=12 + br 4 (;@11;) + end + end + local.get $3 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @14 + local.get $3 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @15 + i32.const 31 + else + local.get $3 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $5 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $5 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $5 + i32.const 2 + i32.shl + i32.const 3956 + i32.add + local.set $1 + local.get $6 + local.get $5 + i32.store offset=28 + local.get $6 + i32.const 16 + i32.add + local.tee $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store + local.get $2 + i32.const 1 + local.get $5 + i32.shl + local.tee $0 + i32.and + i32.eqz + if ;; label = @14 + block ;; label = @15 + i32.const 3656 + local.get $2 + local.get $0 + i32.or + i32.store + local.get $1 + local.get $6 + i32.store + local.get $6 + local.get $1 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 4 (;@11;) + end + end + local.get $1 + i32.load + local.set $0 + i32.const 25 + local.get $5 + i32.const 1 + i32.shr_u + i32.sub + local.set $1 + local.get $3 + local.get $5 + i32.const 31 + i32.eq + if (result i32) ;; label = @14 + i32.const 0 + else + local.get $1 + end + i32.shl + local.set $5 + block $label$151 ;; label = @14 + block $label$152 ;; label = @15 + block $label$153 ;; label = @16 + loop $label$154 ;; label = @17 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.eq + br_if 2 (;@15;) + local.get $5 + i32.const 1 + i32.shl + local.set $2 + local.get $0 + i32.const 16 + i32.add + local.get $5 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $5 + i32.load + local.tee $1 + i32.eqz + br_if 1 (;@16;) + local.get $2 + local.set $5 + local.get $1 + local.set $0 + br 0 (;@17;) + end + end + local.get $5 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @16 + call $fimport$10 + else + block ;; label = @17 + local.get $5 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 6 (;@11;) + end + end + br 1 (;@14;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $2 + i32.const 3668 + i32.load + local.tee $1 + i32.ge_u + local.get $0 + local.get $1 + i32.ge_u + i32.and + if ;; label = @15 + block ;; label = @16 + local.get $2 + local.get $6 + i32.store offset=12 + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $2 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $6 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + end + end + local.get $14 + global.set $global$1 + local.get $4 + i32.const 8 + i32.add + return + end + else + local.get $7 + local.set $0 + end + else + local.get $7 + local.set $0 + end + end + else + local.get $7 + local.set $0 + end + end + end + end + end + i32.const 3660 + i32.load + local.tee $1 + local.get $0 + i32.ge_u + if ;; label = @2 + block ;; label = @3 + i32.const 3672 + i32.load + local.set $2 + local.get $1 + local.get $0 + i32.sub + local.tee $3 + i32.const 15 + i32.gt_u + if ;; label = @4 + block ;; label = @5 + i32.const 3672 + local.get $2 + local.get $0 + i32.add + local.tee $1 + i32.store + i32.const 3660 + local.get $3 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $1 + local.get $3 + i32.add + local.get $3 + i32.store + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + end + else + block ;; label = @5 + i32.const 3660 + i32.const 0 + i32.store + i32.const 3672 + i32.const 0 + i32.store + local.get $2 + local.get $1 + i32.const 3 + i32.or + i32.store offset=4 + local.get $2 + local.get $1 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + end + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + i32.const 3664 + i32.load + local.tee $10 + local.get $0 + i32.gt_u + if ;; label = @2 + block ;; label = @3 + i32.const 3664 + local.get $10 + local.get $0 + i32.sub + local.tee $3 + i32.store + i32.const 3676 + i32.const 3676 + i32.load + local.tee $2 + local.get $0 + i32.add + local.tee $1 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + i32.const 4124 + i32.load + if (result i32) ;; label = @2 + i32.const 4132 + i32.load + else + block (result i32) ;; label = @3 + i32.const 4132 + i32.const 4096 + i32.store + i32.const 4128 + i32.const 4096 + i32.store + i32.const 4136 + i32.const -1 + i32.store + i32.const 4140 + i32.const -1 + i32.store + i32.const 4144 + i32.const 0 + i32.store + i32.const 4096 + i32.const 0 + i32.store + local.get $18 + local.get $18 + i32.const -16 + i32.and + i32.const 1431655768 + i32.xor + local.tee $1 + i32.store + i32.const 4124 + local.get $1 + i32.store + i32.const 4096 + end + end + local.tee $1 + local.get $0 + i32.const 47 + i32.add + local.tee $13 + i32.add + local.tee $8 + i32.const 0 + local.get $1 + i32.sub + local.tee $4 + i32.and + local.tee $6 + local.get $0 + i32.le_u + if ;; label = @2 + block ;; label = @3 + local.get $14 + global.set $global$1 + i32.const 0 + return + end + end + i32.const 4092 + i32.load + local.tee $2 + if ;; label = @2 + i32.const 4084 + i32.load + local.tee $3 + local.get $6 + i32.add + local.tee $1 + local.get $3 + i32.le_u + local.get $1 + local.get $2 + i32.gt_u + i32.or + if ;; label = @3 + block ;; label = @4 + local.get $14 + global.set $global$1 + i32.const 0 + return + end + end + end + local.get $0 + i32.const 48 + i32.add + local.set $7 + block $label$171 ;; label = @2 + block $label$172 ;; label = @3 + i32.const 4096 + i32.load + i32.const 4 + i32.and + i32.eqz + if ;; label = @4 + block ;; label = @5 + block $label$174 ;; label = @6 + block $label$175 ;; label = @7 + block $label$176 ;; label = @8 + i32.const 3676 + i32.load + local.tee $3 + i32.eqz + br_if 0 (;@8;) + i32.const 4100 + local.set $2 + loop $label$177 ;; label = @9 + block $label$178 ;; label = @10 + local.get $2 + i32.load + local.tee $1 + local.get $3 + i32.le_u + if ;; label = @11 + local.get $1 + local.get $2 + i32.const 4 + i32.add + local.tee $5 + i32.load + i32.add + local.get $3 + i32.gt_u + br_if 1 (;@10;) + end + local.get $2 + i32.load offset=8 + local.tee $1 + i32.eqz + br_if 2 (;@8;) + local.get $1 + local.set $2 + br 1 (;@9;) + end + end + local.get $8 + local.get $10 + i32.sub + local.get $4 + i32.and + local.tee $3 + i32.const 2147483647 + i32.lt_u + if ;; label = @9 + local.get $3 + call $38 + local.tee $1 + local.get $2 + i32.load + local.get $5 + i32.load + i32.add + i32.eq + if ;; label = @10 + local.get $1 + i32.const -1 + i32.ne + br_if 7 (;@3;) + else + block ;; label = @11 + local.get $1 + local.set $2 + local.get $3 + local.set $1 + br 4 (;@7;) + end + end + end + br 2 (;@6;) + end + i32.const 0 + call $38 + local.tee $1 + i32.const -1 + i32.ne + if ;; label = @8 + block ;; label = @9 + i32.const 4128 + i32.load + local.tee $2 + i32.const -1 + i32.add + local.tee $5 + local.get $1 + local.tee $3 + i32.add + i32.const 0 + local.get $2 + i32.sub + i32.and + local.get $3 + i32.sub + local.set $2 + local.get $5 + local.get $3 + i32.and + if (result i32) ;; label = @10 + local.get $2 + else + i32.const 0 + end + local.get $6 + i32.add + local.tee $3 + i32.const 4084 + i32.load + local.tee $5 + i32.add + local.set $4 + local.get $3 + local.get $0 + i32.gt_u + local.get $3 + i32.const 2147483647 + i32.lt_u + i32.and + if ;; label = @10 + block ;; label = @11 + i32.const 4092 + i32.load + local.tee $2 + if ;; label = @12 + local.get $4 + local.get $5 + i32.le_u + local.get $4 + local.get $2 + i32.gt_u + i32.or + br_if 6 (;@6;) + end + local.get $3 + call $38 + local.tee $2 + local.get $1 + i32.eq + br_if 8 (;@3;) + local.get $3 + local.set $1 + br 4 (;@7;) + end + end + end + end + br 1 (;@6;) + end + i32.const 0 + local.get $1 + i32.sub + local.set $5 + local.get $7 + local.get $1 + i32.gt_u + local.get $1 + i32.const 2147483647 + i32.lt_u + local.get $2 + i32.const -1 + i32.ne + i32.and + i32.and + if ;; label = @7 + local.get $13 + local.get $1 + i32.sub + i32.const 4132 + i32.load + local.tee $3 + i32.add + i32.const 0 + local.get $3 + i32.sub + i32.and + local.tee $3 + i32.const 2147483647 + i32.lt_u + if ;; label = @8 + local.get $3 + call $38 + i32.const -1 + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $5 + call $38 + drop + br 4 (;@6;) + end + else + local.get $3 + local.get $1 + i32.add + local.set $3 + end + else + local.get $1 + local.set $3 + end + else + local.get $1 + local.set $3 + end + local.get $2 + i32.const -1 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $2 + local.set $1 + br 5 (;@3;) + end + end + end + i32.const 4096 + i32.const 4096 + i32.load + i32.const 4 + i32.or + i32.store + end + end + local.get $6 + i32.const 2147483647 + i32.lt_u + if ;; label = @4 + local.get $6 + call $38 + local.tee $1 + i32.const 0 + call $38 + local.tee $3 + i32.lt_u + local.get $1 + i32.const -1 + i32.ne + local.get $3 + i32.const -1 + i32.ne + i32.and + i32.and + if ;; label = @5 + local.get $3 + local.get $1 + i32.sub + local.tee $3 + local.get $0 + i32.const 40 + i32.add + i32.gt_u + br_if 2 (;@3;) + end + end + br 1 (;@2;) + end + i32.const 4084 + i32.const 4084 + i32.load + local.get $3 + i32.add + local.tee $2 + i32.store + local.get $2 + i32.const 4088 + i32.load + i32.gt_u + if ;; label = @3 + i32.const 4088 + local.get $2 + i32.store + end + block $label$198 ;; label = @3 + i32.const 3676 + i32.load + local.tee $8 + if ;; label = @4 + block ;; label = @5 + i32.const 4100 + local.set $2 + block $label$200 ;; label = @6 + block $label$201 ;; label = @7 + loop $label$202 ;; label = @8 + local.get $1 + local.get $2 + i32.load + local.tee $4 + local.get $2 + i32.const 4 + i32.add + local.tee $7 + i32.load + local.tee $5 + i32.add + i32.eq + br_if 1 (;@7;) + local.get $2 + i32.load offset=8 + local.tee $2 + br_if 0 (;@8;) + end + br 1 (;@6;) + end + local.get $2 + i32.load offset=12 + i32.const 8 + i32.and + i32.eqz + if ;; label = @7 + local.get $8 + local.get $1 + i32.lt_u + local.get $8 + local.get $4 + i32.ge_u + i32.and + if ;; label = @8 + block ;; label = @9 + local.get $7 + local.get $5 + local.get $3 + i32.add + i32.store + i32.const 3664 + i32.load + local.set $5 + i32.const 0 + local.get $8 + i32.const 8 + i32.add + local.tee $2 + i32.sub + i32.const 7 + i32.and + local.set $1 + i32.const 3676 + local.get $8 + local.get $2 + i32.const 7 + i32.and + if (result i32) ;; label = @10 + local.get $1 + else + i32.const 0 + local.tee $1 + end + i32.add + local.tee $2 + i32.store + i32.const 3664 + local.get $3 + local.get $1 + i32.sub + local.get $5 + i32.add + local.tee $1 + i32.store + local.get $2 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $1 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 3680 + i32.const 4140 + i32.load + i32.store + br 6 (;@3;) + end + end + end + end + local.get $1 + i32.const 3668 + i32.load + local.tee $2 + i32.lt_u + if ;; label = @6 + block ;; label = @7 + i32.const 3668 + local.get $1 + i32.store + local.get $1 + local.set $2 + end + end + local.get $1 + local.get $3 + i32.add + local.set $10 + i32.const 4100 + local.set $5 + block $label$208 ;; label = @6 + block $label$209 ;; label = @7 + loop $label$210 ;; label = @8 + local.get $5 + i32.load + local.get $10 + i32.eq + br_if 1 (;@7;) + local.get $5 + i32.load offset=8 + local.tee $5 + br_if 0 (;@8;) + i32.const 4100 + local.set $5 + end + br 1 (;@6;) + end + local.get $5 + i32.load offset=12 + i32.const 8 + i32.and + if ;; label = @7 + i32.const 4100 + local.set $5 + else + block ;; label = @8 + local.get $5 + local.get $1 + i32.store + local.get $5 + i32.const 4 + i32.add + local.tee $5 + local.get $5 + i32.load + local.get $3 + i32.add + i32.store + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $4 + i32.sub + i32.const 7 + i32.and + local.set $7 + i32.const 0 + local.get $10 + i32.const 8 + i32.add + local.tee $5 + i32.sub + i32.const 7 + i32.and + local.set $3 + local.get $1 + local.get $4 + i32.const 7 + i32.and + if (result i32) ;; label = @9 + local.get $7 + else + i32.const 0 + end + i32.add + local.tee $13 + local.get $0 + i32.add + local.set $6 + local.get $10 + local.get $5 + i32.const 7 + i32.and + if (result i32) ;; label = @9 + local.get $3 + else + i32.const 0 + end + i32.add + local.tee $4 + local.get $13 + i32.sub + local.get $0 + i32.sub + local.set $7 + local.get $13 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + block $label$217 ;; label = @9 + local.get $4 + local.get $8 + i32.eq + if ;; label = @10 + block ;; label = @11 + i32.const 3664 + i32.const 3664 + i32.load + local.get $7 + i32.add + local.tee $0 + i32.store + i32.const 3676 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + end + else + block ;; label = @11 + local.get $4 + i32.const 3672 + i32.load + i32.eq + if ;; label = @12 + block ;; label = @13 + i32.const 3660 + i32.const 3660 + i32.load + local.get $7 + i32.add + local.tee $0 + i32.store + i32.const 3672 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $0 + i32.add + local.get $0 + i32.store + br 4 (;@9;) + end + end + local.get $4 + i32.load offset=4 + local.tee $0 + i32.const 3 + i32.and + i32.const 1 + i32.eq + if (result i32) ;; label = @12 + block (result i32) ;; label = @13 + local.get $0 + i32.const -8 + i32.and + local.set $11 + local.get $0 + i32.const 3 + i32.shr_u + local.set $1 + block $label$222 ;; label = @14 + local.get $0 + i32.const 256 + i32.lt_u + if ;; label = @15 + block ;; label = @16 + local.get $4 + i32.load offset=12 + local.set $5 + block $label$224 ;; label = @17 + local.get $4 + i32.load offset=8 + local.tee $3 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.tee $0 + i32.ne + if ;; label = @18 + block ;; label = @19 + local.get $3 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $3 + i32.load offset=12 + local.get $4 + i32.eq + br_if 2 (;@17;) + call $fimport$10 + end + end + end + local.get $5 + local.get $3 + i32.eq + if ;; label = @17 + block ;; label = @18 + i32.const 3652 + i32.const 3652 + i32.load + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 4 (;@14;) + end + end + block $label$228 ;; label = @17 + local.get $5 + local.get $0 + i32.eq + if ;; label = @18 + local.get $5 + i32.const 8 + i32.add + local.set $20 + else + block ;; label = @19 + local.get $5 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $5 + i32.const 8 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + block ;; label = @21 + local.get $0 + local.set $20 + br 4 (;@17;) + end + end + call $fimport$10 + end + end + end + local.get $3 + local.get $5 + i32.store offset=12 + local.get $20 + local.get $3 + i32.store + end + else + block ;; label = @16 + local.get $4 + i32.load offset=24 + local.set $8 + block $label$234 ;; label = @17 + local.get $4 + i32.load offset=12 + local.tee $0 + local.get $4 + i32.eq + if ;; label = @18 + block ;; label = @19 + local.get $4 + i32.const 16 + i32.add + local.tee $3 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @20 + local.get $3 + i32.load + local.tee $0 + if ;; label = @21 + local.get $3 + local.set $1 + else + block ;; label = @22 + i32.const 0 + local.set $12 + br 5 (;@17;) + end + end + end + loop $label$239 ;; label = @20 + local.get $0 + i32.const 20 + i32.add + local.tee $5 + i32.load + local.tee $3 + if ;; label = @21 + block ;; label = @22 + local.get $3 + local.set $0 + local.get $5 + local.set $1 + br 2 (;@20;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $5 + i32.load + local.tee $3 + if ;; label = @21 + block ;; label = @22 + local.get $3 + local.set $0 + local.get $5 + local.set $1 + br 2 (;@20;) + end + end + end + local.get $1 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + else + block ;; label = @21 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $12 + end + end + end + else + block ;; label = @19 + local.get $4 + i32.load offset=8 + local.tee $5 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $5 + i32.const 12 + i32.add + local.tee $3 + i32.load + local.get $4 + i32.ne + if ;; label = @20 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + block ;; label = @21 + local.get $3 + local.get $0 + i32.store + local.get $1 + local.get $5 + i32.store + local.get $0 + local.set $12 + end + else + call $fimport$10 + end + end + end + end + local.get $8 + i32.eqz + br_if 2 (;@14;) + block $label$249 ;; label = @17 + local.get $4 + local.get $4 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 3956 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @18 + block ;; label = @19 + local.get $0 + local.get $12 + i32.store + local.get $12 + br_if 2 (;@17;) + i32.const 3656 + i32.const 3656 + i32.load + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 5 (;@14;) + end + else + block ;; label = @19 + local.get $8 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $8 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + local.get $0 + local.get $12 + i32.store + else + local.get $8 + local.get $12 + i32.store offset=20 + end + local.get $12 + i32.eqz + br_if 5 (;@14;) + end + end + end + local.get $12 + i32.const 3668 + i32.load + local.tee $1 + i32.lt_u + if ;; label = @17 + call $fimport$10 + end + local.get $12 + local.get $8 + i32.store offset=24 + local.get $4 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.tee $3 + if ;; label = @17 + local.get $3 + local.get $1 + i32.lt_u + if ;; label = @18 + call $fimport$10 + else + block ;; label = @19 + local.get $12 + local.get $3 + i32.store offset=16 + local.get $3 + local.get $12 + i32.store offset=24 + end + end + end + local.get $0 + i32.load offset=4 + local.tee $0 + i32.eqz + br_if 2 (;@14;) + local.get $0 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @17 + call $fimport$10 + else + block ;; label = @18 + local.get $12 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $12 + i32.store offset=24 + end + end + end + end + end + local.get $11 + local.get $7 + i32.add + local.set $7 + local.get $4 + local.get $11 + i32.add + end + else + local.get $4 + end + local.tee $0 + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const -2 + i32.and + i32.store + local.get $6 + local.get $7 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $7 + i32.add + local.get $7 + i32.store + local.get $7 + i32.const 3 + i32.shr_u + local.set $0 + local.get $7 + i32.const 256 + i32.lt_u + if ;; label = @12 + block ;; label = @13 + local.get $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.set $3 + block $label$263 ;; label = @14 + i32.const 3652 + i32.load + local.tee $1 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @15 + block ;; label = @16 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 3668 + i32.load + i32.ge_u + if ;; label = @17 + block ;; label = @18 + local.get $1 + local.set $21 + local.get $0 + local.set $9 + br 4 (;@14;) + end + end + call $fimport$10 + end + else + block ;; label = @16 + i32.const 3652 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $21 + local.get $3 + local.set $9 + end + end + end + local.get $21 + local.get $6 + i32.store + local.get $9 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $9 + i32.store offset=8 + local.get $6 + local.get $3 + i32.store offset=12 + br 4 (;@9;) + end + end + block $label$267 (result i32) ;; label = @12 + local.get $7 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @13 + block (result i32) ;; label = @14 + i32.const 31 + local.get $7 + i32.const 16777215 + i32.gt_u + br_if 2 (;@12;) + drop + local.get $7 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $3 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $3 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + end + local.tee $2 + i32.const 2 + i32.shl + i32.const 3956 + i32.add + local.set $3 + local.get $6 + local.get $2 + i32.store offset=28 + local.get $6 + i32.const 16 + i32.add + local.tee $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store + i32.const 3656 + i32.load + local.tee $1 + i32.const 1 + local.get $2 + i32.shl + local.tee $0 + i32.and + i32.eqz + if ;; label = @12 + block ;; label = @13 + i32.const 3656 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $3 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 4 (;@9;) + end + end + local.get $3 + i32.load + local.set $0 + i32.const 25 + local.get $2 + i32.const 1 + i32.shr_u + i32.sub + local.set $1 + local.get $7 + local.get $2 + i32.const 31 + i32.eq + if (result i32) ;; label = @12 + i32.const 0 + else + local.get $1 + end + i32.shl + local.set $2 + block $label$273 ;; label = @12 + block $label$274 ;; label = @13 + block $label$275 ;; label = @14 + loop $label$276 ;; label = @15 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.eq + br_if 2 (;@13;) + local.get $2 + i32.const 1 + i32.shl + local.set $3 + local.get $0 + i32.const 16 + i32.add + local.get $2 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $2 + i32.load + local.tee $1 + i32.eqz + br_if 1 (;@14;) + local.get $3 + local.set $2 + local.get $1 + local.set $0 + br 0 (;@15;) + end + end + local.get $2 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$10 + else + block ;; label = @15 + local.get $2 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 6 (;@9;) + end + end + br 1 (;@12;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $2 + i32.const 3668 + i32.load + local.tee $1 + i32.ge_u + local.get $0 + local.get $1 + i32.ge_u + i32.and + if ;; label = @13 + block ;; label = @14 + local.get $2 + local.get $6 + i32.store offset=12 + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $2 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $6 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + end + end + local.get $14 + global.set $global$1 + local.get $13 + i32.const 8 + i32.add + return + end + end + end + loop $label$281 ;; label = @6 + block $label$282 ;; label = @7 + local.get $5 + i32.load + local.tee $2 + local.get $8 + i32.le_u + if ;; label = @8 + local.get $2 + local.get $5 + i32.load offset=4 + i32.add + local.tee $13 + local.get $8 + i32.gt_u + br_if 1 (;@7;) + end + local.get $5 + i32.load offset=8 + local.set $5 + br 1 (;@6;) + end + end + i32.const 0 + local.get $13 + i32.const -47 + i32.add + local.tee $7 + i32.const 8 + i32.add + local.tee $5 + i32.sub + i32.const 7 + i32.and + local.set $2 + local.get $7 + local.get $5 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $2 + else + i32.const 0 + end + i32.add + local.tee $2 + local.get $8 + i32.const 16 + i32.add + local.tee $12 + i32.lt_u + if (result i32) ;; label = @6 + local.get $8 + else + local.get $2 + end + local.tee $7 + i32.const 8 + i32.add + local.set $10 + local.get $7 + i32.const 24 + i32.add + local.set $5 + local.get $3 + i32.const -40 + i32.add + local.set $9 + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $4 + i32.sub + i32.const 7 + i32.and + local.set $2 + i32.const 3676 + local.get $1 + local.get $4 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $2 + else + i32.const 0 + local.tee $2 + end + i32.add + local.tee $4 + i32.store + i32.const 3664 + local.get $9 + local.get $2 + i32.sub + local.tee $2 + i32.store + local.get $4 + local.get $2 + i32.const 1 + i32.or + i32.store offset=4 + local.get $4 + local.get $2 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 3680 + i32.const 4140 + i32.load + i32.store + local.get $7 + i32.const 4 + i32.add + local.tee $2 + i32.const 27 + i32.store + local.get $10 + i32.const 4100 + i64.load align=4 + i64.store align=4 + local.get $10 + i32.const 4108 + i64.load align=4 + i64.store offset=8 align=4 + i32.const 4100 + local.get $1 + i32.store + i32.const 4104 + local.get $3 + i32.store + i32.const 4112 + i32.const 0 + i32.store + i32.const 4108 + local.get $10 + i32.store + local.get $5 + local.set $1 + loop $label$290 ;; label = @6 + local.get $1 + i32.const 4 + i32.add + local.tee $1 + i32.const 7 + i32.store + local.get $1 + i32.const 4 + i32.add + local.get $13 + i32.lt_u + br_if 0 (;@6;) + end + local.get $7 + local.get $8 + i32.ne + if ;; label = @6 + block ;; label = @7 + local.get $2 + local.get $2 + i32.load + i32.const -2 + i32.and + i32.store + local.get $8 + local.get $7 + local.get $8 + i32.sub + local.tee $4 + i32.const 1 + i32.or + i32.store offset=4 + local.get $7 + local.get $4 + i32.store + local.get $4 + i32.const 3 + i32.shr_u + local.set $1 + local.get $4 + i32.const 256 + i32.lt_u + if ;; label = @8 + block ;; label = @9 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.set $2 + i32.const 3652 + i32.load + local.tee $3 + i32.const 1 + local.get $1 + i32.shl + local.tee $1 + i32.and + if ;; label = @10 + local.get $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $1 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$10 + else + block ;; label = @12 + local.get $3 + local.set $15 + local.get $1 + local.set $11 + end + end + else + block ;; label = @11 + i32.const 3652 + local.get $3 + local.get $1 + i32.or + i32.store + local.get $2 + i32.const 8 + i32.add + local.set $15 + local.get $2 + local.set $11 + end + end + local.get $15 + local.get $8 + i32.store + local.get $11 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $11 + i32.store offset=8 + local.get $8 + local.get $2 + i32.store offset=12 + br 6 (;@3;) + end + end + local.get $4 + i32.const 8 + i32.shr_u + local.tee $1 + if (result i32) ;; label = @8 + local.get $4 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @9 + i32.const 31 + else + local.get $4 + i32.const 14 + local.get $1 + local.get $1 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $2 + i32.shl + local.tee $3 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $1 + local.get $2 + i32.or + local.get $3 + local.get $1 + i32.shl + local.tee $3 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $1 + i32.or + i32.sub + local.get $3 + local.get $1 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $1 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $1 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $5 + i32.const 2 + i32.shl + i32.const 3956 + i32.add + local.set $2 + local.get $8 + local.get $5 + i32.store offset=28 + local.get $8 + i32.const 0 + i32.store offset=20 + local.get $12 + i32.const 0 + i32.store + i32.const 3656 + i32.load + local.tee $3 + i32.const 1 + local.get $5 + i32.shl + local.tee $1 + i32.and + i32.eqz + if ;; label = @8 + block ;; label = @9 + i32.const 3656 + local.get $3 + local.get $1 + i32.or + i32.store + local.get $2 + local.get $8 + i32.store + local.get $8 + local.get $2 + i32.store offset=24 + local.get $8 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $8 + i32.store offset=8 + br 6 (;@3;) + end + end + local.get $2 + i32.load + local.set $1 + i32.const 25 + local.get $5 + i32.const 1 + i32.shr_u + i32.sub + local.set $3 + local.get $4 + local.get $5 + i32.const 31 + i32.eq + if (result i32) ;; label = @8 + i32.const 0 + else + local.get $3 + end + i32.shl + local.set $5 + block $label$304 ;; label = @8 + block $label$305 ;; label = @9 + block $label$306 ;; label = @10 + loop $label$307 ;; label = @11 + local.get $1 + i32.load offset=4 + i32.const -8 + i32.and + local.get $4 + i32.eq + br_if 2 (;@9;) + local.get $5 + i32.const 1 + i32.shl + local.set $2 + local.get $1 + i32.const 16 + i32.add + local.get $5 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $5 + i32.load + local.tee $3 + i32.eqz + br_if 1 (;@10;) + local.get $2 + local.set $5 + local.get $3 + local.set $1 + br 0 (;@11;) + end + end + local.get $5 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $5 + local.get $8 + i32.store + local.get $8 + local.get $1 + i32.store offset=24 + local.get $8 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $8 + i32.store offset=8 + br 8 (;@3;) + end + end + br 1 (;@8;) + end + local.get $1 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $5 + i32.const 3668 + i32.load + local.tee $3 + i32.ge_u + local.get $1 + local.get $3 + i32.ge_u + i32.and + if ;; label = @9 + block ;; label = @10 + local.get $5 + local.get $8 + i32.store offset=12 + local.get $2 + local.get $8 + i32.store + local.get $8 + local.get $5 + i32.store offset=8 + local.get $8 + local.get $1 + i32.store offset=12 + local.get $8 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + end + end + else + block ;; label = @5 + i32.const 3668 + i32.load + local.tee $2 + i32.eqz + local.get $1 + local.get $2 + i32.lt_u + i32.or + if ;; label = @6 + i32.const 3668 + local.get $1 + i32.store + end + i32.const 4100 + local.get $1 + i32.store + i32.const 4104 + local.get $3 + i32.store + i32.const 4112 + i32.const 0 + i32.store + i32.const 3688 + i32.const 4124 + i32.load + i32.store + i32.const 3684 + i32.const -1 + i32.store + i32.const 0 + local.set $2 + loop $label$314 ;; label = @6 + local.get $2 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.tee $5 + local.get $5 + i32.store offset=12 + local.get $5 + local.get $5 + i32.store offset=8 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 32 + i32.ne + br_if 0 (;@6;) + end + local.get $3 + i32.const -40 + i32.add + local.set $5 + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $2 + i32.sub + i32.const 7 + i32.and + local.set $3 + i32.const 3676 + local.get $1 + local.get $2 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $3 + else + i32.const 0 + end + local.tee $1 + i32.add + local.tee $3 + i32.store + i32.const 3664 + local.get $5 + local.get $1 + i32.sub + local.tee $1 + i32.store + local.get $3 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $1 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 3680 + i32.const 4140 + i32.load + i32.store + end + end + end + i32.const 3664 + i32.load + local.tee $1 + local.get $0 + i32.gt_u + if ;; label = @3 + block ;; label = @4 + i32.const 3664 + local.get $1 + local.get $0 + i32.sub + local.tee $3 + i32.store + i32.const 3676 + i32.const 3676 + i32.load + local.tee $2 + local.get $0 + i32.add + local.tee $1 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + end + call $12 + i32.const 12 + i32.store + local.get $14 + global.set $global$1 + i32.const 0 + end ) - ) - (func $43 (; 56 ;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - (call_indirect (type $0) - (local.get $1) - (local.get $2) - (local.get $3) - (i32.add - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.const 2) - ) + (func $36 (;49;) (type $2) (param $0 i32) + (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) + block $label$1 ;; label = @1 + local.get $0 + i32.eqz + if ;; label = @2 + return + end + local.get $0 + i32.const -8 + i32.add + local.tee $1 + i32.const 3668 + i32.load + local.tee $11 + i32.lt_u + if ;; label = @2 + call $fimport$10 + end + local.get $0 + i32.const -4 + i32.add + i32.load + local.tee $0 + i32.const 3 + i32.and + local.tee $8 + i32.const 1 + i32.eq + if ;; label = @2 + call $fimport$10 + end + local.get $1 + local.get $0 + i32.const -8 + i32.and + local.tee $4 + i32.add + local.set $6 + block $label$5 ;; label = @2 + local.get $0 + i32.const 1 + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $1 + local.set $3 + local.get $4 + local.set $2 + end + else + block ;; label = @4 + local.get $8 + i32.eqz + if ;; label = @5 + return + end + local.get $1 + i32.const 0 + local.get $1 + i32.load + local.tee $8 + i32.sub + i32.add + local.tee $0 + local.get $11 + i32.lt_u + if ;; label = @5 + call $fimport$10 + end + local.get $8 + local.get $4 + i32.add + local.set $1 + local.get $0 + i32.const 3672 + i32.load + i32.eq + if ;; label = @5 + block ;; label = @6 + local.get $6 + i32.const 4 + i32.add + local.tee $2 + i32.load + local.tee $3 + i32.const 3 + i32.and + i32.const 3 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 6 (;@2;) + end + end + i32.const 3660 + local.get $1 + i32.store + local.get $2 + local.get $3 + i32.const -2 + i32.and + i32.store + local.get $0 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $0 + local.get $1 + i32.add + local.get $1 + i32.store + return + end + end + local.get $8 + i32.const 3 + i32.shr_u + local.set $10 + local.get $8 + i32.const 256 + i32.lt_u + if ;; label = @5 + block ;; label = @6 + local.get $0 + i32.load offset=12 + local.set $3 + local.get $0 + i32.load offset=8 + local.tee $4 + local.get $10 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.tee $2 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $4 + local.get $11 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $4 + i32.load offset=12 + local.get $0 + i32.ne + if ;; label = @9 + call $fimport$10 + end + end + end + local.get $3 + local.get $4 + i32.eq + if ;; label = @7 + block ;; label = @8 + i32.const 3652 + i32.const 3652 + i32.load + i32.const 1 + local.get $10 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 6 (;@2;) + end + end + local.get $3 + local.get $2 + i32.eq + if ;; label = @7 + local.get $3 + i32.const 8 + i32.add + local.set $5 + else + block ;; label = @8 + local.get $3 + local.get $11 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $3 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.get $0 + i32.eq + if ;; label = @9 + local.get $2 + local.set $5 + else + call $fimport$10 + end + end + end + local.get $4 + local.get $3 + i32.store offset=12 + local.get $5 + local.get $4 + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 4 (;@2;) + end + end + local.get $0 + i32.load offset=24 + local.set $12 + block $label$22 ;; label = @5 + local.get $0 + i32.load offset=12 + local.tee $4 + local.get $0 + i32.eq + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 16 + i32.add + local.tee $5 + i32.const 4 + i32.add + local.tee $8 + i32.load + local.tee $4 + if ;; label = @8 + local.get $8 + local.set $5 + else + local.get $5 + i32.load + local.tee $4 + i32.eqz + if ;; label = @9 + block ;; label = @10 + i32.const 0 + local.set $7 + br 5 (;@5;) + end + end + end + loop $label$27 ;; label = @8 + local.get $4 + i32.const 20 + i32.add + local.tee $8 + i32.load + local.tee $10 + if ;; label = @9 + block ;; label = @10 + local.get $10 + local.set $4 + local.get $8 + local.set $5 + br 2 (;@8;) + end + end + local.get $4 + i32.const 16 + i32.add + local.tee $8 + i32.load + local.tee $10 + if ;; label = @9 + block ;; label = @10 + local.get $10 + local.set $4 + local.get $8 + local.set $5 + br 2 (;@8;) + end + end + end + local.get $5 + local.get $11 + i32.lt_u + if ;; label = @8 + call $fimport$10 + else + block ;; label = @9 + local.get $5 + i32.const 0 + i32.store + local.get $4 + local.set $7 + end + end + end + else + block ;; label = @7 + local.get $0 + i32.load offset=8 + local.tee $5 + local.get $11 + i32.lt_u + if ;; label = @8 + call $fimport$10 + end + local.get $5 + i32.const 12 + i32.add + local.tee $8 + i32.load + local.get $0 + i32.ne + if ;; label = @8 + call $fimport$10 + end + local.get $4 + i32.const 8 + i32.add + local.tee $10 + i32.load + local.get $0 + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $8 + local.get $4 + i32.store + local.get $10 + local.get $5 + i32.store + local.get $4 + local.set $7 + end + else + call $fimport$10 + end + end + end + end + local.get $12 + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $0 + i32.load offset=28 + local.tee $4 + i32.const 2 + i32.shl + i32.const 3956 + i32.add + local.tee $5 + i32.load + i32.eq + if ;; label = @7 + block ;; label = @8 + local.get $5 + local.get $7 + i32.store + local.get $7 + i32.eqz + if ;; label = @9 + block ;; label = @10 + i32.const 3656 + i32.const 3656 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 8 (;@2;) + end + end + end + else + block ;; label = @8 + local.get $12 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $12 + i32.const 16 + i32.add + local.tee $4 + i32.load + local.get $0 + i32.eq + if ;; label = @9 + local.get $4 + local.get $7 + i32.store + else + local.get $12 + local.get $7 + i32.store offset=20 + end + local.get $7 + i32.eqz + if ;; label = @9 + block ;; label = @10 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 8 (;@2;) + end + end + end + end + local.get $7 + i32.const 3668 + i32.load + local.tee $5 + i32.lt_u + if ;; label = @7 + call $fimport$10 + end + local.get $7 + local.get $12 + i32.store offset=24 + local.get $0 + i32.const 16 + i32.add + local.tee $8 + i32.load + local.tee $4 + if ;; label = @7 + local.get $4 + local.get $5 + i32.lt_u + if ;; label = @8 + call $fimport$10 + else + block ;; label = @9 + local.get $7 + local.get $4 + i32.store offset=16 + local.get $4 + local.get $7 + i32.store offset=24 + end + end + end + local.get $8 + i32.load offset=4 + local.tee $4 + if ;; label = @7 + local.get $4 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @8 + call $fimport$10 + else + block ;; label = @9 + local.get $7 + local.get $4 + i32.store offset=20 + local.get $4 + local.get $7 + i32.store offset=24 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + else + block ;; label = @8 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + end + else + block ;; label = @6 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + end + end + end + local.get $3 + local.get $6 + i32.ge_u + if ;; label = @2 + call $fimport$10 + end + local.get $6 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 1 + i32.and + i32.eqz + if ;; label = @2 + call $fimport$10 + end + local.get $0 + i32.const 2 + i32.and + if ;; label = @2 + block ;; label = @3 + local.get $1 + local.get $0 + i32.const -2 + i32.and + i32.store + local.get $3 + local.get $2 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $2 + i32.add + local.get $2 + i32.store + end + else + block ;; label = @3 + local.get $6 + i32.const 3676 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 3664 + i32.const 3664 + i32.load + local.get $2 + i32.add + local.tee $0 + i32.store + i32.const 3676 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + i32.const 3672 + i32.load + i32.ne + if ;; label = @6 + return + end + i32.const 3672 + i32.const 0 + i32.store + i32.const 3660 + i32.const 0 + i32.store + return + end + end + local.get $6 + i32.const 3672 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 3660 + i32.const 3660 + i32.load + local.get $2 + i32.add + local.tee $0 + i32.store + i32.const 3672 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $0 + i32.add + local.get $0 + i32.store + return + end + end + local.get $0 + i32.const -8 + i32.and + local.get $2 + i32.add + local.set $5 + local.get $0 + i32.const 3 + i32.shr_u + local.set $4 + block $label$61 ;; label = @4 + local.get $0 + i32.const 256 + i32.lt_u + if ;; label = @5 + block ;; label = @6 + local.get $6 + i32.load offset=12 + local.set $2 + local.get $6 + i32.load offset=8 + local.tee $1 + local.get $4 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.tee $0 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $1 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $1 + i32.load offset=12 + local.get $6 + i32.ne + if ;; label = @9 + call $fimport$10 + end + end + end + local.get $2 + local.get $1 + i32.eq + if ;; label = @7 + block ;; label = @8 + i32.const 3652 + i32.const 3652 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 4 (;@4;) + end + end + local.get $2 + local.get $0 + i32.eq + if ;; label = @7 + local.get $2 + i32.const 8 + i32.add + local.set $14 + else + block ;; label = @8 + local.get $2 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $2 + i32.const 8 + i32.add + local.tee $0 + i32.load + local.get $6 + i32.eq + if ;; label = @9 + local.get $0 + local.set $14 + else + call $fimport$10 + end + end + end + local.get $1 + local.get $2 + i32.store offset=12 + local.get $14 + local.get $1 + i32.store + end + else + block ;; label = @6 + local.get $6 + i32.load offset=24 + local.set $7 + block $label$73 ;; label = @7 + local.get $6 + i32.load offset=12 + local.tee $0 + local.get $6 + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $6 + i32.const 16 + i32.add + local.tee $2 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + if ;; label = @10 + local.get $1 + local.set $2 + else + local.get $2 + i32.load + local.tee $0 + i32.eqz + if ;; label = @11 + block ;; label = @12 + i32.const 0 + local.set $9 + br 5 (;@7;) + end + end + end + loop $label$78 ;; label = @10 + local.get $0 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $4 + if ;; label = @11 + block ;; label = @12 + local.get $4 + local.set $0 + local.get $1 + local.set $2 + br 2 (;@10;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $4 + if ;; label = @11 + block ;; label = @12 + local.get $4 + local.set $0 + local.get $1 + local.set $2 + br 2 (;@10;) + end + end + end + local.get $2 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $2 + i32.const 0 + i32.store + local.get $0 + local.set $9 + end + end + end + else + block ;; label = @9 + local.get $6 + i32.load offset=8 + local.tee $2 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + end + local.get $2 + i32.const 12 + i32.add + local.tee $1 + i32.load + local.get $6 + i32.ne + if ;; label = @10 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $4 + i32.load + local.get $6 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $1 + local.get $0 + i32.store + local.get $4 + local.get $2 + i32.store + local.get $0 + local.set $9 + end + else + call $fimport$10 + end + end + end + end + local.get $7 + if ;; label = @7 + block ;; label = @8 + local.get $6 + local.get $6 + i32.load offset=28 + local.tee $0 + i32.const 2 + i32.shl + i32.const 3956 + i32.add + local.tee $2 + i32.load + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $2 + local.get $9 + i32.store + local.get $9 + i32.eqz + if ;; label = @11 + block ;; label = @12 + i32.const 3656 + i32.const 3656 + i32.load + i32.const 1 + local.get $0 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 8 (;@4;) + end + end + end + else + block ;; label = @10 + local.get $7 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$10 + end + local.get $7 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $6 + i32.eq + if ;; label = @11 + local.get $0 + local.get $9 + i32.store + else + local.get $7 + local.get $9 + i32.store offset=20 + end + local.get $9 + i32.eqz + br_if 6 (;@4;) + end + end + local.get $9 + i32.const 3668 + i32.load + local.tee $2 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $9 + local.get $7 + i32.store offset=24 + local.get $6 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + if ;; label = @9 + local.get $0 + local.get $2 + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $9 + local.get $0 + i32.store offset=16 + local.get $0 + local.get $9 + i32.store offset=24 + end + end + end + local.get $1 + i32.load offset=4 + local.tee $0 + if ;; label = @9 + local.get $0 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $9 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $9 + i32.store offset=24 + end + end + end + end + end + end + end + end + local.get $3 + local.get $5 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $5 + i32.add + local.get $5 + i32.store + local.get $3 + i32.const 3672 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 3660 + local.get $5 + i32.store + return + end + else + local.get $5 + local.set $2 + end + end + end + local.get $2 + i32.const 3 + i32.shr_u + local.set $1 + local.get $2 + i32.const 256 + i32.lt_u + if ;; label = @2 + block ;; label = @3 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3692 + i32.add + local.set $0 + i32.const 3652 + i32.load + local.tee $2 + i32.const 1 + local.get $1 + i32.shl + local.tee $1 + i32.and + if ;; label = @4 + local.get $0 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $1 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @5 + call $fimport$10 + else + block ;; label = @6 + local.get $2 + local.set $15 + local.get $1 + local.set $13 + end + end + else + block ;; label = @5 + i32.const 3652 + local.get $2 + local.get $1 + i32.or + i32.store + local.get $0 + i32.const 8 + i32.add + local.set $15 + local.get $0 + local.set $13 + end + end + local.get $15 + local.get $3 + i32.store + local.get $13 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $13 + i32.store offset=8 + local.get $3 + local.get $0 + i32.store offset=12 + return + end + end + local.get $2 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @2 + local.get $2 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @3 + i32.const 31 + else + local.get $2 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $4 + local.get $0 + i32.or + local.get $1 + local.get $4 + i32.shl + local.tee $0 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $1 + i32.or + i32.sub + local.get $0 + local.get $1 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $1 + i32.const 2 + i32.shl + i32.const 3956 + i32.add + local.set $0 + local.get $3 + local.get $1 + i32.store offset=28 + local.get $3 + i32.const 0 + i32.store offset=20 + local.get $3 + i32.const 0 + i32.store offset=16 + block $label$113 ;; label = @2 + i32.const 3656 + i32.load + local.tee $4 + i32.const 1 + local.get $1 + i32.shl + local.tee $5 + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $0 + i32.load + local.set $0 + i32.const 25 + local.get $1 + i32.const 1 + i32.shr_u + i32.sub + local.set $4 + local.get $2 + local.get $1 + i32.const 31 + i32.eq + if (result i32) ;; label = @5 + i32.const 0 + else + local.get $4 + end + i32.shl + local.set $1 + block $label$117 ;; label = @5 + block $label$118 ;; label = @6 + block $label$119 ;; label = @7 + loop $label$120 ;; label = @8 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $2 + i32.eq + br_if 2 (;@6;) + local.get $1 + i32.const 1 + i32.shl + local.set $4 + local.get $0 + i32.const 16 + i32.add + local.get $1 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $1 + i32.load + local.tee $5 + i32.eqz + br_if 1 (;@7;) + local.get $4 + local.set $1 + local.get $5 + local.set $0 + br 0 (;@8;) + end + end + local.get $1 + i32.const 3668 + i32.load + i32.lt_u + if ;; label = @7 + call $fimport$10 + else + block ;; label = @8 + local.get $1 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.store offset=24 + local.get $3 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $3 + i32.store offset=8 + br 6 (;@2;) + end + end + br 1 (;@5;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $2 + i32.const 3668 + i32.load + local.tee $4 + i32.ge_u + local.get $0 + local.get $4 + i32.ge_u + i32.and + if ;; label = @6 + block ;; label = @7 + local.get $2 + local.get $3 + i32.store offset=12 + local.get $1 + local.get $3 + i32.store + local.get $3 + local.get $2 + i32.store offset=8 + local.get $3 + local.get $0 + i32.store offset=12 + local.get $3 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + else + block ;; label = @4 + i32.const 3656 + local.get $4 + local.get $5 + i32.or + i32.store + local.get $0 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.store offset=24 + local.get $3 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $3 + i32.store offset=8 + end + end + end + i32.const 3684 + i32.const 3684 + i32.load + i32.const -1 + i32.add + local.tee $0 + i32.store + local.get $0 + if ;; label = @2 + return + else + i32.const 4108 + local.set $0 + end + loop $label$128 ;; label = @2 + local.get $0 + i32.load + local.tee $2 + i32.const 8 + i32.add + local.set $0 + local.get $2 + br_if 0 (;@2;) + end + i32.const 3684 + i32.const -1 + i32.store + end ) - ) - (func $44 (; 57 ;) (type $5) (param $0 i32) (param $1 i32) - (call_indirect (type $2) - (local.get $1) - (i32.add - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 6) - ) + (func $37 (;50;) (type $6) + nop ) - ) - (func $45 (; 58 ;) (type $1) (param $0 i32) (result i32) - (block $label$1 (result i32) - (call $fimport$3 - (i32.const 0) - ) - (i32.const 0) + (func $38 (;51;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$0 + i32.load + local.tee $2 + local.get $0 + i32.const 15 + i32.add + i32.const -16 + i32.and + local.tee $0 + i32.add + local.set $1 + local.get $0 + i32.const 0 + i32.gt_s + local.get $1 + local.get $2 + i32.lt_s + i32.and + local.get $1 + i32.const 0 + i32.lt_s + i32.or + if ;; label = @2 + block ;; label = @3 + call $fimport$6 + drop + i32.const 12 + call $fimport$11 + i32.const -1 + return + end + end + global.get $global$0 + local.get $1 + i32.store + local.get $1 + call $fimport$5 + i32.gt_s + if ;; label = @2 + call $fimport$4 + i32.eqz + if ;; label = @3 + block ;; label = @4 + i32.const 12 + call $fimport$11 + global.get $global$0 + local.get $2 + i32.store + i32.const -1 + return + end + end + end + local.get $2 + end ) - ) - (func $46 (; 59 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (block $label$1 (result i32) - (call $fimport$3 - (i32.const 1) - ) - (i32.const 0) + (func $39 (;52;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + local.get $2 + i32.add + local.set $4 + local.get $2 + i32.const 20 + i32.ge_s + if ;; label = @2 + block ;; label = @3 + local.get $1 + i32.const 255 + i32.and + local.set $1 + local.get $0 + i32.const 3 + i32.and + local.tee $3 + if ;; label = @4 + block ;; label = @5 + local.get $0 + i32.const 4 + i32.add + local.get $3 + i32.sub + local.set $3 + loop $label$4 ;; label = @6 + local.get $0 + local.get $3 + i32.lt_s + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.get $1 + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br 2 (;@6;) + end + end + end + end + end + local.get $1 + local.get $1 + i32.const 8 + i32.shl + i32.or + local.get $1 + i32.const 16 + i32.shl + i32.or + local.get $1 + i32.const 24 + i32.shl + i32.or + local.set $3 + local.get $4 + i32.const -4 + i32.and + local.set $5 + loop $label$6 ;; label = @4 + local.get $0 + local.get $5 + i32.lt_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $3 + i32.store + local.get $0 + i32.const 4 + i32.add + local.set $0 + br 2 (;@4;) + end + end + end + end + end + loop $label$8 ;; label = @2 + local.get $0 + local.get $4 + i32.lt_s + if ;; label = @3 + block ;; label = @4 + local.get $0 + local.get $1 + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br 2 (;@2;) + end + end + end + local.get $0 + local.get $2 + i32.sub + end ) - ) - (func $47 (; 60 ;) (type $2) (param $0 i32) - (call $fimport$3 - (i32.const 2) + (func $40 (;53;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + block $label$1 (result i32) ;; label = @1 + local.get $2 + i32.const 4096 + i32.ge_s + if ;; label = @2 + local.get $0 + local.get $1 + local.get $2 + call $fimport$12 + return + end + local.get $0 + local.set $3 + local.get $0 + i32.const 3 + i32.and + local.get $1 + i32.const 3 + i32.and + i32.eq + if ;; label = @2 + block ;; label = @3 + loop $label$4 ;; label = @4 + local.get $0 + i32.const 3 + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $2 + i32.eqz + if ;; label = @7 + local.get $3 + return + end + local.get $0 + local.get $1 + i32.load8_s + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + br 2 (;@4;) + end + end + end + loop $label$7 ;; label = @4 + local.get $2 + i32.const 4 + i32.ge_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $1 + i32.load + i32.store + local.get $0 + i32.const 4 + i32.add + local.set $0 + local.get $1 + i32.const 4 + i32.add + local.set $1 + local.get $2 + i32.const 4 + i32.sub + local.set $2 + br 2 (;@4;) + end + end + end + end + end + loop $label$9 ;; label = @2 + local.get $2 + i32.const 0 + i32.gt_s + if ;; label = @3 + block ;; label = @4 + local.get $0 + local.get $1 + i32.load8_s + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + br 2 (;@2;) + end + end + end + local.get $3 + end ) - ) -) - + (func $41 (;54;) (type $3) (result i32) + i32.const 0 + ) + (func $42 (;55;) (type $4) (param $0 i32) (param $1 i32) (result i32) + local.get $1 + local.get $0 + i32.const 1 + i32.and + i32.const 0 + i32.add + call_indirect (type $1) + ) + (func $43 (;56;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) + local.get $1 + local.get $2 + local.get $3 + local.get $0 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + ) + (func $44 (;57;) (type $5) (param $0 i32) (param $1 i32) + local.get $1 + local.get $0 + i32.const 1 + i32.and + i32.const 6 + i32.add + call_indirect (type $2) + ) + (func $45 (;58;) (type $1) (param $0 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + i32.const 0 + call $fimport$3 + i32.const 0 + end + ) + (func $46 (;59;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + i32.const 1 + call $fimport$3 + i32.const 0 + end + ) + (func $47 (;60;) (type $2) (param $0 i32) + i32.const 2 + call $fimport$3 + ) + (global $global$0 (;5;) (mut i32) global.get $gimport$0) + (global $global$1 (;6;) (mut i32) global.get $gimport$1) + (global $global$2 (;7;) (mut i32) global.get $gimport$2) + (global $global$3 (;8;) (mut i32) i32.const 0) + (global $global$4 (;9;) (mut i32) i32.const 0) + (global $global$5 (;10;) (mut i32) i32.const 0) + (export "_sbrk" (func $38)) + (export "_free" (func $36)) + (export "_main" (func $8)) + (export "_pthread_self" (func $41)) + (export "_memset" (func $39)) + (export "_malloc" (func $35)) + (export "_memcpy" (func $40)) + (export "___errno_location" (func $12)) + (export "runPostSets" (func $37)) + (export "stackAlloc" (func $0)) + (export "stackSave" (func $1)) + (export "stackRestore" (func $2)) + (export "establishStackSpace" (func $3)) + (export "setThrew" (func $4)) + (export "setTempRet0" (func $5)) + (export "getTempRet0" (func $6)) + (export "dynCall_ii" (func $42)) + (export "dynCall_iiii" (func $43)) + (export "dynCall_vi" (func $44)) + (elem (;0;) (global.get $gimport$19) func $45 $9 $46 $14 $10 $15 $47 $16) + (data (;0;) (i32.const 1024) "\04\04\00\00\05") + (data (;1;) (i32.const 1040) "\01") + (data (;2;) (i32.const 1064) "\01\00\00\00\02\00\00\00<\10\00\00\00\04") + (data (;3;) (i32.const 1088) "\01") + (data (;4;) (i32.const 1103) "\0a\ff\ff\ff\ff") + (data (;5;) (i32.const 1140) "error: %d\0a\00Pfannkuchen(%d) = %d.\0a\00%d\00\11\00\0a\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\09\00\00\00\00\0b") + (data (;6;) (i32.const 1209) "\11\00\0f\0a\11\11\11\03\0a\07\00\01\13\09\0b\0b\00\00\09\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") + (data (;7;) (i32.const 1258) "\0b") + (data (;8;) (i32.const 1267) "\11\00\0a\0a\11\11\11\00\0a\00\00\02\00\09\0b\00\00\00\09\00\0b\00\00\0b") + (data (;9;) (i32.const 1316) "\0c") + (data (;10;) (i32.const 1328) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c") + (data (;11;) (i32.const 1374) "\0e") + (data (;12;) (i32.const 1386) "\0d\00\00\00\04\0d\00\00\00\00\09\0e\00\00\00\00\00\0e\00\00\0e") + (data (;13;) (i32.const 1432) "\10") + (data (;14;) (i32.const 1444) "\0f\00\00\00\00\0f\00\00\00\00\09\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") + (data (;15;) (i32.const 1499) "\12\00\00\00\12\12\12\00\00\00\00\00\00\09") + (data (;16;) (i32.const 1548) "\0b") + (data (;17;) (i32.const 1560) "\0a\00\00\00\00\0a\00\00\00\00\09\0b\00\00\00\00\00\0b\00\00\0b") + (data (;18;) (i32.const 1606) "\0c") + (data (;19;) (i32.const 1618) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\22\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\09\0a\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\5c]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") +) \ No newline at end of file diff --git a/cranelift/wasm/wasmtests/embenchen_fasta.wat b/cranelift/wasm/wasmtests/embenchen_fasta.wat index 54baa68e151a..028615e9f3d4 100644 --- a/cranelift/wasm/wasmtests/embenchen_fasta.wat +++ b/cranelift/wasm/wasmtests/embenchen_fasta.wat @@ -1,16547 +1,12056 @@ (module - (type $0 (func (param i32 i32 i32) (result i32))) - (type $1 (func)) - (type $2 (func (param i32) (result i32))) - (type $3 (func (param i32))) - (type $4 (func (result i32))) - (type $5 (func (param i32 i32))) - (type $6 (func (param i32 i32) (result i32))) - (type $7 (func (param i32 i32 i32 i32 i32) (result i32))) - (type $8 (func (param i32 i32 i32))) - (type $9 (func (param i64 i32) (result i32))) - (type $10 (func (param i32 i32 i32 i32 i32))) - (type $11 (func (param f64 i32) (result f64))) - (type $12 (func (param i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory $16 2048 2048)) - (data (i32.const 1024) "&\02\00\00a\00\00\00q=\8a>\00\00\00\00c\00\00\00\8f\c2\f5=\00\00\00\00g\00\00\00\8f\c2\f5=\00\00\00\00t\00\00\00q=\8a>\00\00\00\00B\00\00\00\n\d7\a3<\00\00\00\00D\00\00\00\n\d7\a3<\00\00\00\00H\00\00\00\n\d7\a3<\00\00\00\00K\00\00\00\n\d7\a3<\00\00\00\00M\00\00\00\n\d7\a3<\00\00\00\00N\00\00\00\n\d7\a3<\00\00\00\00R\00\00\00\n\d7\a3<\00\00\00\00S\00\00\00\n\d7\a3<\00\00\00\00V\00\00\00\n\d7\a3<\00\00\00\00W\00\00\00\n\d7\a3<\00\00\00\00Y\00\00\00\n\d7\a3<") - (data (i32.const 1220) "a\00\00\00\e9\1c\9b>\00\00\00\00c\00\00\00r\bdJ>\00\00\00\00g\00\00\00\d7IJ>\00\00\00\00t\00\00\00r_\9a>") - (data (i32.const 1280) "\04\05\00\00\05") - (data (i32.const 1296) "\01") - (data (i32.const 1320) "\01\00\00\00\02\00\00\00L\12\00\00\00\04") - (data (i32.const 1344) "\01") - (data (i32.const 1359) "\n\ff\ff\ff\ff") - (data (i32.const 1396) "*\00\00\00error: %d\n\00GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA\00\11\00\n\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\t\00\00\00\00\0b") - (data (i32.const 1731) "\11\00\0f\n\11\11\11\03\n\07\00\01\13\t\0b\0b\00\00\t\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") - (data (i32.const 1780) "\0b") - (data (i32.const 1789) "\11\00\n\n\11\11\11\00\n\00\00\02\00\t\0b\00\00\00\t\00\0b\00\00\0b") - (data (i32.const 1838) "\0c") - (data (i32.const 1850) "\0c\00\00\00\00\0c\00\00\00\00\t\0c\00\00\00\00\00\0c\00\00\0c") - (data (i32.const 1896) "\0e") - (data (i32.const 1908) "\0d\00\00\00\04\0d\00\00\00\00\t\0e\00\00\00\00\00\0e\00\00\0e") - (data (i32.const 1954) "\10") - (data (i32.const 1966) "\0f\00\00\00\00\0f\00\00\00\00\t\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") - (data (i32.const 2021) "\12\00\00\00\12\12\12\00\00\00\00\00\00\t") - (data (i32.const 2070) "\0b") - (data (i32.const 2082) "\n\00\00\00\00\n\00\00\00\00\t\0b\00\00\00\00\00\0b\00\00\0b") - (data (i32.const 2128) "\0c") - (data (i32.const 2140) "\0c\00\00\00\00\0c\00\00\00\00\t\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\"\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e\'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\t\n\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\\]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") - (import "env" "table" (table $timport$17 9 9 funcref)) - (elem (global.get $gimport$19) $53 $9 $54 $14 $10 $15 $55 $16 $56) - (import "env" "DYNAMICTOP_PTR" (global $gimport$0 i32)) - (import "env" "STACKTOP" (global $gimport$1 i32)) - (import "env" "STACK_MAX" (global $gimport$2 i32)) - (import "env" "memoryBase" (global $gimport$18 i32)) - (import "env" "tableBase" (global $gimport$19 i32)) - (import "env" "abort" (func $fimport$3 (param i32))) - (import "env" "enlargeMemory" (func $fimport$4 (result i32))) - (import "env" "getTotalMemory" (func $fimport$5 (result i32))) - (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (result i32))) - (import "env" "_pthread_cleanup_pop" (func $fimport$7 (param i32))) - (import "env" "_abort" (func $fimport$8)) - (import "env" "_pthread_cleanup_push" (func $fimport$9 (param i32 i32))) - (import "env" "___syscall6" (func $fimport$10 (param i32 i32) (result i32))) - (import "env" "___setErrNo" (func $fimport$11 (param i32))) - (import "env" "_emscripten_memcpy_big" (func $fimport$12 (param i32 i32 i32) (result i32))) - (import "env" "___syscall54" (func $fimport$13 (param i32 i32) (result i32))) - (import "env" "___syscall140" (func $fimport$14 (param i32 i32) (result i32))) - (import "env" "___syscall146" (func $fimport$15 (param i32 i32) (result i32))) - (global $global$0 (mut i32) (global.get $gimport$0)) - (global $global$1 (mut i32) (global.get $gimport$1)) - (global $global$2 (mut i32) (global.get $gimport$2)) - (global $global$3 (mut i32) (i32.const 0)) - (global $global$4 (mut i32) (i32.const 0)) - (global $global$5 (mut i32) (i32.const 0)) - (export "_sbrk" (func $45)) - (export "_free" (func $38)) - (export "_main" (func $7)) - (export "_pthread_self" (func $48)) - (export "_memset" (func $46)) - (export "_malloc" (func $37)) - (export "_memcpy" (func $47)) - (export "___errno_location" (func $12)) - (export "runPostSets" (func $44)) - (export "stackAlloc" (func $0)) - (export "stackSave" (func $1)) - (export "stackRestore" (func $2)) - (export "establishStackSpace" (func $3)) - (export "setThrew" (func $4)) - (export "setTempRet0" (func $5)) - (export "getTempRet0" (func $6)) - (export "dynCall_ii" (func $49)) - (export "dynCall_iiii" (func $50)) - (export "dynCall_vi" (func $51)) - (export "dynCall_v" (func $52)) - (func $0 (; 13 ;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - (block $label$1 (result i32) - (local.set $1 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (local.get $0) - ) - ) - (global.set $global$1 - (i32.and - (i32.add - (global.get $global$1) - (i32.const 15) - ) - (i32.const -16) - ) - ) - (local.get $1) + (type $0 (;0;) (func (param i32 i32 i32) (result i32))) + (type $1 (;1;) (func)) + (type $2 (;2;) (func (param i32) (result i32))) + (type $3 (;3;) (func (param i32))) + (type $4 (;4;) (func (result i32))) + (type $5 (;5;) (func (param i32 i32))) + (type $6 (;6;) (func (param i32 i32) (result i32))) + (type $7 (;7;) (func (param i32 i32 i32 i32 i32) (result i32))) + (type $8 (;8;) (func (param i32 i32 i32))) + (type $9 (;9;) (func (param i64 i32) (result i32))) + (type $10 (;10;) (func (param i32 i32 i32 i32 i32))) + (type $11 (;11;) (func (param f64 i32) (result f64))) + (type $12 (;12;) (func (param i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory $16 (;0;) 2048 2048)) + (import "env" "table" (table $timport$17 (;0;) 9 9 funcref)) + (import "env" "DYNAMICTOP_PTR" (global $gimport$0 (;0;) i32)) + (import "env" "STACKTOP" (global $gimport$1 (;1;) i32)) + (import "env" "STACK_MAX" (global $gimport$2 (;2;) i32)) + (import "env" "memoryBase" (global $gimport$18 (;3;) i32)) + (import "env" "tableBase" (global $gimport$19 (;4;) i32)) + (import "env" "abort" (func $fimport$3 (;0;) (type $3))) + (import "env" "enlargeMemory" (func $fimport$4 (;1;) (type $4))) + (import "env" "getTotalMemory" (func $fimport$5 (;2;) (type $4))) + (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (;3;) (type $4))) + (import "env" "_pthread_cleanup_pop" (func $fimport$7 (;4;) (type $3))) + (import "env" "_abort" (func $fimport$8 (;5;) (type $1))) + (import "env" "_pthread_cleanup_push" (func $fimport$9 (;6;) (type $5))) + (import "env" "___syscall6" (func $fimport$10 (;7;) (type $6))) + (import "env" "___setErrNo" (func $fimport$11 (;8;) (type $3))) + (import "env" "_emscripten_memcpy_big" (func $fimport$12 (;9;) (type $0))) + (import "env" "___syscall54" (func $fimport$13 (;10;) (type $6))) + (import "env" "___syscall140" (func $fimport$14 (;11;) (type $6))) + (import "env" "___syscall146" (func $fimport$15 (;12;) (type $6))) + (func $0 (;13;) (type $2) (param $0 i32) (result i32) + (local $1 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $1 + global.get $global$1 + local.get $0 + i32.add + global.set $global$1 + global.get $global$1 + i32.const 15 + i32.add + i32.const -16 + i32.and + global.set $global$1 + local.get $1 + end ) - ) - (func $1 (; 14 ;) (type $4) (result i32) - (global.get $global$1) - ) - (func $2 (; 15 ;) (type $3) (param $0 i32) - (global.set $global$1 - (local.get $0) + (func $1 (;14;) (type $4) (result i32) + global.get $global$1 ) - ) - (func $3 (; 16 ;) (type $5) (param $0 i32) (param $1 i32) - (block $label$1 - (global.set $global$1 - (local.get $0) - ) - (global.set $global$2 - (local.get $1) - ) + (func $2 (;15;) (type $3) (param $0 i32) + local.get $0 + global.set $global$1 ) - ) - (func $4 (; 17 ;) (type $5) (param $0 i32) (param $1 i32) - (if - (i32.eqz - (global.get $global$3) - ) - (block - (global.set $global$3 - (local.get $0) - ) - (global.set $global$4 - (local.get $1) - ) - ) + (func $3 (;16;) (type $5) (param $0 i32) (param $1 i32) + block $label$1 ;; label = @1 + local.get $0 + global.set $global$1 + local.get $1 + global.set $global$2 + end ) - ) - (func $5 (; 18 ;) (type $3) (param $0 i32) - (global.set $global$5 - (local.get $0) + (func $4 (;17;) (type $5) (param $0 i32) (param $1 i32) + global.get $global$3 + i32.eqz + if ;; label = @1 + block ;; label = @2 + local.get $0 + global.set $global$3 + local.get $1 + global.set $global$4 + end + end ) - ) - (func $6 (; 19 ;) (type $4) (result i32) - (global.get $global$5) - ) - (func $7 (; 20 ;) (type $6) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 f32) - (local $12 f32) - (local $13 f64) - (block $label$1 (result i32) - (local.set $5 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 4256) - ) - ) - (local.set $3 - (local.get $5) - ) - (local.set $6 - (i32.add - (local.get $5) - (i32.const 2128) - ) - ) - (local.set $7 - (i32.add - (local.get $5) - (i32.const 8) - ) - ) - (block $label$2 - (block $label$3 - (br_if $label$3 - (i32.le_s - (local.get $0) - (i32.const 1) - ) - ) - (block $label$4 - (block $label$5 - (block $label$6 - (block $label$7 - (block $label$8 - (block $label$9 - (block $label$10 - (br_table $label$5 $label$10 $label$8 $label$9 $label$7 $label$6 $label$4 - (i32.sub - (local.tee $0 - (i32.load8_s - (i32.load offset=4 - (local.get $1) - ) - ) - ) - (i32.const 48) - ) - ) - ) - (local.set $4 - (i32.const 950000) - ) - (br $label$2) - ) - (br $label$3) - ) - (local.set $4 - (i32.const 9500000) - ) - (br $label$2) - ) - (local.set $4 - (i32.const 95000000) - ) - (br $label$2) - ) - (local.set $4 - (i32.const 190000000) - ) - (br $label$2) - ) - (global.set $global$1 - (local.get $5) - ) - (return - (i32.const 0) - ) - ) - (i32.store - (local.get $3) - (i32.add - (local.get $0) - (i32.const -48) - ) - ) - (drop - (call $34 - (i32.const 1400) - (local.get $3) - ) - ) - (global.set $global$1 - (local.get $5) - ) - (return - (i32.const -1) - ) - ) - (local.set $4 - (i32.const 19000000) - ) - ) - (drop - (call $47 - (local.tee $8 - (call $40 - (i32.const 347) - ) - ) - (i32.const 1411) - (i32.const 287) - ) - ) - (i64.store align=1 - (local.tee $0 - (i32.add - (local.get $8) - (i32.const 287) - ) - ) - (i64.load align=1 - (i32.const 1411) - ) - ) - (i64.store offset=8 align=1 - (local.get $0) - (i64.load align=1 - (i32.const 1419) - ) - ) - (i64.store offset=16 align=1 - (local.get $0) - (i64.load align=1 - (i32.const 1427) - ) - ) - (i64.store offset=24 align=1 - (local.get $0) - (i64.load align=1 - (i32.const 1435) - ) - ) - (i64.store offset=32 align=1 - (local.get $0) - (i64.load align=1 - (i32.const 1443) - ) - ) - (i64.store offset=40 align=1 - (local.get $0) - (i64.load align=1 - (i32.const 1451) - ) - ) - (i64.store offset=48 align=1 - (local.get $0) - (i64.load align=1 - (i32.const 1459) - ) - ) - (i32.store offset=56 align=1 - (local.get $0) - (i32.load align=1 - (i32.const 1467) - ) - ) - (local.set $0 - (i32.shl - (local.get $4) - (i32.const 1) - ) - ) - (local.set $1 - (i32.const 0) - ) - (loop $label$11 - (drop - (call $47 - (local.tee $2 - (call $40 - (i32.add - (local.tee $3 - (if (result i32) - (i32.lt_u - (local.get $0) - (i32.const 60) - ) - (local.get $0) - (i32.const 60) - ) - ) - (i32.const 2) - ) - ) - ) - (i32.add - (local.get $8) - (local.get $1) - ) - (local.get $3) - ) - ) - (i32.store8 - (i32.add - (local.get $2) - (local.get $3) - ) - (i32.const 0) - ) - (if - (i32.gt_s - (local.tee $10 - (call $31 - (local.get $2) - ) - ) - (local.tee $9 - (i32.load - (i32.const 1024) - ) - ) - ) - (if - (i32.gt_s - (local.get $9) - (i32.const 0) - ) - (block - (i32.store8 - (i32.add - (local.get $2) - (local.get $9) - ) - (i32.const 0) - ) - (drop - (call $35 - (local.get $2) - ) - ) - (i32.store - (i32.const 1024) - (i32.const 0) - ) - ) - ) - (block - (drop - (call $35 - (local.get $2) - ) - ) - (i32.store - (i32.const 1024) - (i32.sub - (i32.load - (i32.const 1024) - ) - (local.get $10) - ) - ) - ) - ) - (call $41 - (local.get $2) - ) - (local.set $1 - (i32.add - (local.tee $2 - (i32.add - (local.get $3) - (local.get $1) - ) - ) - (i32.const -287) - ) - ) - (if - (i32.le_u - (local.get $2) - (i32.const 287) - ) - (local.set $1 - (local.get $2) - ) - ) - (br_if $label$11 - (local.tee $0 - (i32.sub - (local.get $0) - (local.get $3) - ) - ) - ) - ) - (call $42 - (local.get $8) - ) - (if - (i32.load - (i32.const 1028) - ) - (block - (local.set $0 - (i32.const 1028) - ) - (local.set $11 - (f32.const 0) - ) - (loop $label$19 - (local.set $12 - (f32.demote_f64 - (if (result f64) - (f64.lt - (local.tee $13 - (f64.promote_f32 - (local.tee $11 - (f32.add - (local.get $11) - (f32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - ) - ) - ) - ) - ) - (f64.const 1) - ) - (local.get $13) - (f64.const 1) - ) - ) - ) - (f32.store - (local.get $1) - (local.get $12) - ) - (i32.store offset=8 - (local.get $0) - (i32.trunc_f32_s - (f32.mul - (local.get $12) - (f32.const 512) - ) - ) - ) - (br_if $label$19 - (i32.load - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 12) - ) - ) - ) - ) - (local.set $1 - (i32.const 0) - ) - (local.set $0 - (i32.const 1028) - ) - ) - ) - (block - (local.set $1 - (i32.const 0) - ) - (local.set $0 - (i32.const 1028) - ) - ) - ) - (loop $label$23 - (loop $label$24 - (local.set $3 - (i32.add - (local.get $0) - (i32.const 12) - ) - ) - (if - (i32.and - (i32.gt_u - (local.get $1) - (local.tee $2 - (i32.load offset=8 - (local.get $0) - ) - ) - ) - (i32.ne - (local.get $2) - (i32.const 0) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (br $label$24) - ) - ) - ) - (i32.store - (i32.add - (local.get $6) - (i32.shl - (local.get $1) - (i32.const 2) - ) - ) - (local.get $0) - ) - (br_if $label$23 - (i32.ne - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (i32.const 513) - ) - ) - ) - (i32.store - (i32.add - (local.get $6) - (i32.const 2116) - ) - (i32.const 0) - ) - (local.set $0 - (i32.mul - (local.get $4) - (i32.const 3) - ) - ) - (loop $label$26 - (call $8 - (local.get $6) - (local.tee $1 - (if (result i32) - (i32.lt_u - (local.get $0) - (i32.const 60) - ) - (local.get $0) - (i32.const 60) - ) - ) - ) - (br_if $label$26 - (local.tee $0 - (i32.sub - (local.get $0) - (local.get $1) - ) - ) - ) - ) - (if - (i32.load - (i32.const 1220) - ) - (block - (local.set $0 - (i32.const 1220) - ) - (local.set $11 - (f32.const 0) - ) - (loop $label$30 - (local.set $12 - (f32.demote_f64 - (if (result f64) - (f64.lt - (local.tee $13 - (f64.promote_f32 - (local.tee $11 - (f32.add - (local.get $11) - (f32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - ) - ) - ) - ) - ) - (f64.const 1) - ) - (local.get $13) - (f64.const 1) - ) - ) - ) - (f32.store - (local.get $1) - (local.get $12) - ) - (i32.store offset=8 - (local.get $0) - (i32.trunc_f32_s - (f32.mul - (local.get $12) - (f32.const 512) - ) - ) - ) - (br_if $label$30 - (i32.load - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 12) - ) - ) - ) - ) - (local.set $1 - (i32.const 0) - ) - (local.set $0 - (i32.const 1220) - ) - ) - ) - (block - (local.set $1 - (i32.const 0) - ) - (local.set $0 - (i32.const 1220) - ) - ) - ) - (loop $label$34 - (loop $label$35 - (local.set $3 - (i32.add - (local.get $0) - (i32.const 12) - ) - ) - (if - (i32.and - (i32.gt_u - (local.get $1) - (local.tee $2 - (i32.load offset=8 - (local.get $0) - ) - ) - ) - (i32.ne - (local.get $2) - (i32.const 0) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (br $label$35) - ) - ) - ) - (i32.store - (i32.add - (local.get $7) - (i32.shl - (local.get $1) - (i32.const 2) - ) - ) - (local.get $0) - ) - (br_if $label$34 - (i32.ne - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (i32.const 513) - ) - ) - ) - (i32.store - (i32.add - (local.get $7) - (i32.const 2116) - ) - (i32.const 0) - ) - (local.set $0 - (i32.mul - (local.get $4) - (i32.const 5) - ) - ) - (loop $label$37 - (call $8 - (local.get $7) - (local.tee $1 - (if (result i32) - (i32.lt_u - (local.get $0) - (i32.const 60) - ) - (local.get $0) - (i32.const 60) - ) - ) - ) - (br_if $label$37 - (local.tee $0 - (i32.sub - (local.get $0) - (local.get $1) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - ) - (global.set $global$1 - (local.get $5) - ) - (local.get $0) + (func $5 (;18;) (type $3) (param $0 i32) + local.get $0 + global.set $global$5 ) - ) - (func $8 (; 21 ;) (type $5) (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 f32) - (block $label$1 - (if - (local.get $1) - (block - (local.set $3 - (i32.const 0) - ) - (local.set $2 - (i32.load - (i32.const 1396) - ) - ) - (loop $label$3 - (local.set $2 - (i32.load - (i32.add - (local.get $0) - (i32.shl - (i32.trunc_f32_s - (f32.mul - (local.tee $6 - (f32.div - (f32.convert_i32_u - (local.tee $4 - (i32.rem_u - (i32.add - (i32.mul - (local.get $2) - (i32.const 3877) - ) - (i32.const 29573) - ) - (i32.const 139968) - ) - ) - ) - (f32.const 139968) - ) - ) - (f32.const 512) - ) - ) - (i32.const 2) - ) - ) - ) - ) - (loop $label$4 - (local.set $5 - (i32.add - (local.get $2) - (i32.const 12) - ) - ) - (if - (f32.lt - (f32.load offset=4 - (local.get $2) - ) - (local.get $6) - ) - (block - (local.set $2 - (local.get $5) - ) - (br $label$4) - ) - ) - ) - (i32.store8 - (i32.add - (i32.add - (local.get $0) - (i32.const 2052) - ) - (local.get $3) - ) - (i32.load - (local.get $2) - ) - ) - (if - (i32.ne - (local.tee $3 - (i32.add - (local.get $3) - (i32.const 1) - ) - ) - (local.get $1) - ) - (block - (local.set $2 - (local.get $4) - ) - (br $label$3) - ) - ) - ) - (i32.store - (i32.const 1396) - (local.get $4) - ) - ) - ) - (i32.store8 - (i32.add - (i32.add - (local.get $0) - (i32.const 2052) - ) - (local.get $1) - ) - (i32.const 10) - ) - (i32.store8 - (i32.add - (i32.add - (local.get $0) - (i32.const 2052) - ) - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - (i32.store - (i32.add - (local.get $0) - (i32.const 2116) - ) - (local.get $1) - ) - (if - (i32.le_s - (local.tee $3 - (call $31 - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 2052) - ) - ) - ) - ) - (local.tee $2 - (i32.load - (i32.const 1024) - ) - ) - ) - (block - (drop - (call $35 - (local.get $1) - ) - ) - (i32.store - (i32.const 1024) - (i32.sub - (i32.load - (i32.const 1024) - ) - (local.get $3) - ) - ) - (return) - ) - ) - (if - (i32.le_s - (local.get $2) - (i32.const 0) - ) - (return) - ) - (i32.store8 - (i32.add - (i32.add - (local.get $0) - (i32.const 2052) - ) - (local.get $2) - ) - (i32.const 0) - ) - (drop - (call $35 - (local.get $1) - ) - ) - (i32.store8 - (i32.add - (i32.add - (local.get $0) - (i32.const 2052) - ) - (i32.load - (i32.const 1024) - ) - ) - (i32.const 122) - ) - (i32.store - (i32.const 1024) - (i32.const 0) - ) + (func $6 (;19;) (type $4) (result i32) + global.get $global$5 ) - ) - (func $9 (; 22 ;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store - (local.tee $2 - (local.get $1) - ) - (i32.load offset=60 - (local.get $0) - ) - ) - (local.set $0 - (call $11 - (call $fimport$10 - (i32.const 6) - (local.get $2) - ) - ) - ) - (global.set $global$1 - (local.get $1) - ) - (local.get $0) + (func $7 (;20;) (type $6) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 f32) (local $12 f32) (local $13 f64) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $5 + global.get $global$1 + i32.const 4256 + i32.add + global.set $global$1 + local.get $5 + local.set $3 + local.get $5 + i32.const 2128 + i32.add + local.set $6 + local.get $5 + i32.const 8 + i32.add + local.set $7 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $0 + i32.const 1 + i32.le_s + br_if 0 (;@3;) + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + block $label$6 ;; label = @6 + block $label$7 ;; label = @7 + block $label$8 ;; label = @8 + block $label$9 ;; label = @9 + block $label$10 ;; label = @10 + local.get $1 + i32.load offset=4 + i32.load8_s + local.tee $0 + i32.const 48 + i32.sub + br_table 5 (;@5;) 0 (;@10;) 2 (;@8;) 1 (;@9;) 3 (;@7;) 4 (;@6;) 6 (;@4;) + end + i32.const 950000 + local.set $4 + br 7 (;@2;) + end + br 5 (;@3;) + end + i32.const 9500000 + local.set $4 + br 5 (;@2;) + end + i32.const 95000000 + local.set $4 + br 4 (;@2;) + end + i32.const 190000000 + local.set $4 + br 3 (;@2;) + end + local.get $5 + global.set $global$1 + i32.const 0 + return + end + local.get $3 + local.get $0 + i32.const -48 + i32.add + i32.store + i32.const 1400 + local.get $3 + call $34 + drop + local.get $5 + global.set $global$1 + i32.const -1 + return + end + i32.const 19000000 + local.set $4 + end + i32.const 347 + call $40 + local.tee $8 + i32.const 1411 + i32.const 287 + call $47 + drop + local.get $8 + i32.const 287 + i32.add + local.tee $0 + i32.const 1411 + i64.load align=1 + i64.store align=1 + local.get $0 + i32.const 1419 + i64.load align=1 + i64.store offset=8 align=1 + local.get $0 + i32.const 1427 + i64.load align=1 + i64.store offset=16 align=1 + local.get $0 + i32.const 1435 + i64.load align=1 + i64.store offset=24 align=1 + local.get $0 + i32.const 1443 + i64.load align=1 + i64.store offset=32 align=1 + local.get $0 + i32.const 1451 + i64.load align=1 + i64.store offset=40 align=1 + local.get $0 + i32.const 1459 + i64.load align=1 + i64.store offset=48 align=1 + local.get $0 + i32.const 1467 + i32.load align=1 + i32.store offset=56 align=1 + local.get $4 + i32.const 1 + i32.shl + local.set $0 + i32.const 0 + local.set $1 + loop $label$11 ;; label = @2 + local.get $0 + i32.const 60 + i32.lt_u + if (result i32) ;; label = @3 + local.get $0 + else + i32.const 60 + end + local.tee $3 + i32.const 2 + i32.add + call $40 + local.tee $2 + local.get $8 + local.get $1 + i32.add + local.get $3 + call $47 + drop + local.get $2 + local.get $3 + i32.add + i32.const 0 + i32.store8 + local.get $2 + call $31 + local.tee $10 + i32.const 1024 + i32.load + local.tee $9 + i32.gt_s + if ;; label = @3 + local.get $9 + i32.const 0 + i32.gt_s + if ;; label = @4 + block ;; label = @5 + local.get $2 + local.get $9 + i32.add + i32.const 0 + i32.store8 + local.get $2 + call $35 + drop + i32.const 1024 + i32.const 0 + i32.store + end + end + else + block ;; label = @4 + local.get $2 + call $35 + drop + i32.const 1024 + i32.const 1024 + i32.load + local.get $10 + i32.sub + i32.store + end + end + local.get $2 + call $41 + local.get $3 + local.get $1 + i32.add + local.tee $2 + i32.const -287 + i32.add + local.set $1 + local.get $2 + i32.const 287 + i32.le_u + if ;; label = @3 + local.get $2 + local.set $1 + end + local.get $0 + local.get $3 + i32.sub + local.tee $0 + br_if 0 (;@2;) + end + local.get $8 + call $42 + i32.const 1028 + i32.load + if ;; label = @2 + block ;; label = @3 + i32.const 1028 + local.set $0 + f32.const 0x0p+0 (;=0;) + local.set $11 + loop $label$19 ;; label = @4 + local.get $11 + local.get $0 + i32.const 4 + i32.add + local.tee $1 + f32.load + f32.add + local.tee $11 + f64.promote_f32 + local.tee $13 + f64.const 0x1p+0 (;=1;) + f64.lt + if (result f64) ;; label = @5 + local.get $13 + else + f64.const 0x1p+0 (;=1;) + end + f32.demote_f64 + local.set $12 + local.get $1 + local.get $12 + f32.store + local.get $0 + local.get $12 + f32.const 0x1p+9 (;=512;) + f32.mul + i32.trunc_f32_s + i32.store offset=8 + local.get $0 + i32.const 12 + i32.add + local.tee $0 + i32.load + br_if 0 (;@4;) + i32.const 0 + local.set $1 + i32.const 1028 + local.set $0 + end + end + else + block ;; label = @3 + i32.const 0 + local.set $1 + i32.const 1028 + local.set $0 + end + end + loop $label$23 ;; label = @2 + loop $label$24 ;; label = @3 + local.get $0 + i32.const 12 + i32.add + local.set $3 + local.get $1 + local.get $0 + i32.load offset=8 + local.tee $2 + i32.gt_u + local.get $2 + i32.const 0 + i32.ne + i32.and + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.set $0 + br 2 (;@3;) + end + end + end + local.get $6 + local.get $1 + i32.const 2 + i32.shl + i32.add + local.get $0 + i32.store + local.get $1 + i32.const 1 + i32.add + local.tee $1 + i32.const 513 + i32.ne + br_if 0 (;@2;) + end + local.get $6 + i32.const 2116 + i32.add + i32.const 0 + i32.store + local.get $4 + i32.const 3 + i32.mul + local.set $0 + loop $label$26 ;; label = @2 + local.get $6 + local.get $0 + i32.const 60 + i32.lt_u + if (result i32) ;; label = @3 + local.get $0 + else + i32.const 60 + end + local.tee $1 + call $8 + local.get $0 + local.get $1 + i32.sub + local.tee $0 + br_if 0 (;@2;) + end + i32.const 1220 + i32.load + if ;; label = @2 + block ;; label = @3 + i32.const 1220 + local.set $0 + f32.const 0x0p+0 (;=0;) + local.set $11 + loop $label$30 ;; label = @4 + local.get $11 + local.get $0 + i32.const 4 + i32.add + local.tee $1 + f32.load + f32.add + local.tee $11 + f64.promote_f32 + local.tee $13 + f64.const 0x1p+0 (;=1;) + f64.lt + if (result f64) ;; label = @5 + local.get $13 + else + f64.const 0x1p+0 (;=1;) + end + f32.demote_f64 + local.set $12 + local.get $1 + local.get $12 + f32.store + local.get $0 + local.get $12 + f32.const 0x1p+9 (;=512;) + f32.mul + i32.trunc_f32_s + i32.store offset=8 + local.get $0 + i32.const 12 + i32.add + local.tee $0 + i32.load + br_if 0 (;@4;) + i32.const 0 + local.set $1 + i32.const 1220 + local.set $0 + end + end + else + block ;; label = @3 + i32.const 0 + local.set $1 + i32.const 1220 + local.set $0 + end + end + loop $label$34 ;; label = @2 + loop $label$35 ;; label = @3 + local.get $0 + i32.const 12 + i32.add + local.set $3 + local.get $1 + local.get $0 + i32.load offset=8 + local.tee $2 + i32.gt_u + local.get $2 + i32.const 0 + i32.ne + i32.and + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.set $0 + br 2 (;@3;) + end + end + end + local.get $7 + local.get $1 + i32.const 2 + i32.shl + i32.add + local.get $0 + i32.store + local.get $1 + i32.const 1 + i32.add + local.tee $1 + i32.const 513 + i32.ne + br_if 0 (;@2;) + end + local.get $7 + i32.const 2116 + i32.add + i32.const 0 + i32.store + local.get $4 + i32.const 5 + i32.mul + local.set $0 + loop $label$37 ;; label = @2 + local.get $7 + local.get $0 + i32.const 60 + i32.lt_u + if (result i32) ;; label = @3 + local.get $0 + else + i32.const 60 + end + local.tee $1 + call $8 + local.get $0 + local.get $1 + i32.sub + local.tee $0 + br_if 0 (;@2;) + i32.const 0 + local.set $0 + end + local.get $5 + global.set $global$1 + local.get $0 + end ) - ) - (func $10 (; 23 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 32) - ) - ) - (i32.store - (local.tee $3 - (local.get $4) - ) - (i32.load offset=60 - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.const 0) - ) - (i32.store offset=8 - (local.get $3) - (local.get $1) - ) - (i32.store offset=12 - (local.get $3) - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - (i32.store offset=16 - (local.get $3) - (local.get $2) - ) - (local.set $0 - (if (result i32) - (i32.lt_s - (call $11 - (call $fimport$14 - (i32.const 140) - (local.get $3) - ) - ) - (i32.const 0) - ) - (block (result i32) - (i32.store - (local.get $0) - (i32.const -1) - ) - (i32.const -1) - ) - (i32.load - (local.get $0) - ) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $0) + (func $8 (;21;) (type $5) (param $0 i32) (param $1 i32) + (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 f32) + block $label$1 ;; label = @1 + local.get $1 + if ;; label = @2 + block ;; label = @3 + i32.const 0 + local.set $3 + i32.const 1396 + i32.load + local.set $2 + loop $label$3 ;; label = @4 + local.get $0 + local.get $2 + i32.const 3877 + i32.mul + i32.const 29573 + i32.add + i32.const 139968 + i32.rem_u + local.tee $4 + f32.convert_i32_u + f32.const 0x1.116p+17 (;=139968;) + f32.div + local.tee $6 + f32.const 0x1p+9 (;=512;) + f32.mul + i32.trunc_f32_s + i32.const 2 + i32.shl + i32.add + i32.load + local.set $2 + loop $label$4 ;; label = @5 + local.get $2 + i32.const 12 + i32.add + local.set $5 + local.get $2 + f32.load offset=4 + local.get $6 + f32.lt + if ;; label = @6 + block ;; label = @7 + local.get $5 + local.set $2 + br 2 (;@5;) + end + end + end + local.get $0 + i32.const 2052 + i32.add + local.get $3 + i32.add + local.get $2 + i32.load + i32.store8 + local.get $3 + i32.const 1 + i32.add + local.tee $3 + local.get $1 + i32.ne + if ;; label = @5 + block ;; label = @6 + local.get $4 + local.set $2 + br 2 (;@4;) + end + end + end + i32.const 1396 + local.get $4 + i32.store + end + end + local.get $0 + i32.const 2052 + i32.add + local.get $1 + i32.add + i32.const 10 + i32.store8 + local.get $0 + i32.const 2052 + i32.add + local.get $1 + i32.const 1 + i32.add + local.tee $1 + i32.add + i32.const 0 + i32.store8 + local.get $0 + i32.const 2116 + i32.add + local.get $1 + i32.store + local.get $0 + i32.const 2052 + i32.add + local.tee $1 + call $31 + local.tee $3 + i32.const 1024 + i32.load + local.tee $2 + i32.le_s + if ;; label = @2 + block ;; label = @3 + local.get $1 + call $35 + drop + i32.const 1024 + i32.const 1024 + i32.load + local.get $3 + i32.sub + i32.store + return + end + end + local.get $2 + i32.const 0 + i32.le_s + if ;; label = @2 + return + end + local.get $0 + i32.const 2052 + i32.add + local.get $2 + i32.add + i32.const 0 + i32.store8 + local.get $1 + call $35 + drop + local.get $0 + i32.const 2052 + i32.add + i32.const 1024 + i32.load + i32.add + i32.const 122 + i32.store8 + i32.const 1024 + i32.const 0 + i32.store + end ) - ) - (func $11 (; 24 ;) (type $2) (param $0 i32) (result i32) - (if (result i32) - (i32.gt_u - (local.get $0) - (i32.const -4096) - ) - (block (result i32) - (i32.store - (call $12) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - (local.get $0) + (func $9 (;22;) (type $2) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $1 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $1 + local.tee $2 + local.get $0 + i32.load offset=60 + i32.store + i32.const 6 + local.get $2 + call $fimport$10 + call $11 + local.set $0 + local.get $1 + global.set $global$1 + local.get $0 + end ) - ) - (func $12 (; 25 ;) (type $4) (result i32) - (i32.const 4172) - ) - (func $13 (; 26 ;) (type $3) (param $0 i32) - (nop) - ) - (func $14 (; 27 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 80) - ) - ) - (local.set $3 - (local.get $4) - ) - (local.set $5 - (i32.add - (local.get $4) - (i32.const 12) - ) - ) - (i32.store offset=36 - (local.get $0) - (i32.const 3) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 64) - ) - ) - (block - (i32.store - (local.get $3) - (i32.load offset=60 - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.const 21505) - ) - (i32.store offset=8 - (local.get $3) - (local.get $5) - ) - (if - (call $fimport$13 - (i32.const 54) - (local.get $3) - ) - (i32.store8 offset=75 - (local.get $0) - (i32.const -1) - ) - ) - ) - ) - (local.set $0 - (call $15 - (local.get $0) - (local.get $1) - (local.get $2) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $0) + (func $10 (;23;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 32 + i32.add + global.set $global$1 + local.get $4 + local.tee $3 + local.get $0 + i32.load offset=60 + i32.store + local.get $3 + i32.const 0 + i32.store offset=4 + local.get $3 + local.get $1 + i32.store offset=8 + local.get $3 + local.get $4 + i32.const 20 + i32.add + local.tee $0 + i32.store offset=12 + local.get $3 + local.get $2 + i32.store offset=16 + i32.const 140 + local.get $3 + call $fimport$14 + call $11 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $0 + i32.const -1 + i32.store + i32.const -1 + end + else + local.get $0 + i32.load + end + local.set $0 + local.get $4 + global.set $global$1 + local.get $0 + end ) - ) - (func $15 (; 28 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (block $label$1 (result i32) - (local.set $8 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 48) - ) - ) - (local.set $9 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - (local.set $10 - (local.get $8) - ) - (i32.store - (local.tee $3 - (i32.add - (local.get $8) - (i32.const 32) - ) - ) - (local.tee $4 - (i32.load - (local.tee $6 - (i32.add - (local.get $0) - (i32.const 28) - ) - ) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (local.tee $5 - (i32.sub - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - (local.get $4) - ) - ) - ) - (i32.store offset=8 - (local.get $3) - (local.get $1) - ) - (i32.store offset=12 - (local.get $3) - (local.get $2) - ) - (local.set $13 - (i32.add - (local.get $0) - (i32.const 60) - ) - ) - (local.set $14 - (i32.add - (local.get $0) - (i32.const 44) - ) - ) - (local.set $1 - (local.get $3) - ) - (local.set $4 - (i32.const 2) - ) - (local.set $12 - (i32.add - (local.get $5) - (local.get $2) - ) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (loop $label$5 - (if - (i32.load - (i32.const 4128) - ) - (block - (call $fimport$9 - (i32.const 1) - (local.get $0) - ) - (i32.store - (local.get $10) - (i32.load - (local.get $13) - ) - ) - (i32.store offset=4 - (local.get $10) - (local.get $1) - ) - (i32.store offset=8 - (local.get $10) - (local.get $4) - ) - (local.set $3 - (call $11 - (call $fimport$15 - (i32.const 146) - (local.get $10) - ) - ) - ) - (call $fimport$7 - (i32.const 0) - ) - ) - (block - (i32.store - (local.get $9) - (i32.load - (local.get $13) - ) - ) - (i32.store offset=4 - (local.get $9) - (local.get $1) - ) - (i32.store offset=8 - (local.get $9) - (local.get $4) - ) - (local.set $3 - (call $11 - (call $fimport$15 - (i32.const 146) - (local.get $9) - ) - ) - ) - ) - ) - (br_if $label$4 - (i32.eq - (local.get $12) - (local.get $3) - ) - ) - (br_if $label$3 - (i32.lt_s - (local.get $3) - (i32.const 0) - ) - ) - (local.set $5 - (if (result i32) - (i32.gt_u - (local.get $3) - (local.tee $5 - (i32.load offset=4 - (local.get $1) - ) - ) - ) - (block (result i32) - (i32.store - (local.get $6) - (local.tee $7 - (i32.load - (local.get $14) - ) - ) - ) - (i32.store - (local.get $11) - (local.get $7) - ) - (local.set $7 - (i32.load offset=12 - (local.get $1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (local.set $4 - (i32.add - (local.get $4) - (i32.const -1) - ) - ) - (i32.sub - (local.get $3) - (local.get $5) - ) - ) - (if (result i32) - (i32.eq - (local.get $4) - (i32.const 2) - ) - (block (result i32) - (i32.store - (local.get $6) - (i32.add - (i32.load - (local.get $6) - ) - (local.get $3) - ) - ) - (local.set $7 - (local.get $5) - ) - (local.set $4 - (i32.const 2) - ) - (local.get $3) - ) - (block (result i32) - (local.set $7 - (local.get $5) - ) - (local.get $3) - ) - ) - ) - ) - (i32.store - (local.get $1) - (i32.add - (i32.load - (local.get $1) - ) - (local.get $5) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.sub - (local.get $7) - (local.get $5) - ) - ) - (local.set $12 - (i32.sub - (local.get $12) - (local.get $3) - ) - ) - (br $label$5) - ) - ) - (i32.store offset=16 - (local.get $0) - (i32.add - (local.tee $1 - (i32.load - (local.get $14) - ) - ) - (i32.load offset=48 - (local.get $0) - ) - ) - ) - (i32.store - (local.get $6) - (local.get $1) - ) - (i32.store - (local.get $11) - (local.get $1) - ) - (br $label$2) - ) - (i32.store offset=16 - (local.get $0) - (i32.const 0) - ) - (i32.store - (local.get $6) - (i32.const 0) - ) - (i32.store - (local.get $11) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (local.set $2 - (if (result i32) - (i32.eq - (local.get $4) - (i32.const 2) - ) - (i32.const 0) - (i32.sub - (local.get $2) - (i32.load offset=4 - (local.get $1) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $8) - ) - (local.get $2) + (func $11 (;24;) (type $2) (param $0 i32) (result i32) + local.get $0 + i32.const -4096 + i32.gt_u + if (result i32) ;; label = @1 + block (result i32) ;; label = @2 + call $12 + i32.const 0 + local.get $0 + i32.sub + i32.store + i32.const -1 + end + else + local.get $0 + end ) - ) - (func $16 (; 29 ;) (type $3) (param $0 i32) - (if - (i32.eqz - (i32.load offset=68 - (local.get $0) - ) - ) - (call $13 - (local.get $0) - ) + (func $12 (;25;) (type $4) (result i32) + i32.const 4172 ) - ) - (func $17 (; 30 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $5 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (if - (i32.and - (local.tee $4 - (i32.ne - (local.get $2) - (i32.const 0) - ) - ) - (i32.ne - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.const 0) - ) - ) - (block - (local.set $4 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (local.set $3 - (local.get $2) - ) - (local.set $2 - (local.get $0) - ) - (loop $label$6 - (if - (i32.eq - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.get $4) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (br $label$3) - ) - ) - (br_if $label$6 - (i32.and - (local.tee $0 - (i32.ne - (local.tee $3 - (i32.add - (local.get $3) - (i32.const -1) - ) - ) - (i32.const 0) - ) - ) - (i32.ne - (i32.and - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 3) - ) - (i32.const 0) - ) - ) - ) - (br $label$4) - ) - ) - (block - (local.set $3 - (local.get $2) - ) - (local.set $2 - (local.get $0) - ) - (local.set $0 - (local.get $4) - ) - ) - ) - ) - (if - (local.get $0) - (block - (local.set $0 - (local.get $3) - ) - (br $label$3) - ) - (local.set $0 - (i32.const 0) - ) - ) - (br $label$2) - ) - (if - (i32.ne - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.tee $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $3 - (i32.mul - (local.get $5) - (i32.const 16843009) - ) - ) - (block $label$12 - (block $label$13 - (br_if $label$13 - (i32.le_u - (local.get $0) - (i32.const 3) - ) - ) - (loop $label$14 - (if - (i32.eqz - (i32.and - (i32.xor - (i32.and - (local.tee $4 - (i32.xor - (i32.load - (local.get $2) - ) - (local.get $3) - ) - ) - (i32.const -2139062144) - ) - (i32.const -2139062144) - ) - (i32.add - (local.get $4) - (i32.const -16843009) - ) - ) - ) - (block - (local.set $2 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - (br_if $label$14 - (i32.gt_u - (local.tee $0 - (i32.add - (local.get $0) - (i32.const -4) - ) - ) - (i32.const 3) - ) - ) - (br $label$13) - ) - ) - ) - (br $label$12) - ) - (if - (i32.eqz - (local.get $0) - ) - (block - (local.set $0 - (i32.const 0) - ) - (br $label$2) - ) - ) - ) - (loop $label$17 - (br_if $label$2 - (i32.eq - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.get $1) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (local.set $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (br_if $label$17 - (local.tee $0 - (i32.add - (local.get $0) - (i32.const -1) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - ) - ) - ) - ) - (if (result i32) - (local.get $0) - (local.get $2) - (i32.const 0) - ) + (func $13 (;26;) (type $3) (param $0 i32) + nop ) - ) - (func $18 (; 31 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 224) - ) - ) - (local.set $5 - (i32.add - (local.get $4) - (i32.const 136) - ) - ) - (i64.store align=4 - (local.tee $3 - (i32.add - (local.get $4) - (i32.const 80) - ) - ) - (i64.const 0) - ) - (i64.store offset=8 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=16 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=24 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=32 align=4 - (local.get $3) - (i64.const 0) - ) - (i32.store - (local.tee $6 - (i32.add - (local.get $4) - (i32.const 120) - ) - ) - (i32.load - (local.get $2) - ) - ) - (if - (i32.lt_s - (call $19 - (i32.const 0) - (local.get $1) - (local.get $6) - (local.tee $2 - (local.get $4) - ) - (local.get $3) - ) - (i32.const 0) - ) - (local.set $1 - (i32.const -1) - ) - (block - (local.set $12 - (if (result i32) - (i32.gt_s - (i32.load offset=76 - (local.get $0) - ) - (i32.const -1) - ) - (call $20 - (local.get $0) - ) - (i32.const 0) - ) - ) - (local.set $7 - (i32.load - (local.get $0) - ) - ) - (if - (i32.lt_s - (i32.load8_s offset=74 - (local.get $0) - ) - (i32.const 1) - ) - (i32.store - (local.get $0) - (i32.and - (local.get $7) - (i32.const -33) - ) - ) - ) - (if - (i32.load - (local.tee $8 - (i32.add - (local.get $0) - (i32.const 48) - ) - ) - ) - (local.set $1 - (call $19 - (local.get $0) - (local.get $1) - (local.get $6) - (local.get $2) - (local.get $3) - ) - ) - (block - (local.set $10 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 44) - ) - ) - ) - ) - (i32.store - (local.get $9) - (local.get $5) - ) - (i32.store - (local.tee $13 - (i32.add - (local.get $0) - (i32.const 28) - ) - ) - (local.get $5) - ) - (i32.store - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - (local.get $5) - ) - (i32.store - (local.get $8) - (i32.const 80) - ) - (i32.store - (local.tee $14 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - (i32.add - (local.get $5) - (i32.const 80) - ) - ) - (local.set $1 - (call $19 - (local.get $0) - (local.get $1) - (local.get $6) - (local.get $2) - (local.get $3) - ) - ) - (if - (local.get $10) - (block - (drop - (call_indirect (type $0) - (local.get $0) - (i32.const 0) - (i32.const 0) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $0) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $11) - ) - ) - (local.set $1 - (i32.const -1) - ) - ) - (i32.store - (local.get $9) - (local.get $10) - ) - (i32.store - (local.get $8) - (i32.const 0) - ) - (i32.store - (local.get $14) - (i32.const 0) - ) - (i32.store - (local.get $13) - (i32.const 0) - ) - (i32.store - (local.get $11) - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (local.get $0) - (i32.or - (local.tee $2 - (i32.load - (local.get $0) - ) - ) - (i32.and - (local.get $7) - (i32.const 32) - ) - ) - ) - (if - (local.get $12) - (call $13 - (local.get $0) - ) - ) - (if - (i32.and - (local.get $2) - (i32.const 32) - ) - (local.set $1 - (i32.const -1) - ) - ) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $1) + (func $14 (;27;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 80 + i32.add + global.set $global$1 + local.get $4 + local.set $3 + local.get $4 + i32.const 12 + i32.add + local.set $5 + local.get $0 + i32.const 3 + i32.store offset=36 + local.get $0 + i32.load + i32.const 64 + i32.and + i32.eqz + if ;; label = @2 + block ;; label = @3 + local.get $3 + local.get $0 + i32.load offset=60 + i32.store + local.get $3 + i32.const 21505 + i32.store offset=4 + local.get $3 + local.get $5 + i32.store offset=8 + i32.const 54 + local.get $3 + call $fimport$13 + if ;; label = @4 + local.get $0 + i32.const -1 + i32.store8 offset=75 + end + end + end + local.get $0 + local.get $1 + local.get $2 + call $15 + local.set $0 + local.get $4 + global.set $global$1 + local.get $0 + end ) - ) - (func $19 (; 32 ;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 i32) - (local $17 i32) - (local $18 i32) - (local $19 i32) - (local $20 i32) - (local $21 i32) - (local $22 i32) - (local $23 i32) - (local $24 i32) - (local $25 i32) - (local $26 i32) - (local $27 i32) - (local $28 i32) - (local $29 i32) - (local $30 i32) - (local $31 i32) - (local $32 i32) - (local $33 i32) - (local $34 i32) - (local $35 i32) - (local $36 i32) - (local $37 i32) - (local $38 i32) - (local $39 i32) - (local $40 i32) - (local $41 i32) - (local $42 i32) - (local $43 i32) - (local $44 i32) - (local $45 i32) - (local $46 i32) - (local $47 i32) - (local $48 i32) - (local $49 i32) - (local $50 i64) - (local $51 i64) - (local $52 f64) - (local $53 f64) - (block $label$1 (result i32) - (local.set $23 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 624) - ) - ) - (local.set $20 - (i32.add - (local.get $23) - (i32.const 16) - ) - ) - (local.set $16 - (local.get $23) - ) - (local.set $36 - (i32.add - (local.get $23) - (i32.const 528) - ) - ) - (local.set $30 - (i32.ne - (local.get $0) - (i32.const 0) - ) - ) - (local.set $38 - (local.tee $21 - (i32.add - (local.tee $17 - (i32.add - (local.get $23) - (i32.const 536) - ) - ) - (i32.const 40) - ) - ) - ) - (local.set $39 - (i32.add - (local.get $17) - (i32.const 39) - ) - ) - (local.set $42 - (i32.add - (local.tee $37 - (i32.add - (local.get $23) - (i32.const 8) - ) - ) - (i32.const 4) - ) - ) - (local.set $43 - (i32.sub - (i32.const 0) - (local.tee $27 - (local.tee $19 - (i32.add - (local.get $23) - (i32.const 588) - ) - ) - ) - ) - ) - (local.set $33 - (i32.add - (local.tee $17 - (i32.add - (local.get $23) - (i32.const 576) - ) - ) - (i32.const 12) - ) - ) - (local.set $40 - (i32.add - (local.get $17) - (i32.const 11) - ) - ) - (local.set $44 - (i32.sub - (local.tee $28 - (local.get $33) - ) - (local.get $27) - ) - ) - (local.set $45 - (i32.sub - (i32.const -2) - (local.get $27) - ) - ) - (local.set $46 - (i32.add - (local.get $28) - (i32.const 2) - ) - ) - (local.set $48 - (i32.add - (local.tee $47 - (i32.add - (local.get $23) - (i32.const 24) - ) - ) - (i32.const 288) - ) - ) - (local.set $41 - (local.tee $31 - (i32.add - (local.get $19) - (i32.const 9) - ) - ) - ) - (local.set $34 - (i32.add - (local.get $19) - (i32.const 8) - ) - ) - (local.set $15 - (i32.const 0) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $17 - (i32.const 0) - ) - (block $label$2 - (block $label$3 - (loop $label$4 - (block $label$5 - (if - (i32.gt_s - (local.get $15) - (i32.const -1) - ) - (local.set $15 - (if (result i32) - (i32.gt_s - (local.get $10) - (i32.sub - (i32.const 2147483647) - (local.get $15) - ) - ) - (block (result i32) - (i32.store - (call $12) - (i32.const 75) - ) - (i32.const -1) - ) - (i32.add - (local.get $10) - (local.get $15) - ) - ) - ) - ) - (br_if $label$3 - (i32.eqz - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.get $1) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (local.set $11 - (local.get $1) - ) - (block $label$9 - (block $label$10 - (loop $label$11 - (block $label$12 - (block $label$13 - (block $label$14 - (block $label$15 - (br_table $label$14 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$15 $label$13 - (i32.sub - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 0) - ) - ) - ) - (local.set $5 - (local.get $11) - ) - (br $label$10) - ) - (local.set $5 - (local.get $11) - ) - (br $label$12) - ) - (local.set $5 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (br $label$11) - ) - ) - (br $label$9) - ) - (loop $label$16 - (br_if $label$9 - (i32.ne - (i32.load8_s offset=1 - (local.get $5) - ) - (i32.const 37) - ) - ) - (local.set $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - (br_if $label$16 - (i32.eq - (i32.load8_s - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 2) - ) - ) - ) - (i32.const 37) - ) - ) - ) - ) - (local.set $10 - (i32.sub - (local.get $11) - (local.get $1) - ) - ) - (if - (local.get $30) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (local.get $10) - (local.get $0) - ) - ) - ) - ) - (if - (local.get $10) - (block - (local.set $1 - (local.get $5) - ) - (br $label$4) - ) - ) - (local.set $10 - (if (result i32) - (i32.lt_u - (local.tee $9 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $10 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block (result i32) - (local.set $10 - (i32.add - (local.get $5) - (i32.const 3) - ) - ) - (if - (local.tee $12 - (i32.eq - (i32.load8_s offset=2 - (local.get $5) - ) - (i32.const 36) - ) - ) - (local.set $11 - (local.get $10) - ) - ) - (if - (local.get $12) - (local.set $17 - (i32.const 1) - ) - ) - (local.set $5 - (i32.load8_s - (local.get $11) - ) - ) - (if - (i32.eqz - (local.get $12) - ) - (local.set $9 - (i32.const -1) - ) - ) - (local.get $17) - ) - (block (result i32) - (local.set $5 - (local.get $10) - ) - (local.set $9 - (i32.const -1) - ) - (local.get $17) - ) - ) - ) - (block $label$25 - (if - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (i32.const 32) - ) - (block - (local.set $17 - (i32.const 0) - ) - (loop $label$27 - (br_if $label$25 - (i32.eqz - (i32.and - (i32.shl - (i32.const 1) - (local.get $12) - ) - (i32.const 75913) - ) - ) - ) - (local.set $17 - (i32.or - (i32.shl - (i32.const 1) - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (local.get $17) - ) - ) - (br_if $label$27 - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (i32.const 32) - ) - ) - ) - ) - (local.set $17 - (i32.const 0) - ) - ) - ) - (block $label$29 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 42) - ) - (block - (local.set $11 - (block $label$31 (result i32) - (block $label$32 - (br_if $label$32 - (i32.ge_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - (br_if $label$32 - (i32.ne - (i32.load8_s offset=2 - (local.get $11) - ) - (i32.const 36) - ) - ) - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $12) - (i32.const 2) - ) - ) - (i32.const 10) - ) - (local.set $8 - (i32.const 1) - ) - (local.set $10 - (i32.wrap_i64 - (i64.load - (i32.add - (local.get $3) - (i32.shl - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -48) - ) - (i32.const 3) - ) - ) - ) - ) - ) - (br $label$31 - (i32.add - (local.get $11) - (i32.const 3) - ) - ) - ) - (if - (local.get $10) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $12 - (local.get $17) - ) - (local.set $17 - (i32.const 0) - ) - (local.set $11 - (local.get $7) - ) - (local.set $10 - (i32.const 0) - ) - (br $label$29) - ) - ) - (local.set $10 - (i32.load - (local.tee $11 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (local.set $8 - (i32.const 0) - ) - (local.get $7) - ) - ) - (local.set $12 - (i32.or - (local.get $17) - (i32.const 8192) - ) - ) - (local.set $7 - (i32.sub - (i32.const 0) - (local.get $10) - ) - ) - (local.set $5 - (i32.load8_s - (local.get $11) - ) - ) - (if - (i32.eqz - (local.tee $6 - (i32.lt_s - (local.get $10) - (i32.const 0) - ) - ) - ) - (local.set $12 - (local.get $17) - ) - ) - (local.set $17 - (local.get $8) - ) - (if - (local.get $6) - (local.set $10 - (local.get $7) - ) - ) - ) - (if - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block - (local.set $7 - (i32.const 0) - ) - (local.set $5 - (local.get $12) - ) - (loop $label$39 - (local.set $7 - (i32.add - (i32.mul - (local.get $7) - (i32.const 10) - ) - (local.get $5) - ) - ) - (br_if $label$39 - (i32.lt_u - (local.tee $5 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $12 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - ) - (if - (i32.lt_s - (local.get $7) - (i32.const 0) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - (block - (local.set $5 - (local.get $12) - ) - (local.set $12 - (local.get $17) - ) - (local.set $17 - (local.get $10) - ) - (local.set $10 - (local.get $7) - ) - ) - ) - ) - (block - (local.set $12 - (local.get $17) - ) - (local.set $17 - (local.get $10) - ) - (local.set $10 - (i32.const 0) - ) - ) - ) - ) - ) - (block $label$43 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 46) - ) - (block - (if - (i32.ne - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 42) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block - (local.set $11 - (local.get $7) - ) - (local.set $7 - (i32.const 0) - ) - ) - (block - (local.set $5 - (i32.const 0) - ) - (local.set $11 - (local.get $7) - ) - (br $label$43) - ) - ) - (loop $label$48 - (local.set $5 - (i32.add - (i32.mul - (local.get $7) - (i32.const 10) - ) - (local.get $5) - ) - ) - (br_if $label$43 - (i32.ge_u - (local.tee $8 - (i32.add - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - (local.set $7 - (local.get $5) - ) - (local.set $5 - (local.get $8) - ) - (br $label$48) - ) - ) - ) - (if - (i32.lt_u - (local.tee $5 - (i32.add - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 2) - ) - ) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (if - (i32.eq - (i32.load8_s offset=3 - (local.get $11) - ) - (i32.const 36) - ) - (block - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $5) - (i32.const 2) - ) - ) - (i32.const 10) - ) - (local.set $5 - (i32.wrap_i64 - (i64.load - (i32.add - (local.get $3) - (i32.shl - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -48) - ) - (i32.const 3) - ) - ) - ) - ) - ) - (local.set $11 - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (br $label$43) - ) - ) - ) - (if - (local.get $17) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $11 - (if (result i32) - (local.get $30) - (block (result i32) - (local.set $5 - (i32.load - (local.tee $11 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (local.get $7) - ) - (block (result i32) - (local.set $5 - (i32.const 0) - ) - (local.get $7) - ) - ) - ) - ) - (local.set $5 - (i32.const -1) - ) - ) - ) - (local.set $7 - (local.get $11) - ) - (local.set $8 - (i32.const 0) - ) - (loop $label$55 - (if - (i32.gt_u - (local.tee $6 - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -65) - ) - ) - (i32.const 57) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $11 - (i32.add - (local.get $7) - (i32.const 1) - ) - ) - (if - (i32.lt_u - (i32.add - (local.tee $6 - (i32.and - (local.tee $13 - (i32.load8_s - (i32.add - (i32.add - (i32.mul - (local.get $8) - (i32.const 58) - ) - (i32.const 1699) - ) - (local.get $6) - ) - ) - ) - (i32.const 255) - ) - ) - (i32.const -1) - ) - (i32.const 8) - ) - (block - (local.set $7 - (local.get $11) - ) - (local.set $8 - (local.get $6) - ) - (br $label$55) - ) - ) - ) - (if - (i32.eqz - (i32.shr_s - (i32.shl - (local.get $13) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $14 - (i32.gt_s - (local.get $9) - (i32.const -1) - ) - ) - (block $label$59 - (block $label$60 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $13) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 19) - ) - (if - (local.get $14) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - (br $label$60) - ) - (block - (if - (local.get $14) - (block - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $9) - (i32.const 2) - ) - ) - (local.get $6) - ) - (i64.store - (local.get $16) - (i64.load - (i32.add - (local.get $3) - (i32.shl - (local.get $9) - (i32.const 3) - ) - ) - ) - ) - (br $label$60) - ) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $15 - (i32.const 0) - ) - (br $label$5) - ) - ) - (call $22 - (local.get $16) - (local.get $6) - (local.get $2) - ) - ) - ) - (br $label$59) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - ) - ) - (local.set $9 - (i32.and - (local.tee $7 - (i32.load8_s - (local.get $7) - ) - ) - (i32.const -33) - ) - ) - (if - (i32.eqz - (i32.and - (i32.ne - (local.get $8) - (i32.const 0) - ) - (i32.eq - (i32.and - (local.get $7) - (i32.const 15) - ) - (i32.const 3) - ) - ) - ) - (local.set $9 - (local.get $7) - ) - ) - (local.set $7 - (i32.and - (local.get $12) - (i32.const -65537) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 8192) - ) - (local.set $12 - (local.get $7) - ) - ) - (block $label$70 - (block $label$71 - (block $label$72 - (block $label$73 - (block $label$74 - (block $label$75 - (block $label$76 - (block $label$77 - (block $label$78 - (block $label$79 - (block $label$80 - (block $label$81 - (block $label$82 - (block $label$83 - (block $label$84 - (block $label$85 - (block $label$86 - (block $label$87 - (block $label$88 - (block $label$89 - (br_table $label$78 $label$77 $label$80 $label$77 $label$78 $label$78 $label$78 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$79 $label$77 $label$77 $label$77 $label$77 $label$87 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$78 $label$77 $label$83 $label$85 $label$78 $label$78 $label$78 $label$77 $label$85 $label$77 $label$77 $label$77 $label$82 $label$89 $label$86 $label$88 $label$77 $label$77 $label$81 $label$77 $label$84 $label$77 $label$77 $label$87 $label$77 - (i32.sub - (local.get $9) - (i32.const 65) - ) - ) - ) - (block $label$90 - (block $label$91 - (block $label$92 - (block $label$93 - (block $label$94 - (block $label$95 - (block $label$96 - (block $label$97 - (br_table $label$97 $label$96 $label$95 $label$94 $label$93 $label$90 $label$92 $label$91 $label$90 - (i32.sub - (i32.shr_s - (i32.shl - (i32.and - (local.get $8) - (i32.const 255) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 0) - ) - ) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i64.store - (i32.load - (local.get $16) - ) - (i64.extend_i32_s - (local.get $15) - ) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store16 - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store8 - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i64.store - (i32.load - (local.get $16) - ) - (i64.extend_i32_s - (local.get $15) - ) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $12 - (i32.or - (local.get $12) - (i32.const 8) - ) - ) - (if - (i32.le_u - (local.get $5) - (i32.const 8) - ) - (local.set $5 - (i32.const 8) - ) - ) - (local.set $9 - (i32.const 120) - ) - (br $label$76) - ) - (br $label$76) - ) - (if - (i64.eq - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (local.set $7 - (local.get $21) - ) - (block - (local.set $1 - (local.get $21) - ) - (loop $label$101 - (i64.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i64.or - (i64.and - (local.get $50) - (i64.const 7) - ) - (i64.const 48) - ) - ) - (br_if $label$101 - (i64.ne - (local.tee $50 - (i64.shr_u - (local.get $50) - (i64.const 3) - ) - ) - (i64.const 0) - ) - ) - (local.set $7 - (local.get $1) - ) - ) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 8) - ) - (block - (local.set $8 - (i32.add - (local.tee $1 - (i32.sub - (local.get $38) - (local.get $7) - ) - ) - (i32.const 1) - ) - ) - (if - (i32.le_s - (local.get $5) - (local.get $1) - ) - (local.set $5 - (local.get $8) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 2179) - ) - (br $label$71) - ) - (block - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 2179) - ) - (br $label$71) - ) - ) - ) - (if - (i64.lt_s - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (block - (i64.store - (local.get $16) - (local.tee $50 - (i64.sub - (i64.const 0) - (local.get $50) - ) - ) - ) - (local.set $6 - (i32.const 1) - ) - (local.set $8 - (i32.const 2179) - ) - (br $label$75) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 2048) - ) - (block - (local.set $6 - (i32.const 1) - ) - (local.set $8 - (i32.const 2180) - ) - (br $label$75) - ) - (block - (local.set $6 - (local.tee $1 - (i32.and - (local.get $12) - (i32.const 1) - ) - ) - ) - (local.set $8 - (if (result i32) - (local.get $1) - (i32.const 2181) - (i32.const 2179) - ) - ) - (br $label$75) - ) - ) - ) - (local.set $50 - (i64.load - (local.get $16) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 2179) - ) - (br $label$75) - ) - (i64.store8 - (local.get $39) - (i64.load - (local.get $16) - ) - ) - (local.set $1 - (local.get $39) - ) - (local.set $12 - (local.get $7) - ) - (local.set $7 - (i32.const 1) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 2179) - ) - (local.set $5 - (local.get $21) - ) - (br $label$70) - ) - (local.set $1 - (call $24 - (i32.load - (call $12) - ) - ) - ) - (br $label$74) - ) - (if - (i32.eqz - (local.tee $1 - (i32.load - (local.get $16) - ) - ) - ) - (local.set $1 - (i32.const 2189) - ) - ) - (br $label$74) - ) - (i64.store32 - (local.get $37) - (i64.load - (local.get $16) - ) - ) - (i32.store - (local.get $42) - (i32.const 0) - ) - (i32.store - (local.get $16) - (local.get $37) - ) - (local.set $7 - (local.get $37) - ) - (local.set $6 - (i32.const -1) - ) - (br $label$73) - ) - (local.set $7 - (i32.load - (local.get $16) - ) - ) - (if - (local.get $5) - (block - (local.set $6 - (local.get $5) - ) - (br $label$73) - ) - (block - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (i32.const 0) - (local.get $12) - ) - (local.set $1 - (i32.const 0) - ) - (br $label$72) - ) - ) - ) - (local.set $52 - (f64.load - (local.get $16) - ) - ) - (i32.store - (local.get $20) - (i32.const 0) - ) - (local.set $26 - (if (result i32) - (i64.lt_s - (i64.reinterpret_f64 - (local.get $52) - ) - (i64.const 0) - ) - (block (result i32) - (local.set $24 - (i32.const 1) - ) - (local.set $52 - (f64.neg - (local.get $52) - ) - ) - (i32.const 2196) - ) - (block (result i32) - (local.set $1 - (i32.and - (local.get $12) - (i32.const 1) - ) - ) - (if (result i32) - (i32.and - (local.get $12) - (i32.const 2048) - ) - (block (result i32) - (local.set $24 - (i32.const 1) - ) - (i32.const 2199) - ) - (block (result i32) - (local.set $24 - (local.get $1) - ) - (if (result i32) - (local.get $1) - (i32.const 2202) - (i32.const 2197) - ) - ) - ) - ) - ) - ) - (block $label$119 - (if - (i64.lt_u - (i64.and - (i64.reinterpret_f64 - (local.get $52) - ) - (i64.const 9218868437227405312) - ) - (i64.const 9218868437227405312) - ) - (block - (if - (local.tee $1 - (f64.ne - (local.tee $52 - (f64.mul - (call $27 - (local.get $52) - (local.get $20) - ) - (f64.const 2) - ) - ) - (f64.const 0) - ) - ) - (i32.store - (local.get $20) - (i32.add - (i32.load - (local.get $20) - ) - (i32.const -1) - ) - ) - ) - (if - (i32.eq - (local.tee $22 - (i32.or - (local.get $9) - (i32.const 32) - ) - ) - (i32.const 97) - ) - (block - (local.set $1 - (i32.add - (local.get $26) - (i32.const 9) - ) - ) - (if - (local.tee $6 - (i32.and - (local.get $9) - (i32.const 32) - ) - ) - (local.set $26 - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.or - (i32.gt_u - (local.get $5) - (i32.const 11) - ) - (i32.eqz - (local.tee $1 - (i32.sub - (i32.const 12) - (local.get $5) - ) - ) - ) - ) - ) - (block - (local.set $53 - (f64.const 8) - ) - (loop $label$125 - (local.set $53 - (f64.mul - (local.get $53) - (f64.const 16) - ) - ) - (br_if $label$125 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - (local.set $52 - (if (result f64) - (i32.eq - (i32.load8_s - (local.get $26) - ) - (i32.const 45) - ) - (f64.neg - (f64.add - (local.get $53) - (f64.sub - (f64.neg - (local.get $52) - ) - (local.get $53) - ) - ) - ) - (f64.sub - (f64.add - (local.get $52) - (local.get $53) - ) - (local.get $53) - ) - ) - ) - ) - ) - (local.set $1 - (i32.sub - (i32.const 0) - (local.tee $7 - (i32.load - (local.get $20) - ) - ) - ) - ) - (if - (i32.eq - (local.tee $1 - (call $23 - (i64.extend_i32_s - (if (result i32) - (i32.lt_s - (local.get $7) - (i32.const 0) - ) - (local.get $1) - (local.get $7) - ) - ) - (local.get $33) - ) - ) - (local.get $33) - ) - (block - (i32.store8 - (local.get $40) - (i32.const 48) - ) - (local.set $1 - (local.get $40) - ) - ) - ) - (local.set $13 - (i32.or - (local.get $24) - (i32.const 2) - ) - ) - (i32.store8 - (i32.add - (local.get $1) - (i32.const -1) - ) - (i32.add - (i32.and - (i32.shr_s - (local.get $7) - (i32.const 31) - ) - (i32.const 2) - ) - (i32.const 43) - ) - ) - (i32.store8 - (local.tee $8 - (i32.add - (local.get $1) - (i32.const -2) - ) - ) - (i32.add - (local.get $9) - (i32.const 15) - ) - ) - (local.set $9 - (i32.lt_s - (local.get $5) - (i32.const 1) - ) - ) - (local.set $14 - (i32.eqz - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - ) - (local.set $1 - (local.get $19) - ) - (loop $label$131 - (i32.store8 - (local.get $1) - (i32.or - (i32.load8_u - (i32.add - (local.tee $7 - (i32.trunc_f64_s - (local.get $52) - ) - ) - (i32.const 2163) - ) - ) - (local.get $6) - ) - ) - (local.set $52 - (f64.mul - (f64.sub - (local.get $52) - (f64.convert_i32_s - (local.get $7) - ) - ) - (f64.const 16) - ) - ) - (local.set $1 - (block $label$132 (result i32) - (if (result i32) - (i32.eq - (i32.sub - (local.tee $7 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.get $27) - ) - (i32.const 1) - ) - (block (result i32) - (drop - (br_if $label$132 - (local.get $7) - (i32.and - (local.get $14) - (i32.and - (local.get $9) - (f64.eq - (local.get $52) - (f64.const 0) - ) - ) - ) - ) - ) - (i32.store8 - (local.get $7) - (i32.const 46) - ) - (i32.add - (local.get $1) - (i32.const 2) - ) - ) - (local.get $7) - ) - ) - ) - (br_if $label$131 - (f64.ne - (local.get $52) - (f64.const 0) - ) - ) - ) - (local.set $6 - (i32.sub - (i32.add - (local.get $46) - (local.get $5) - ) - (local.tee $7 - (local.get $8) - ) - ) - ) - (local.set $9 - (i32.add - (i32.sub - (local.get $44) - (local.get $7) - ) - (local.get $1) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $5 - (i32.add - (if (result i32) - (i32.and - (i32.ne - (local.get $5) - (i32.const 0) - ) - (i32.lt_s - (i32.add - (local.get $45) - (local.get $1) - ) - (local.get $5) - ) - ) - (local.get $6) - (local.tee $6 - (local.get $9) - ) - ) - (local.get $13) - ) - ) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $26) - (local.get $13) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (local.set $1 - (i32.sub - (local.get $1) - (local.get $27) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $19) - (local.get $1) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (i32.sub - (local.get $6) - (i32.add - (local.get $1) - (local.tee $1 - (i32.sub - (local.get $28) - (local.get $7) - ) - ) - ) - ) - (i32.const 0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $8) - (local.get $1) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $5) - (local.get $10) - ) - (local.set $10 - (local.get $5) - ) - ) - (br $label$119) - ) - ) - (if - (local.get $1) - (block - (i32.store - (local.get $20) - (local.tee $6 - (i32.add - (i32.load - (local.get $20) - ) - (i32.const -28) - ) - ) - ) - (local.set $52 - (f64.mul - (local.get $52) - (f64.const 268435456) - ) - ) - ) - (local.set $6 - (i32.load - (local.get $20) - ) - ) - ) - (local.set $8 - (local.tee $7 - (if (result i32) - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (local.get $47) - (local.get $48) - ) - ) - ) - (loop $label$145 - (i32.store - (local.get $8) - (local.tee $1 - (i32.trunc_f64_s - (local.get $52) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (br_if $label$145 - (f64.ne - (local.tee $52 - (f64.mul - (f64.sub - (local.get $52) - (f64.convert_i32_u - (local.get $1) - ) - ) - (f64.const 1e9) - ) - ) - (f64.const 0) - ) - ) - ) - (if - (i32.gt_s - (local.get $6) - (i32.const 0) - ) - (block - (local.set $1 - (local.get $7) - ) - (loop $label$147 - (local.set $14 - (if (result i32) - (i32.gt_s - (local.get $6) - (i32.const 29) - ) - (i32.const 29) - (local.get $6) - ) - ) - (block $label$150 - (if - (i32.ge_u - (local.tee $6 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - (local.get $1) - ) - (block - (local.set $50 - (i64.extend_i32_u - (local.get $14) - ) - ) - (local.set $13 - (i32.const 0) - ) - (loop $label$152 - (i64.store32 - (local.get $6) - (i64.rem_u - (local.tee $51 - (i64.add - (i64.shl - (i64.extend_i32_u - (i32.load - (local.get $6) - ) - ) - (local.get $50) - ) - (i64.extend_i32_u - (local.get $13) - ) - ) - ) - (i64.const 1000000000) - ) - ) - (local.set $13 - (i32.wrap_i64 - (i64.div_u - (local.get $51) - (i64.const 1000000000) - ) - ) - ) - (br_if $label$152 - (i32.ge_u - (local.tee $6 - (i32.add - (local.get $6) - (i32.const -4) - ) - ) - (local.get $1) - ) - ) - ) - (br_if $label$150 - (i32.eqz - (local.get $13) - ) - ) - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -4) - ) - ) - (local.get $13) - ) - ) - ) - ) - (loop $label$153 - (if - (i32.gt_u - (local.get $8) - (local.get $1) - ) - (if - (i32.eqz - (i32.load - (local.tee $6 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - ) - (block - (local.set $8 - (local.get $6) - ) - (br $label$153) - ) - ) - ) - ) - (i32.store - (local.get $20) - (local.tee $6 - (i32.sub - (i32.load - (local.get $20) - ) - (local.get $14) - ) - ) - ) - (br_if $label$147 - (i32.gt_s - (local.get $6) - (i32.const 0) - ) - ) - ) - ) - (local.set $1 - (local.get $7) - ) - ) - (local.set $18 - (if (result i32) - (i32.lt_s - (local.get $5) - (i32.const 0) - ) - (i32.const 6) - (local.get $5) - ) - ) - (if - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (block - (local.set $14 - (i32.add - (i32.div_s - (i32.add - (local.get $18) - (i32.const 25) - ) - (i32.const 9) - ) - (i32.const 1) - ) - ) - (local.set $25 - (i32.eq - (local.get $22) - (i32.const 102) - ) - ) - (local.set $5 - (local.get $8) - ) - (loop $label$160 - (if - (i32.gt_s - (local.tee $13 - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (i32.const 9) - ) - (local.set $13 - (i32.const 9) - ) - ) - (block $label$162 - (if - (i32.lt_u - (local.get $1) - (local.get $5) - ) - (block - (local.set $29 - (i32.add - (i32.shl - (i32.const 1) - (local.get $13) - ) - (i32.const -1) - ) - ) - (local.set $35 - (i32.shr_u - (i32.const 1000000000) - (local.get $13) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (local.get $1) - ) - (loop $label$164 - (i32.store - (local.get $8) - (i32.add - (i32.shr_u - (local.tee $32 - (i32.load - (local.get $8) - ) - ) - (local.get $13) - ) - (local.get $6) - ) - ) - (local.set $6 - (i32.mul - (i32.and - (local.get $32) - (local.get $29) - ) - (local.get $35) - ) - ) - (br_if $label$164 - (i32.lt_u - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (local.get $5) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (local.get $8) - ) - ) - (br_if $label$162 - (i32.eqz - (local.get $6) - ) - ) - (i32.store - (local.get $5) - (local.get $6) - ) - (local.set $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - ) - (block - (local.set $8 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (local.get $8) - ) - ) - ) - ) - ) - (local.set $6 - (i32.add - (local.tee $8 - (if (result i32) - (local.get $25) - (local.get $7) - (local.get $1) - ) - ) - (i32.shl - (local.get $14) - (i32.const 2) - ) - ) - ) - (if - (i32.gt_s - (i32.shr_s - (i32.sub - (local.get $5) - (local.get $8) - ) - (i32.const 2) - ) - (local.get $14) - ) - (local.set $5 - (local.get $6) - ) - ) - (i32.store - (local.get $20) - (local.tee $6 - (i32.add - (i32.load - (local.get $20) - ) - (local.get $13) - ) - ) - ) - (br_if $label$160 - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - ) - (local.set $13 - (local.get $5) - ) - ) - ) - (local.set $13 - (local.get $8) - ) - ) - (local.set $25 - (local.get $7) - ) - (block $label$172 - (if - (i32.lt_u - (local.get $1) - (local.get $13) - ) - (block - (local.set $5 - (i32.mul - (i32.shr_s - (i32.sub - (local.get $25) - (local.get $1) - ) - (i32.const 2) - ) - (i32.const 9) - ) - ) - (br_if $label$172 - (i32.lt_u - (local.tee $6 - (i32.load - (local.get $1) - ) - ) - (i32.const 10) - ) - ) - (local.set $8 - (i32.const 10) - ) - (loop $label$174 - (local.set $5 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$174 - (i32.ge_u - (local.get $6) - (local.tee $8 - (i32.mul - (local.get $8) - (i32.const 10) - ) - ) - ) - ) - ) - ) - (local.set $5 - (i32.const 0) - ) - ) - ) - (local.set $29 - (i32.eq - (local.get $22) - (i32.const 103) - ) - ) - (local.set $35 - (i32.ne - (local.get $18) - (i32.const 0) - ) - ) - (if - (i32.lt_s - (local.tee $8 - (i32.add - (i32.sub - (local.get $18) - (if (result i32) - (i32.ne - (local.get $22) - (i32.const 102) - ) - (local.get $5) - (i32.const 0) - ) - ) - (i32.shr_s - (i32.shl - (i32.and - (local.get $35) - (local.get $29) - ) - (i32.const 31) - ) - (i32.const 31) - ) - ) - ) - (i32.add - (i32.mul - (i32.shr_s - (i32.sub - (local.get $13) - (local.get $25) - ) - (i32.const 2) - ) - (i32.const 9) - ) - (i32.const -9) - ) - ) - (block - (if - (i32.lt_s - (local.tee $8 - (i32.add - (i32.rem_s - (local.tee $14 - (i32.add - (local.get $8) - (i32.const 9216) - ) - ) - (i32.const 9) - ) - (i32.const 1) - ) - ) - (i32.const 9) - ) - (block - (local.set $6 - (i32.const 10) - ) - (loop $label$180 - (local.set $6 - (i32.mul - (local.get $6) - (i32.const 10) - ) - ) - (br_if $label$180 - (i32.ne - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 1) - ) - ) - (i32.const 9) - ) - ) - ) - ) - (local.set $6 - (i32.const 10) - ) - ) - (local.set $14 - (i32.rem_u - (local.tee $22 - (i32.load - (local.tee $8 - (i32.add - (i32.add - (local.get $7) - (i32.const 4) - ) - (i32.shl - (i32.add - (i32.div_s - (local.get $14) - (i32.const 9) - ) - (i32.const -1024) - ) - (i32.const 2) - ) - ) - ) - ) - ) - (local.get $6) - ) - ) - (block $label$182 - (if - (i32.eqz - (i32.and - (local.tee $32 - (i32.eq - (i32.add - (local.get $8) - (i32.const 4) - ) - (local.get $13) - ) - ) - (i32.eqz - (local.get $14) - ) - ) - ) - (block - (local.set $52 - (if (result f64) - (i32.lt_u - (local.get $14) - (local.tee $49 - (i32.div_s - (local.get $6) - (i32.const 2) - ) - ) - ) - (f64.const 0.5) - (if (result f64) - (i32.and - (local.get $32) - (i32.eq - (local.get $14) - (local.get $49) - ) - ) - (f64.const 1) - (f64.const 1.5) - ) - ) - ) - (local.set $53 - (if (result f64) - (i32.and - (i32.div_u - (local.get $22) - (local.get $6) - ) - (i32.const 1) - ) - (f64.const 9007199254740994) - (f64.const 9007199254740992) - ) - ) - (block $label$190 - (if - (local.get $24) - (block - (br_if $label$190 - (i32.ne - (i32.load8_s - (local.get $26) - ) - (i32.const 45) - ) - ) - (local.set $53 - (f64.neg - (local.get $53) - ) - ) - (local.set $52 - (f64.neg - (local.get $52) - ) - ) - ) - ) - ) - (i32.store - (local.get $8) - (local.tee $14 - (i32.sub - (local.get $22) - (local.get $14) - ) - ) - ) - (br_if $label$182 - (f64.eq - (f64.add - (local.get $53) - (local.get $52) - ) - (local.get $53) - ) - ) - (i32.store - (local.get $8) - (local.tee $5 - (i32.add - (local.get $14) - (local.get $6) - ) - ) - ) - (if - (i32.gt_u - (local.get $5) - (i32.const 999999999) - ) - (loop $label$193 - (i32.store - (local.get $8) - (i32.const 0) - ) - (if - (i32.lt_u - (local.tee $8 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - (local.get $1) - ) - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -4) - ) - ) - (i32.const 0) - ) - ) - (i32.store - (local.get $8) - (local.tee $5 - (i32.add - (i32.load - (local.get $8) - ) - (i32.const 1) - ) - ) - ) - (br_if $label$193 - (i32.gt_u - (local.get $5) - (i32.const 999999999) - ) - ) - ) - ) - (local.set $5 - (i32.mul - (i32.shr_s - (i32.sub - (local.get $25) - (local.get $1) - ) - (i32.const 2) - ) - (i32.const 9) - ) - ) - (br_if $label$182 - (i32.lt_u - (local.tee $14 - (i32.load - (local.get $1) - ) - ) - (i32.const 10) - ) - ) - (local.set $6 - (i32.const 10) - ) - (loop $label$195 - (local.set $5 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$195 - (i32.ge_u - (local.get $14) - (local.tee $6 - (i32.mul - (local.get $6) - (i32.const 10) - ) - ) - ) - ) - ) - ) - ) - ) - (local.set $14 - (local.get $1) - ) - (local.set $6 - (local.get $5) - ) - (if - (i32.le_u - (local.get $13) - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - ) - (local.set $8 - (local.get $13) - ) - ) - ) - (block - (local.set $14 - (local.get $1) - ) - (local.set $6 - (local.get $5) - ) - (local.set $8 - (local.get $13) - ) - ) - ) - (local.set $32 - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (loop $label$198 - (block $label$199 - (if - (i32.le_u - (local.get $8) - (local.get $14) - ) - (block - (local.set $22 - (i32.const 0) - ) - (br $label$199) - ) - ) - (if - (i32.load - (local.tee $1 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - (local.set $22 - (i32.const 1) - ) - (block - (local.set $8 - (local.get $1) - ) - (br $label$198) - ) - ) - ) - ) - (block $label$203 - (if - (local.get $29) - (block - (local.set $1 - (if (result i32) - (i32.and - (i32.gt_s - (local.tee $1 - (i32.add - (i32.xor - (i32.and - (local.get $35) - (i32.const 1) - ) - (i32.const 1) - ) - (local.get $18) - ) - ) - (local.get $6) - ) - (i32.gt_s - (local.get $6) - (i32.const -5) - ) - ) - (block (result i32) - (local.set $5 - (i32.add - (local.get $9) - (i32.const -1) - ) - ) - (i32.sub - (i32.add - (local.get $1) - (i32.const -1) - ) - (local.get $6) - ) - ) - (block (result i32) - (local.set $5 - (i32.add - (local.get $9) - (i32.const -2) - ) - ) - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - (br_if $label$203 - (local.tee $13 - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - ) - (block $label$207 - (if - (local.get $22) - (block - (if - (i32.eqz - (local.tee $18 - (i32.load - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - ) - (block - (local.set $9 - (i32.const 9) - ) - (br $label$207) - ) - ) - (if - (i32.rem_u - (local.get $18) - (i32.const 10) - ) - (block - (local.set $9 - (i32.const 0) - ) - (br $label$207) - ) - (block - (local.set $13 - (i32.const 10) - ) - (local.set $9 - (i32.const 0) - ) - ) - ) - (loop $label$212 - (local.set $9 - (i32.add - (local.get $9) - (i32.const 1) - ) - ) - (br_if $label$212 - (i32.eqz - (i32.rem_u - (local.get $18) - (local.tee $13 - (i32.mul - (local.get $13) - (i32.const 10) - ) - ) - ) - ) - ) - ) - ) - (local.set $9 - (i32.const 9) - ) - ) - ) - (local.set $18 - (i32.add - (i32.mul - (i32.shr_s - (i32.sub - (local.get $8) - (local.get $25) - ) - (i32.const 2) - ) - (i32.const 9) - ) - (i32.const -9) - ) - ) - (if - (i32.eq - (i32.or - (local.get $5) - (i32.const 32) - ) - (i32.const 102) - ) - (block - (local.set $13 - (i32.const 0) - ) - (if - (i32.ge_s - (local.get $1) - (if (result i32) - (i32.lt_s - (local.tee $9 - (i32.sub - (local.get $18) - (local.get $9) - ) - ) - (i32.const 0) - ) - (local.tee $9 - (i32.const 0) - ) - (local.get $9) - ) - ) - (local.set $1 - (local.get $9) - ) - ) - ) - (block - (local.set $13 - (i32.const 0) - ) - (if - (i32.ge_s - (local.get $1) - (if (result i32) - (i32.lt_s - (local.tee $9 - (i32.sub - (i32.add - (local.get $18) - (local.get $6) - ) - (local.get $9) - ) - ) - (i32.const 0) - ) - (local.tee $9 - (i32.const 0) - ) - (local.get $9) - ) - ) - (local.set $1 - (local.get $9) - ) - ) - ) - ) - ) - (block - (local.set $13 - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - (local.set $1 - (local.get $18) - ) - (local.set $5 - (local.get $9) - ) - ) - ) - ) - (if - (local.tee $25 - (i32.eq - (i32.or - (local.get $5) - (i32.const 32) - ) - (i32.const 102) - ) - ) - (block - (local.set $9 - (i32.const 0) - ) - (if - (i32.le_s - (local.get $6) - (i32.const 0) - ) - (local.set $6 - (i32.const 0) - ) - ) - ) - (block - (if - (i32.lt_s - (i32.sub - (local.get $28) - (local.tee $9 - (call $23 - (i64.extend_i32_s - (if (result i32) - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (local.get $32) - (local.get $6) - ) - ) - (local.get $33) - ) - ) - ) - (i32.const 2) - ) - (loop $label$229 - (i32.store8 - (local.tee $9 - (i32.add - (local.get $9) - (i32.const -1) - ) - ) - (i32.const 48) - ) - (br_if $label$229 - (i32.lt_s - (i32.sub - (local.get $28) - (local.get $9) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.store8 - (i32.add - (local.get $9) - (i32.const -1) - ) - (i32.add - (i32.and - (i32.shr_s - (local.get $6) - (i32.const 31) - ) - (i32.const 2) - ) - (i32.const 43) - ) - ) - (i32.store8 - (local.tee $6 - (i32.add - (local.get $9) - (i32.const -2) - ) - ) - (local.get $5) - ) - (local.set $9 - (local.get $6) - ) - (local.set $6 - (i32.sub - (local.get $28) - (local.get $6) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $18 - (i32.add - (i32.add - (i32.add - (i32.add - (local.get $24) - (i32.const 1) - ) - (local.get $1) - ) - (i32.ne - (local.tee $29 - (i32.or - (local.get $1) - (local.get $13) - ) - ) - (i32.const 0) - ) - ) - (local.get $6) - ) - ) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $26) - (local.get $24) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $18) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (block $label$231 - (if - (local.get $25) - (block - (local.set $6 - (local.tee $9 - (if (result i32) - (i32.gt_u - (local.get $14) - (local.get $7) - ) - (local.get $7) - (local.get $14) - ) - ) - ) - (loop $label$235 - (local.set $5 - (call $23 - (i64.extend_i32_u - (i32.load - (local.get $6) - ) - ) - (local.get $31) - ) - ) - (block $label$236 - (if - (i32.eq - (local.get $6) - (local.get $9) - ) - (block - (br_if $label$236 - (i32.ne - (local.get $5) - (local.get $31) - ) - ) - (i32.store8 - (local.get $34) - (i32.const 48) - ) - (local.set $5 - (local.get $34) - ) - ) - (block - (br_if $label$236 - (i32.le_u - (local.get $5) - (local.get $19) - ) - ) - (drop - (call $46 - (local.get $19) - (i32.const 48) - (i32.sub - (local.get $5) - (local.get $27) - ) - ) - ) - (loop $label$239 - (br_if $label$239 - (i32.gt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $5) - (i32.sub - (local.get $41) - (local.get $5) - ) - (local.get $0) - ) - ) - ) - (if - (i32.le_u - (local.tee $5 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - (local.get $7) - ) - (block - (local.set $6 - (local.get $5) - ) - (br $label$235) - ) - ) - ) - (block $label$242 - (if - (local.get $29) - (block - (br_if $label$242 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (i32.const 2231) - (i32.const 1) - (local.get $0) - ) - ) - ) - ) - ) - (if - (i32.and - (i32.gt_s - (local.get $1) - (i32.const 0) - ) - (i32.lt_u - (local.get $5) - (local.get $8) - ) - ) - (loop $label$245 - (if - (i32.gt_u - (local.tee $7 - (call $23 - (i64.extend_i32_u - (i32.load - (local.get $5) - ) - ) - (local.get $31) - ) - ) - (local.get $19) - ) - (block - (drop - (call $46 - (local.get $19) - (i32.const 48) - (i32.sub - (local.get $7) - (local.get $27) - ) - ) - ) - (loop $label$247 - (br_if $label$247 - (i32.gt_u - (local.tee $7 - (i32.add - (local.get $7) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $7) - (if (result i32) - (i32.gt_s - (local.get $1) - (i32.const 9) - ) - (i32.const 9) - (local.get $1) - ) - (local.get $0) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $1) - (i32.const -9) - ) - ) - (if - (i32.and - (i32.gt_s - (local.get $1) - (i32.const 9) - ) - (i32.lt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - (local.get $8) - ) - ) - (block - (local.set $1 - (local.get $7) - ) - (br $label$245) - ) - (local.set $1 - (local.get $7) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (i32.add - (local.get $1) - (i32.const 9) - ) - (i32.const 9) - (i32.const 0) - ) - ) - (block - (local.set $5 - (i32.add - (local.get $14) - (i32.const 4) - ) - ) - (if - (i32.eqz - (local.get $22) - ) - (local.set $8 - (local.get $5) - ) - ) - (if - (i32.gt_s - (local.get $1) - (i32.const -1) - ) - (block - (local.set $13 - (i32.eqz - (local.get $13) - ) - ) - (local.set $7 - (local.get $14) - ) - (local.set $5 - (local.get $1) - ) - (loop $label$256 - (if - (i32.eq - (local.tee $1 - (call $23 - (i64.extend_i32_u - (i32.load - (local.get $7) - ) - ) - (local.get $31) - ) - ) - (local.get $31) - ) - (block - (i32.store8 - (local.get $34) - (i32.const 48) - ) - (local.set $1 - (local.get $34) - ) - ) - ) - (block $label$258 - (if - (i32.eq - (local.get $7) - (local.get $14) - ) - (block - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (i32.const 1) - (local.get $0) - ) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (br_if $label$258 - (i32.and - (local.get $13) - (i32.lt_s - (local.get $5) - (i32.const 1) - ) - ) - ) - (br_if $label$258 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (i32.const 2231) - (i32.const 1) - (local.get $0) - ) - ) - ) - (block - (br_if $label$258 - (i32.le_u - (local.get $1) - (local.get $19) - ) - ) - (drop - (call $46 - (local.get $19) - (i32.const 48) - (i32.add - (local.get $1) - (local.get $43) - ) - ) - ) - (loop $label$262 - (br_if $label$262 - (i32.gt_u - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - ) - (local.set $6 - (i32.sub - (local.get $41) - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (if (result i32) - (i32.gt_s - (local.get $5) - (local.get $6) - ) - (local.get $6) - (local.get $5) - ) - (local.get $0) - ) - ) - ) - (br_if $label$256 - (i32.and - (i32.lt_u - (local.tee $7 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (local.get $8) - ) - (i32.gt_s - (local.tee $5 - (i32.sub - (local.get $5) - (local.get $6) - ) - ) - (i32.const -1) - ) - ) - ) - (local.set $1 - (local.get $5) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (i32.add - (local.get $1) - (i32.const 18) - ) - (i32.const 18) - (i32.const 0) - ) - (br_if $label$231 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $9) - (i32.sub - (local.get $28) - (local.get $9) - ) - (local.get $0) - ) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $18) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $18) - (local.get $10) - ) - (local.set $10 - (local.get $18) - ) - ) - ) - (block - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $8 - (i32.add - (if (result i32) - (local.tee $6 - (i32.or - (f64.ne - (local.get $52) - (local.get $52) - ) - (i32.const 0) - ) - ) - (local.tee $24 - (i32.const 0) - ) - (local.get $24) - ) - (i32.const 3) - ) - ) - (local.get $7) - ) - (if - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 32) - ) - ) - (block - (drop - (call $21 - (local.get $26) - (local.get $24) - (local.get $0) - ) - ) - (local.set $1 - (i32.load - (local.get $0) - ) - ) - ) - ) - (local.set $7 - (if (result i32) - (local.tee $5 - (i32.ne - (i32.and - (local.get $9) - (i32.const 32) - ) - (i32.const 0) - ) - ) - (i32.const 2215) - (i32.const 2219) - ) - ) - (local.set $5 - (if (result i32) - (local.get $5) - (i32.const 2223) - (i32.const 2227) - ) - ) - (if - (i32.eqz - (local.get $6) - ) - (local.set $5 - (local.get $7) - ) - ) - (if - (i32.eqz - (i32.and - (local.get $1) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $5) - (i32.const 3) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $8) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $8) - (local.get $10) - ) - (local.set $10 - (local.get $8) - ) - ) - ) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $7 - (local.get $5) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 2179) - ) - (local.set $5 - (local.get $21) - ) - (br $label$70) - ) - (local.set $7 - (i32.and - (local.get $9) - (i32.const 32) - ) - ) - (local.set $7 - (if (result i32) - (i64.eq - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (block (result i32) - (local.set $50 - (i64.const 0) - ) - (local.get $21) - ) - (block (result i32) - (local.set $1 - (local.get $21) - ) - (loop $label$280 - (i32.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i32.or - (i32.load8_u - (i32.add - (i32.and - (i32.wrap_i64 - (local.get $50) - ) - (i32.const 15) - ) - (i32.const 2163) - ) - ) - (local.get $7) - ) - ) - (br_if $label$280 - (i64.ne - (local.tee $50 - (i64.shr_u - (local.get $50) - (i64.const 4) - ) - ) - (i64.const 0) - ) - ) - ) - (local.set $50 - (i64.load - (local.get $16) - ) - ) - (local.get $1) - ) - ) - ) - (local.set $8 - (i32.add - (i32.shr_s - (local.get $9) - (i32.const 4) - ) - (i32.const 2179) - ) - ) - (if - (local.tee $1 - (i32.or - (i32.eqz - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - (i64.eq - (local.get $50) - (i64.const 0) - ) - ) - ) - (local.set $8 - (i32.const 2179) - ) - ) - (local.set $6 - (if (result i32) - (local.get $1) - (i32.const 0) - (i32.const 2) - ) - ) - (br $label$71) - ) - (local.set $7 - (call $23 - (local.get $50) - (local.get $21) - ) - ) - (br $label$71) - ) - (local.set $14 - (i32.eqz - (local.tee $13 - (call $17 - (local.get $1) - (i32.const 0) - (local.get $5) - ) - ) - ) - ) - (local.set $8 - (i32.sub - (local.get $13) - (local.get $1) - ) - ) - (local.set $9 - (i32.add - (local.get $1) - (local.get $5) - ) - ) - (local.set $12 - (local.get $7) - ) - (local.set $7 - (if (result i32) - (local.get $14) - (local.get $5) - (local.get $8) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 2179) - ) - (local.set $5 - (if (result i32) - (local.get $14) - (local.get $9) - (local.get $13) - ) - ) - (br $label$70) - ) - (local.set $1 - (i32.const 0) - ) - (local.set $5 - (i32.const 0) - ) - (local.set $8 - (local.get $7) - ) - (loop $label$288 - (block $label$289 - (br_if $label$289 - (i32.eqz - (local.tee $9 - (i32.load - (local.get $8) - ) - ) - ) - ) - (br_if $label$289 - (i32.or - (i32.lt_s - (local.tee $5 - (call $26 - (local.get $36) - (local.get $9) - ) - ) - (i32.const 0) - ) - (i32.gt_u - (local.get $5) - (i32.sub - (local.get $6) - (local.get $1) - ) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (br_if $label$288 - (i32.gt_u - (local.get $6) - (local.tee $1 - (i32.add - (local.get $5) - (local.get $1) - ) - ) - ) - ) - ) - ) - (if - (i32.lt_s - (local.get $5) - (i32.const 0) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $1) - (local.get $12) - ) - (if - (local.get $1) - (block - (local.set $5 - (i32.const 0) - ) - (loop $label$292 - (br_if $label$72 - (i32.eqz - (local.tee $8 - (i32.load - (local.get $7) - ) - ) - ) - ) - (br_if $label$72 - (i32.gt_s - (local.tee $5 - (i32.add - (local.tee $8 - (call $26 - (local.get $36) - (local.get $8) - ) - ) - (local.get $5) - ) - ) - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $36) - (local.get $8) - (local.get $0) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (br_if $label$292 - (i32.lt_u - (local.get $5) - (local.get $1) - ) - ) - (br $label$72) - ) - ) - (block - (local.set $1 - (i32.const 0) - ) - (br $label$72) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $1) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.le_s - (local.get $10) - (local.get $1) - ) - (local.set $10 - (local.get $1) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $1 - (i32.and - (local.get $12) - (i32.const -65537) - ) - ) - (if - (i32.gt_s - (local.get $5) - (i32.const -1) - ) - (local.set $12 - (local.get $1) - ) - ) - (local.set $5 - (if (result i32) - (i32.or - (local.get $5) - (local.tee $9 - (i64.ne - (i64.load - (local.get $16) - ) - (i64.const 0) - ) - ) - ) - (block (result i32) - (local.set $1 - (local.get $7) - ) - (if - (i32.gt_s - (local.get $5) - (local.tee $7 - (i32.add - (i32.xor - (i32.and - (local.get $9) - (i32.const 1) - ) - (i32.const 1) - ) - (i32.sub - (local.get $38) - (local.get $7) - ) - ) - ) - ) - (local.set $7 - (local.get $5) - ) - ) - (local.get $21) - ) - (block (result i32) - (local.set $1 - (local.get $21) - ) - (local.set $7 - (i32.const 0) - ) - (local.get $21) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (if (result i32) - (i32.lt_s - (local.get $10) - (local.tee $5 - (i32.add - (if (result i32) - (i32.lt_s - (local.get $7) - (local.tee $9 - (i32.sub - (local.get $5) - (local.get $1) - ) - ) - ) - (local.tee $7 - (local.get $9) - ) - (local.get $7) - ) - (local.get $6) - ) - ) - ) - (local.tee $10 - (local.get $5) - ) - (local.get $10) - ) - (local.get $5) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $8) - (local.get $6) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $7) - (local.get $9) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (local.get $9) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - ) - (br $label$2) - ) - (if - (i32.eqz - (local.get $0) - ) - (if - (local.get $17) - (block - (local.set $0 - (i32.const 1) - ) - (loop $label$308 - (if - (local.tee $1 - (i32.load - (i32.add - (local.get $4) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - ) - ) - (block - (call $22 - (i32.add - (local.get $3) - (i32.shl - (local.get $0) - (i32.const 3) - ) - ) - (local.get $1) - (local.get $2) - ) - (br_if $label$308 - (i32.lt_s - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.const 10) - ) - ) - (local.set $15 - (i32.const 1) - ) - (br $label$2) - ) - ) - ) - (loop $label$310 - (if - (i32.load - (i32.add - (local.get $4) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$2) - ) - ) - (br_if $label$310 - (i32.lt_s - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.const 10) - ) - ) - (local.set $15 - (i32.const 1) - ) - ) - ) - (local.set $15 - (i32.const 0) - ) - ) - ) - ) - (global.set $global$1 - (local.get $23) - ) - (local.get $15) + (func $15 (;28;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $8 + global.get $global$1 + i32.const 48 + i32.add + global.set $global$1 + local.get $8 + i32.const 16 + i32.add + local.set $9 + local.get $8 + local.set $10 + local.get $8 + i32.const 32 + i32.add + local.tee $3 + local.get $0 + i32.const 28 + i32.add + local.tee $6 + i32.load + local.tee $4 + i32.store + local.get $3 + local.get $0 + i32.const 20 + i32.add + local.tee $11 + i32.load + local.get $4 + i32.sub + local.tee $5 + i32.store offset=4 + local.get $3 + local.get $1 + i32.store offset=8 + local.get $3 + local.get $2 + i32.store offset=12 + local.get $0 + i32.const 60 + i32.add + local.set $13 + local.get $0 + i32.const 44 + i32.add + local.set $14 + local.get $3 + local.set $1 + i32.const 2 + local.set $4 + local.get $5 + local.get $2 + i32.add + local.set $12 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + loop $label$5 ;; label = @5 + i32.const 4128 + i32.load + if ;; label = @6 + block ;; label = @7 + i32.const 1 + local.get $0 + call $fimport$9 + local.get $10 + local.get $13 + i32.load + i32.store + local.get $10 + local.get $1 + i32.store offset=4 + local.get $10 + local.get $4 + i32.store offset=8 + i32.const 146 + local.get $10 + call $fimport$15 + call $11 + local.set $3 + i32.const 0 + call $fimport$7 + end + else + block ;; label = @7 + local.get $9 + local.get $13 + i32.load + i32.store + local.get $9 + local.get $1 + i32.store offset=4 + local.get $9 + local.get $4 + i32.store offset=8 + i32.const 146 + local.get $9 + call $fimport$15 + call $11 + local.set $3 + end + end + local.get $12 + local.get $3 + i32.eq + br_if 1 (;@4;) + local.get $3 + i32.const 0 + i32.lt_s + br_if 2 (;@3;) + local.get $3 + local.get $1 + i32.load offset=4 + local.tee $5 + i32.gt_u + if (result i32) ;; label = @6 + block (result i32) ;; label = @7 + local.get $6 + local.get $14 + i32.load + local.tee $7 + i32.store + local.get $11 + local.get $7 + i32.store + local.get $1 + i32.load offset=12 + local.set $7 + local.get $1 + i32.const 8 + i32.add + local.set $1 + local.get $4 + i32.const -1 + i32.add + local.set $4 + local.get $3 + local.get $5 + i32.sub + end + else + local.get $4 + i32.const 2 + i32.eq + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + local.get $6 + local.get $6 + i32.load + local.get $3 + i32.add + i32.store + local.get $5 + local.set $7 + i32.const 2 + local.set $4 + local.get $3 + end + else + block (result i32) ;; label = @8 + local.get $5 + local.set $7 + local.get $3 + end + end + end + local.set $5 + local.get $1 + local.get $1 + i32.load + local.get $5 + i32.add + i32.store + local.get $1 + local.get $7 + local.get $5 + i32.sub + i32.store offset=4 + local.get $12 + local.get $3 + i32.sub + local.set $12 + br 0 (;@5;) + end + end + local.get $0 + local.get $14 + i32.load + local.tee $1 + local.get $0 + i32.load offset=48 + i32.add + i32.store offset=16 + local.get $6 + local.get $1 + i32.store + local.get $11 + local.get $1 + i32.store + br 1 (;@2;) + end + local.get $0 + i32.const 0 + i32.store offset=16 + local.get $6 + i32.const 0 + i32.store + local.get $11 + i32.const 0 + i32.store + local.get $0 + local.get $0 + i32.load + i32.const 32 + i32.or + i32.store + local.get $4 + i32.const 2 + i32.eq + if (result i32) ;; label = @3 + i32.const 0 + else + local.get $2 + local.get $1 + i32.load offset=4 + i32.sub + end + local.set $2 + end + local.get $8 + global.set $global$1 + local.get $2 + end ) - ) - (func $20 (; 33 ;) (type $2) (param $0 i32) (result i32) - (i32.const 0) - ) - (func $21 (; 34 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (block $label$1 (result i32) - (block $label$2 - (block $label$3 - (br_if $label$3 - (local.tee $3 - (i32.load - (local.tee $4 - (i32.add - (local.get $2) - (i32.const 16) - ) - ) - ) - ) - ) - (if - (call $30 - (local.get $2) - ) - (local.set $3 - (i32.const 0) - ) - (block - (local.set $3 - (i32.load - (local.get $4) - ) - ) - (br $label$3) - ) - ) - (br $label$2) - ) - (if - (i32.lt_u - (i32.sub - (local.get $3) - (local.tee $4 - (i32.load - (local.tee $5 - (i32.add - (local.get $2) - (i32.const 20) - ) - ) - ) - ) - ) - (local.get $1) - ) - (block - (local.set $3 - (call_indirect (type $0) - (local.get $2) - (local.get $0) - (local.get $1) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $2) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - ) - (br $label$2) - ) - ) - (local.set $2 - (block $label$7 (result i32) - (if (result i32) - (i32.gt_s - (i32.load8_s offset=75 - (local.get $2) - ) - (i32.const -1) - ) - (block (result i32) - (local.set $3 - (local.get $1) - ) - (loop $label$9 - (drop - (br_if $label$7 - (i32.const 0) - (i32.eqz - (local.get $3) - ) - ) - ) - (if - (i32.ne - (i32.load8_s - (i32.add - (local.get $0) - (local.tee $6 - (i32.add - (local.get $3) - (i32.const -1) - ) - ) - ) - ) - (i32.const 10) - ) - (block - (local.set $3 - (local.get $6) - ) - (br $label$9) - ) - ) - ) - (br_if $label$2 - (i32.lt_u - (call_indirect (type $0) - (local.get $2) - (local.get $0) - (local.get $3) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $2) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - (local.get $3) - ) - ) - (local.set $4 - (i32.load - (local.get $5) - ) - ) - (local.set $1 - (i32.sub - (local.get $1) - (local.get $3) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (local.get $3) - ) - ) - (local.get $3) - ) - (i32.const 0) - ) - ) - ) - (drop - (call $47 - (local.get $4) - (local.get $0) - (local.get $1) - ) - ) - (i32.store - (local.get $5) - (i32.add - (i32.load - (local.get $5) - ) - (local.get $1) - ) - ) - (local.set $3 - (i32.add - (local.get $2) - (local.get $1) - ) - ) - ) - (local.get $3) + (func $16 (;29;) (type $3) (param $0 i32) + local.get $0 + i32.load offset=68 + i32.eqz + if ;; label = @1 + local.get $0 + call $13 + end ) - ) - (func $22 (; 35 ;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i64) - (local $5 f64) - (block $label$1 - (if - (i32.le_u - (local.get $1) - (i32.const 20) - ) - (block $label$3 - (block $label$4 - (block $label$5 - (block $label$6 - (block $label$7 - (block $label$8 - (block $label$9 - (block $label$10 - (block $label$11 - (block $label$12 - (block $label$13 - (br_table $label$13 $label$12 $label$11 $label$10 $label$9 $label$8 $label$7 $label$6 $label$5 $label$4 $label$3 - (i32.sub - (local.get $1) - (i32.const 9) - ) - ) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i32.store - (local.get $0) - (local.get $3) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (local.get $3) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (local.get $3) - ) - ) - (br $label$1) - ) - (local.set $4 - (i64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (i64.store - (local.get $0) - (local.get $4) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (i32.shr_s - (i32.shl - (i32.and - (local.get $3) - (i32.const 65535) - ) - (i32.const 16) - ) - (i32.const 16) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (i32.and - (local.get $3) - (i32.const 65535) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (i32.shr_s - (i32.shl - (i32.and - (local.get $3) - (i32.const 255) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (i32.and - (local.get $3) - (i32.const 255) - ) - ) - ) - (br $label$1) - ) - (local.set $5 - (f64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (f64.store - (local.get $0) - (local.get $5) - ) - (br $label$1) - ) - (local.set $5 - (f64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (f64.store - (local.get $0) - (local.get $5) - ) - ) - ) + (func $17 (;30;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + local.get $1 + i32.const 255 + i32.and + local.set $5 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + local.get $2 + i32.const 0 + i32.ne + local.tee $4 + local.get $0 + i32.const 3 + i32.and + i32.const 0 + i32.ne + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $1 + i32.const 255 + i32.and + local.set $4 + local.get $2 + local.set $3 + local.get $0 + local.set $2 + loop $label$6 ;; label = @7 + local.get $2 + i32.load8_s + local.get $4 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $3 + local.set $0 + br 6 (;@3;) + end + end + local.get $3 + i32.const -1 + i32.add + local.tee $3 + i32.const 0 + i32.ne + local.tee $0 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 3 + i32.and + i32.const 0 + i32.ne + i32.and + br_if 0 (;@7;) + br 3 (;@4;) + end + end + else + block ;; label = @6 + local.get $2 + local.set $3 + local.get $0 + local.set $2 + local.get $4 + local.set $0 + end + end + end + local.get $0 + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.set $0 + br 2 (;@3;) + end + else + i32.const 0 + local.set $0 + end + br 1 (;@2;) + end + local.get $2 + i32.load8_s + local.get $1 + i32.const 255 + i32.and + local.tee $1 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.ne + if ;; label = @3 + block ;; label = @4 + local.get $5 + i32.const 16843009 + i32.mul + local.set $3 + block $label$12 ;; label = @5 + block $label$13 ;; label = @6 + local.get $0 + i32.const 3 + i32.le_u + br_if 0 (;@6;) + loop $label$14 ;; label = @7 + local.get $2 + i32.load + local.get $3 + i32.xor + local.tee $4 + i32.const -2139062144 + i32.and + i32.const -2139062144 + i32.xor + local.get $4 + i32.const -16843009 + i32.add + i32.and + i32.eqz + if ;; label = @8 + block ;; label = @9 + local.get $2 + i32.const 4 + i32.add + local.set $2 + local.get $0 + i32.const -4 + i32.add + local.tee $0 + i32.const 3 + i32.gt_u + br_if 2 (;@7;) + br 3 (;@6;) + end + end + end + br 1 (;@5;) + end + local.get $0 + i32.eqz + if ;; label = @6 + block ;; label = @7 + i32.const 0 + local.set $0 + br 5 (;@2;) + end + end + end + loop $label$17 ;; label = @5 + local.get $2 + i32.load8_s + local.get $1 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eq + br_if 3 (;@2;) + local.get $2 + i32.const 1 + i32.add + local.set $2 + local.get $0 + i32.const -1 + i32.add + local.tee $0 + br_if 0 (;@5;) + i32.const 0 + local.set $0 + end + end + end + end + local.get $0 + if (result i32) ;; label = @2 + local.get $2 + else + i32.const 0 + end + end ) - ) - (func $23 (; 36 ;) (type $9) (param $0 i64) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i64) - (block $label$1 (result i32) - (local.set $2 - (i32.wrap_i64 - (local.get $0) - ) - ) - (if - (i64.gt_u - (local.get $0) - (i64.const 4294967295) - ) - (block - (loop $label$3 - (i64.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i64.or - (i64.rem_u - (local.get $0) - (i64.const 10) - ) - (i64.const 48) - ) - ) - (local.set $4 - (i64.div_u - (local.get $0) - (i64.const 10) - ) - ) - (if - (i64.gt_u - (local.get $0) - (i64.const 42949672959) - ) - (block - (local.set $0 - (local.get $4) - ) - (br $label$3) - ) - ) - ) - (local.set $2 - (i32.wrap_i64 - (local.get $4) - ) - ) - ) - ) - (if - (local.get $2) - (loop $label$6 - (i32.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i32.or - (i32.rem_u - (local.get $2) - (i32.const 10) - ) - (i32.const 48) - ) - ) - (local.set $3 - (i32.div_u - (local.get $2) - (i32.const 10) - ) - ) - (if - (i32.ge_u - (local.get $2) - (i32.const 10) - ) - (block - (local.set $2 - (local.get $3) - ) - (br $label$6) - ) - ) - ) - ) - (local.get $1) + (func $18 (;31;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 224 + i32.add + global.set $global$1 + local.get $4 + i32.const 136 + i32.add + local.set $5 + local.get $4 + i32.const 80 + i32.add + local.tee $3 + i64.const 0 + i64.store align=4 + local.get $3 + i64.const 0 + i64.store offset=8 align=4 + local.get $3 + i64.const 0 + i64.store offset=16 align=4 + local.get $3 + i64.const 0 + i64.store offset=24 align=4 + local.get $3 + i64.const 0 + i64.store offset=32 align=4 + local.get $4 + i32.const 120 + i32.add + local.tee $6 + local.get $2 + i32.load + i32.store + i32.const 0 + local.get $1 + local.get $6 + local.get $4 + local.tee $2 + local.get $3 + call $19 + i32.const 0 + i32.lt_s + if ;; label = @2 + i32.const -1 + local.set $1 + else + block ;; label = @3 + local.get $0 + i32.load offset=76 + i32.const -1 + i32.gt_s + if (result i32) ;; label = @4 + local.get $0 + call $20 + else + i32.const 0 + end + local.set $12 + local.get $0 + i32.load + local.set $7 + local.get $0 + i32.load8_s offset=74 + i32.const 1 + i32.lt_s + if ;; label = @4 + local.get $0 + local.get $7 + i32.const -33 + i32.and + i32.store + end + local.get $0 + i32.const 48 + i32.add + local.tee $8 + i32.load + if ;; label = @4 + local.get $0 + local.get $1 + local.get $6 + local.get $2 + local.get $3 + call $19 + local.set $1 + else + block ;; label = @5 + local.get $0 + i32.const 44 + i32.add + local.tee $9 + i32.load + local.set $10 + local.get $9 + local.get $5 + i32.store + local.get $0 + i32.const 28 + i32.add + local.tee $13 + local.get $5 + i32.store + local.get $0 + i32.const 20 + i32.add + local.tee $11 + local.get $5 + i32.store + local.get $8 + i32.const 80 + i32.store + local.get $0 + i32.const 16 + i32.add + local.tee $14 + local.get $5 + i32.const 80 + i32.add + i32.store + local.get $0 + local.get $1 + local.get $6 + local.get $2 + local.get $3 + call $19 + local.set $1 + local.get $10 + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 0 + i32.const 0 + local.get $0 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + drop + local.get $11 + i32.load + i32.eqz + if ;; label = @8 + i32.const -1 + local.set $1 + end + local.get $9 + local.get $10 + i32.store + local.get $8 + i32.const 0 + i32.store + local.get $14 + i32.const 0 + i32.store + local.get $13 + i32.const 0 + i32.store + local.get $11 + i32.const 0 + i32.store + end + end + end + end + local.get $0 + local.get $0 + i32.load + local.tee $2 + local.get $7 + i32.const 32 + i32.and + i32.or + i32.store + local.get $12 + if ;; label = @4 + local.get $0 + call $13 + end + local.get $2 + i32.const 32 + i32.and + if ;; label = @4 + i32.const -1 + local.set $1 + end + end + end + local.get $4 + global.set $global$1 + local.get $1 + end ) - ) - (func $24 (; 37 ;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.const 0) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (loop $label$5 - (br_if $label$4 - (i32.eq - (i32.load8_u - (i32.add - (local.get $1) - (i32.const 2233) - ) - ) - (local.get $0) - ) - ) - (br_if $label$5 - (i32.ne - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (i32.const 87) - ) - ) - (local.set $1 - (i32.const 87) - ) - (local.set $0 - (i32.const 2321) - ) - (br $label$3) - ) - ) - (if - (local.get $1) - (block - (local.set $0 - (i32.const 2321) - ) - (br $label$3) - ) - (local.set $0 - (i32.const 2321) - ) - ) - (br $label$2) - ) - (loop $label$8 - (local.set $2 - (local.get $0) - ) - (loop $label$9 - (local.set $0 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (if - (i32.load8_s - (local.get $2) - ) - (block - (local.set $2 - (local.get $0) - ) - (br $label$9) - ) - ) - ) - (br_if $label$8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - ) - (local.get $0) + (func $19 (;32;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) + (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) (local $22 i32) (local $23 i32) (local $24 i32) (local $25 i32) (local $26 i32) (local $27 i32) (local $28 i32) (local $29 i32) (local $30 i32) (local $31 i32) (local $32 i32) (local $33 i32) (local $34 i32) (local $35 i32) (local $36 i32) (local $37 i32) (local $38 i32) (local $39 i32) (local $40 i32) (local $41 i32) (local $42 i32) (local $43 i32) (local $44 i32) (local $45 i32) (local $46 i32) (local $47 i32) (local $48 i32) (local $49 i32) (local $50 i64) (local $51 i64) (local $52 f64) (local $53 f64) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $23 + global.get $global$1 + i32.const 624 + i32.add + global.set $global$1 + local.get $23 + i32.const 16 + i32.add + local.set $20 + local.get $23 + local.set $16 + local.get $23 + i32.const 528 + i32.add + local.set $36 + local.get $0 + i32.const 0 + i32.ne + local.set $30 + local.get $23 + i32.const 536 + i32.add + local.tee $17 + i32.const 40 + i32.add + local.tee $21 + local.set $38 + local.get $17 + i32.const 39 + i32.add + local.set $39 + local.get $23 + i32.const 8 + i32.add + local.tee $37 + i32.const 4 + i32.add + local.set $42 + i32.const 0 + local.get $23 + i32.const 588 + i32.add + local.tee $19 + local.tee $27 + i32.sub + local.set $43 + local.get $23 + i32.const 576 + i32.add + local.tee $17 + i32.const 12 + i32.add + local.set $33 + local.get $17 + i32.const 11 + i32.add + local.set $40 + local.get $33 + local.tee $28 + local.get $27 + i32.sub + local.set $44 + i32.const -2 + local.get $27 + i32.sub + local.set $45 + local.get $28 + i32.const 2 + i32.add + local.set $46 + local.get $23 + i32.const 24 + i32.add + local.tee $47 + i32.const 288 + i32.add + local.set $48 + local.get $19 + i32.const 9 + i32.add + local.tee $31 + local.set $41 + local.get $19 + i32.const 8 + i32.add + local.set $34 + i32.const 0 + local.set $15 + i32.const 0 + local.set $10 + i32.const 0 + local.set $17 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + loop $label$4 ;; label = @4 + block $label$5 ;; label = @5 + local.get $15 + i32.const -1 + i32.gt_s + if ;; label = @6 + local.get $10 + i32.const 2147483647 + local.get $15 + i32.sub + i32.gt_s + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + call $12 + i32.const 75 + i32.store + i32.const -1 + end + else + local.get $10 + local.get $15 + i32.add + end + local.set $15 + end + local.get $1 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eqz + br_if 2 (;@3;) + local.get $1 + local.set $11 + block $label$9 ;; label = @6 + block $label$10 ;; label = @7 + loop $label$11 ;; label = @8 + block $label$12 ;; label = @9 + block $label$13 ;; label = @10 + block $label$14 ;; label = @11 + block $label$15 ;; label = @12 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 0 + i32.sub + br_table 1 (;@11;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 0 (;@12;) 2 (;@10;) + end + local.get $11 + local.set $5 + br 4 (;@7;) + end + local.get $11 + local.set $5 + br 1 (;@9;) + end + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.set $5 + br 1 (;@8;) + end + end + br 1 (;@6;) + end + loop $label$16 ;; label = @7 + local.get $5 + i32.load8_s offset=1 + i32.const 37 + i32.ne + br_if 1 (;@6;) + local.get $11 + i32.const 1 + i32.add + local.set $11 + local.get $5 + i32.const 2 + i32.add + local.tee $5 + i32.load8_s + i32.const 37 + i32.eq + br_if 0 (;@7;) + end + end + local.get $11 + local.get $1 + i32.sub + local.set $10 + local.get $30 + if ;; label = @6 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @7 + local.get $1 + local.get $10 + local.get $0 + call $21 + drop + end + end + local.get $10 + if ;; label = @6 + block ;; label = @7 + local.get $5 + local.set $1 + br 3 (;@4;) + end + end + local.get $5 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $10 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $9 + i32.const 10 + i32.lt_u + if (result i32) ;; label = @6 + block (result i32) ;; label = @7 + local.get $5 + i32.const 3 + i32.add + local.set $10 + local.get $5 + i32.load8_s offset=2 + i32.const 36 + i32.eq + local.tee $12 + if ;; label = @8 + local.get $10 + local.set $11 + end + local.get $12 + if ;; label = @8 + i32.const 1 + local.set $17 + end + local.get $11 + i32.load8_s + local.set $5 + local.get $12 + i32.eqz + if ;; label = @8 + i32.const -1 + local.set $9 + end + local.get $17 + end + else + block (result i32) ;; label = @7 + local.get $10 + local.set $5 + i32.const -1 + local.set $9 + local.get $17 + end + end + local.set $10 + block $label$25 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + local.tee $12 + i32.const 32 + i32.lt_u + if ;; label = @7 + block ;; label = @8 + i32.const 0 + local.set $17 + loop $label$27 ;; label = @9 + i32.const 1 + local.get $12 + i32.shl + i32.const 75913 + i32.and + i32.eqz + br_if 3 (;@6;) + i32.const 1 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + i32.shl + local.get $17 + i32.or + local.set $17 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + local.tee $12 + i32.const 32 + i32.lt_u + br_if 0 (;@9;) + end + end + else + i32.const 0 + local.set $17 + end + end + block $label$29 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 42 + i32.eq + if ;; label = @7 + block ;; label = @8 + block $label$31 (result i32) ;; label = @9 + block $label$32 ;; label = @10 + local.get $11 + i32.const 1 + i32.add + local.tee $7 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $12 + i32.const 10 + i32.ge_u + br_if 0 (;@10;) + local.get $11 + i32.load8_s offset=2 + i32.const 36 + i32.ne + br_if 0 (;@10;) + local.get $4 + local.get $12 + i32.const 2 + i32.shl + i32.add + i32.const 10 + i32.store + i32.const 1 + local.set $8 + local.get $3 + local.get $7 + i32.load8_s + i32.const -48 + i32.add + i32.const 3 + i32.shl + i32.add + i64.load + i32.wrap_i64 + local.set $10 + local.get $11 + i32.const 3 + i32.add + br 1 (;@9;) + end + local.get $10 + if ;; label = @10 + block ;; label = @11 + i32.const -1 + local.set $15 + br 6 (;@5;) + end + end + local.get $30 + i32.eqz + if ;; label = @10 + block ;; label = @11 + local.get $17 + local.set $12 + i32.const 0 + local.set $17 + local.get $7 + local.set $11 + i32.const 0 + local.set $10 + br 5 (;@6;) + end + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $11 + i32.load + local.set $10 + local.get $2 + local.get $11 + i32.const 4 + i32.add + i32.store + i32.const 0 + local.set $8 + local.get $7 + end + local.set $11 + local.get $17 + i32.const 8192 + i32.or + local.set $12 + i32.const 0 + local.get $10 + i32.sub + local.set $7 + local.get $11 + i32.load8_s + local.set $5 + local.get $10 + i32.const 0 + i32.lt_s + local.tee $6 + i32.eqz + if ;; label = @9 + local.get $17 + local.set $12 + end + local.get $8 + local.set $17 + local.get $6 + if ;; label = @9 + local.get $7 + local.set $10 + end + end + else + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $12 + i32.const 10 + i32.lt_u + if ;; label = @8 + block ;; label = @9 + i32.const 0 + local.set $7 + local.get $12 + local.set $5 + loop $label$39 ;; label = @10 + local.get $7 + i32.const 10 + i32.mul + local.get $5 + i32.add + local.set $7 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $12 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + br_if 0 (;@10;) + end + local.get $7 + i32.const 0 + i32.lt_s + if ;; label = @10 + block ;; label = @11 + i32.const -1 + local.set $15 + br 6 (;@5;) + end + else + block ;; label = @11 + local.get $12 + local.set $5 + local.get $17 + local.set $12 + local.get $10 + local.set $17 + local.get $7 + local.set $10 + end + end + end + else + block ;; label = @9 + local.get $17 + local.set $12 + local.get $10 + local.set $17 + i32.const 0 + local.set $10 + end + end + end + end + block $label$43 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 46 + i32.eq + if ;; label = @7 + block ;; label = @8 + local.get $11 + i32.const 1 + i32.add + local.tee $7 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 42 + i32.ne + if ;; label = @9 + block ;; label = @10 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + if ;; label = @11 + block ;; label = @12 + local.get $7 + local.set $11 + i32.const 0 + local.set $7 + end + else + block ;; label = @12 + i32.const 0 + local.set $5 + local.get $7 + local.set $11 + br 6 (;@6;) + end + end + loop $label$48 ;; label = @11 + local.get $7 + i32.const 10 + i32.mul + local.get $5 + i32.add + local.set $5 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + i32.const -48 + i32.add + local.tee $8 + i32.const 10 + i32.ge_u + br_if 5 (;@6;) + local.get $5 + local.set $7 + local.get $8 + local.set $5 + br 0 (;@11;) + end + end + end + local.get $11 + i32.const 2 + i32.add + local.tee $7 + i32.load8_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + if ;; label = @9 + local.get $11 + i32.load8_s offset=3 + i32.const 36 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.get $5 + i32.const 2 + i32.shl + i32.add + i32.const 10 + i32.store + local.get $3 + local.get $7 + i32.load8_s + i32.const -48 + i32.add + i32.const 3 + i32.shl + i32.add + i64.load + i32.wrap_i64 + local.set $5 + local.get $11 + i32.const 4 + i32.add + local.set $11 + br 5 (;@6;) + end + end + end + local.get $17 + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + end + local.get $30 + if (result i32) ;; label = @9 + block (result i32) ;; label = @10 + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $11 + i32.load + local.set $5 + local.get $2 + local.get $11 + i32.const 4 + i32.add + i32.store + local.get $7 + end + else + block (result i32) ;; label = @10 + i32.const 0 + local.set $5 + local.get $7 + end + end + local.set $11 + end + else + i32.const -1 + local.set $5 + end + end + local.get $11 + local.set $7 + i32.const 0 + local.set $8 + loop $label$55 ;; label = @6 + local.get $7 + i32.load8_s + i32.const -65 + i32.add + local.tee $6 + i32.const 57 + i32.gt_u + if ;; label = @7 + block ;; label = @8 + i32.const -1 + local.set $15 + br 3 (;@5;) + end + end + local.get $7 + i32.const 1 + i32.add + local.set $11 + local.get $8 + i32.const 58 + i32.mul + i32.const 1699 + i32.add + local.get $6 + i32.add + i32.load8_s + local.tee $13 + i32.const 255 + i32.and + local.tee $6 + i32.const -1 + i32.add + i32.const 8 + i32.lt_u + if ;; label = @7 + block ;; label = @8 + local.get $11 + local.set $7 + local.get $6 + local.set $8 + br 2 (;@6;) + end + end + end + local.get $13 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eqz + if ;; label = @6 + block ;; label = @7 + i32.const -1 + local.set $15 + br 2 (;@5;) + end + end + local.get $9 + i32.const -1 + i32.gt_s + local.set $14 + block $label$59 ;; label = @6 + block $label$60 ;; label = @7 + local.get $13 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 19 + i32.eq + if ;; label = @8 + local.get $14 + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + else + br 2 (;@7;) + end + else + block ;; label = @9 + local.get $14 + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.get $9 + i32.const 2 + i32.shl + i32.add + local.get $6 + i32.store + local.get $16 + local.get $3 + local.get $9 + i32.const 3 + i32.shl + i32.add + i64.load + i64.store + br 4 (;@7;) + end + end + local.get $30 + i32.eqz + if ;; label = @10 + block ;; label = @11 + i32.const 0 + local.set $15 + br 6 (;@5;) + end + end + local.get $16 + local.get $6 + local.get $2 + call $22 + end + end + br 1 (;@6;) + end + local.get $30 + i32.eqz + if ;; label = @7 + block ;; label = @8 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 4 (;@4;) + end + end + end + local.get $7 + i32.load8_s + local.tee $7 + i32.const -33 + i32.and + local.set $9 + local.get $8 + i32.const 0 + i32.ne + local.get $7 + i32.const 15 + i32.and + i32.const 3 + i32.eq + i32.and + i32.eqz + if ;; label = @6 + local.get $7 + local.set $9 + end + local.get $12 + i32.const -65537 + i32.and + local.set $7 + local.get $12 + i32.const 8192 + i32.and + if ;; label = @6 + local.get $7 + local.set $12 + end + block $label$70 ;; label = @6 + block $label$71 ;; label = @7 + block $label$72 ;; label = @8 + block $label$73 ;; label = @9 + block $label$74 ;; label = @10 + block $label$75 ;; label = @11 + block $label$76 ;; label = @12 + block $label$77 ;; label = @13 + block $label$78 ;; label = @14 + block $label$79 ;; label = @15 + block $label$80 ;; label = @16 + block $label$81 ;; label = @17 + block $label$82 ;; label = @18 + block $label$83 ;; label = @19 + block $label$84 ;; label = @20 + block $label$85 ;; label = @21 + block $label$86 ;; label = @22 + block $label$87 ;; label = @23 + block $label$88 ;; label = @24 + block $label$89 ;; label = @25 + local.get $9 + i32.const 65 + i32.sub + br_table 11 (;@14;) 12 (;@13;) 9 (;@16;) 12 (;@13;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 10 (;@15;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 11 (;@14;) 12 (;@13;) 6 (;@19;) 4 (;@21;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 4 (;@21;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 7 (;@18;) 0 (;@25;) 3 (;@22;) 1 (;@24;) 12 (;@13;) 12 (;@13;) 8 (;@17;) 12 (;@13;) 5 (;@20;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) + end + block $label$90 ;; label = @25 + block $label$91 ;; label = @26 + block $label$92 ;; label = @27 + block $label$93 ;; label = @28 + block $label$94 ;; label = @29 + block $label$95 ;; label = @30 + block $label$96 ;; label = @31 + block $label$97 ;; label = @32 + local.get $8 + i32.const 255 + i32.and + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 0 + i32.sub + br_table 0 (;@32;) 1 (;@31;) 2 (;@30;) 3 (;@29;) 4 (;@28;) 7 (;@25;) 5 (;@27;) 6 (;@26;) 7 (;@25;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 27 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 26 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i64.extend_i32_s + i64.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 25 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store16 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 24 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store8 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 23 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 22 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i64.extend_i32_s + i64.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 21 (;@4;) + end + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 20 (;@4;) + end + local.get $12 + i32.const 8 + i32.or + local.set $12 + local.get $5 + i32.const 8 + i32.le_u + if ;; label = @24 + i32.const 8 + local.set $5 + end + i32.const 120 + local.set $9 + br 11 (;@12;) + end + br 10 (;@12;) + end + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.eq + if ;; label = @22 + local.get $21 + local.set $7 + else + block ;; label = @23 + local.get $21 + local.set $1 + loop $label$101 ;; label = @24 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $50 + i64.const 7 + i64.and + i64.const 48 + i64.or + i64.store8 + local.get $50 + i64.const 3 + i64.shr_u + local.tee $50 + i64.const 0 + i64.ne + br_if 0 (;@24;) + local.get $1 + local.set $7 + end + end + end + local.get $12 + i32.const 8 + i32.and + if ;; label = @22 + block ;; label = @23 + local.get $38 + local.get $7 + i32.sub + local.tee $1 + i32.const 1 + i32.add + local.set $8 + local.get $5 + local.get $1 + i32.le_s + if ;; label = @24 + local.get $8 + local.set $5 + end + i32.const 0 + local.set $6 + i32.const 2179 + local.set $8 + br 16 (;@7;) + end + else + block ;; label = @23 + i32.const 0 + local.set $6 + i32.const 2179 + local.set $8 + br 16 (;@7;) + end + end + end + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.lt_s + if ;; label = @21 + block ;; label = @22 + local.get $16 + i64.const 0 + local.get $50 + i64.sub + local.tee $50 + i64.store + i32.const 1 + local.set $6 + i32.const 2179 + local.set $8 + br 11 (;@11;) + end + end + local.get $12 + i32.const 2048 + i32.and + if ;; label = @21 + block ;; label = @22 + i32.const 1 + local.set $6 + i32.const 2180 + local.set $8 + br 11 (;@11;) + end + else + block ;; label = @22 + local.get $12 + i32.const 1 + i32.and + local.tee $1 + local.set $6 + local.get $1 + if (result i32) ;; label = @23 + i32.const 2181 + else + i32.const 2179 + end + local.set $8 + br 11 (;@11;) + end + end + end + local.get $16 + i64.load + local.set $50 + i32.const 0 + local.set $6 + i32.const 2179 + local.set $8 + br 8 (;@11;) + end + local.get $39 + local.get $16 + i64.load + i64.store8 + local.get $39 + local.set $1 + local.get $7 + local.set $12 + i32.const 1 + local.set $7 + i32.const 0 + local.set $6 + i32.const 2179 + local.set $8 + local.get $21 + local.set $5 + br 12 (;@6;) + end + call $12 + i32.load + call $24 + local.set $1 + br 7 (;@10;) + end + local.get $16 + i32.load + local.tee $1 + i32.eqz + if ;; label = @17 + i32.const 2189 + local.set $1 + end + br 6 (;@10;) + end + local.get $37 + local.get $16 + i64.load + i64.store32 + local.get $42 + i32.const 0 + i32.store + local.get $16 + local.get $37 + i32.store + local.get $37 + local.set $7 + i32.const -1 + local.set $6 + br 6 (;@9;) + end + local.get $16 + i32.load + local.set $7 + local.get $5 + if ;; label = @15 + block ;; label = @16 + local.get $5 + local.set $6 + br 7 (;@9;) + end + else + block ;; label = @16 + local.get $0 + i32.const 32 + local.get $10 + i32.const 0 + local.get $12 + call $25 + i32.const 0 + local.set $1 + br 8 (;@8;) + end + end + end + local.get $16 + f64.load + local.set $52 + local.get $20 + i32.const 0 + i32.store + local.get $52 + i64.reinterpret_f64 + i64.const 0 + i64.lt_s + if (result i32) ;; label = @14 + block (result i32) ;; label = @15 + i32.const 1 + local.set $24 + local.get $52 + f64.neg + local.set $52 + i32.const 2196 + end + else + block (result i32) ;; label = @15 + local.get $12 + i32.const 1 + i32.and + local.set $1 + local.get $12 + i32.const 2048 + i32.and + if (result i32) ;; label = @16 + block (result i32) ;; label = @17 + i32.const 1 + local.set $24 + i32.const 2199 + end + else + block (result i32) ;; label = @17 + local.get $1 + local.set $24 + local.get $1 + if (result i32) ;; label = @18 + i32.const 2202 + else + i32.const 2197 + end + end + end + end + end + local.set $26 + block $label$119 ;; label = @14 + local.get $52 + i64.reinterpret_f64 + i64.const 9218868437227405312 + i64.and + i64.const 9218868437227405312 + i64.lt_u + if ;; label = @15 + block ;; label = @16 + local.get $52 + local.get $20 + call $27 + f64.const 0x1p+1 (;=2;) + f64.mul + local.tee $52 + f64.const 0x0p+0 (;=0;) + f64.ne + local.tee $1 + if ;; label = @17 + local.get $20 + local.get $20 + i32.load + i32.const -1 + i32.add + i32.store + end + local.get $9 + i32.const 32 + i32.or + local.tee $22 + i32.const 97 + i32.eq + if ;; label = @17 + block ;; label = @18 + local.get $26 + i32.const 9 + i32.add + local.set $1 + local.get $9 + i32.const 32 + i32.and + local.tee $6 + if ;; label = @19 + local.get $1 + local.set $26 + end + local.get $5 + i32.const 11 + i32.gt_u + i32.const 12 + local.get $5 + i32.sub + local.tee $1 + i32.eqz + i32.or + i32.eqz + if ;; label = @19 + block ;; label = @20 + f64.const 0x1p+3 (;=8;) + local.set $53 + loop $label$125 ;; label = @21 + local.get $53 + f64.const 0x1p+4 (;=16;) + f64.mul + local.set $53 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + br_if 0 (;@21;) + end + local.get $26 + i32.load8_s + i32.const 45 + i32.eq + if (result f64) ;; label = @21 + local.get $53 + local.get $52 + f64.neg + local.get $53 + f64.sub + f64.add + f64.neg + else + local.get $52 + local.get $53 + f64.add + local.get $53 + f64.sub + end + local.set $52 + end + end + i32.const 0 + local.get $20 + i32.load + local.tee $7 + i32.sub + local.set $1 + local.get $7 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @19 + local.get $1 + else + local.get $7 + end + i64.extend_i32_s + local.get $33 + call $23 + local.tee $1 + local.get $33 + i32.eq + if ;; label = @19 + block ;; label = @20 + local.get $40 + i32.const 48 + i32.store8 + local.get $40 + local.set $1 + end + end + local.get $24 + i32.const 2 + i32.or + local.set $13 + local.get $1 + i32.const -1 + i32.add + local.get $7 + i32.const 31 + i32.shr_s + i32.const 2 + i32.and + i32.const 43 + i32.add + i32.store8 + local.get $1 + i32.const -2 + i32.add + local.tee $8 + local.get $9 + i32.const 15 + i32.add + i32.store8 + local.get $5 + i32.const 1 + i32.lt_s + local.set $9 + local.get $12 + i32.const 8 + i32.and + i32.eqz + local.set $14 + local.get $19 + local.set $1 + loop $label$131 ;; label = @19 + local.get $1 + local.get $52 + i32.trunc_f64_s + local.tee $7 + i32.const 2163 + i32.add + i32.load8_u + local.get $6 + i32.or + i32.store8 + local.get $52 + local.get $7 + f64.convert_i32_s + f64.sub + f64.const 0x1p+4 (;=16;) + f64.mul + local.set $52 + block $label$132 (result i32) ;; label = @20 + local.get $1 + i32.const 1 + i32.add + local.tee $7 + local.get $27 + i32.sub + i32.const 1 + i32.eq + if (result i32) ;; label = @21 + block (result i32) ;; label = @22 + local.get $7 + local.get $14 + local.get $9 + local.get $52 + f64.const 0x0p+0 (;=0;) + f64.eq + i32.and + i32.and + br_if 2 (;@20;) + drop + local.get $7 + i32.const 46 + i32.store8 + local.get $1 + i32.const 2 + i32.add + end + else + local.get $7 + end + end + local.set $1 + local.get $52 + f64.const 0x0p+0 (;=0;) + f64.ne + br_if 0 (;@19;) + end + local.get $46 + local.get $5 + i32.add + local.get $8 + local.tee $7 + i32.sub + local.set $6 + local.get $44 + local.get $7 + i32.sub + local.get $1 + i32.add + local.set $9 + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + i32.const 0 + i32.ne + local.get $45 + local.get $1 + i32.add + local.get $5 + i32.lt_s + i32.and + if (result i32) ;; label = @19 + local.get $6 + else + local.get $9 + local.tee $6 + end + local.get $13 + i32.add + local.tee $5 + local.get $12 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $26 + local.get $13 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $5 + local.get $12 + i32.const 65536 + i32.xor + call $25 + local.get $1 + local.get $27 + i32.sub + local.set $1 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $19 + local.get $1 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $6 + local.get $1 + local.get $28 + local.get $7 + i32.sub + local.tee $1 + i32.add + i32.sub + i32.const 0 + i32.const 0 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $8 + local.get $1 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $5 + local.get $10 + i32.ge_s + if ;; label = @19 + local.get $5 + local.set $10 + end + br 4 (;@14;) + end + end + local.get $1 + if ;; label = @17 + block ;; label = @18 + local.get $20 + local.get $20 + i32.load + i32.const -28 + i32.add + local.tee $6 + i32.store + local.get $52 + f64.const 0x1p+28 (;=268435456;) + f64.mul + local.set $52 + end + else + local.get $20 + i32.load + local.set $6 + end + local.get $6 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @17 + local.get $47 + else + local.get $48 + end + local.tee $7 + local.set $8 + loop $label$145 ;; label = @17 + local.get $8 + local.get $52 + i32.trunc_f64_s + local.tee $1 + i32.store + local.get $8 + i32.const 4 + i32.add + local.set $8 + local.get $52 + local.get $1 + f64.convert_i32_u + f64.sub + f64.const 0x1.dcd65p+29 (;=1000000000;) + f64.mul + local.tee $52 + f64.const 0x0p+0 (;=0;) + f64.ne + br_if 0 (;@17;) + end + local.get $6 + i32.const 0 + i32.gt_s + if ;; label = @17 + block ;; label = @18 + local.get $7 + local.set $1 + loop $label$147 ;; label = @19 + local.get $6 + i32.const 29 + i32.gt_s + if (result i32) ;; label = @20 + i32.const 29 + else + local.get $6 + end + local.set $14 + block $label$150 ;; label = @20 + local.get $8 + i32.const -4 + i32.add + local.tee $6 + local.get $1 + i32.ge_u + if ;; label = @21 + block ;; label = @22 + local.get $14 + i64.extend_i32_u + local.set $50 + i32.const 0 + local.set $13 + loop $label$152 ;; label = @23 + local.get $6 + local.get $6 + i32.load + i64.extend_i32_u + local.get $50 + i64.shl + local.get $13 + i64.extend_i32_u + i64.add + local.tee $51 + i64.const 1000000000 + i64.rem_u + i64.store32 + local.get $51 + i64.const 1000000000 + i64.div_u + i32.wrap_i64 + local.set $13 + local.get $6 + i32.const -4 + i32.add + local.tee $6 + local.get $1 + i32.ge_u + br_if 0 (;@23;) + end + local.get $13 + i32.eqz + br_if 2 (;@20;) + local.get $1 + i32.const -4 + i32.add + local.tee $1 + local.get $13 + i32.store + end + end + end + loop $label$153 ;; label = @20 + local.get $8 + local.get $1 + i32.gt_u + if ;; label = @21 + local.get $8 + i32.const -4 + i32.add + local.tee $6 + i32.load + i32.eqz + if ;; label = @22 + block ;; label = @23 + local.get $6 + local.set $8 + br 3 (;@20;) + end + end + end + end + local.get $20 + local.get $20 + i32.load + local.get $14 + i32.sub + local.tee $6 + i32.store + local.get $6 + i32.const 0 + i32.gt_s + br_if 0 (;@19;) + end + end + else + local.get $7 + local.set $1 + end + local.get $5 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @17 + i32.const 6 + else + local.get $5 + end + local.set $18 + local.get $6 + i32.const 0 + i32.lt_s + if ;; label = @17 + block ;; label = @18 + local.get $18 + i32.const 25 + i32.add + i32.const 9 + i32.div_s + i32.const 1 + i32.add + local.set $14 + local.get $22 + i32.const 102 + i32.eq + local.set $25 + local.get $8 + local.set $5 + loop $label$160 ;; label = @19 + i32.const 0 + local.get $6 + i32.sub + local.tee $13 + i32.const 9 + i32.gt_s + if ;; label = @20 + i32.const 9 + local.set $13 + end + block $label$162 ;; label = @20 + local.get $1 + local.get $5 + i32.lt_u + if ;; label = @21 + block ;; label = @22 + i32.const 1 + local.get $13 + i32.shl + i32.const -1 + i32.add + local.set $29 + i32.const 1000000000 + local.get $13 + i32.shr_u + local.set $35 + i32.const 0 + local.set $6 + local.get $1 + local.set $8 + loop $label$164 ;; label = @23 + local.get $8 + local.get $8 + i32.load + local.tee $32 + local.get $13 + i32.shr_u + local.get $6 + i32.add + i32.store + local.get $32 + local.get $29 + i32.and + local.get $35 + i32.mul + local.set $6 + local.get $8 + i32.const 4 + i32.add + local.tee $8 + local.get $5 + i32.lt_u + br_if 0 (;@23;) + end + local.get $1 + i32.const 4 + i32.add + local.set $8 + local.get $1 + i32.load + i32.eqz + if ;; label = @23 + local.get $8 + local.set $1 + end + local.get $6 + i32.eqz + br_if 2 (;@20;) + local.get $5 + local.get $6 + i32.store + local.get $5 + i32.const 4 + i32.add + local.set $5 + end + else + block ;; label = @22 + local.get $1 + i32.const 4 + i32.add + local.set $8 + local.get $1 + i32.load + i32.eqz + if ;; label = @23 + local.get $8 + local.set $1 + end + end + end + end + local.get $25 + if (result i32) ;; label = @20 + local.get $7 + else + local.get $1 + end + local.tee $8 + local.get $14 + i32.const 2 + i32.shl + i32.add + local.set $6 + local.get $5 + local.get $8 + i32.sub + i32.const 2 + i32.shr_s + local.get $14 + i32.gt_s + if ;; label = @20 + local.get $6 + local.set $5 + end + local.get $20 + local.get $20 + i32.load + local.get $13 + i32.add + local.tee $6 + i32.store + local.get $6 + i32.const 0 + i32.lt_s + br_if 0 (;@19;) + local.get $5 + local.set $13 + end + end + else + local.get $8 + local.set $13 + end + local.get $7 + local.set $25 + block $label$172 ;; label = @17 + local.get $1 + local.get $13 + i32.lt_u + if ;; label = @18 + block ;; label = @19 + local.get $25 + local.get $1 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + local.set $5 + local.get $1 + i32.load + local.tee $6 + i32.const 10 + i32.lt_u + br_if 2 (;@17;) + i32.const 10 + local.set $8 + loop $label$174 ;; label = @20 + local.get $5 + i32.const 1 + i32.add + local.set $5 + local.get $6 + local.get $8 + i32.const 10 + i32.mul + local.tee $8 + i32.ge_u + br_if 0 (;@20;) + end + end + else + i32.const 0 + local.set $5 + end + end + local.get $22 + i32.const 103 + i32.eq + local.set $29 + local.get $18 + i32.const 0 + i32.ne + local.set $35 + local.get $18 + local.get $22 + i32.const 102 + i32.ne + if (result i32) ;; label = @17 + local.get $5 + else + i32.const 0 + end + i32.sub + local.get $35 + local.get $29 + i32.and + i32.const 31 + i32.shl + i32.const 31 + i32.shr_s + i32.add + local.tee $8 + local.get $13 + local.get $25 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + i32.const -9 + i32.add + i32.lt_s + if ;; label = @17 + block ;; label = @18 + local.get $8 + i32.const 9216 + i32.add + local.tee $14 + i32.const 9 + i32.rem_s + i32.const 1 + i32.add + local.tee $8 + i32.const 9 + i32.lt_s + if ;; label = @19 + block ;; label = @20 + i32.const 10 + local.set $6 + loop $label$180 ;; label = @21 + local.get $6 + i32.const 10 + i32.mul + local.set $6 + local.get $8 + i32.const 1 + i32.add + local.tee $8 + i32.const 9 + i32.ne + br_if 0 (;@21;) + end + end + else + i32.const 10 + local.set $6 + end + local.get $7 + i32.const 4 + i32.add + local.get $14 + i32.const 9 + i32.div_s + i32.const -1024 + i32.add + i32.const 2 + i32.shl + i32.add + local.tee $8 + i32.load + local.tee $22 + local.get $6 + i32.rem_u + local.set $14 + block $label$182 ;; label = @19 + local.get $8 + i32.const 4 + i32.add + local.get $13 + i32.eq + local.tee $32 + local.get $14 + i32.eqz + i32.and + i32.eqz + if ;; label = @20 + block ;; label = @21 + local.get $14 + local.get $6 + i32.const 2 + i32.div_s + local.tee $49 + i32.lt_u + if (result f64) ;; label = @22 + f64.const 0x1p-1 (;=0.5;) + else + local.get $32 + local.get $14 + local.get $49 + i32.eq + i32.and + if (result f64) ;; label = @23 + f64.const 0x1p+0 (;=1;) + else + f64.const 0x1.8p+0 (;=1.5;) + end + end + local.set $52 + local.get $22 + local.get $6 + i32.div_u + i32.const 1 + i32.and + if (result f64) ;; label = @22 + f64.const 0x1.0000000000001p+53 (;=9007199254740994;) + else + f64.const 0x1p+53 (;=9007199254740992;) + end + local.set $53 + block $label$190 ;; label = @22 + local.get $24 + if ;; label = @23 + block ;; label = @24 + local.get $26 + i32.load8_s + i32.const 45 + i32.ne + br_if 2 (;@22;) + local.get $53 + f64.neg + local.set $53 + local.get $52 + f64.neg + local.set $52 + end + end + end + local.get $8 + local.get $22 + local.get $14 + i32.sub + local.tee $14 + i32.store + local.get $53 + local.get $52 + f64.add + local.get $53 + f64.eq + br_if 2 (;@19;) + local.get $8 + local.get $14 + local.get $6 + i32.add + local.tee $5 + i32.store + local.get $5 + i32.const 999999999 + i32.gt_u + if ;; label = @22 + loop $label$193 ;; label = @23 + local.get $8 + i32.const 0 + i32.store + local.get $8 + i32.const -4 + i32.add + local.tee $8 + local.get $1 + i32.lt_u + if ;; label = @24 + local.get $1 + i32.const -4 + i32.add + local.tee $1 + i32.const 0 + i32.store + end + local.get $8 + local.get $8 + i32.load + i32.const 1 + i32.add + local.tee $5 + i32.store + local.get $5 + i32.const 999999999 + i32.gt_u + br_if 0 (;@23;) + end + end + local.get $25 + local.get $1 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + local.set $5 + local.get $1 + i32.load + local.tee $14 + i32.const 10 + i32.lt_u + br_if 2 (;@19;) + i32.const 10 + local.set $6 + loop $label$195 ;; label = @22 + local.get $5 + i32.const 1 + i32.add + local.set $5 + local.get $14 + local.get $6 + i32.const 10 + i32.mul + local.tee $6 + i32.ge_u + br_if 0 (;@22;) + end + end + end + end + local.get $1 + local.set $14 + local.get $5 + local.set $6 + local.get $13 + local.get $8 + i32.const 4 + i32.add + local.tee $8 + i32.le_u + if ;; label = @19 + local.get $13 + local.set $8 + end + end + else + block ;; label = @18 + local.get $1 + local.set $14 + local.get $5 + local.set $6 + local.get $13 + local.set $8 + end + end + i32.const 0 + local.get $6 + i32.sub + local.set $32 + loop $label$198 ;; label = @17 + block $label$199 ;; label = @18 + local.get $8 + local.get $14 + i32.le_u + if ;; label = @19 + block ;; label = @20 + i32.const 0 + local.set $22 + br 2 (;@18;) + end + end + local.get $8 + i32.const -4 + i32.add + local.tee $1 + i32.load + if ;; label = @19 + i32.const 1 + local.set $22 + else + block ;; label = @20 + local.get $1 + local.set $8 + br 3 (;@17;) + end + end + end + end + block $label$203 ;; label = @17 + local.get $29 + if ;; label = @18 + block ;; label = @19 + local.get $35 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $18 + i32.add + local.tee $1 + local.get $6 + i32.gt_s + local.get $6 + i32.const -5 + i32.gt_s + i32.and + if (result i32) ;; label = @20 + block (result i32) ;; label = @21 + local.get $9 + i32.const -1 + i32.add + local.set $5 + local.get $1 + i32.const -1 + i32.add + local.get $6 + i32.sub + end + else + block (result i32) ;; label = @21 + local.get $9 + i32.const -2 + i32.add + local.set $5 + local.get $1 + i32.const -1 + i32.add + end + end + local.set $1 + local.get $12 + i32.const 8 + i32.and + local.tee $13 + br_if 2 (;@17;) + block $label$207 ;; label = @20 + local.get $22 + if ;; label = @21 + block ;; label = @22 + local.get $8 + i32.const -4 + i32.add + i32.load + local.tee $18 + i32.eqz + if ;; label = @23 + block ;; label = @24 + i32.const 9 + local.set $9 + br 4 (;@20;) + end + end + local.get $18 + i32.const 10 + i32.rem_u + if ;; label = @23 + block ;; label = @24 + i32.const 0 + local.set $9 + br 4 (;@20;) + end + else + block ;; label = @24 + i32.const 10 + local.set $13 + i32.const 0 + local.set $9 + end + end + loop $label$212 ;; label = @23 + local.get $9 + i32.const 1 + i32.add + local.set $9 + local.get $18 + local.get $13 + i32.const 10 + i32.mul + local.tee $13 + i32.rem_u + i32.eqz + br_if 0 (;@23;) + end + end + else + i32.const 9 + local.set $9 + end + end + local.get $8 + local.get $25 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + i32.const -9 + i32.add + local.set $18 + local.get $5 + i32.const 32 + i32.or + i32.const 102 + i32.eq + if ;; label = @20 + block ;; label = @21 + i32.const 0 + local.set $13 + local.get $1 + local.get $18 + local.get $9 + i32.sub + local.tee $9 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @22 + i32.const 0 + local.tee $9 + else + local.get $9 + end + i32.ge_s + if ;; label = @22 + local.get $9 + local.set $1 + end + end + else + block ;; label = @21 + i32.const 0 + local.set $13 + local.get $1 + local.get $18 + local.get $6 + i32.add + local.get $9 + i32.sub + local.tee $9 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @22 + i32.const 0 + local.tee $9 + else + local.get $9 + end + i32.ge_s + if ;; label = @22 + local.get $9 + local.set $1 + end + end + end + end + else + block ;; label = @19 + local.get $12 + i32.const 8 + i32.and + local.set $13 + local.get $18 + local.set $1 + local.get $9 + local.set $5 + end + end + end + local.get $5 + i32.const 32 + i32.or + i32.const 102 + i32.eq + local.tee $25 + if ;; label = @17 + block ;; label = @18 + i32.const 0 + local.set $9 + local.get $6 + i32.const 0 + i32.le_s + if ;; label = @19 + i32.const 0 + local.set $6 + end + end + else + block ;; label = @18 + local.get $28 + local.get $6 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @19 + local.get $32 + else + local.get $6 + end + i64.extend_i32_s + local.get $33 + call $23 + local.tee $9 + i32.sub + i32.const 2 + i32.lt_s + if ;; label = @19 + loop $label$229 ;; label = @20 + local.get $9 + i32.const -1 + i32.add + local.tee $9 + i32.const 48 + i32.store8 + local.get $28 + local.get $9 + i32.sub + i32.const 2 + i32.lt_s + br_if 0 (;@20;) + end + end + local.get $9 + i32.const -1 + i32.add + local.get $6 + i32.const 31 + i32.shr_s + i32.const 2 + i32.and + i32.const 43 + i32.add + i32.store8 + local.get $9 + i32.const -2 + i32.add + local.tee $6 + local.get $5 + i32.store8 + local.get $6 + local.set $9 + local.get $28 + local.get $6 + i32.sub + local.set $6 + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $24 + i32.const 1 + i32.add + local.get $1 + i32.add + local.get $1 + local.get $13 + i32.or + local.tee $29 + i32.const 0 + i32.ne + i32.add + local.get $6 + i32.add + local.tee $18 + local.get $12 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + local.get $26 + local.get $24 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $18 + local.get $12 + i32.const 65536 + i32.xor + call $25 + block $label$231 ;; label = @17 + local.get $25 + if ;; label = @18 + block ;; label = @19 + local.get $14 + local.get $7 + i32.gt_u + if (result i32) ;; label = @20 + local.get $7 + else + local.get $14 + end + local.tee $9 + local.set $6 + loop $label$235 ;; label = @20 + local.get $6 + i32.load + i64.extend_i32_u + local.get $31 + call $23 + local.set $5 + block $label$236 ;; label = @21 + local.get $6 + local.get $9 + i32.eq + if ;; label = @22 + block ;; label = @23 + local.get $5 + local.get $31 + i32.ne + br_if 2 (;@21;) + local.get $34 + i32.const 48 + i32.store8 + local.get $34 + local.set $5 + end + else + block ;; label = @23 + local.get $5 + local.get $19 + i32.le_u + br_if 2 (;@21;) + local.get $19 + i32.const 48 + local.get $5 + local.get $27 + i32.sub + call $46 + drop + loop $label$239 ;; label = @24 + local.get $5 + i32.const -1 + i32.add + local.tee $5 + local.get $19 + i32.gt_u + br_if 0 (;@24;) + end + end + end + end + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @21 + local.get $5 + local.get $41 + local.get $5 + i32.sub + local.get $0 + call $21 + drop + end + local.get $6 + i32.const 4 + i32.add + local.tee $5 + local.get $7 + i32.le_u + if ;; label = @21 + block ;; label = @22 + local.get $5 + local.set $6 + br 2 (;@20;) + end + end + end + block $label$242 ;; label = @20 + local.get $29 + if ;; label = @21 + block ;; label = @22 + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@20;) + i32.const 2231 + i32.const 1 + local.get $0 + call $21 + drop + end + end + end + local.get $1 + i32.const 0 + i32.gt_s + local.get $5 + local.get $8 + i32.lt_u + i32.and + if ;; label = @20 + loop $label$245 ;; label = @21 + local.get $5 + i32.load + i64.extend_i32_u + local.get $31 + call $23 + local.tee $7 + local.get $19 + i32.gt_u + if ;; label = @22 + block ;; label = @23 + local.get $19 + i32.const 48 + local.get $7 + local.get $27 + i32.sub + call $46 + drop + loop $label$247 ;; label = @24 + local.get $7 + i32.const -1 + i32.add + local.tee $7 + local.get $19 + i32.gt_u + br_if 0 (;@24;) + end + end + end + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @22 + local.get $7 + local.get $1 + i32.const 9 + i32.gt_s + if (result i32) ;; label = @23 + i32.const 9 + else + local.get $1 + end + local.get $0 + call $21 + drop + end + local.get $1 + i32.const -9 + i32.add + local.set $7 + local.get $1 + i32.const 9 + i32.gt_s + local.get $5 + i32.const 4 + i32.add + local.tee $5 + local.get $8 + i32.lt_u + i32.and + if ;; label = @22 + block ;; label = @23 + local.get $7 + local.set $1 + br 2 (;@21;) + end + else + local.get $7 + local.set $1 + end + end + end + local.get $0 + i32.const 48 + local.get $1 + i32.const 9 + i32.add + i32.const 9 + i32.const 0 + call $25 + end + else + block ;; label = @19 + local.get $14 + i32.const 4 + i32.add + local.set $5 + local.get $22 + i32.eqz + if ;; label = @20 + local.get $5 + local.set $8 + end + local.get $1 + i32.const -1 + i32.gt_s + if ;; label = @20 + block ;; label = @21 + local.get $13 + i32.eqz + local.set $13 + local.get $14 + local.set $7 + local.get $1 + local.set $5 + loop $label$256 ;; label = @22 + local.get $7 + i32.load + i64.extend_i32_u + local.get $31 + call $23 + local.tee $1 + local.get $31 + i32.eq + if ;; label = @23 + block ;; label = @24 + local.get $34 + i32.const 48 + i32.store8 + local.get $34 + local.set $1 + end + end + block $label$258 ;; label = @23 + local.get $7 + local.get $14 + i32.eq + if ;; label = @24 + block ;; label = @25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @26 + local.get $1 + i32.const 1 + local.get $0 + call $21 + drop + end + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $13 + local.get $5 + i32.const 1 + i32.lt_s + i32.and + br_if 2 (;@23;) + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@23;) + i32.const 2231 + i32.const 1 + local.get $0 + call $21 + drop + end + else + block ;; label = @25 + local.get $1 + local.get $19 + i32.le_u + br_if 2 (;@23;) + local.get $19 + i32.const 48 + local.get $1 + local.get $43 + i32.add + call $46 + drop + loop $label$262 ;; label = @26 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $19 + i32.gt_u + br_if 0 (;@26;) + end + end + end + end + local.get $41 + local.get $1 + i32.sub + local.set $6 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @23 + local.get $1 + local.get $5 + local.get $6 + i32.gt_s + if (result i32) ;; label = @24 + local.get $6 + else + local.get $5 + end + local.get $0 + call $21 + drop + end + local.get $7 + i32.const 4 + i32.add + local.tee $7 + local.get $8 + i32.lt_u + local.get $5 + local.get $6 + i32.sub + local.tee $5 + i32.const -1 + i32.gt_s + i32.and + br_if 0 (;@22;) + local.get $5 + local.set $1 + end + end + end + local.get $0 + i32.const 48 + local.get $1 + i32.const 18 + i32.add + i32.const 18 + i32.const 0 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@17;) + local.get $9 + local.get $28 + local.get $9 + i32.sub + local.get $0 + call $21 + drop + end + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $18 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $18 + local.get $10 + i32.ge_s + if ;; label = @17 + local.get $18 + local.set $10 + end + end + else + block ;; label = @16 + local.get $0 + i32.const 32 + local.get $10 + local.get $52 + local.get $52 + f64.ne + i32.const 0 + i32.or + local.tee $6 + if (result i32) ;; label = @17 + i32.const 0 + local.tee $24 + else + local.get $24 + end + i32.const 3 + i32.add + local.tee $8 + local.get $7 + call $25 + local.get $0 + i32.load + local.tee $1 + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + block ;; label = @18 + local.get $26 + local.get $24 + local.get $0 + call $21 + drop + local.get $0 + i32.load + local.set $1 + end + end + local.get $9 + i32.const 32 + i32.and + i32.const 0 + i32.ne + local.tee $5 + if (result i32) ;; label = @17 + i32.const 2215 + else + i32.const 2219 + end + local.set $7 + local.get $5 + if (result i32) ;; label = @17 + i32.const 2223 + else + i32.const 2227 + end + local.set $5 + local.get $6 + i32.eqz + if ;; label = @17 + local.get $7 + local.set $5 + end + local.get $1 + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + local.get $5 + i32.const 3 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $8 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $8 + local.get $10 + i32.ge_s + if ;; label = @17 + local.get $8 + local.set $10 + end + end + end + end + local.get $11 + local.set $1 + br 9 (;@4;) + end + local.get $5 + local.set $7 + i32.const 0 + local.set $6 + i32.const 2179 + local.set $8 + local.get $21 + local.set $5 + br 6 (;@6;) + end + local.get $9 + i32.const 32 + i32.and + local.set $7 + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.eq + if (result i32) ;; label = @12 + block (result i32) ;; label = @13 + i64.const 0 + local.set $50 + local.get $21 + end + else + block (result i32) ;; label = @13 + local.get $21 + local.set $1 + loop $label$280 ;; label = @14 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $50 + i32.wrap_i64 + i32.const 15 + i32.and + i32.const 2163 + i32.add + i32.load8_u + local.get $7 + i32.or + i32.store8 + local.get $50 + i64.const 4 + i64.shr_u + local.tee $50 + i64.const 0 + i64.ne + br_if 0 (;@14;) + end + local.get $16 + i64.load + local.set $50 + local.get $1 + end + end + local.set $7 + local.get $9 + i32.const 4 + i32.shr_s + i32.const 2179 + i32.add + local.set $8 + local.get $12 + i32.const 8 + i32.and + i32.eqz + local.get $50 + i64.const 0 + i64.eq + i32.or + local.tee $1 + if ;; label = @12 + i32.const 2179 + local.set $8 + end + local.get $1 + if (result i32) ;; label = @12 + i32.const 0 + else + i32.const 2 + end + local.set $6 + br 4 (;@7;) + end + local.get $50 + local.get $21 + call $23 + local.set $7 + br 3 (;@7;) + end + local.get $1 + i32.const 0 + local.get $5 + call $17 + local.tee $13 + i32.eqz + local.set $14 + local.get $13 + local.get $1 + i32.sub + local.set $8 + local.get $1 + local.get $5 + i32.add + local.set $9 + local.get $7 + local.set $12 + local.get $14 + if (result i32) ;; label = @10 + local.get $5 + else + local.get $8 + end + local.set $7 + i32.const 0 + local.set $6 + i32.const 2179 + local.set $8 + local.get $14 + if (result i32) ;; label = @10 + local.get $9 + else + local.get $13 + end + local.set $5 + br 3 (;@6;) + end + i32.const 0 + local.set $1 + i32.const 0 + local.set $5 + local.get $7 + local.set $8 + loop $label$288 ;; label = @9 + block $label$289 ;; label = @10 + local.get $8 + i32.load + local.tee $9 + i32.eqz + br_if 0 (;@10;) + local.get $36 + local.get $9 + call $26 + local.tee $5 + i32.const 0 + i32.lt_s + local.get $5 + local.get $6 + local.get $1 + i32.sub + i32.gt_u + i32.or + br_if 0 (;@10;) + local.get $8 + i32.const 4 + i32.add + local.set $8 + local.get $6 + local.get $5 + local.get $1 + i32.add + local.tee $1 + i32.gt_u + br_if 1 (;@9;) + end + end + local.get $5 + i32.const 0 + i32.lt_s + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $1 + local.get $12 + call $25 + local.get $1 + if ;; label = @9 + block ;; label = @10 + i32.const 0 + local.set $5 + loop $label$292 ;; label = @11 + local.get $7 + i32.load + local.tee $8 + i32.eqz + br_if 3 (;@8;) + local.get $36 + local.get $8 + call $26 + local.tee $8 + local.get $5 + i32.add + local.tee $5 + local.get $1 + i32.gt_s + br_if 3 (;@8;) + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @12 + local.get $36 + local.get $8 + local.get $0 + call $21 + drop + end + local.get $7 + i32.const 4 + i32.add + local.set $7 + local.get $5 + local.get $1 + i32.lt_u + br_if 0 (;@11;) + br 3 (;@8;) + end + end + else + block ;; label = @10 + i32.const 0 + local.set $1 + br 2 (;@8;) + end + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $1 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $10 + local.get $1 + i32.le_s + if ;; label = @8 + local.get $1 + local.set $10 + end + local.get $11 + local.set $1 + br 3 (;@4;) + end + local.get $12 + i32.const -65537 + i32.and + local.set $1 + local.get $5 + i32.const -1 + i32.gt_s + if ;; label = @7 + local.get $1 + local.set $12 + end + local.get $5 + local.get $16 + i64.load + i64.const 0 + i64.ne + local.tee $9 + i32.or + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + local.get $7 + local.set $1 + local.get $5 + local.get $9 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $38 + local.get $7 + i32.sub + i32.add + local.tee $7 + i32.gt_s + if ;; label = @9 + local.get $5 + local.set $7 + end + local.get $21 + end + else + block (result i32) ;; label = @8 + local.get $21 + local.set $1 + i32.const 0 + local.set $7 + local.get $21 + end + end + local.set $5 + end + local.get $0 + i32.const 32 + local.get $10 + local.get $7 + local.get $5 + local.get $1 + i32.sub + local.tee $9 + i32.lt_s + if (result i32) ;; label = @6 + local.get $9 + local.tee $7 + else + local.get $7 + end + local.get $6 + i32.add + local.tee $5 + i32.lt_s + if (result i32) ;; label = @6 + local.get $5 + local.tee $10 + else + local.get $10 + end + local.get $5 + local.get $12 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @6 + local.get $8 + local.get $6 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $5 + local.get $12 + i32.const 65536 + i32.xor + call $25 + local.get $0 + i32.const 48 + local.get $7 + local.get $9 + i32.const 0 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @6 + local.get $1 + local.get $9 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $11 + local.set $1 + br 1 (;@4;) + end + end + br 1 (;@2;) + end + local.get $0 + i32.eqz + if ;; label = @3 + local.get $17 + if ;; label = @4 + block ;; label = @5 + i32.const 1 + local.set $0 + loop $label$308 ;; label = @6 + local.get $4 + local.get $0 + i32.const 2 + i32.shl + i32.add + i32.load + local.tee $1 + if ;; label = @7 + block ;; label = @8 + local.get $3 + local.get $0 + i32.const 3 + i32.shl + i32.add + local.get $1 + local.get $2 + call $22 + local.get $0 + i32.const 1 + i32.add + local.tee $0 + i32.const 10 + i32.lt_s + br_if 2 (;@6;) + i32.const 1 + local.set $15 + br 6 (;@2;) + end + end + end + loop $label$310 ;; label = @6 + local.get $4 + local.get $0 + i32.const 2 + i32.shl + i32.add + i32.load + if ;; label = @7 + block ;; label = @8 + i32.const -1 + local.set $15 + br 6 (;@2;) + end + end + local.get $0 + i32.const 1 + i32.add + local.tee $0 + i32.const 10 + i32.lt_s + br_if 0 (;@6;) + i32.const 1 + local.set $15 + end + end + else + i32.const 0 + local.set $15 + end + end + end + local.get $23 + global.set $global$1 + local.get $15 + end ) - ) - (func $25 (; 38 ;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (block $label$1 - (local.set $7 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 256) - ) - ) - (local.set $6 - (local.get $7) - ) - (block $label$2 - (if - (i32.and - (i32.gt_s - (local.get $2) - (local.get $3) - ) - (i32.eqz - (i32.and - (local.get $4) - (i32.const 73728) - ) - ) - ) - (block - (drop - (call $46 - (local.get $6) - (local.get $1) - (if (result i32) - (i32.gt_u - (local.tee $5 - (i32.sub - (local.get $2) - (local.get $3) - ) - ) - (i32.const 256) - ) - (i32.const 256) - (local.get $5) - ) - ) - ) - (local.set $4 - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 32) - ) - ) - ) - (if - (i32.gt_u - (local.get $5) - (i32.const 255) - ) - (block - (loop $label$7 - (if - (local.get $4) - (block - (drop - (call $21 - (local.get $6) - (i32.const 256) - (local.get $0) - ) - ) - (local.set $1 - (i32.load - (local.get $0) - ) - ) - ) - ) - (local.set $4 - (i32.eqz - (i32.and - (local.get $1) - (i32.const 32) - ) - ) - ) - (br_if $label$7 - (i32.gt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const -256) - ) - ) - (i32.const 255) - ) - ) - ) - (br_if $label$2 - (i32.eqz - (local.get $4) - ) - ) - (local.set $5 - (i32.and - (i32.sub - (local.get $2) - (local.get $3) - ) - (i32.const 255) - ) - ) - ) - (br_if $label$2 - (i32.eqz - (local.get $4) - ) - ) - ) - (drop - (call $21 - (local.get $6) - (local.get $5) - (local.get $0) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $7) - ) + (func $20 (;33;) (type $2) (param $0 i32) (result i32) + i32.const 0 ) - ) - (func $26 (; 39 ;) (type $6) (param $0 i32) (param $1 i32) (result i32) - (if (result i32) - (local.get $0) - (call $29 - (local.get $0) - (local.get $1) - (i32.const 0) - ) - (i32.const 0) + (func $21 (;34;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) + block $label$1 (result i32) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $2 + i32.const 16 + i32.add + local.tee $4 + i32.load + local.tee $3 + br_if 0 (;@3;) + local.get $2 + call $30 + if ;; label = @4 + i32.const 0 + local.set $3 + else + block ;; label = @5 + local.get $4 + i32.load + local.set $3 + br 2 (;@3;) + end + end + br 1 (;@2;) + end + local.get $3 + local.get $2 + i32.const 20 + i32.add + local.tee $5 + i32.load + local.tee $4 + i32.sub + local.get $1 + i32.lt_u + if ;; label = @3 + block ;; label = @4 + local.get $2 + local.get $0 + local.get $1 + local.get $2 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + local.set $3 + br 2 (;@2;) + end + end + block $label$7 (result i32) ;; label = @3 + local.get $2 + i32.load8_s offset=75 + i32.const -1 + i32.gt_s + if (result i32) ;; label = @4 + block (result i32) ;; label = @5 + local.get $1 + local.set $3 + loop $label$9 ;; label = @6 + i32.const 0 + local.get $3 + i32.eqz + br_if 3 (;@3;) + drop + local.get $0 + local.get $3 + i32.const -1 + i32.add + local.tee $6 + i32.add + i32.load8_s + i32.const 10 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $6 + local.set $3 + br 2 (;@6;) + end + end + end + local.get $2 + local.get $0 + local.get $3 + local.get $2 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + local.get $3 + i32.lt_u + br_if 3 (;@2;) + local.get $5 + i32.load + local.set $4 + local.get $1 + local.get $3 + i32.sub + local.set $1 + local.get $0 + local.get $3 + i32.add + local.set $0 + local.get $3 + end + else + i32.const 0 + end + end + local.set $2 + local.get $4 + local.get $0 + local.get $1 + call $47 + drop + local.get $5 + local.get $5 + i32.load + local.get $1 + i32.add + i32.store + local.get $2 + local.get $1 + i32.add + local.set $3 + end + local.get $3 + end ) - ) - (func $27 (; 40 ;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (call $28 - (local.get $0) - (local.get $1) + (func $22 (;35;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) (local $4 i64) (local $5 f64) + block $label$1 ;; label = @1 + local.get $1 + i32.const 20 + i32.le_u + if ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + block $label$6 ;; label = @6 + block $label$7 ;; label = @7 + block $label$8 ;; label = @8 + block $label$9 ;; label = @9 + block $label$10 ;; label = @10 + block $label$11 ;; label = @11 + block $label$12 ;; label = @12 + block $label$13 ;; label = @13 + local.get $1 + i32.const 9 + i32.sub + br_table 0 (;@13;) 1 (;@12;) 2 (;@11;) 3 (;@10;) 4 (;@9;) 5 (;@8;) 6 (;@7;) 7 (;@6;) 8 (;@5;) 9 (;@4;) 10 (;@3;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.store + br 11 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i64.extend_i32_s + i64.store + br 10 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i64.extend_i32_u + i64.store + br 9 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + i64.load + local.set $4 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $4 + i64.store + br 8 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 65535 + i32.and + i32.const 16 + i32.shl + i32.const 16 + i32.shr_s + i64.extend_i32_s + i64.store + br 7 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 65535 + i32.and + i64.extend_i32_u + i64.store + br 6 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 255 + i32.and + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i64.extend_i32_s + i64.store + br 5 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 255 + i32.and + i64.extend_i32_u + i64.store + br 4 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + f64.load + local.set $5 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $5 + f64.store + br 3 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + f64.load + local.set $5 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $5 + f64.store + end + end + end ) - ) - (func $28 (; 41 ;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (local $2 i64) - (local $3 i64) - (block $label$1 (result f64) - (block $label$2 - (block $label$3 - (block $label$4 - (block $label$5 - (br_table $label$5 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$4 $label$3 - (i32.sub - (i32.shr_s - (i32.shl - (i32.and - (i32.and - (i32.wrap_i64 - (local.tee $3 - (i64.shr_u - (local.tee $2 - (i64.reinterpret_f64 - (local.get $0) - ) - ) - (i64.const 52) - ) - ) - ) - (i32.const 65535) - ) - (i32.const 2047) - ) - (i32.const 16) - ) - (i32.const 16) - ) - (i32.const 0) - ) - ) - ) - (i32.store - (local.get $1) - (if (result i32) - (f64.ne - (local.get $0) - (f64.const 0) - ) - (block (result i32) - (local.set $0 - (call $28 - (f64.mul - (local.get $0) - (f64.const 18446744073709551615) - ) - (local.get $1) - ) - ) - (i32.add - (i32.load - (local.get $1) - ) - (i32.const -64) - ) - ) - (i32.const 0) - ) - ) - (br $label$2) - ) - (br $label$2) - ) - (i32.store - (local.get $1) - (i32.add - (i32.and - (i32.wrap_i64 - (local.get $3) - ) - (i32.const 2047) - ) - (i32.const -1022) - ) - ) - (local.set $0 - (f64.reinterpret_i64 - (i64.or - (i64.and - (local.get $2) - (i64.const -9218868437227405313) - ) - (i64.const 4602678819172646912) - ) - ) - ) - ) - (local.get $0) + (func $23 (;36;) (type $9) (param $0 i64) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i64) + block $label$1 (result i32) ;; label = @1 + local.get $0 + i32.wrap_i64 + local.set $2 + local.get $0 + i64.const 4294967295 + i64.gt_u + if ;; label = @2 + block ;; label = @3 + loop $label$3 ;; label = @4 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $0 + i64.const 10 + i64.rem_u + i64.const 48 + i64.or + i64.store8 + local.get $0 + i64.const 10 + i64.div_u + local.set $4 + local.get $0 + i64.const 42949672959 + i64.gt_u + if ;; label = @5 + block ;; label = @6 + local.get $4 + local.set $0 + br 2 (;@4;) + end + end + end + local.get $4 + i32.wrap_i64 + local.set $2 + end + end + local.get $2 + if ;; label = @2 + loop $label$6 ;; label = @3 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $2 + i32.const 10 + i32.rem_u + i32.const 48 + i32.or + i32.store8 + local.get $2 + i32.const 10 + i32.div_u + local.set $3 + local.get $2 + i32.const 10 + i32.ge_u + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.set $2 + br 2 (;@3;) + end + end + end + end + local.get $1 + end ) - ) - (func $29 (; 42 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (block $label$1 (result i32) - (if (result i32) - (local.get $0) - (block (result i32) - (if - (i32.lt_u - (local.get $1) - (i32.const 128) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (br $label$1 - (i32.const 1) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (i32.const 2048) - ) - (block - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 192) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (br $label$1 - (i32.const 2) - ) - ) - ) - (if - (i32.or - (i32.lt_u - (local.get $1) - (i32.const 55296) - ) - (i32.eq - (i32.and - (local.get $1) - (i32.const -8192) - ) - (i32.const 57344) - ) - ) - (block - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 12) - ) - (i32.const 224) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=2 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (br $label$1 - (i32.const 3) - ) - ) - ) - (if (result i32) - (i32.lt_u - (i32.add - (local.get $1) - (i32.const -65536) - ) - (i32.const 1048576) - ) - (block (result i32) - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 18) - ) - (i32.const 240) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 12) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=2 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=3 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.const 4) - ) - (block (result i32) - (i32.store - (call $12) - (i32.const 84) - ) - (i32.const -1) - ) - ) - ) - (i32.const 1) - ) + (func $24 (;37;) (type $2) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + i32.const 0 + local.set $1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + loop $label$5 ;; label = @5 + local.get $1 + i32.const 2233 + i32.add + i32.load8_u + local.get $0 + i32.eq + br_if 1 (;@4;) + local.get $1 + i32.const 1 + i32.add + local.tee $1 + i32.const 87 + i32.ne + br_if 0 (;@5;) + i32.const 87 + local.set $1 + i32.const 2321 + local.set $0 + br 2 (;@3;) + end + end + local.get $1 + if ;; label = @4 + block ;; label = @5 + i32.const 2321 + local.set $0 + br 2 (;@3;) + end + else + i32.const 2321 + local.set $0 + end + br 1 (;@2;) + end + loop $label$8 ;; label = @3 + local.get $0 + local.set $2 + loop $label$9 ;; label = @4 + local.get $2 + i32.const 1 + i32.add + local.set $0 + local.get $2 + i32.load8_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.set $2 + br 2 (;@4;) + end + end + end + local.get $1 + i32.const -1 + i32.add + local.tee $1 + br_if 0 (;@3;) + end + end + local.get $0 + end ) - ) - (func $30 (; 43 ;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.load8_s - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 74) - ) - ) - ) - ) - (i32.store8 - (local.get $2) - (i32.or - (i32.add - (local.get $1) - (i32.const 255) - ) - (local.get $1) - ) - ) - (local.tee $0 - (if (result i32) - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 8) - ) - (block (result i32) - (i32.store - (local.get $0) - (i32.or - (local.get $1) - (i32.const 32) - ) - ) - (i32.const -1) - ) - (block (result i32) - (i32.store offset=8 - (local.get $0) - (i32.const 0) - ) - (i32.store offset=4 - (local.get $0) - (i32.const 0) - ) - (i32.store offset=28 - (local.get $0) - (local.tee $1 - (i32.load offset=44 - (local.get $0) - ) - ) - ) - (i32.store offset=20 - (local.get $0) - (local.get $1) - ) - (i32.store offset=16 - (local.get $0) - (i32.add - (local.get $1) - (i32.load offset=48 - (local.get $0) - ) - ) - ) - (i32.const 0) - ) - ) - ) + (func $25 (;38;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) + (local $5 i32) (local $6 i32) (local $7 i32) + block $label$1 ;; label = @1 + global.get $global$1 + local.set $7 + global.get $global$1 + i32.const 256 + i32.add + global.set $global$1 + local.get $7 + local.set $6 + block $label$2 ;; label = @2 + local.get $2 + local.get $3 + i32.gt_s + local.get $4 + i32.const 73728 + i32.and + i32.eqz + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $6 + local.get $1 + local.get $2 + local.get $3 + i32.sub + local.tee $5 + i32.const 256 + i32.gt_u + if (result i32) ;; label = @5 + i32.const 256 + else + local.get $5 + end + call $46 + drop + local.get $0 + i32.load + local.tee $1 + i32.const 32 + i32.and + i32.eqz + local.set $4 + local.get $5 + i32.const 255 + i32.gt_u + if ;; label = @5 + block ;; label = @6 + loop $label$7 ;; label = @7 + local.get $4 + if ;; label = @8 + block ;; label = @9 + local.get $6 + i32.const 256 + local.get $0 + call $21 + drop + local.get $0 + i32.load + local.set $1 + end + end + local.get $1 + i32.const 32 + i32.and + i32.eqz + local.set $4 + local.get $5 + i32.const -256 + i32.add + local.tee $5 + i32.const 255 + i32.gt_u + br_if 0 (;@7;) + end + local.get $4 + i32.eqz + br_if 4 (;@2;) + local.get $2 + local.get $3 + i32.sub + i32.const 255 + i32.and + local.set $5 + end + else + local.get $4 + i32.eqz + br_if 3 (;@2;) + end + local.get $6 + local.get $5 + local.get $0 + call $21 + drop + end + end + end + local.get $7 + global.set $global$1 + end ) - ) - (func $31 (; 44 ;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (block $label$1 (result i32) - (block $label$2 - (block $label$3 - (br_if $label$3 - (i32.eqz - (i32.and - (local.tee $2 - (local.get $0) - ) - (i32.const 3) - ) - ) - ) - (local.set $1 - (local.get $2) - ) - (loop $label$4 - (if - (i32.eqz - (i32.load8_s - (local.get $0) - ) - ) - (block - (local.set $0 - (local.get $1) - ) - (br $label$2) - ) - ) - (br_if $label$4 - (i32.and - (local.tee $1 - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 3) - ) - ) - (br $label$3) - ) - ) - (loop $label$6 - (local.set $1 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - (if - (i32.eqz - (i32.and - (i32.xor - (i32.and - (local.tee $3 - (i32.load - (local.get $0) - ) - ) - (i32.const -2139062144) - ) - (i32.const -2139062144) - ) - (i32.add - (local.get $3) - (i32.const -16843009) - ) - ) - ) - (block - (local.set $0 - (local.get $1) - ) - (br $label$6) - ) - ) - ) - (if - (i32.shr_s - (i32.shl - (i32.and - (local.get $3) - (i32.const 255) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (loop $label$9 - (br_if $label$9 - (i32.load8_s - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - ) - ) - ) - ) - ) - (i32.sub - (local.get $0) - (local.get $2) - ) + (func $26 (;39;) (type $6) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + if (result i32) ;; label = @1 + local.get $0 + local.get $1 + i32.const 0 + call $29 + else + i32.const 0 + end ) - ) - (func $32 (; 45 ;) (type $6) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (block $label$1 (result i32) - (local.set $3 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store8 - (local.tee $4 - (local.get $3) - ) - (local.tee $7 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - ) - (block $label$2 - (block $label$3 - (br_if $label$3 - (local.tee $5 - (i32.load - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - ) - (if - (call $30 - (local.get $0) - ) - (local.set $1 - (i32.const -1) - ) - (block - (local.set $5 - (i32.load - (local.get $2) - ) - ) - (br $label$3) - ) - ) - (br $label$2) - ) - (if - (i32.lt_u - (local.tee $6 - (i32.load - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (local.get $5) - ) - (if - (i32.ne - (local.tee $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (i32.load8_s offset=75 - (local.get $0) - ) - ) - (block - (i32.store - (local.get $2) - (i32.add - (local.get $6) - (i32.const 1) - ) - ) - (i32.store8 - (local.get $6) - (local.get $7) - ) - (br $label$2) - ) - ) - ) - (local.set $1 - (if (result i32) - (i32.eq - (call_indirect (type $0) - (local.get $0) - (local.get $4) - (i32.const 1) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $0) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - (i32.const 1) - ) - (i32.load8_u - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (global.set $global$1 - (local.get $3) - ) - (local.get $1) + (func $27 (;40;) (type $11) (param $0 f64) (param $1 i32) (result f64) + local.get $0 + local.get $1 + call $28 ) - ) - (func $33 (; 46 ;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $4 - (i32.mul - (local.get $2) - (local.get $1) - ) - ) - (if - (i32.gt_s - (i32.load offset=76 - (local.get $3) - ) - (i32.const -1) - ) - (block - (local.set $5 - (i32.eqz - (call $20 - (local.get $3) - ) - ) - ) - (local.set $0 - (call $21 - (local.get $0) - (local.get $4) - (local.get $3) - ) - ) - (if - (i32.eqz - (local.get $5) - ) - (call $13 - (local.get $3) - ) - ) - ) - (local.set $0 - (call $21 - (local.get $0) - (local.get $4) - (local.get $3) - ) - ) - ) - (if - (i32.ne - (local.get $0) - (local.get $4) - ) - (local.set $2 - (i32.div_u - (local.get $0) - (local.get $1) - ) - ) - ) - (local.get $2) + (func $28 (;41;) (type $11) (param $0 f64) (param $1 i32) (result f64) + (local $2 i64) (local $3 i64) + block $label$1 (result f64) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + local.get $0 + i64.reinterpret_f64 + local.tee $2 + i64.const 52 + i64.shr_u + local.tee $3 + i32.wrap_i64 + i32.const 65535 + i32.and + i32.const 2047 + i32.and + i32.const 16 + i32.shl + i32.const 16 + i32.shr_s + i32.const 0 + i32.sub + br_table 0 (;@5;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 1 (;@4;) 2 (;@3;) + end + local.get $1 + local.get $0 + f64.const 0x0p+0 (;=0;) + f64.ne + if (result i32) ;; label = @5 + block (result i32) ;; label = @6 + local.get $0 + f64.const 0x1p+64 (;=18446744073709552000;) + f64.mul + local.get $1 + call $28 + local.set $0 + local.get $1 + i32.load + i32.const -64 + i32.add + end + else + i32.const 0 + end + i32.store + br 2 (;@2;) + end + br 1 (;@2;) + end + local.get $1 + local.get $3 + i32.wrap_i64 + i32.const 2047 + i32.and + i32.const -1022 + i32.add + i32.store + local.get $2 + i64.const -9218868437227405313 + i64.and + i64.const 4602678819172646912 + i64.or + f64.reinterpret_i64 + local.set $0 + end + local.get $0 + end ) - ) - (func $34 (; 47 ;) (type $6) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (block $label$1 (result i32) - (local.set $2 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store - (local.tee $3 - (local.get $2) - ) - (local.get $1) - ) - (local.set $0 - (call $18 - (i32.load - (i32.const 1280) - ) - (local.get $0) - (local.get $3) - ) - ) - (global.set $global$1 - (local.get $2) - ) - (local.get $0) + (func $29 (;42;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $1 + i32.const 128 + i32.lt_u + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.store8 + i32.const 1 + br 4 (;@1;) + end + end + local.get $1 + i32.const 2048 + i32.lt_u + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 192 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + i32.const 2 + br 4 (;@1;) + end + end + local.get $1 + i32.const 55296 + i32.lt_u + local.get $1 + i32.const -8192 + i32.and + i32.const 57344 + i32.eq + i32.or + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.const 12 + i32.shr_u + i32.const 224 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=2 + i32.const 3 + br 4 (;@1;) + end + end + local.get $1 + i32.const -65536 + i32.add + i32.const 1048576 + i32.lt_u + if (result i32) ;; label = @4 + block (result i32) ;; label = @5 + local.get $0 + local.get $1 + i32.const 18 + i32.shr_u + i32.const 240 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 12 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=2 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=3 + i32.const 4 + end + else + block (result i32) ;; label = @5 + call $12 + i32.const 84 + i32.store + i32.const -1 + end + end + end + else + i32.const 1 + end + end ) - ) - (func $35 (; 48 ;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (block $label$1 (result i32) - (local.set $2 - (if (result i32) - (i32.gt_s - (i32.load offset=76 - (local.tee $1 - (i32.load - (i32.const 1280) - ) - ) - ) - (i32.const -1) - ) - (call $20 - (local.get $1) - ) - (i32.const 0) - ) - ) - (local.set $0 - (block $label$4 (result i32) - (if (result i32) - (i32.lt_s - (call $36 - (local.get $0) - (local.get $1) - ) - (i32.const 0) - ) - (i32.const 1) - (block (result i32) - (if - (i32.ne - (i32.load8_s offset=75 - (local.get $1) - ) - (i32.const 10) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $3 - (i32.add - (local.get $1) - (i32.const 20) - ) - ) - ) - ) - (i32.load offset=16 - (local.get $1) - ) - ) - (block - (i32.store - (local.get $3) - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.store8 - (local.get $0) - (i32.const 10) - ) - (br $label$4 - (i32.const 0) - ) - ) - ) - ) - (i32.lt_s - (call $32 - (local.get $1) - (i32.const 10) - ) - (i32.const 0) - ) - ) - ) - ) - ) - (if - (local.get $2) - (call $13 - (local.get $1) - ) - ) - (i32.shr_s - (i32.shl - (local.get $0) - (i32.const 31) - ) - (i32.const 31) - ) + (func $30 (;43;) (type $2) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + i32.const 74 + i32.add + local.tee $2 + i32.load8_s + local.set $1 + local.get $2 + local.get $1 + i32.const 255 + i32.add + local.get $1 + i32.or + i32.store8 + local.get $0 + i32.load + local.tee $1 + i32.const 8 + i32.and + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $0 + local.get $1 + i32.const 32 + i32.or + i32.store + i32.const -1 + end + else + block (result i32) ;; label = @3 + local.get $0 + i32.const 0 + i32.store offset=8 + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + local.get $0 + i32.load offset=44 + local.tee $1 + i32.store offset=28 + local.get $0 + local.get $1 + i32.store offset=20 + local.get $0 + local.get $1 + local.get $0 + i32.load offset=48 + i32.add + i32.store offset=16 + i32.const 0 + end + end + local.tee $0 + end ) - ) - (func $36 (; 49 ;) (type $6) (param $0 i32) (param $1 i32) (result i32) - (i32.add - (call $33 - (local.get $0) - (call $31 - (local.get $0) - ) - (i32.const 1) - (local.get $1) - ) - (i32.const -1) + (func $31 (;44;) (type $2) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) (local $3 i32) + block $label$1 (result i32) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $0 + local.tee $2 + i32.const 3 + i32.and + i32.eqz + br_if 0 (;@3;) + local.get $2 + local.set $1 + loop $label$4 ;; label = @4 + local.get $0 + i32.load8_s + i32.eqz + if ;; label = @5 + block ;; label = @6 + local.get $1 + local.set $0 + br 4 (;@2;) + end + end + local.get $0 + i32.const 1 + i32.add + local.tee $0 + local.tee $1 + i32.const 3 + i32.and + br_if 0 (;@4;) + br 1 (;@3;) + end + end + loop $label$6 ;; label = @3 + local.get $0 + i32.const 4 + i32.add + local.set $1 + local.get $0 + i32.load + local.tee $3 + i32.const -2139062144 + i32.and + i32.const -2139062144 + i32.xor + local.get $3 + i32.const -16843009 + i32.add + i32.and + i32.eqz + if ;; label = @4 + block ;; label = @5 + local.get $1 + local.set $0 + br 2 (;@3;) + end + end + end + local.get $3 + i32.const 255 + i32.and + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + if ;; label = @3 + loop $label$9 ;; label = @4 + local.get $0 + i32.const 1 + i32.add + local.tee $0 + i32.load8_s + br_if 0 (;@4;) + end + end + end + local.get $0 + local.get $2 + i32.sub + end ) - ) - (func $37 (; 50 ;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 i32) - (local $17 i32) - (local $18 i32) - (local $19 i32) - (local $20 i32) - (local $21 i32) - (block $label$1 (result i32) - (local.set $14 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (local.set $18 - (local.get $14) - ) - (block $label$2 - (if - (i32.lt_u - (local.get $0) - (i32.const 245) - ) - (block - (local.set $3 - (i32.and - (i32.add - (local.get $0) - (i32.const 11) - ) - (i32.const -8) - ) - ) - (if - (i32.and - (local.tee $0 - (i32.shr_u - (local.tee $8 - (i32.load - (i32.const 4176) - ) - ) - (local.tee $2 - (i32.shr_u - (if (result i32) - (i32.lt_u - (local.get $0) - (i32.const 11) - ) - (local.tee $3 - (i32.const 16) - ) - (local.get $3) - ) - (i32.const 3) - ) - ) - ) - ) - (i32.const 3) - ) - (block - (local.set $4 - (i32.load - (local.tee $1 - (i32.add - (local.tee $7 - (i32.load - (local.tee $3 - (i32.add - (local.tee $2 - (i32.add - (i32.shl - (i32.shl - (local.tee $5 - (i32.add - (i32.xor - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 1) - ) - (local.get $2) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $4) - ) - (i32.store - (i32.const 4176) - (i32.and - (local.get $8) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $5) - ) - (i32.const -1) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 12) - ) - ) - ) - (local.get $7) - ) - (block - (i32.store - (local.get $0) - (local.get $2) - ) - (i32.store - (local.get $3) - (local.get $4) - ) - ) - (call $fimport$8) - ) - ) - ) - (i32.store offset=4 - (local.get $7) - (i32.or - (local.tee $0 - (i32.shl - (local.get $5) - (i32.const 3) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $7) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (local.get $1) - ) - ) - ) - (if - (i32.gt_u - (local.get $3) - (local.tee $16 - (i32.load - (i32.const 4184) - ) - ) - ) - (block - (if - (local.get $0) - (block - (local.set $5 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.tee $0 - (i32.and - (i32.shl - (local.get $0) - (local.get $2) - ) - (i32.or - (local.tee $0 - (i32.shl - (i32.const 2) - (local.get $2) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (local.set $12 - (i32.load - (local.tee $5 - (i32.add - (local.tee $9 - (i32.load - (local.tee $2 - (i32.add - (local.tee $4 - (i32.add - (i32.shl - (i32.shl - (local.tee $11 - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $0) - (local.get $5) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $5) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.eq - (local.get $4) - (local.get $12) - ) - (i32.store - (i32.const 4176) - (local.tee $7 - (i32.and - (local.get $8) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $11) - ) - (i32.const -1) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $12) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $12) - (i32.const 12) - ) - ) - ) - (local.get $9) - ) - (block - (i32.store - (local.get $0) - (local.get $4) - ) - (i32.store - (local.get $2) - (local.get $12) - ) - (local.set $7 - (local.get $8) - ) - ) - (call $fimport$8) - ) - ) - ) - (i32.store offset=4 - (local.get $9) - (i32.or - (local.get $3) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.tee $4 - (i32.add - (local.get $9) - (local.get $3) - ) - ) - (i32.or - (local.tee $11 - (i32.sub - (i32.shl - (local.get $11) - (i32.const 3) - ) - (local.get $3) - ) - ) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $4) - (local.get $11) - ) - (local.get $11) - ) - (if - (local.get $16) - (block - (local.set $9 - (i32.load - (i32.const 4196) - ) - ) - (local.set $2 - (i32.add - (i32.shl - (i32.shl - (local.tee $0 - (i32.shr_u - (local.get $16) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - (if - (i32.and - (local.get $7) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $3 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (local.set $6 - (local.get $3) - ) - (local.set $1 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 4176) - (i32.or - (local.get $7) - (local.get $0) - ) - ) - (local.set $6 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (local.set $1 - (local.get $2) - ) - ) - ) - (i32.store - (local.get $6) - (local.get $9) - ) - (i32.store offset=12 - (local.get $1) - (local.get $9) - ) - (i32.store offset=8 - (local.get $9) - (local.get $1) - ) - (i32.store offset=12 - (local.get $9) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 4184) - (local.get $11) - ) - (i32.store - (i32.const 4196) - (local.get $4) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (local.get $5) - ) - ) - ) - (if - (local.tee $6 - (i32.load - (i32.const 4180) - ) - ) - (block - (local.set $2 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.get $6) - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (local.set $9 - (i32.sub - (i32.and - (i32.load offset=4 - (local.tee $2 - (i32.load - (i32.add - (i32.shl - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $0) - (local.get $2) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $2) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - ) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.set $1 - (local.get $2) - ) - (loop $label$25 - (block $label$26 - (if - (i32.eqz - (local.tee $0 - (i32.load offset=16 - (local.get $1) - ) - ) - ) - (br_if $label$26 - (i32.eqz - (local.tee $0 - (i32.load offset=20 - (local.get $1) - ) - ) - ) - ) - ) - (if - (local.tee $7 - (i32.lt_u - (local.tee $1 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.get $9) - ) - ) - (local.set $9 - (local.get $1) - ) - ) - (local.set $1 - (local.get $0) - ) - (if - (local.get $7) - (local.set $2 - (local.get $0) - ) - ) - (br $label$25) - ) - ) - (if - (i32.lt_u - (local.get $2) - (local.tee $12 - (i32.load - (i32.const 4192) - ) - ) - ) - (call $fimport$8) - ) - (if - (i32.ge_u - (local.get $2) - (local.tee $13 - (i32.add - (local.get $2) - (local.get $3) - ) - ) - ) - (call $fimport$8) - ) - (local.set $15 - (i32.load offset=24 - (local.get $2) - ) - ) - (block $label$32 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $2) - ) - ) - (local.get $2) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 20) - ) - ) - ) - ) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 16) - ) - ) - ) - ) - ) - (block - (local.set $4 - (i32.const 0) - ) - (br $label$32) - ) - ) - ) - (loop $label$36 - (if - (local.tee $7 - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (local.set $1 - (local.get $11) - ) - (br $label$36) - ) - ) - (if - (local.tee $7 - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (local.set $1 - (local.get $11) - ) - (br $label$36) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $12) - ) - (call $fimport$8) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $4 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $11 - (i32.load offset=8 - (local.get $2) - ) - ) - (local.get $12) - ) - (call $fimport$8) - ) - (if - (i32.ne - (i32.load - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 12) - ) - ) - ) - (local.get $2) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $2) - ) - (block - (i32.store - (local.get $7) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $11) - ) - (local.set $4 - (local.get $0) - ) - ) - (call $fimport$8) - ) - ) - ) - ) - (block $label$46 - (if - (local.get $15) - (block - (if - (i32.eq - (local.get $2) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $2) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $4) - ) - (if - (i32.eqz - (local.get $4) - ) - (block - (i32.store - (i32.const 4180) - (i32.and - (local.get $6) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$46) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $15) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $15) - (i32.const 16) - ) - ) - ) - (local.get $2) - ) - (i32.store - (local.get $0) - (local.get $4) - ) - (i32.store offset=20 - (local.get $15) - (local.get $4) - ) - ) - (br_if $label$46 - (i32.eqz - (local.get $4) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $4) - (local.tee $0 - (i32.load - (i32.const 4192) - ) - ) - ) - (call $fimport$8) - ) - (i32.store offset=24 - (local.get $4) - (local.get $15) - ) - (if - (local.tee $1 - (i32.load offset=16 - (local.get $2) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $0) - ) - (call $fimport$8) - (block - (i32.store offset=16 - (local.get $4) - (local.get $1) - ) - (i32.store offset=24 - (local.get $1) - (local.get $4) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=20 - (local.get $2) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (i32.store offset=20 - (local.get $4) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $4) - ) - ) - ) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $9) - (i32.const 16) - ) - (block - (i32.store offset=4 - (local.get $2) - (i32.or - (local.tee $0 - (i32.add - (local.get $9) - (local.get $3) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $2) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - (block - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $3) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.get $13) - (i32.or - (local.get $9) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $13) - (local.get $9) - ) - (local.get $9) - ) - (if - (local.get $16) - (block - (local.set $7 - (i32.load - (i32.const 4196) - ) - ) - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.tee $0 - (i32.shr_u - (local.get $16) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - (if - (i32.and - (local.get $8) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (local.set $10 - (local.get $1) - ) - (local.set $5 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 4176) - (i32.or - (local.get $8) - (local.get $0) - ) - ) - (local.set $10 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $5 - (local.get $3) - ) - ) - ) - (i32.store - (local.get $10) - (local.get $7) - ) - (i32.store offset=12 - (local.get $5) - (local.get $7) - ) - (i32.store offset=8 - (local.get $7) - (local.get $5) - ) - (i32.store offset=12 - (local.get $7) - (local.get $3) - ) - ) - ) - (i32.store - (i32.const 4184) - (local.get $9) - ) - (i32.store - (i32.const 4196) - (local.get $13) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - (local.set $0 - (local.get $3) - ) - ) - ) - (local.set $0 - (local.get $3) - ) - ) - ) - (if - (i32.gt_u - (local.get $0) - (i32.const -65) - ) - (local.set $0 - (i32.const -1) - ) - (block - (local.set $7 - (i32.and - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 11) - ) - ) - (i32.const -8) - ) - ) - (if - (local.tee $5 - (i32.load - (i32.const 4180) - ) - ) - (block - (local.set $17 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $0) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $7) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $7) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $3 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $3) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (local.set $3 - (i32.sub - (i32.const 0) - (local.get $7) - ) - ) - (block $label$78 - (block $label$79 - (block $label$80 - (if - (local.tee $1 - (i32.load - (i32.add - (i32.shl - (local.get $17) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - ) - (block - (local.set $0 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $17) - (i32.const 1) - ) - ) - ) - (local.set $4 - (i32.const 0) - ) - (local.set $10 - (i32.shl - (local.get $7) - (if (result i32) - (i32.eq - (local.get $17) - (i32.const 31) - ) - (i32.const 0) - (local.get $0) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - (loop $label$84 - (if - (i32.lt_u - (local.tee $6 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $1) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.get $3) - ) - (if - (local.get $6) - (block - (local.set $3 - (local.get $6) - ) - (local.set $0 - (local.get $1) - ) - ) - (block - (local.set $3 - (i32.const 0) - ) - (local.set $0 - (local.get $1) - ) - (br $label$79) - ) - ) - ) - (local.set $1 - (if (result i32) - (i32.or - (i32.eqz - (local.tee $19 - (i32.load offset=20 - (local.get $1) - ) - ) - ) - (i32.eq - (local.get $19) - (local.tee $6 - (i32.load - (i32.add - (i32.add - (local.get $1) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $10) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - (local.get $4) - (local.get $19) - ) - ) - (local.set $10 - (i32.shl - (local.get $10) - (i32.xor - (i32.and - (local.tee $4 - (i32.eqz - (local.get $6) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (if - (local.get $4) - (block - (local.set $4 - (local.get $1) - ) - (local.set $1 - (local.get $0) - ) - (br $label$80) - ) - (block - (local.set $4 - (local.get $1) - ) - (local.set $1 - (local.get $6) - ) - (br $label$84) - ) - ) - ) - ) - (block - (local.set $4 - (i32.const 0) - ) - (local.set $1 - (i32.const 0) - ) - ) - ) - ) - (br_if $label$79 - (local.tee $0 - (if (result i32) - (i32.and - (i32.eqz - (local.get $4) - ) - (i32.eqz - (local.get $1) - ) - ) - (block (result i32) - (if - (i32.eqz - (local.tee $0 - (i32.and - (local.get $5) - (i32.or - (local.tee $0 - (i32.shl - (i32.const 2) - (local.get $17) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (br $label$2) - ) - ) - (local.set $10 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.get $0) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (i32.load - (i32.add - (i32.shl - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $0) - (local.get $10) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $10) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - ) - (local.get $4) - ) - ) - ) - (local.set $4 - (local.get $1) - ) - (br $label$78) - ) - (loop $label$96 - (if - (local.tee $10 - (i32.lt_u - (local.tee $4 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.get $3) - ) - ) - (local.set $3 - (local.get $4) - ) - ) - (if - (local.get $10) - (local.set $1 - (local.get $0) - ) - ) - (if - (local.tee $4 - (i32.load offset=16 - (local.get $0) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (br $label$96) - ) - ) - (br_if $label$96 - (local.tee $0 - (i32.load offset=20 - (local.get $0) - ) - ) - ) - (local.set $4 - (local.get $1) - ) - ) - ) - (if - (local.get $4) - (if - (i32.lt_u - (local.get $3) - (i32.sub - (i32.load - (i32.const 4184) - ) - (local.get $7) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (local.tee $12 - (i32.load - (i32.const 4192) - ) - ) - ) - (call $fimport$8) - ) - (if - (i32.ge_u - (local.get $4) - (local.tee $6 - (i32.add - (local.get $4) - (local.get $7) - ) - ) - ) - (call $fimport$8) - ) - (local.set $10 - (i32.load offset=24 - (local.get $4) - ) - ) - (block $label$104 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $4) - ) - ) - (local.get $4) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - ) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - ) - (block - (local.set $13 - (i32.const 0) - ) - (br $label$104) - ) - ) - ) - (loop $label$108 - (if - (local.tee $11 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $11) - ) - (local.set $1 - (local.get $9) - ) - (br $label$108) - ) - ) - (if - (local.tee $11 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $11) - ) - (local.set $1 - (local.get $9) - ) - (br $label$108) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $12) - ) - (call $fimport$8) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $13 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $9 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.get $12) - ) - (call $fimport$8) - ) - (if - (i32.ne - (i32.load - (local.tee $11 - (i32.add - (local.get $9) - (i32.const 12) - ) - ) - ) - (local.get $4) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (i32.store - (local.get $11) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $9) - ) - (local.set $13 - (local.get $0) - ) - ) - (call $fimport$8) - ) - ) - ) - ) - (block $label$118 - (if - (local.get $10) - (block - (if - (i32.eq - (local.get $4) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $4) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $13) - ) - (if - (i32.eqz - (local.get $13) - ) - (block - (i32.store - (i32.const 4180) - (local.tee $2 - (i32.and - (local.get $5) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - ) - (br $label$118) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $10) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $10) - (i32.const 16) - ) - ) - ) - (local.get $4) - ) - (i32.store - (local.get $0) - (local.get $13) - ) - (i32.store offset=20 - (local.get $10) - (local.get $13) - ) - ) - (if - (i32.eqz - (local.get $13) - ) - (block - (local.set $2 - (local.get $5) - ) - (br $label$118) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $13) - (local.tee $0 - (i32.load - (i32.const 4192) - ) - ) - ) - (call $fimport$8) - ) - (i32.store offset=24 - (local.get $13) - (local.get $10) - ) - (if - (local.tee $1 - (i32.load offset=16 - (local.get $4) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $0) - ) - (call $fimport$8) - (block - (i32.store offset=16 - (local.get $13) - (local.get $1) - ) - (i32.store offset=24 - (local.get $1) - (local.get $13) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=20 - (local.get $4) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (i32.store offset=20 - (local.get $13) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $13) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (block $label$136 - (if - (i32.lt_u - (local.get $3) - (i32.const 16) - ) - (block - (i32.store offset=4 - (local.get $4) - (i32.or - (local.tee $0 - (i32.add - (local.get $3) - (local.get $7) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $4) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - (block - (i32.store offset=4 - (local.get $4) - (i32.or - (local.get $7) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $3) - ) - (local.get $3) - ) - (local.set $0 - (i32.shr_u - (local.get $3) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $3) - (i32.const 256) - ) - (block - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.get $0) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - (if - (i32.and - (local.tee $1 - (i32.load - (i32.const 4176) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (local.set $16 - (local.get $1) - ) - (local.set $8 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 4176) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (local.set $16 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $8 - (local.get $3) - ) - ) - ) - (i32.store - (local.get $16) - (local.get $6) - ) - (i32.store offset=12 - (local.get $8) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $8) - ) - (i32.store offset=12 - (local.get $6) - (local.get $3) - ) - (br $label$136) - ) - ) - (local.set $1 - (i32.add - (i32.shl - (local.tee $5 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $3) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $3) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $3) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $5 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $5) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - (i32.store offset=28 - (local.get $6) - (local.get $5) - ) - (i32.store offset=4 - (local.tee $0 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.get $2) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $5) - ) - ) - ) - ) - (block - (i32.store - (i32.const 4180) - (i32.or - (local.get $2) - (local.get $0) - ) - ) - (i32.store - (local.get $1) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $1) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$136) - ) - ) - (local.set $0 - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $5) - (i32.const 1) - ) - ) - ) - (local.set $5 - (i32.shl - (local.get $3) - (if (result i32) - (i32.eq - (local.get $5) - (i32.const 31) - ) - (i32.const 0) - (local.get $1) - ) - ) - ) - (block $label$151 - (block $label$152 - (block $label$153 - (loop $label$154 - (br_if $label$152 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.set $2 - (i32.shl - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$153 - (i32.eqz - (local.tee $1 - (i32.load - (local.tee $5 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $5) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $5 - (local.get $2) - ) - (local.set $0 - (local.get $1) - ) - (br $label$154) - ) - ) - (if - (i32.lt_u - (local.get $5) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (i32.store - (local.get $5) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $0) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$136) - ) - ) - (br $label$151) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $3 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $1 - (i32.load - (i32.const 4192) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $1) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $6) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $2) - ) - (i32.store offset=12 - (local.get $6) - (local.get $0) - ) - (i32.store offset=24 - (local.get $6) - (i32.const 0) - ) - ) - (call $fimport$8) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $4) - (i32.const 8) - ) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - ) - ) - ) - ) - (if - (i32.ge_u - (local.tee $1 - (i32.load - (i32.const 4184) - ) - ) - (local.get $0) - ) - (block - (local.set $2 - (i32.load - (i32.const 4196) - ) - ) - (if - (i32.gt_u - (local.tee $3 - (i32.sub - (local.get $1) - (local.get $0) - ) - ) - (i32.const 15) - ) - (block - (i32.store - (i32.const 4196) - (local.tee $1 - (i32.add - (local.get $2) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 4184) - (local.get $3) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $1) - (local.get $3) - ) - (local.get $3) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - ) - (block - (i32.store - (i32.const 4184) - (i32.const 0) - ) - (i32.store - (i32.const 4196) - (i32.const 0) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $1) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $2) - (local.get $1) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.gt_u - (local.tee $10 - (i32.load - (i32.const 4188) - ) - ) - (local.get $0) - ) - (block - (i32.store - (i32.const 4188) - (local.tee $3 - (i32.sub - (local.get $10) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 4200) - (local.tee $1 - (i32.add - (local.tee $2 - (i32.load - (i32.const 4200) - ) - ) - (local.get $0) - ) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.le_u - (local.tee $6 - (i32.and - (local.tee $8 - (i32.add - (local.tee $1 - (if (result i32) - (i32.load - (i32.const 4648) - ) - (i32.load - (i32.const 4656) - ) - (block (result i32) - (i32.store - (i32.const 4656) - (i32.const 4096) - ) - (i32.store - (i32.const 4652) - (i32.const 4096) - ) - (i32.store - (i32.const 4660) - (i32.const -1) - ) - (i32.store - (i32.const 4664) - (i32.const -1) - ) - (i32.store - (i32.const 4668) - (i32.const 0) - ) - (i32.store - (i32.const 4620) - (i32.const 0) - ) - (i32.store - (local.get $18) - (local.tee $1 - (i32.xor - (i32.and - (local.get $18) - (i32.const -16) - ) - (i32.const 1431655768) - ) - ) - ) - (i32.store - (i32.const 4648) - (local.get $1) - ) - (i32.const 4096) - ) - ) - ) - (local.tee $13 - (i32.add - (local.get $0) - (i32.const 47) - ) - ) - ) - ) - (local.tee $4 - (i32.sub - (i32.const 0) - (local.get $1) - ) - ) - ) - ) - (local.get $0) - ) - (block - (global.set $global$1 - (local.get $14) - ) - (return - (i32.const 0) - ) - ) - ) - (if - (local.tee $2 - (i32.load - (i32.const 4616) - ) - ) - (if - (i32.or - (i32.le_u - (local.tee $1 - (i32.add - (local.tee $3 - (i32.load - (i32.const 4608) - ) - ) - (local.get $6) - ) - ) - (local.get $3) - ) - (i32.gt_u - (local.get $1) - (local.get $2) - ) - ) - (block - (global.set $global$1 - (local.get $14) - ) - (return - (i32.const 0) - ) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $0) - (i32.const 48) - ) - ) - (block $label$171 - (block $label$172 - (if - (i32.eqz - (i32.and - (i32.load - (i32.const 4620) - ) - (i32.const 4) - ) - ) - (block - (block $label$174 - (block $label$175 - (block $label$176 - (br_if $label$176 - (i32.eqz - (local.tee $3 - (i32.load - (i32.const 4200) - ) - ) - ) - ) - (local.set $2 - (i32.const 4624) - ) - (loop $label$177 - (block $label$178 - (if - (i32.le_u - (local.tee $1 - (i32.load - (local.get $2) - ) - ) - (local.get $3) - ) - (br_if $label$178 - (i32.gt_u - (i32.add - (local.get $1) - (i32.load - (local.tee $5 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - ) - ) - (local.get $3) - ) - ) - ) - (br_if $label$176 - (i32.eqz - (local.tee $1 - (i32.load offset=8 - (local.get $2) - ) - ) - ) - ) - (local.set $2 - (local.get $1) - ) - (br $label$177) - ) - ) - (if - (i32.lt_u - (local.tee $3 - (i32.and - (i32.sub - (local.get $8) - (local.get $10) - ) - (local.get $4) - ) - ) - (i32.const 2147483647) - ) - (if - (i32.eq - (local.tee $1 - (call $45 - (local.get $3) - ) - ) - (i32.add - (i32.load - (local.get $2) - ) - (i32.load - (local.get $5) - ) - ) - ) - (br_if $label$172 - (i32.ne - (local.get $1) - (i32.const -1) - ) - ) - (block - (local.set $2 - (local.get $1) - ) - (local.set $1 - (local.get $3) - ) - (br $label$175) - ) - ) - ) - (br $label$174) - ) - (if - (i32.ne - (local.tee $1 - (call $45 - (i32.const 0) - ) - ) - (i32.const -1) - ) - (block - (local.set $2 - (i32.sub - (i32.and - (i32.add - (local.tee $5 - (i32.add - (local.tee $2 - (i32.load - (i32.const 4652) - ) - ) - (i32.const -1) - ) - ) - (local.tee $3 - (local.get $1) - ) - ) - (i32.sub - (i32.const 0) - (local.get $2) - ) - ) - (local.get $3) - ) - ) - (local.set $4 - (i32.add - (local.tee $3 - (i32.add - (if (result i32) - (i32.and - (local.get $5) - (local.get $3) - ) - (local.get $2) - (i32.const 0) - ) - (local.get $6) - ) - ) - (local.tee $5 - (i32.load - (i32.const 4608) - ) - ) - ) - ) - (if - (i32.and - (i32.gt_u - (local.get $3) - (local.get $0) - ) - (i32.lt_u - (local.get $3) - (i32.const 2147483647) - ) - ) - (block - (if - (local.tee $2 - (i32.load - (i32.const 4616) - ) - ) - (br_if $label$174 - (i32.or - (i32.le_u - (local.get $4) - (local.get $5) - ) - (i32.gt_u - (local.get $4) - (local.get $2) - ) - ) - ) - ) - (br_if $label$172 - (i32.eq - (local.tee $2 - (call $45 - (local.get $3) - ) - ) - (local.get $1) - ) - ) - (local.set $1 - (local.get $3) - ) - (br $label$175) - ) - ) - ) - ) - (br $label$174) - ) - (local.set $5 - (i32.sub - (i32.const 0) - (local.get $1) - ) - ) - (if - (i32.and - (i32.gt_u - (local.get $7) - (local.get $1) - ) - (i32.and - (i32.lt_u - (local.get $1) - (i32.const 2147483647) - ) - (i32.ne - (local.get $2) - (i32.const -1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $3 - (i32.and - (i32.add - (i32.sub - (local.get $13) - (local.get $1) - ) - (local.tee $3 - (i32.load - (i32.const 4656) - ) - ) - ) - (i32.sub - (i32.const 0) - (local.get $3) - ) - ) - ) - (i32.const 2147483647) - ) - (if - (i32.eq - (call $45 - (local.get $3) - ) - (i32.const -1) - ) - (block - (drop - (call $45 - (local.get $5) - ) - ) - (br $label$174) - ) - (local.set $3 - (i32.add - (local.get $3) - (local.get $1) - ) - ) - ) - (local.set $3 - (local.get $1) - ) - ) - (local.set $3 - (local.get $1) - ) - ) - (if - (i32.ne - (local.get $2) - (i32.const -1) - ) - (block - (local.set $1 - (local.get $2) - ) - (br $label$172) - ) - ) - ) - (i32.store - (i32.const 4620) - (i32.or - (i32.load - (i32.const 4620) - ) - (i32.const 4) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $6) - (i32.const 2147483647) - ) - (if - (i32.and - (i32.lt_u - (local.tee $1 - (call $45 - (local.get $6) - ) - ) - (local.tee $3 - (call $45 - (i32.const 0) - ) - ) - ) - (i32.and - (i32.ne - (local.get $1) - (i32.const -1) - ) - (i32.ne - (local.get $3) - (i32.const -1) - ) - ) - ) - (br_if $label$172 - (i32.gt_u - (local.tee $3 - (i32.sub - (local.get $3) - (local.get $1) - ) - ) - (i32.add - (local.get $0) - (i32.const 40) - ) - ) - ) - ) - ) - (br $label$171) - ) - (i32.store - (i32.const 4608) - (local.tee $2 - (i32.add - (i32.load - (i32.const 4608) - ) - (local.get $3) - ) - ) - ) - (if - (i32.gt_u - (local.get $2) - (i32.load - (i32.const 4612) - ) - ) - (i32.store - (i32.const 4612) - (local.get $2) - ) - ) - (block $label$198 - (if - (local.tee $8 - (i32.load - (i32.const 4200) - ) - ) - (block - (local.set $2 - (i32.const 4624) - ) - (block $label$200 - (block $label$201 - (loop $label$202 - (br_if $label$201 - (i32.eq - (local.get $1) - (i32.add - (local.tee $4 - (i32.load - (local.get $2) - ) - ) - (local.tee $5 - (i32.load - (local.tee $7 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - ) - ) - ) - ) - ) - (br_if $label$202 - (local.tee $2 - (i32.load offset=8 - (local.get $2) - ) - ) - ) - ) - (br $label$200) - ) - (if - (i32.eqz - (i32.and - (i32.load offset=12 - (local.get $2) - ) - (i32.const 8) - ) - ) - (if - (i32.and - (i32.lt_u - (local.get $8) - (local.get $1) - ) - (i32.ge_u - (local.get $8) - (local.get $4) - ) - ) - (block - (i32.store - (local.get $7) - (i32.add - (local.get $5) - (local.get $3) - ) - ) - (local.set $5 - (i32.load - (i32.const 4188) - ) - ) - (local.set $1 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $2 - (i32.add - (local.get $8) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 4200) - (local.tee $2 - (i32.add - (local.get $8) - (if (result i32) - (i32.and - (local.get $2) - (i32.const 7) - ) - (local.get $1) - (local.tee $1 - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 4188) - (local.tee $1 - (i32.add - (i32.sub - (local.get $3) - (local.get $1) - ) - (local.get $5) - ) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $2) - (local.get $1) - ) - (i32.const 40) - ) - (i32.store - (i32.const 4204) - (i32.load - (i32.const 4664) - ) - ) - (br $label$198) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.tee $2 - (i32.load - (i32.const 4192) - ) - ) - ) - (block - (i32.store - (i32.const 4192) - (local.get $1) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - (local.set $10 - (i32.add - (local.get $1) - (local.get $3) - ) - ) - (local.set $5 - (i32.const 4624) - ) - (block $label$208 - (block $label$209 - (loop $label$210 - (br_if $label$209 - (i32.eq - (i32.load - (local.get $5) - ) - (local.get $10) - ) - ) - (br_if $label$210 - (local.tee $5 - (i32.load offset=8 - (local.get $5) - ) - ) - ) - (local.set $5 - (i32.const 4624) - ) - ) - (br $label$208) - ) - (if - (i32.and - (i32.load offset=12 - (local.get $5) - ) - (i32.const 8) - ) - (local.set $5 - (i32.const 4624) - ) - (block - (i32.store - (local.get $5) - (local.get $1) - ) - (i32.store - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - (i32.add - (i32.load - (local.get $5) - ) - (local.get $3) - ) - ) - (local.set $7 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $4 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $3 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $5 - (i32.add - (local.get $10) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $6 - (i32.add - (local.tee $13 - (i32.add - (local.get $1) - (if (result i32) - (i32.and - (local.get $4) - (i32.const 7) - ) - (local.get $7) - (i32.const 0) - ) - ) - ) - (local.get $0) - ) - ) - (local.set $7 - (i32.sub - (i32.sub - (local.tee $4 - (i32.add - (local.get $10) - (if (result i32) - (i32.and - (local.get $5) - (i32.const 7) - ) - (local.get $3) - (i32.const 0) - ) - ) - ) - (local.get $13) - ) - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $13) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (block $label$217 - (if - (i32.eq - (local.get $4) - (local.get $8) - ) - (block - (i32.store - (i32.const 4188) - (local.tee $0 - (i32.add - (i32.load - (i32.const 4188) - ) - (local.get $7) - ) - ) - ) - (i32.store - (i32.const 4200) - (local.get $6) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - ) - (block - (if - (i32.eq - (local.get $4) - (i32.load - (i32.const 4196) - ) - ) - (block - (i32.store - (i32.const 4184) - (local.tee $0 - (i32.add - (i32.load - (i32.const 4184) - ) - (local.get $7) - ) - ) - ) - (i32.store - (i32.const 4196) - (local.get $6) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $0) - ) - (local.get $0) - ) - (br $label$217) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (local.tee $0 - (if (result i32) - (i32.eq - (i32.and - (local.tee $0 - (i32.load offset=4 - (local.get $4) - ) - ) - (i32.const 3) - ) - (i32.const 1) - ) - (block (result i32) - (local.set $11 - (i32.and - (local.get $0) - (i32.const -8) - ) - ) - (local.set $1 - (i32.shr_u - (local.get $0) - (i32.const 3) - ) - ) - (block $label$222 - (if - (i32.lt_u - (local.get $0) - (i32.const 256) - ) - (block - (local.set $5 - (i32.load offset=12 - (local.get $4) - ) - ) - (block $label$224 - (if - (i32.ne - (local.tee $3 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.tee $0 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $3) - (local.get $2) - ) - (call $fimport$8) - ) - (br_if $label$224 - (i32.eq - (i32.load offset=12 - (local.get $3) - ) - (local.get $4) - ) - ) - (call $fimport$8) - ) - ) - ) - (if - (i32.eq - (local.get $5) - (local.get $3) - ) - (block - (i32.store - (i32.const 4176) - (i32.and - (i32.load - (i32.const 4176) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$222) - ) - ) - (block $label$228 - (if - (i32.eq - (local.get $5) - (local.get $0) - ) - (local.set $20 - (i32.add - (local.get $5) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $5) - (local.get $2) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $5) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (local.set $20 - (local.get $0) - ) - (br $label$228) - ) - ) - (call $fimport$8) - ) - ) - ) - (i32.store offset=12 - (local.get $3) - (local.get $5) - ) - (i32.store - (local.get $20) - (local.get $3) - ) - ) - (block - (local.set $8 - (i32.load offset=24 - (local.get $4) - ) - ) - (block $label$234 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $4) - ) - ) - (local.get $4) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.tee $3 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load - (local.get $3) - ) - ) - (local.set $1 - (local.get $3) - ) - (block - (local.set $12 - (i32.const 0) - ) - (br $label$234) - ) - ) - ) - (loop $label$239 - (if - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $5) - ) - (br $label$239) - ) - ) - (if - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $5) - ) - (br $label$239) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $2) - ) - (call $fimport$8) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $12 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.get $2) - ) - (call $fimport$8) - ) - (if - (i32.ne - (i32.load - (local.tee $3 - (i32.add - (local.get $5) - (i32.const 12) - ) - ) - ) - (local.get $4) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (i32.store - (local.get $3) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $5) - ) - (local.set $12 - (local.get $0) - ) - ) - (call $fimport$8) - ) - ) - ) - ) - (br_if $label$222 - (i32.eqz - (local.get $8) - ) - ) - (block $label$249 - (if - (i32.eq - (local.get $4) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $4) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $12) - ) - (br_if $label$249 - (local.get $12) - ) - (i32.store - (i32.const 4180) - (i32.and - (i32.load - (i32.const 4180) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$222) - ) - (block - (if - (i32.lt_u - (local.get $8) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - ) - (local.get $4) - ) - (i32.store - (local.get $0) - (local.get $12) - ) - (i32.store offset=20 - (local.get $8) - (local.get $12) - ) - ) - (br_if $label$222 - (i32.eqz - (local.get $12) - ) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $12) - (local.tee $1 - (i32.load - (i32.const 4192) - ) - ) - ) - (call $fimport$8) - ) - (i32.store offset=24 - (local.get $12) - (local.get $8) - ) - (if - (local.tee $3 - (i32.load - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $3) - (local.get $1) - ) - (call $fimport$8) - (block - (i32.store offset=16 - (local.get $12) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $12) - ) - ) - ) - ) - (br_if $label$222 - (i32.eqz - (local.tee $0 - (i32.load offset=4 - (local.get $0) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (i32.store offset=20 - (local.get $12) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $12) - ) - ) - ) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $11) - (local.get $7) - ) - ) - (i32.add - (local.get $4) - (local.get $11) - ) - ) - (local.get $4) - ) - ) - (i32.const 4) - ) - ) - (i32.and - (i32.load - (local.get $0) - ) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $7) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $7) - ) - (local.get $7) - ) - (local.set $0 - (i32.shr_u - (local.get $7) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $7) - (i32.const 256) - ) - (block - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.get $0) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - (block $label$263 - (if - (i32.and - (local.tee $1 - (i32.load - (i32.const 4176) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (block - (if - (i32.ge_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 4192) - ) - ) - (block - (local.set $21 - (local.get $1) - ) - (local.set $9 - (local.get $0) - ) - (br $label$263) - ) - ) - (call $fimport$8) - ) - (block - (i32.store - (i32.const 4176) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (local.set $21 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $9 - (local.get $3) - ) - ) - ) - ) - (i32.store - (local.get $21) - (local.get $6) - ) - (i32.store offset=12 - (local.get $9) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $9) - ) - (i32.store offset=12 - (local.get $6) - (local.get $3) - ) - (br $label$217) - ) - ) - (local.set $3 - (i32.add - (i32.shl - (local.tee $2 - (block $label$267 (result i32) - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $7) - (i32.const 8) - ) - ) - (block (result i32) - (drop - (br_if $label$267 - (i32.const 31) - (i32.gt_u - (local.get $7) - (i32.const 16777215) - ) - ) - ) - (i32.or - (i32.and - (i32.shr_u - (local.get $7) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $3 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $3) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - (i32.store offset=28 - (local.get $6) - (local.get $2) - ) - (i32.store offset=4 - (local.tee $0 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (i32.const 4180) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $2) - ) - ) - ) - ) - (block - (i32.store - (i32.const 4180) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $3) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$217) - ) - ) - (local.set $0 - (i32.load - (local.get $3) - ) - ) - (local.set $1 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $2) - (i32.const 1) - ) - ) - ) - (local.set $2 - (i32.shl - (local.get $7) - (if (result i32) - (i32.eq - (local.get $2) - (i32.const 31) - ) - (i32.const 0) - (local.get $1) - ) - ) - ) - (block $label$273 - (block $label$274 - (block $label$275 - (loop $label$276 - (br_if $label$274 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.set $3 - (i32.shl - (local.get $2) - (i32.const 1) - ) - ) - (br_if $label$275 - (i32.eqz - (local.tee $1 - (i32.load - (local.tee $2 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $2) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $2 - (local.get $3) - ) - (local.set $0 - (local.get $1) - ) - (br $label$276) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (i32.store - (local.get $2) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $0) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$217) - ) - ) - (br $label$273) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $3 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $1 - (i32.load - (i32.const 4192) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $1) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $6) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $2) - ) - (i32.store offset=12 - (local.get $6) - (local.get $0) - ) - (i32.store offset=24 - (local.get $6) - (i32.const 0) - ) - ) - (call $fimport$8) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $13) - (i32.const 8) - ) - ) - ) - ) - ) - (loop $label$281 - (block $label$282 - (if - (i32.le_u - (local.tee $2 - (i32.load - (local.get $5) - ) - ) - (local.get $8) - ) - (br_if $label$282 - (i32.gt_u - (local.tee $13 - (i32.add - (local.get $2) - (i32.load offset=4 - (local.get $5) - ) - ) - ) - (local.get $8) - ) - ) - ) - (local.set $5 - (i32.load offset=8 - (local.get $5) - ) - ) - (br $label$281) - ) - ) - (local.set $2 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $5 - (i32.add - (local.tee $7 - (i32.add - (local.get $13) - (i32.const -47) - ) - ) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $10 - (i32.add - (local.tee $7 - (if (result i32) - (i32.lt_u - (local.tee $2 - (i32.add - (local.get $7) - (if (result i32) - (i32.and - (local.get $5) - (i32.const 7) - ) - (local.get $2) - (i32.const 0) - ) - ) - ) - (local.tee $12 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - ) - (local.get $8) - (local.get $2) - ) - ) - (i32.const 8) - ) - ) - (local.set $5 - (i32.add - (local.get $7) - (i32.const 24) - ) - ) - (local.set $9 - (i32.add - (local.get $3) - (i32.const -40) - ) - ) - (local.set $2 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $4 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 4200) - (local.tee $4 - (i32.add - (local.get $1) - (if (result i32) - (i32.and - (local.get $4) - (i32.const 7) - ) - (local.get $2) - (local.tee $2 - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 4188) - (local.tee $2 - (i32.sub - (local.get $9) - (local.get $2) - ) - ) - ) - (i32.store offset=4 - (local.get $4) - (i32.or - (local.get $2) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $4) - (local.get $2) - ) - (i32.const 40) - ) - (i32.store - (i32.const 4204) - (i32.load - (i32.const 4664) - ) - ) - (i32.store - (local.tee $2 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (i32.const 27) - ) - (i64.store align=4 - (local.get $10) - (i64.load align=4 - (i32.const 4624) - ) - ) - (i64.store offset=8 align=4 - (local.get $10) - (i64.load align=4 - (i32.const 4632) - ) - ) - (i32.store - (i32.const 4624) - (local.get $1) - ) - (i32.store - (i32.const 4628) - (local.get $3) - ) - (i32.store - (i32.const 4636) - (i32.const 0) - ) - (i32.store - (i32.const 4632) - (local.get $10) - ) - (local.set $1 - (local.get $5) - ) - (loop $label$290 - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i32.const 7) - ) - (br_if $label$290 - (i32.lt_u - (i32.add - (local.get $1) - (i32.const 4) - ) - (local.get $13) - ) - ) - ) - (if - (i32.ne - (local.get $7) - (local.get $8) - ) - (block - (i32.store - (local.get $2) - (i32.and - (i32.load - (local.get $2) - ) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $8) - (i32.or - (local.tee $4 - (i32.sub - (local.get $7) - (local.get $8) - ) - ) - (i32.const 1) - ) - ) - (i32.store - (local.get $7) - (local.get $4) - ) - (local.set $1 - (i32.shr_u - (local.get $4) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $4) - (i32.const 256) - ) - (block - (local.set $2 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - (if - (i32.and - (local.tee $3 - (i32.load - (i32.const 4176) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.load - (local.tee $3 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (local.set $15 - (local.get $3) - ) - (local.set $11 - (local.get $1) - ) - ) - ) - (block - (i32.store - (i32.const 4176) - (i32.or - (local.get $3) - (local.get $1) - ) - ) - (local.set $15 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (local.set $11 - (local.get $2) - ) - ) - ) - (i32.store - (local.get $15) - (local.get $8) - ) - (i32.store offset=12 - (local.get $11) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $11) - ) - (i32.store offset=12 - (local.get $8) - (local.get $2) - ) - (br $label$198) - ) - ) - (local.set $2 - (i32.add - (i32.shl - (local.tee $5 - (if (result i32) - (local.tee $1 - (i32.shr_u - (local.get $4) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $4) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $4) - (i32.add - (local.tee $1 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $3 - (i32.shl - (local.get $1) - (local.tee $2 - (i32.and - (i32.shr_u - (i32.add - (local.get $1) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $2) - ) - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $3 - (i32.shl - (local.get $3) - (local.get $1) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $3) - (local.get $1) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $1) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - (i32.store offset=28 - (local.get $8) - (local.get $5) - ) - (i32.store offset=20 - (local.get $8) - (i32.const 0) - ) - (i32.store - (local.get $12) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.tee $3 - (i32.load - (i32.const 4180) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $5) - ) - ) - ) - ) - (block - (i32.store - (i32.const 4180) - (i32.or - (local.get $3) - (local.get $1) - ) - ) - (i32.store - (local.get $2) - (local.get $8) - ) - (i32.store offset=24 - (local.get $8) - (local.get $2) - ) - (i32.store offset=12 - (local.get $8) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $8) - ) - (br $label$198) - ) - ) - (local.set $1 - (i32.load - (local.get $2) - ) - ) - (local.set $3 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $5) - (i32.const 1) - ) - ) - ) - (local.set $5 - (i32.shl - (local.get $4) - (if (result i32) - (i32.eq - (local.get $5) - (i32.const 31) - ) - (i32.const 0) - (local.get $3) - ) - ) - ) - (block $label$304 - (block $label$305 - (block $label$306 - (loop $label$307 - (br_if $label$305 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $1) - ) - (i32.const -8) - ) - (local.get $4) - ) - ) - (local.set $2 - (i32.shl - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$306 - (i32.eqz - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (i32.add - (local.get $1) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $5) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $5 - (local.get $2) - ) - (local.set $1 - (local.get $3) - ) - (br $label$307) - ) - ) - (if - (i32.lt_u - (local.get $5) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (i32.store - (local.get $5) - (local.get $8) - ) - (i32.store offset=24 - (local.get $8) - (local.get $1) - ) - (i32.store offset=12 - (local.get $8) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $8) - ) - (br $label$198) - ) - ) - (br $label$304) - ) - (if - (i32.and - (i32.ge_u - (local.tee $5 - (i32.load - (local.tee $2 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - ) - (local.tee $3 - (i32.load - (i32.const 4192) - ) - ) - ) - (i32.ge_u - (local.get $1) - (local.get $3) - ) - ) - (block - (i32.store offset=12 - (local.get $5) - (local.get $8) - ) - (i32.store - (local.get $2) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $5) - ) - (i32.store offset=12 - (local.get $8) - (local.get $1) - ) - (i32.store offset=24 - (local.get $8) - (i32.const 0) - ) - ) - (call $fimport$8) - ) - ) - ) - ) - ) - (block - (if - (i32.or - (i32.eqz - (local.tee $2 - (i32.load - (i32.const 4192) - ) - ) - ) - (i32.lt_u - (local.get $1) - (local.get $2) - ) - ) - (i32.store - (i32.const 4192) - (local.get $1) - ) - ) - (i32.store - (i32.const 4624) - (local.get $1) - ) - (i32.store - (i32.const 4628) - (local.get $3) - ) - (i32.store - (i32.const 4636) - (i32.const 0) - ) - (i32.store - (i32.const 4212) - (i32.load - (i32.const 4648) - ) - ) - (i32.store - (i32.const 4208) - (i32.const -1) - ) - (local.set $2 - (i32.const 0) - ) - (loop $label$314 - (i32.store offset=12 - (local.tee $5 - (i32.add - (i32.shl - (i32.shl - (local.get $2) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - (local.get $5) - ) - (i32.store offset=8 - (local.get $5) - (local.get $5) - ) - (br_if $label$314 - (i32.ne - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 32) - ) - ) - ) - (local.set $5 - (i32.add - (local.get $3) - (i32.const -40) - ) - ) - (local.set $3 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $2 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 4200) - (local.tee $3 - (i32.add - (local.get $1) - (local.tee $1 - (if (result i32) - (i32.and - (local.get $2) - (i32.const 7) - ) - (local.get $3) - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 4188) - (local.tee $1 - (i32.sub - (local.get $5) - (local.get $1) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $3) - (local.get $1) - ) - (i32.const 40) - ) - (i32.store - (i32.const 4204) - (i32.load - (i32.const 4664) - ) - ) - ) - ) - ) - (if - (i32.gt_u - (local.tee $1 - (i32.load - (i32.const 4188) - ) - ) - (local.get $0) - ) - (block - (i32.store - (i32.const 4188) - (local.tee $3 - (i32.sub - (local.get $1) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 4200) - (local.tee $1 - (i32.add - (local.tee $2 - (i32.load - (i32.const 4200) - ) - ) - (local.get $0) - ) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - ) - (i32.store - (call $12) - (i32.const 12) - ) - (global.set $global$1 - (local.get $14) - ) - (i32.const 0) + (func $32 (;45;) (type $6) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $3 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $3 + local.tee $4 + local.get $1 + i32.const 255 + i32.and + local.tee $7 + i32.store8 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $0 + i32.const 16 + i32.add + local.tee $2 + i32.load + local.tee $5 + br_if 0 (;@3;) + local.get $0 + call $30 + if ;; label = @4 + i32.const -1 + local.set $1 + else + block ;; label = @5 + local.get $2 + i32.load + local.set $5 + br 2 (;@3;) + end + end + br 1 (;@2;) + end + local.get $0 + i32.const 20 + i32.add + local.tee $2 + i32.load + local.tee $6 + local.get $5 + i32.lt_u + if ;; label = @3 + local.get $1 + i32.const 255 + i32.and + local.tee $1 + local.get $0 + i32.load8_s offset=75 + i32.ne + if ;; label = @4 + block ;; label = @5 + local.get $2 + local.get $6 + i32.const 1 + i32.add + i32.store + local.get $6 + local.get $7 + i32.store8 + br 3 (;@2;) + end + end + end + local.get $0 + local.get $4 + i32.const 1 + local.get $0 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + i32.const 1 + i32.eq + if (result i32) ;; label = @3 + local.get $4 + i32.load8_u + else + i32.const -1 + end + local.set $1 + end + local.get $3 + global.set $global$1 + local.get $1 + end ) - ) - (func $38 (; 51 ;) (type $3) (param $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (block $label$1 - (if - (i32.eqz - (local.get $0) - ) - (return) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.add - (local.get $0) - (i32.const -8) - ) - ) - (local.tee $11 - (i32.load - (i32.const 4192) - ) - ) - ) - (call $fimport$8) - ) - (if - (i32.eq - (local.tee $8 - (i32.and - (local.tee $0 - (i32.load - (i32.add - (local.get $0) - (i32.const -4) - ) - ) - ) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (call $fimport$8) - ) - (local.set $6 - (i32.add - (local.get $1) - (local.tee $4 - (i32.and - (local.get $0) - (i32.const -8) - ) - ) - ) - ) - (block $label$5 - (if - (i32.and - (local.get $0) - (i32.const 1) - ) - (block - (local.set $3 - (local.get $1) - ) - (local.set $2 - (local.get $4) - ) - ) - (block - (if - (i32.eqz - (local.get $8) - ) - (return) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.add - (local.get $1) - (i32.sub - (i32.const 0) - (local.tee $8 - (i32.load - (local.get $1) - ) - ) - ) - ) - ) - (local.get $11) - ) - (call $fimport$8) - ) - (local.set $1 - (i32.add - (local.get $8) - (local.get $4) - ) - ) - (if - (i32.eq - (local.get $0) - (i32.load - (i32.const 4196) - ) - ) - (block - (if - (i32.ne - (i32.and - (local.tee $3 - (i32.load - (local.tee $2 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - ) - ) - (i32.const 3) - ) - (i32.const 3) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (i32.store - (i32.const 4184) - (local.get $1) - ) - (i32.store - (local.get $2) - (i32.and - (local.get $3) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $0) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $0) - (local.get $1) - ) - (local.get $1) - ) - (return) - ) - ) - (local.set $10 - (i32.shr_u - (local.get $8) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $8) - (i32.const 256) - ) - (block - (local.set $3 - (i32.load offset=12 - (local.get $0) - ) - ) - (if - (i32.ne - (local.tee $4 - (i32.load offset=8 - (local.get $0) - ) - ) - (local.tee $2 - (i32.add - (i32.shl - (i32.shl - (local.get $10) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (local.get $11) - ) - (call $fimport$8) - ) - (if - (i32.ne - (i32.load offset=12 - (local.get $4) - ) - (local.get $0) - ) - (call $fimport$8) - ) - ) - ) - (if - (i32.eq - (local.get $3) - (local.get $4) - ) - (block - (i32.store - (i32.const 4176) - (i32.and - (i32.load - (i32.const 4176) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $10) - ) - (i32.const -1) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (if - (i32.eq - (local.get $3) - (local.get $2) - ) - (local.set $5 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $3) - (local.get $11) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $2 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - (local.get $0) - ) - (local.set $5 - (local.get $2) - ) - (call $fimport$8) - ) - ) - ) - (i32.store offset=12 - (local.get $4) - (local.get $3) - ) - (i32.store - (local.get $5) - (local.get $4) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (local.set $12 - (i32.load offset=24 - (local.get $0) - ) - ) - (block $label$22 - (if - (i32.eq - (local.tee $4 - (i32.load offset=12 - (local.get $0) - ) - ) - (local.get $0) - ) - (block - (if - (local.tee $4 - (i32.load - (local.tee $8 - (i32.add - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - (local.set $5 - (local.get $8) - ) - (if - (i32.eqz - (local.tee $4 - (i32.load - (local.get $5) - ) - ) - ) - (block - (local.set $7 - (i32.const 0) - ) - (br $label$22) - ) - ) - ) - (loop $label$27 - (if - (local.tee $10 - (i32.load - (local.tee $8 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $4 - (local.get $10) - ) - (local.set $5 - (local.get $8) - ) - (br $label$27) - ) - ) - (if - (local.tee $10 - (i32.load - (local.tee $8 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $4 - (local.get $10) - ) - (local.set $5 - (local.get $8) - ) - (br $label$27) - ) - ) - ) - (if - (i32.lt_u - (local.get $5) - (local.get $11) - ) - (call $fimport$8) - (block - (i32.store - (local.get $5) - (i32.const 0) - ) - (local.set $7 - (local.get $4) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.load offset=8 - (local.get $0) - ) - ) - (local.get $11) - ) - (call $fimport$8) - ) - (if - (i32.ne - (i32.load - (local.tee $8 - (i32.add - (local.get $5) - (i32.const 12) - ) - ) - ) - (local.get $0) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $10 - (i32.add - (local.get $4) - (i32.const 8) - ) - ) - ) - (local.get $0) - ) - (block - (i32.store - (local.get $8) - (local.get $4) - ) - (i32.store - (local.get $10) - (local.get $5) - ) - (local.set $7 - (local.get $4) - ) - ) - (call $fimport$8) - ) - ) - ) - ) - (if - (local.get $12) - (block - (if - (i32.eq - (local.get $0) - (i32.load - (local.tee $5 - (i32.add - (i32.shl - (local.tee $4 - (i32.load offset=28 - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - ) - ) - (block - (i32.store - (local.get $5) - (local.get $7) - ) - (if - (i32.eqz - (local.get $7) - ) - (block - (i32.store - (i32.const 4180) - (i32.and - (i32.load - (i32.const 4180) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $12) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $4 - (i32.add - (local.get $12) - (i32.const 16) - ) - ) - ) - (local.get $0) - ) - (i32.store - (local.get $4) - (local.get $7) - ) - (i32.store offset=20 - (local.get $12) - (local.get $7) - ) - ) - (if - (i32.eqz - (local.get $7) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $7) - (local.tee $5 - (i32.load - (i32.const 4192) - ) - ) - ) - (call $fimport$8) - ) - (i32.store offset=24 - (local.get $7) - (local.get $12) - ) - (if - (local.tee $4 - (i32.load - (local.tee $8 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $4) - (local.get $5) - ) - (call $fimport$8) - (block - (i32.store offset=16 - (local.get $7) - (local.get $4) - ) - (i32.store offset=24 - (local.get $4) - (local.get $7) - ) - ) - ) - ) - (if - (local.tee $4 - (i32.load offset=4 - (local.get $8) - ) - ) - (if - (i32.lt_u - (local.get $4) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (i32.store offset=20 - (local.get $7) - (local.get $4) - ) - (i32.store offset=24 - (local.get $4) - (local.get $7) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - ) - ) - ) - (if - (i32.ge_u - (local.get $3) - (local.get $6) - ) - (call $fimport$8) - ) - (if - (i32.eqz - (i32.and - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - ) - ) - (i32.const 1) - ) - ) - (call $fimport$8) - ) - (if - (i32.and - (local.get $0) - (i32.const 2) - ) - (block - (i32.store - (local.get $1) - (i32.and - (local.get $0) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $2) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $2) - ) - (local.get $2) - ) - ) - (block - (if - (i32.eq - (local.get $6) - (i32.load - (i32.const 4200) - ) - ) - (block - (i32.store - (i32.const 4188) - (local.tee $0 - (i32.add - (i32.load - (i32.const 4188) - ) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 4200) - (local.get $3) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (if - (i32.ne - (local.get $3) - (i32.load - (i32.const 4196) - ) - ) - (return) - ) - (i32.store - (i32.const 4196) - (i32.const 0) - ) - (i32.store - (i32.const 4184) - (i32.const 0) - ) - (return) - ) - ) - (if - (i32.eq - (local.get $6) - (i32.load - (i32.const 4196) - ) - ) - (block - (i32.store - (i32.const 4184) - (local.tee $0 - (i32.add - (i32.load - (i32.const 4184) - ) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 4196) - (local.get $3) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $0) - ) - (local.get $0) - ) - (return) - ) - ) - (local.set $5 - (i32.add - (i32.and - (local.get $0) - (i32.const -8) - ) - (local.get $2) - ) - ) - (local.set $4 - (i32.shr_u - (local.get $0) - (i32.const 3) - ) - ) - (block $label$61 - (if - (i32.lt_u - (local.get $0) - (i32.const 256) - ) - (block - (local.set $2 - (i32.load offset=12 - (local.get $6) - ) - ) - (if - (i32.ne - (local.tee $1 - (i32.load offset=8 - (local.get $6) - ) - ) - (local.tee $0 - (i32.add - (i32.shl - (i32.shl - (local.get $4) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $1) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - ) - (if - (i32.ne - (i32.load offset=12 - (local.get $1) - ) - (local.get $6) - ) - (call $fimport$8) - ) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $1) - ) - (block - (i32.store - (i32.const 4176) - (i32.and - (i32.load - (i32.const 4176) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (br $label$61) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $0) - ) - (local.set $14 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - (local.get $6) - ) - (local.set $14 - (local.get $0) - ) - (call $fimport$8) - ) - ) - ) - (i32.store offset=12 - (local.get $1) - (local.get $2) - ) - (i32.store - (local.get $14) - (local.get $1) - ) - ) - (block - (local.set $7 - (i32.load offset=24 - (local.get $6) - ) - ) - (block $label$73 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $6) - ) - ) - (local.get $6) - ) - (block - (if - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.tee $2 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - (local.set $2 - (local.get $1) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.get $2) - ) - ) - ) - (block - (local.set $9 - (i32.const 0) - ) - (br $label$73) - ) - ) - ) - (loop $label$78 - (if - (local.tee $4 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (local.set $2 - (local.get $1) - ) - (br $label$78) - ) - ) - (if - (local.tee $4 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (local.set $2 - (local.get $1) - ) - (br $label$78) - ) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (i32.store - (local.get $2) - (i32.const 0) - ) - (local.set $9 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $2 - (i32.load offset=8 - (local.get $6) - ) - ) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - ) - (if - (i32.ne - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 12) - ) - ) - ) - (local.get $6) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $4 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $6) - ) - (block - (i32.store - (local.get $1) - (local.get $0) - ) - (i32.store - (local.get $4) - (local.get $2) - ) - (local.set $9 - (local.get $0) - ) - ) - (call $fimport$8) - ) - ) - ) - ) - (if - (local.get $7) - (block - (if - (i32.eq - (local.get $6) - (i32.load - (local.tee $2 - (i32.add - (i32.shl - (local.tee $0 - (i32.load offset=28 - (local.get $6) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - ) - ) - (block - (i32.store - (local.get $2) - (local.get $9) - ) - (if - (i32.eqz - (local.get $9) - ) - (block - (i32.store - (i32.const 4180) - (i32.and - (i32.load - (i32.const 4180) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $0) - ) - (i32.const -1) - ) - ) - ) - (br $label$61) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $7) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $7) - (i32.const 16) - ) - ) - ) - (local.get $6) - ) - (i32.store - (local.get $0) - (local.get $9) - ) - (i32.store offset=20 - (local.get $7) - (local.get $9) - ) - ) - (br_if $label$61 - (i32.eqz - (local.get $9) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $9) - (local.tee $2 - (i32.load - (i32.const 4192) - ) - ) - ) - (call $fimport$8) - ) - (i32.store offset=24 - (local.get $9) - (local.get $7) - ) - (if - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $0) - (local.get $2) - ) - (call $fimport$8) - (block - (i32.store offset=16 - (local.get $9) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $9) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=4 - (local.get $1) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (i32.store offset=20 - (local.get $9) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $9) - ) - ) - ) - ) - ) - ) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $5) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $5) - ) - (local.get $5) - ) - (if - (i32.eq - (local.get $3) - (i32.load - (i32.const 4196) - ) - ) - (block - (i32.store - (i32.const 4184) - (local.get $5) - ) - (return) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - ) - (local.set $1 - (i32.shr_u - (local.get $2) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.const 256) - ) - (block - (local.set $0 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 4216) - ) - ) - (if - (i32.and - (local.tee $2 - (i32.load - (i32.const 4176) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.load - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (local.set $15 - (local.get $2) - ) - (local.set $13 - (local.get $1) - ) - ) - ) - (block - (i32.store - (i32.const 4176) - (i32.or - (local.get $2) - (local.get $1) - ) - ) - (local.set $15 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - (local.set $13 - (local.get $0) - ) - ) - ) - (i32.store - (local.get $15) - (local.get $3) - ) - (i32.store offset=12 - (local.get $13) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $13) - ) - (i32.store offset=12 - (local.get $3) - (local.get $0) - ) - (return) - ) - ) - (local.set $0 - (i32.add - (i32.shl - (local.tee $1 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $2) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $2) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $2) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $4 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $0) - ) - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $0 - (i32.shl - (local.get $1) - (local.get $4) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $0) - (local.get $1) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 4480) - ) - ) - (i32.store offset=28 - (local.get $3) - (local.get $1) - ) - (i32.store offset=20 - (local.get $3) - (i32.const 0) - ) - (i32.store offset=16 - (local.get $3) - (i32.const 0) - ) - (block $label$113 - (if - (i32.and - (local.tee $4 - (i32.load - (i32.const 4180) - ) - ) - (local.tee $5 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (block - (local.set $0 - (i32.load - (local.get $0) - ) - ) - (local.set $4 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $1) - (i32.const 1) - ) - ) - ) - (local.set $1 - (i32.shl - (local.get $2) - (if (result i32) - (i32.eq - (local.get $1) - (i32.const 31) - ) - (i32.const 0) - (local.get $4) - ) - ) - ) - (block $label$117 - (block $label$118 - (block $label$119 - (loop $label$120 - (br_if $label$118 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $2) - ) - ) - (local.set $4 - (i32.shl - (local.get $1) - (i32.const 1) - ) - ) - (br_if $label$119 - (i32.eqz - (local.tee $5 - (i32.load - (local.tee $1 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $1) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $1 - (local.get $4) - ) - (local.set $0 - (local.get $5) - ) - (br $label$120) - ) - ) - (if - (i32.lt_u - (local.get $1) - (i32.load - (i32.const 4192) - ) - ) - (call $fimport$8) - (block - (i32.store - (local.get $1) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $0) - ) - (i32.store offset=12 - (local.get $3) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $3) - ) - (br $label$113) - ) - ) - (br $label$117) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $4 - (i32.load - (i32.const 4192) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $4) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $3) - ) - (i32.store - (local.get $1) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $2) - ) - (i32.store offset=12 - (local.get $3) - (local.get $0) - ) - (i32.store offset=24 - (local.get $3) - (i32.const 0) - ) - ) - (call $fimport$8) - ) - ) - ) - (block - (i32.store - (i32.const 4180) - (i32.or - (local.get $4) - (local.get $5) - ) - ) - (i32.store - (local.get $0) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $0) - ) - (i32.store offset=12 - (local.get $3) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $3) - ) - ) - ) - ) - (i32.store - (i32.const 4208) - (local.tee $0 - (i32.add - (i32.load - (i32.const 4208) - ) - (i32.const -1) - ) - ) - ) - (if - (local.get $0) - (return) - (local.set $0 - (i32.const 4632) - ) - ) - (loop $label$128 - (local.set $0 - (i32.add - (local.tee $2 - (i32.load - (local.get $0) - ) - ) - (i32.const 8) - ) - ) - (br_if $label$128 - (local.get $2) - ) - ) - (i32.store - (i32.const 4208) - (i32.const -1) - ) + (func $33 (;46;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) + (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + local.get $2 + local.get $1 + i32.mul + local.set $4 + local.get $3 + i32.load offset=76 + i32.const -1 + i32.gt_s + if ;; label = @2 + block ;; label = @3 + local.get $3 + call $20 + i32.eqz + local.set $5 + local.get $0 + local.get $4 + local.get $3 + call $21 + local.set $0 + local.get $5 + i32.eqz + if ;; label = @4 + local.get $3 + call $13 + end + end + else + local.get $0 + local.get $4 + local.get $3 + call $21 + local.set $0 + end + local.get $0 + local.get $4 + i32.ne + if ;; label = @2 + local.get $0 + local.get $1 + i32.div_u + local.set $2 + end + local.get $2 + end ) - ) - (func $39 (; 52 ;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - (block $label$1 (result i32) - (if - (i32.eqz - (local.get $0) - ) - (local.set $0 - (i32.const 1) - ) - ) - (loop $label$3 - (block $label$4 - (if - (local.tee $1 - (call $37 - (local.get $0) - ) - ) - (block - (local.set $0 - (local.get $1) - ) - (br $label$4) - ) - ) - (if - (local.tee $1 - (call $43) - ) - (block - (call_indirect (type $1) - (i32.add - (i32.and - (local.get $1) - (i32.const 0) - ) - (i32.const 8) - ) - ) - (br $label$3) - ) - (local.set $0 - (i32.const 0) - ) - ) - ) - ) - (local.get $0) + (func $34 (;47;) (type $6) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $2 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $2 + local.tee $3 + local.get $1 + i32.store + i32.const 1280 + i32.load + local.get $0 + local.get $3 + call $18 + local.set $0 + local.get $2 + global.set $global$1 + local.get $0 + end ) - ) - (func $40 (; 53 ;) (type $2) (param $0 i32) (result i32) - (call $39 - (local.get $0) + (func $35 (;48;) (type $2) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) (local $3 i32) + block $label$1 (result i32) ;; label = @1 + i32.const 1280 + i32.load + local.tee $1 + i32.load offset=76 + i32.const -1 + i32.gt_s + if (result i32) ;; label = @2 + local.get $1 + call $20 + else + i32.const 0 + end + local.set $2 + block $label$4 (result i32) ;; label = @2 + local.get $0 + local.get $1 + call $36 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @3 + i32.const 1 + else + block (result i32) ;; label = @4 + local.get $1 + i32.load8_s offset=75 + i32.const 10 + i32.ne + if ;; label = @5 + local.get $1 + i32.const 20 + i32.add + local.tee $3 + i32.load + local.tee $0 + local.get $1 + i32.load offset=16 + i32.lt_u + if ;; label = @6 + block ;; label = @7 + local.get $3 + local.get $0 + i32.const 1 + i32.add + i32.store + local.get $0 + i32.const 10 + i32.store8 + i32.const 0 + br 5 (;@2;) + end + end + end + local.get $1 + i32.const 10 + call $32 + i32.const 0 + i32.lt_s + end + end + end + local.set $0 + local.get $2 + if ;; label = @2 + local.get $1 + call $13 + end + local.get $0 + i32.const 31 + i32.shl + i32.const 31 + i32.shr_s + end ) - ) - (func $41 (; 54 ;) (type $3) (param $0 i32) - (call $38 - (local.get $0) + (func $36 (;49;) (type $6) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + local.get $0 + call $31 + i32.const 1 + local.get $1 + call $33 + i32.const -1 + i32.add ) - ) - (func $42 (; 55 ;) (type $3) (param $0 i32) - (call $41 - (local.get $0) + (func $37 (;50;) (type $2) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $14 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $14 + local.set $18 + block $label$2 ;; label = @2 + local.get $0 + i32.const 245 + i32.lt_u + if ;; label = @3 + block ;; label = @4 + local.get $0 + i32.const 11 + i32.add + i32.const -8 + i32.and + local.set $3 + i32.const 4176 + i32.load + local.tee $8 + local.get $0 + i32.const 11 + i32.lt_u + if (result i32) ;; label = @5 + i32.const 16 + local.tee $3 + else + local.get $3 + end + i32.const 3 + i32.shr_u + local.tee $2 + i32.shr_u + local.tee $0 + i32.const 3 + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $0 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $2 + i32.add + local.tee $5 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.tee $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $7 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.set $4 + local.get $2 + local.get $4 + i32.eq + if ;; label = @7 + i32.const 4176 + local.get $8 + i32.const 1 + local.get $5 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + else + block ;; label = @8 + local.get $4 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$8 + end + local.get $4 + i32.const 12 + i32.add + local.tee $0 + i32.load + local.get $7 + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $0 + local.get $2 + i32.store + local.get $3 + local.get $4 + i32.store + end + else + call $fimport$8 + end + end + end + local.get $7 + local.get $5 + i32.const 3 + i32.shl + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $7 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + local.get $14 + global.set $global$1 + local.get $1 + return + end + end + local.get $3 + i32.const 4184 + i32.load + local.tee $16 + i32.gt_u + if ;; label = @5 + block ;; label = @6 + local.get $0 + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.get $2 + i32.shl + i32.const 2 + local.get $2 + i32.shl + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.or + i32.and + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $5 + local.get $0 + local.get $5 + i32.shr_u + local.tee $2 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $5 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + i32.add + local.tee $11 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.tee $4 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $9 + i32.const 8 + i32.add + local.tee $5 + i32.load + local.set $12 + local.get $4 + local.get $12 + i32.eq + if ;; label = @9 + i32.const 4176 + local.get $8 + i32.const 1 + local.get $11 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $7 + i32.store + else + block ;; label = @10 + local.get $12 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$8 + end + local.get $12 + i32.const 12 + i32.add + local.tee $0 + i32.load + local.get $9 + i32.eq + if ;; label = @11 + block ;; label = @12 + local.get $0 + local.get $4 + i32.store + local.get $2 + local.get $12 + i32.store + local.get $8 + local.set $7 + end + else + call $fimport$8 + end + end + end + local.get $9 + local.get $3 + i32.const 3 + i32.or + i32.store offset=4 + local.get $9 + local.get $3 + i32.add + local.tee $4 + local.get $11 + i32.const 3 + i32.shl + local.get $3 + i32.sub + local.tee $11 + i32.const 1 + i32.or + i32.store offset=4 + local.get $4 + local.get $11 + i32.add + local.get $11 + i32.store + local.get $16 + if ;; label = @9 + block ;; label = @10 + i32.const 4196 + i32.load + local.set $9 + local.get $16 + i32.const 3 + i32.shr_u + local.tee $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.set $2 + local.get $7 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @11 + local.get $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $0 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @12 + call $fimport$8 + else + block ;; label = @13 + local.get $3 + local.set $6 + local.get $0 + local.set $1 + end + end + else + block ;; label = @12 + i32.const 4176 + local.get $7 + local.get $0 + i32.or + i32.store + local.get $2 + i32.const 8 + i32.add + local.set $6 + local.get $2 + local.set $1 + end + end + local.get $6 + local.get $9 + i32.store + local.get $1 + local.get $9 + i32.store offset=12 + local.get $9 + local.get $1 + i32.store offset=8 + local.get $9 + local.get $2 + i32.store offset=12 + end + end + i32.const 4184 + local.get $11 + i32.store + i32.const 4196 + local.get $4 + i32.store + local.get $14 + global.set $global$1 + local.get $5 + return + end + end + i32.const 4180 + i32.load + local.tee $6 + if ;; label = @7 + block ;; label = @8 + local.get $6 + i32.const 0 + local.get $6 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $2 + local.get $0 + local.get $2 + i32.shr_u + local.tee $1 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $2 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 4480 + i32.add + i32.load + local.tee $2 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.sub + local.set $9 + local.get $2 + local.set $1 + loop $label$25 ;; label = @9 + block $label$26 ;; label = @10 + local.get $1 + i32.load offset=16 + local.tee $0 + i32.eqz + if ;; label = @11 + local.get $1 + i32.load offset=20 + local.tee $0 + i32.eqz + br_if 1 (;@10;) + end + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.sub + local.tee $1 + local.get $9 + i32.lt_u + local.tee $7 + if ;; label = @11 + local.get $1 + local.set $9 + end + local.get $0 + local.set $1 + local.get $7 + if ;; label = @11 + local.get $0 + local.set $2 + end + br 1 (;@9;) + end + end + local.get $2 + i32.const 4192 + i32.load + local.tee $12 + i32.lt_u + if ;; label = @9 + call $fimport$8 + end + local.get $2 + local.get $2 + local.get $3 + i32.add + local.tee $13 + i32.ge_u + if ;; label = @9 + call $fimport$8 + end + local.get $2 + i32.load offset=24 + local.set $15 + block $label$32 ;; label = @9 + local.get $2 + i32.load offset=12 + local.tee $0 + local.get $2 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $2 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @12 + local.get $2 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @13 + block ;; label = @14 + i32.const 0 + local.set $4 + br 5 (;@9;) + end + end + end + loop $label$36 ;; label = @12 + local.get $0 + i32.const 20 + i32.add + local.tee $11 + i32.load + local.tee $7 + if ;; label = @13 + block ;; label = @14 + local.get $7 + local.set $0 + local.get $11 + local.set $1 + br 2 (;@12;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $11 + i32.load + local.tee $7 + if ;; label = @13 + block ;; label = @14 + local.get $7 + local.set $0 + local.get $11 + local.set $1 + br 2 (;@12;) + end + end + end + local.get $1 + local.get $12 + i32.lt_u + if ;; label = @12 + call $fimport$8 + else + block ;; label = @13 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $4 + end + end + end + else + block ;; label = @11 + local.get $2 + i32.load offset=8 + local.tee $11 + local.get $12 + i32.lt_u + if ;; label = @12 + call $fimport$8 + end + local.get $11 + i32.const 12 + i32.add + local.tee $7 + i32.load + local.get $2 + i32.ne + if ;; label = @12 + call $fimport$8 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $2 + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $7 + local.get $0 + i32.store + local.get $1 + local.get $11 + i32.store + local.get $0 + local.set $4 + end + else + call $fimport$8 + end + end + end + end + block $label$46 ;; label = @9 + local.get $15 + if ;; label = @10 + block ;; label = @11 + local.get $2 + local.get $2 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 4480 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $0 + local.get $4 + i32.store + local.get $4 + i32.eqz + if ;; label = @14 + block ;; label = @15 + i32.const 4180 + local.get $6 + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 6 (;@9;) + end + end + end + else + block ;; label = @13 + local.get $15 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$8 + end + local.get $15 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $2 + i32.eq + if ;; label = @14 + local.get $0 + local.get $4 + i32.store + else + local.get $15 + local.get $4 + i32.store offset=20 + end + local.get $4 + i32.eqz + br_if 4 (;@9;) + end + end + local.get $4 + i32.const 4192 + i32.load + local.tee $0 + i32.lt_u + if ;; label = @12 + call $fimport$8 + end + local.get $4 + local.get $15 + i32.store offset=24 + local.get $2 + i32.load offset=16 + local.tee $1 + if ;; label = @12 + local.get $1 + local.get $0 + i32.lt_u + if ;; label = @13 + call $fimport$8 + else + block ;; label = @14 + local.get $4 + local.get $1 + i32.store offset=16 + local.get $1 + local.get $4 + i32.store offset=24 + end + end + end + local.get $2 + i32.load offset=20 + local.tee $0 + if ;; label = @12 + local.get $0 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @13 + call $fimport$8 + else + block ;; label = @14 + local.get $4 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $4 + i32.store offset=24 + end + end + end + end + end + end + local.get $9 + i32.const 16 + i32.lt_u + if ;; label = @9 + block ;; label = @10 + local.get $2 + local.get $9 + local.get $3 + i32.add + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + else + block ;; label = @10 + local.get $2 + local.get $3 + i32.const 3 + i32.or + i32.store offset=4 + local.get $13 + local.get $9 + i32.const 1 + i32.or + i32.store offset=4 + local.get $13 + local.get $9 + i32.add + local.get $9 + i32.store + local.get $16 + if ;; label = @11 + block ;; label = @12 + i32.const 4196 + i32.load + local.set $7 + local.get $16 + i32.const 3 + i32.shr_u + local.tee $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.set $3 + local.get $8 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @13 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$8 + else + block ;; label = @15 + local.get $1 + local.set $10 + local.get $0 + local.set $5 + end + end + else + block ;; label = @14 + i32.const 4176 + local.get $8 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $10 + local.get $3 + local.set $5 + end + end + local.get $10 + local.get $7 + i32.store + local.get $5 + local.get $7 + i32.store offset=12 + local.get $7 + local.get $5 + i32.store offset=8 + local.get $7 + local.get $3 + i32.store offset=12 + end + end + i32.const 4184 + local.get $9 + i32.store + i32.const 4196 + local.get $13 + i32.store + end + end + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + else + local.get $3 + local.set $0 + end + end + else + local.get $3 + local.set $0 + end + end + else + local.get $0 + i32.const -65 + i32.gt_u + if ;; label = @4 + i32.const -1 + local.set $0 + else + block ;; label = @5 + local.get $0 + i32.const 11 + i32.add + local.tee $0 + i32.const -8 + i32.and + local.set $7 + i32.const 4180 + i32.load + local.tee $5 + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @8 + local.get $7 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @9 + i32.const 31 + else + local.get $7 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $3 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $3 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.set $17 + i32.const 0 + local.get $7 + i32.sub + local.set $3 + block $label$78 ;; label = @8 + block $label$79 ;; label = @9 + block $label$80 ;; label = @10 + local.get $17 + i32.const 2 + i32.shl + i32.const 4480 + i32.add + i32.load + local.tee $1 + if ;; label = @11 + block ;; label = @12 + i32.const 25 + local.get $17 + i32.const 1 + i32.shr_u + i32.sub + local.set $0 + i32.const 0 + local.set $4 + local.get $7 + local.get $17 + i32.const 31 + i32.eq + if (result i32) ;; label = @13 + i32.const 0 + else + local.get $0 + end + i32.shl + local.set $10 + i32.const 0 + local.set $0 + loop $label$84 ;; label = @13 + local.get $1 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.sub + local.tee $6 + local.get $3 + i32.lt_u + if ;; label = @14 + local.get $6 + if ;; label = @15 + block ;; label = @16 + local.get $6 + local.set $3 + local.get $1 + local.set $0 + end + else + block ;; label = @16 + i32.const 0 + local.set $3 + local.get $1 + local.set $0 + br 7 (;@9;) + end + end + end + local.get $1 + i32.load offset=20 + local.tee $19 + i32.eqz + local.get $19 + local.get $1 + i32.const 16 + i32.add + local.get $10 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + i32.load + local.tee $6 + i32.eq + i32.or + if (result i32) ;; label = @14 + local.get $4 + else + local.get $19 + end + local.set $1 + local.get $10 + local.get $6 + i32.eqz + local.tee $4 + i32.const 1 + i32.and + i32.const 1 + i32.xor + i32.shl + local.set $10 + local.get $4 + if ;; label = @14 + block ;; label = @15 + local.get $1 + local.set $4 + local.get $0 + local.set $1 + br 5 (;@10;) + end + else + block ;; label = @15 + local.get $1 + local.set $4 + local.get $6 + local.set $1 + br 2 (;@13;) + end + end + end + end + else + block ;; label = @12 + i32.const 0 + local.set $4 + i32.const 0 + local.set $1 + end + end + end + local.get $4 + i32.eqz + local.get $1 + i32.eqz + i32.and + if (result i32) ;; label = @10 + block (result i32) ;; label = @11 + local.get $5 + i32.const 2 + local.get $17 + i32.shl + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.or + i32.and + local.tee $0 + i32.eqz + if ;; label = @12 + block ;; label = @13 + local.get $7 + local.set $0 + br 11 (;@2;) + end + end + local.get $0 + i32.const 0 + local.get $0 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $10 + local.get $0 + local.get $10 + i32.shr_u + local.tee $4 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $10 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 4480 + i32.add + i32.load + end + else + local.get $4 + end + local.tee $0 + br_if 0 (;@9;) + local.get $1 + local.set $4 + br 1 (;@8;) + end + loop $label$96 ;; label = @9 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.sub + local.tee $4 + local.get $3 + i32.lt_u + local.tee $10 + if ;; label = @10 + local.get $4 + local.set $3 + end + local.get $10 + if ;; label = @10 + local.get $0 + local.set $1 + end + local.get $0 + i32.load offset=16 + local.tee $4 + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.set $0 + br 2 (;@9;) + end + end + local.get $0 + i32.load offset=20 + local.tee $0 + br_if 0 (;@9;) + local.get $1 + local.set $4 + end + end + local.get $4 + if ;; label = @8 + local.get $3 + i32.const 4184 + i32.load + local.get $7 + i32.sub + i32.lt_u + if ;; label = @9 + block ;; label = @10 + local.get $4 + i32.const 4192 + i32.load + local.tee $12 + i32.lt_u + if ;; label = @11 + call $fimport$8 + end + local.get $4 + local.get $4 + local.get $7 + i32.add + local.tee $6 + i32.ge_u + if ;; label = @11 + call $fimport$8 + end + local.get $4 + i32.load offset=24 + local.set $10 + block $label$104 ;; label = @11 + local.get $4 + i32.load offset=12 + local.tee $0 + local.get $4 + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $4 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @14 + local.get $4 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @15 + block ;; label = @16 + i32.const 0 + local.set $13 + br 5 (;@11;) + end + end + end + loop $label$108 ;; label = @14 + local.get $0 + i32.const 20 + i32.add + local.tee $9 + i32.load + local.tee $11 + if ;; label = @15 + block ;; label = @16 + local.get $11 + local.set $0 + local.get $9 + local.set $1 + br 2 (;@14;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $9 + i32.load + local.tee $11 + if ;; label = @15 + block ;; label = @16 + local.get $11 + local.set $0 + local.get $9 + local.set $1 + br 2 (;@14;) + end + end + end + local.get $1 + local.get $12 + i32.lt_u + if ;; label = @14 + call $fimport$8 + else + block ;; label = @15 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $13 + end + end + end + else + block ;; label = @13 + local.get $4 + i32.load offset=8 + local.tee $9 + local.get $12 + i32.lt_u + if ;; label = @14 + call $fimport$8 + end + local.get $9 + i32.const 12 + i32.add + local.tee $11 + i32.load + local.get $4 + i32.ne + if ;; label = @14 + call $fimport$8 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $4 + i32.eq + if ;; label = @14 + block ;; label = @15 + local.get $11 + local.get $0 + i32.store + local.get $1 + local.get $9 + i32.store + local.get $0 + local.set $13 + end + else + call $fimport$8 + end + end + end + end + block $label$118 ;; label = @11 + local.get $10 + if ;; label = @12 + block ;; label = @13 + local.get $4 + local.get $4 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 4480 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @14 + block ;; label = @15 + local.get $0 + local.get $13 + i32.store + local.get $13 + i32.eqz + if ;; label = @16 + block ;; label = @17 + i32.const 4180 + local.get $5 + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $2 + i32.store + br 6 (;@11;) + end + end + end + else + block ;; label = @15 + local.get $10 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @16 + call $fimport$8 + end + local.get $10 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @16 + local.get $0 + local.get $13 + i32.store + else + local.get $10 + local.get $13 + i32.store offset=20 + end + local.get $13 + i32.eqz + if ;; label = @16 + block ;; label = @17 + local.get $5 + local.set $2 + br 6 (;@11;) + end + end + end + end + local.get $13 + i32.const 4192 + i32.load + local.tee $0 + i32.lt_u + if ;; label = @14 + call $fimport$8 + end + local.get $13 + local.get $10 + i32.store offset=24 + local.get $4 + i32.load offset=16 + local.tee $1 + if ;; label = @14 + local.get $1 + local.get $0 + i32.lt_u + if ;; label = @15 + call $fimport$8 + else + block ;; label = @16 + local.get $13 + local.get $1 + i32.store offset=16 + local.get $1 + local.get $13 + i32.store offset=24 + end + end + end + local.get $4 + i32.load offset=20 + local.tee $0 + if ;; label = @14 + local.get $0 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @15 + call $fimport$8 + else + block ;; label = @16 + local.get $13 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $13 + i32.store offset=24 + local.get $5 + local.set $2 + end + end + else + local.get $5 + local.set $2 + end + end + else + local.get $5 + local.set $2 + end + end + block $label$136 ;; label = @11 + local.get $3 + i32.const 16 + i32.lt_u + if ;; label = @12 + block ;; label = @13 + local.get $4 + local.get $3 + local.get $7 + i32.add + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $4 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + else + block ;; label = @13 + local.get $4 + local.get $7 + i32.const 3 + i32.or + i32.store offset=4 + local.get $6 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $3 + i32.add + local.get $3 + i32.store + local.get $3 + i32.const 3 + i32.shr_u + local.set $0 + local.get $3 + i32.const 256 + i32.lt_u + if ;; label = @14 + block ;; label = @15 + local.get $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.set $3 + i32.const 4176 + i32.load + local.tee $1 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @16 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @17 + call $fimport$8 + else + block ;; label = @18 + local.get $1 + local.set $16 + local.get $0 + local.set $8 + end + end + else + block ;; label = @17 + i32.const 4176 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $16 + local.get $3 + local.set $8 + end + end + local.get $16 + local.get $6 + i32.store + local.get $8 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $8 + i32.store offset=8 + local.get $6 + local.get $3 + i32.store offset=12 + br 4 (;@11;) + end + end + local.get $3 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @14 + local.get $3 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @15 + i32.const 31 + else + local.get $3 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $5 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $5 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $5 + i32.const 2 + i32.shl + i32.const 4480 + i32.add + local.set $1 + local.get $6 + local.get $5 + i32.store offset=28 + local.get $6 + i32.const 16 + i32.add + local.tee $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store + local.get $2 + i32.const 1 + local.get $5 + i32.shl + local.tee $0 + i32.and + i32.eqz + if ;; label = @14 + block ;; label = @15 + i32.const 4180 + local.get $2 + local.get $0 + i32.or + i32.store + local.get $1 + local.get $6 + i32.store + local.get $6 + local.get $1 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 4 (;@11;) + end + end + local.get $1 + i32.load + local.set $0 + i32.const 25 + local.get $5 + i32.const 1 + i32.shr_u + i32.sub + local.set $1 + local.get $3 + local.get $5 + i32.const 31 + i32.eq + if (result i32) ;; label = @14 + i32.const 0 + else + local.get $1 + end + i32.shl + local.set $5 + block $label$151 ;; label = @14 + block $label$152 ;; label = @15 + block $label$153 ;; label = @16 + loop $label$154 ;; label = @17 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.eq + br_if 2 (;@15;) + local.get $5 + i32.const 1 + i32.shl + local.set $2 + local.get $0 + i32.const 16 + i32.add + local.get $5 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $5 + i32.load + local.tee $1 + i32.eqz + br_if 1 (;@16;) + local.get $2 + local.set $5 + local.get $1 + local.set $0 + br 0 (;@17;) + end + end + local.get $5 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @16 + call $fimport$8 + else + block ;; label = @17 + local.get $5 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 6 (;@11;) + end + end + br 1 (;@14;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $2 + i32.const 4192 + i32.load + local.tee $1 + i32.ge_u + local.get $0 + local.get $1 + i32.ge_u + i32.and + if ;; label = @15 + block ;; label = @16 + local.get $2 + local.get $6 + i32.store offset=12 + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $2 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $6 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$8 + end + end + end + end + end + local.get $14 + global.set $global$1 + local.get $4 + i32.const 8 + i32.add + return + end + else + local.get $7 + local.set $0 + end + else + local.get $7 + local.set $0 + end + end + else + local.get $7 + local.set $0 + end + end + end + end + end + i32.const 4184 + i32.load + local.tee $1 + local.get $0 + i32.ge_u + if ;; label = @2 + block ;; label = @3 + i32.const 4196 + i32.load + local.set $2 + local.get $1 + local.get $0 + i32.sub + local.tee $3 + i32.const 15 + i32.gt_u + if ;; label = @4 + block ;; label = @5 + i32.const 4196 + local.get $2 + local.get $0 + i32.add + local.tee $1 + i32.store + i32.const 4184 + local.get $3 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $1 + local.get $3 + i32.add + local.get $3 + i32.store + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + end + else + block ;; label = @5 + i32.const 4184 + i32.const 0 + i32.store + i32.const 4196 + i32.const 0 + i32.store + local.get $2 + local.get $1 + i32.const 3 + i32.or + i32.store offset=4 + local.get $2 + local.get $1 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + end + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + i32.const 4188 + i32.load + local.tee $10 + local.get $0 + i32.gt_u + if ;; label = @2 + block ;; label = @3 + i32.const 4188 + local.get $10 + local.get $0 + i32.sub + local.tee $3 + i32.store + i32.const 4200 + i32.const 4200 + i32.load + local.tee $2 + local.get $0 + i32.add + local.tee $1 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + i32.const 4648 + i32.load + if (result i32) ;; label = @2 + i32.const 4656 + i32.load + else + block (result i32) ;; label = @3 + i32.const 4656 + i32.const 4096 + i32.store + i32.const 4652 + i32.const 4096 + i32.store + i32.const 4660 + i32.const -1 + i32.store + i32.const 4664 + i32.const -1 + i32.store + i32.const 4668 + i32.const 0 + i32.store + i32.const 4620 + i32.const 0 + i32.store + local.get $18 + local.get $18 + i32.const -16 + i32.and + i32.const 1431655768 + i32.xor + local.tee $1 + i32.store + i32.const 4648 + local.get $1 + i32.store + i32.const 4096 + end + end + local.tee $1 + local.get $0 + i32.const 47 + i32.add + local.tee $13 + i32.add + local.tee $8 + i32.const 0 + local.get $1 + i32.sub + local.tee $4 + i32.and + local.tee $6 + local.get $0 + i32.le_u + if ;; label = @2 + block ;; label = @3 + local.get $14 + global.set $global$1 + i32.const 0 + return + end + end + i32.const 4616 + i32.load + local.tee $2 + if ;; label = @2 + i32.const 4608 + i32.load + local.tee $3 + local.get $6 + i32.add + local.tee $1 + local.get $3 + i32.le_u + local.get $1 + local.get $2 + i32.gt_u + i32.or + if ;; label = @3 + block ;; label = @4 + local.get $14 + global.set $global$1 + i32.const 0 + return + end + end + end + local.get $0 + i32.const 48 + i32.add + local.set $7 + block $label$171 ;; label = @2 + block $label$172 ;; label = @3 + i32.const 4620 + i32.load + i32.const 4 + i32.and + i32.eqz + if ;; label = @4 + block ;; label = @5 + block $label$174 ;; label = @6 + block $label$175 ;; label = @7 + block $label$176 ;; label = @8 + i32.const 4200 + i32.load + local.tee $3 + i32.eqz + br_if 0 (;@8;) + i32.const 4624 + local.set $2 + loop $label$177 ;; label = @9 + block $label$178 ;; label = @10 + local.get $2 + i32.load + local.tee $1 + local.get $3 + i32.le_u + if ;; label = @11 + local.get $1 + local.get $2 + i32.const 4 + i32.add + local.tee $5 + i32.load + i32.add + local.get $3 + i32.gt_u + br_if 1 (;@10;) + end + local.get $2 + i32.load offset=8 + local.tee $1 + i32.eqz + br_if 2 (;@8;) + local.get $1 + local.set $2 + br 1 (;@9;) + end + end + local.get $8 + local.get $10 + i32.sub + local.get $4 + i32.and + local.tee $3 + i32.const 2147483647 + i32.lt_u + if ;; label = @9 + local.get $3 + call $45 + local.tee $1 + local.get $2 + i32.load + local.get $5 + i32.load + i32.add + i32.eq + if ;; label = @10 + local.get $1 + i32.const -1 + i32.ne + br_if 7 (;@3;) + else + block ;; label = @11 + local.get $1 + local.set $2 + local.get $3 + local.set $1 + br 4 (;@7;) + end + end + end + br 2 (;@6;) + end + i32.const 0 + call $45 + local.tee $1 + i32.const -1 + i32.ne + if ;; label = @8 + block ;; label = @9 + i32.const 4652 + i32.load + local.tee $2 + i32.const -1 + i32.add + local.tee $5 + local.get $1 + local.tee $3 + i32.add + i32.const 0 + local.get $2 + i32.sub + i32.and + local.get $3 + i32.sub + local.set $2 + local.get $5 + local.get $3 + i32.and + if (result i32) ;; label = @10 + local.get $2 + else + i32.const 0 + end + local.get $6 + i32.add + local.tee $3 + i32.const 4608 + i32.load + local.tee $5 + i32.add + local.set $4 + local.get $3 + local.get $0 + i32.gt_u + local.get $3 + i32.const 2147483647 + i32.lt_u + i32.and + if ;; label = @10 + block ;; label = @11 + i32.const 4616 + i32.load + local.tee $2 + if ;; label = @12 + local.get $4 + local.get $5 + i32.le_u + local.get $4 + local.get $2 + i32.gt_u + i32.or + br_if 6 (;@6;) + end + local.get $3 + call $45 + local.tee $2 + local.get $1 + i32.eq + br_if 8 (;@3;) + local.get $3 + local.set $1 + br 4 (;@7;) + end + end + end + end + br 1 (;@6;) + end + i32.const 0 + local.get $1 + i32.sub + local.set $5 + local.get $7 + local.get $1 + i32.gt_u + local.get $1 + i32.const 2147483647 + i32.lt_u + local.get $2 + i32.const -1 + i32.ne + i32.and + i32.and + if ;; label = @7 + local.get $13 + local.get $1 + i32.sub + i32.const 4656 + i32.load + local.tee $3 + i32.add + i32.const 0 + local.get $3 + i32.sub + i32.and + local.tee $3 + i32.const 2147483647 + i32.lt_u + if ;; label = @8 + local.get $3 + call $45 + i32.const -1 + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $5 + call $45 + drop + br 4 (;@6;) + end + else + local.get $3 + local.get $1 + i32.add + local.set $3 + end + else + local.get $1 + local.set $3 + end + else + local.get $1 + local.set $3 + end + local.get $2 + i32.const -1 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $2 + local.set $1 + br 5 (;@3;) + end + end + end + i32.const 4620 + i32.const 4620 + i32.load + i32.const 4 + i32.or + i32.store + end + end + local.get $6 + i32.const 2147483647 + i32.lt_u + if ;; label = @4 + local.get $6 + call $45 + local.tee $1 + i32.const 0 + call $45 + local.tee $3 + i32.lt_u + local.get $1 + i32.const -1 + i32.ne + local.get $3 + i32.const -1 + i32.ne + i32.and + i32.and + if ;; label = @5 + local.get $3 + local.get $1 + i32.sub + local.tee $3 + local.get $0 + i32.const 40 + i32.add + i32.gt_u + br_if 2 (;@3;) + end + end + br 1 (;@2;) + end + i32.const 4608 + i32.const 4608 + i32.load + local.get $3 + i32.add + local.tee $2 + i32.store + local.get $2 + i32.const 4612 + i32.load + i32.gt_u + if ;; label = @3 + i32.const 4612 + local.get $2 + i32.store + end + block $label$198 ;; label = @3 + i32.const 4200 + i32.load + local.tee $8 + if ;; label = @4 + block ;; label = @5 + i32.const 4624 + local.set $2 + block $label$200 ;; label = @6 + block $label$201 ;; label = @7 + loop $label$202 ;; label = @8 + local.get $1 + local.get $2 + i32.load + local.tee $4 + local.get $2 + i32.const 4 + i32.add + local.tee $7 + i32.load + local.tee $5 + i32.add + i32.eq + br_if 1 (;@7;) + local.get $2 + i32.load offset=8 + local.tee $2 + br_if 0 (;@8;) + end + br 1 (;@6;) + end + local.get $2 + i32.load offset=12 + i32.const 8 + i32.and + i32.eqz + if ;; label = @7 + local.get $8 + local.get $1 + i32.lt_u + local.get $8 + local.get $4 + i32.ge_u + i32.and + if ;; label = @8 + block ;; label = @9 + local.get $7 + local.get $5 + local.get $3 + i32.add + i32.store + i32.const 4188 + i32.load + local.set $5 + i32.const 0 + local.get $8 + i32.const 8 + i32.add + local.tee $2 + i32.sub + i32.const 7 + i32.and + local.set $1 + i32.const 4200 + local.get $8 + local.get $2 + i32.const 7 + i32.and + if (result i32) ;; label = @10 + local.get $1 + else + i32.const 0 + local.tee $1 + end + i32.add + local.tee $2 + i32.store + i32.const 4188 + local.get $3 + local.get $1 + i32.sub + local.get $5 + i32.add + local.tee $1 + i32.store + local.get $2 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $1 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 4204 + i32.const 4664 + i32.load + i32.store + br 6 (;@3;) + end + end + end + end + local.get $1 + i32.const 4192 + i32.load + local.tee $2 + i32.lt_u + if ;; label = @6 + block ;; label = @7 + i32.const 4192 + local.get $1 + i32.store + local.get $1 + local.set $2 + end + end + local.get $1 + local.get $3 + i32.add + local.set $10 + i32.const 4624 + local.set $5 + block $label$208 ;; label = @6 + block $label$209 ;; label = @7 + loop $label$210 ;; label = @8 + local.get $5 + i32.load + local.get $10 + i32.eq + br_if 1 (;@7;) + local.get $5 + i32.load offset=8 + local.tee $5 + br_if 0 (;@8;) + i32.const 4624 + local.set $5 + end + br 1 (;@6;) + end + local.get $5 + i32.load offset=12 + i32.const 8 + i32.and + if ;; label = @7 + i32.const 4624 + local.set $5 + else + block ;; label = @8 + local.get $5 + local.get $1 + i32.store + local.get $5 + i32.const 4 + i32.add + local.tee $5 + local.get $5 + i32.load + local.get $3 + i32.add + i32.store + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $4 + i32.sub + i32.const 7 + i32.and + local.set $7 + i32.const 0 + local.get $10 + i32.const 8 + i32.add + local.tee $5 + i32.sub + i32.const 7 + i32.and + local.set $3 + local.get $1 + local.get $4 + i32.const 7 + i32.and + if (result i32) ;; label = @9 + local.get $7 + else + i32.const 0 + end + i32.add + local.tee $13 + local.get $0 + i32.add + local.set $6 + local.get $10 + local.get $5 + i32.const 7 + i32.and + if (result i32) ;; label = @9 + local.get $3 + else + i32.const 0 + end + i32.add + local.tee $4 + local.get $13 + i32.sub + local.get $0 + i32.sub + local.set $7 + local.get $13 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + block $label$217 ;; label = @9 + local.get $4 + local.get $8 + i32.eq + if ;; label = @10 + block ;; label = @11 + i32.const 4188 + i32.const 4188 + i32.load + local.get $7 + i32.add + local.tee $0 + i32.store + i32.const 4200 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + end + else + block ;; label = @11 + local.get $4 + i32.const 4196 + i32.load + i32.eq + if ;; label = @12 + block ;; label = @13 + i32.const 4184 + i32.const 4184 + i32.load + local.get $7 + i32.add + local.tee $0 + i32.store + i32.const 4196 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $0 + i32.add + local.get $0 + i32.store + br 4 (;@9;) + end + end + local.get $4 + i32.load offset=4 + local.tee $0 + i32.const 3 + i32.and + i32.const 1 + i32.eq + if (result i32) ;; label = @12 + block (result i32) ;; label = @13 + local.get $0 + i32.const -8 + i32.and + local.set $11 + local.get $0 + i32.const 3 + i32.shr_u + local.set $1 + block $label$222 ;; label = @14 + local.get $0 + i32.const 256 + i32.lt_u + if ;; label = @15 + block ;; label = @16 + local.get $4 + i32.load offset=12 + local.set $5 + block $label$224 ;; label = @17 + local.get $4 + i32.load offset=8 + local.tee $3 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.tee $0 + i32.ne + if ;; label = @18 + block ;; label = @19 + local.get $3 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$8 + end + local.get $3 + i32.load offset=12 + local.get $4 + i32.eq + br_if 2 (;@17;) + call $fimport$8 + end + end + end + local.get $5 + local.get $3 + i32.eq + if ;; label = @17 + block ;; label = @18 + i32.const 4176 + i32.const 4176 + i32.load + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 4 (;@14;) + end + end + block $label$228 ;; label = @17 + local.get $5 + local.get $0 + i32.eq + if ;; label = @18 + local.get $5 + i32.const 8 + i32.add + local.set $20 + else + block ;; label = @19 + local.get $5 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$8 + end + local.get $5 + i32.const 8 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + block ;; label = @21 + local.get $0 + local.set $20 + br 4 (;@17;) + end + end + call $fimport$8 + end + end + end + local.get $3 + local.get $5 + i32.store offset=12 + local.get $20 + local.get $3 + i32.store + end + else + block ;; label = @16 + local.get $4 + i32.load offset=24 + local.set $8 + block $label$234 ;; label = @17 + local.get $4 + i32.load offset=12 + local.tee $0 + local.get $4 + i32.eq + if ;; label = @18 + block ;; label = @19 + local.get $4 + i32.const 16 + i32.add + local.tee $3 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @20 + local.get $3 + i32.load + local.tee $0 + if ;; label = @21 + local.get $3 + local.set $1 + else + block ;; label = @22 + i32.const 0 + local.set $12 + br 5 (;@17;) + end + end + end + loop $label$239 ;; label = @20 + local.get $0 + i32.const 20 + i32.add + local.tee $5 + i32.load + local.tee $3 + if ;; label = @21 + block ;; label = @22 + local.get $3 + local.set $0 + local.get $5 + local.set $1 + br 2 (;@20;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $5 + i32.load + local.tee $3 + if ;; label = @21 + block ;; label = @22 + local.get $3 + local.set $0 + local.get $5 + local.set $1 + br 2 (;@20;) + end + end + end + local.get $1 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$8 + else + block ;; label = @21 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $12 + end + end + end + else + block ;; label = @19 + local.get $4 + i32.load offset=8 + local.tee $5 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$8 + end + local.get $5 + i32.const 12 + i32.add + local.tee $3 + i32.load + local.get $4 + i32.ne + if ;; label = @20 + call $fimport$8 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + block ;; label = @21 + local.get $3 + local.get $0 + i32.store + local.get $1 + local.get $5 + i32.store + local.get $0 + local.set $12 + end + else + call $fimport$8 + end + end + end + end + local.get $8 + i32.eqz + br_if 2 (;@14;) + block $label$249 ;; label = @17 + local.get $4 + local.get $4 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 4480 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @18 + block ;; label = @19 + local.get $0 + local.get $12 + i32.store + local.get $12 + br_if 2 (;@17;) + i32.const 4180 + i32.const 4180 + i32.load + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 5 (;@14;) + end + else + block ;; label = @19 + local.get $8 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @20 + call $fimport$8 + end + local.get $8 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + local.get $0 + local.get $12 + i32.store + else + local.get $8 + local.get $12 + i32.store offset=20 + end + local.get $12 + i32.eqz + br_if 5 (;@14;) + end + end + end + local.get $12 + i32.const 4192 + i32.load + local.tee $1 + i32.lt_u + if ;; label = @17 + call $fimport$8 + end + local.get $12 + local.get $8 + i32.store offset=24 + local.get $4 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.tee $3 + if ;; label = @17 + local.get $3 + local.get $1 + i32.lt_u + if ;; label = @18 + call $fimport$8 + else + block ;; label = @19 + local.get $12 + local.get $3 + i32.store offset=16 + local.get $3 + local.get $12 + i32.store offset=24 + end + end + end + local.get $0 + i32.load offset=4 + local.tee $0 + i32.eqz + br_if 2 (;@14;) + local.get $0 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @17 + call $fimport$8 + else + block ;; label = @18 + local.get $12 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $12 + i32.store offset=24 + end + end + end + end + end + local.get $11 + local.get $7 + i32.add + local.set $7 + local.get $4 + local.get $11 + i32.add + end + else + local.get $4 + end + local.tee $0 + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const -2 + i32.and + i32.store + local.get $6 + local.get $7 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $7 + i32.add + local.get $7 + i32.store + local.get $7 + i32.const 3 + i32.shr_u + local.set $0 + local.get $7 + i32.const 256 + i32.lt_u + if ;; label = @12 + block ;; label = @13 + local.get $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.set $3 + block $label$263 ;; label = @14 + i32.const 4176 + i32.load + local.tee $1 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @15 + block ;; label = @16 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 4192 + i32.load + i32.ge_u + if ;; label = @17 + block ;; label = @18 + local.get $1 + local.set $21 + local.get $0 + local.set $9 + br 4 (;@14;) + end + end + call $fimport$8 + end + else + block ;; label = @16 + i32.const 4176 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $21 + local.get $3 + local.set $9 + end + end + end + local.get $21 + local.get $6 + i32.store + local.get $9 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $9 + i32.store offset=8 + local.get $6 + local.get $3 + i32.store offset=12 + br 4 (;@9;) + end + end + block $label$267 (result i32) ;; label = @12 + local.get $7 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @13 + block (result i32) ;; label = @14 + i32.const 31 + local.get $7 + i32.const 16777215 + i32.gt_u + br_if 2 (;@12;) + drop + local.get $7 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $3 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $3 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + end + local.tee $2 + i32.const 2 + i32.shl + i32.const 4480 + i32.add + local.set $3 + local.get $6 + local.get $2 + i32.store offset=28 + local.get $6 + i32.const 16 + i32.add + local.tee $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store + i32.const 4180 + i32.load + local.tee $1 + i32.const 1 + local.get $2 + i32.shl + local.tee $0 + i32.and + i32.eqz + if ;; label = @12 + block ;; label = @13 + i32.const 4180 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $3 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 4 (;@9;) + end + end + local.get $3 + i32.load + local.set $0 + i32.const 25 + local.get $2 + i32.const 1 + i32.shr_u + i32.sub + local.set $1 + local.get $7 + local.get $2 + i32.const 31 + i32.eq + if (result i32) ;; label = @12 + i32.const 0 + else + local.get $1 + end + i32.shl + local.set $2 + block $label$273 ;; label = @12 + block $label$274 ;; label = @13 + block $label$275 ;; label = @14 + loop $label$276 ;; label = @15 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.eq + br_if 2 (;@13;) + local.get $2 + i32.const 1 + i32.shl + local.set $3 + local.get $0 + i32.const 16 + i32.add + local.get $2 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $2 + i32.load + local.tee $1 + i32.eqz + br_if 1 (;@14;) + local.get $3 + local.set $2 + local.get $1 + local.set $0 + br 0 (;@15;) + end + end + local.get $2 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$8 + else + block ;; label = @15 + local.get $2 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 6 (;@9;) + end + end + br 1 (;@12;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $2 + i32.const 4192 + i32.load + local.tee $1 + i32.ge_u + local.get $0 + local.get $1 + i32.ge_u + i32.and + if ;; label = @13 + block ;; label = @14 + local.get $2 + local.get $6 + i32.store offset=12 + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $2 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $6 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$8 + end + end + end + end + end + local.get $14 + global.set $global$1 + local.get $13 + i32.const 8 + i32.add + return + end + end + end + loop $label$281 ;; label = @6 + block $label$282 ;; label = @7 + local.get $5 + i32.load + local.tee $2 + local.get $8 + i32.le_u + if ;; label = @8 + local.get $2 + local.get $5 + i32.load offset=4 + i32.add + local.tee $13 + local.get $8 + i32.gt_u + br_if 1 (;@7;) + end + local.get $5 + i32.load offset=8 + local.set $5 + br 1 (;@6;) + end + end + i32.const 0 + local.get $13 + i32.const -47 + i32.add + local.tee $7 + i32.const 8 + i32.add + local.tee $5 + i32.sub + i32.const 7 + i32.and + local.set $2 + local.get $7 + local.get $5 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $2 + else + i32.const 0 + end + i32.add + local.tee $2 + local.get $8 + i32.const 16 + i32.add + local.tee $12 + i32.lt_u + if (result i32) ;; label = @6 + local.get $8 + else + local.get $2 + end + local.tee $7 + i32.const 8 + i32.add + local.set $10 + local.get $7 + i32.const 24 + i32.add + local.set $5 + local.get $3 + i32.const -40 + i32.add + local.set $9 + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $4 + i32.sub + i32.const 7 + i32.and + local.set $2 + i32.const 4200 + local.get $1 + local.get $4 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $2 + else + i32.const 0 + local.tee $2 + end + i32.add + local.tee $4 + i32.store + i32.const 4188 + local.get $9 + local.get $2 + i32.sub + local.tee $2 + i32.store + local.get $4 + local.get $2 + i32.const 1 + i32.or + i32.store offset=4 + local.get $4 + local.get $2 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 4204 + i32.const 4664 + i32.load + i32.store + local.get $7 + i32.const 4 + i32.add + local.tee $2 + i32.const 27 + i32.store + local.get $10 + i32.const 4624 + i64.load align=4 + i64.store align=4 + local.get $10 + i32.const 4632 + i64.load align=4 + i64.store offset=8 align=4 + i32.const 4624 + local.get $1 + i32.store + i32.const 4628 + local.get $3 + i32.store + i32.const 4636 + i32.const 0 + i32.store + i32.const 4632 + local.get $10 + i32.store + local.get $5 + local.set $1 + loop $label$290 ;; label = @6 + local.get $1 + i32.const 4 + i32.add + local.tee $1 + i32.const 7 + i32.store + local.get $1 + i32.const 4 + i32.add + local.get $13 + i32.lt_u + br_if 0 (;@6;) + end + local.get $7 + local.get $8 + i32.ne + if ;; label = @6 + block ;; label = @7 + local.get $2 + local.get $2 + i32.load + i32.const -2 + i32.and + i32.store + local.get $8 + local.get $7 + local.get $8 + i32.sub + local.tee $4 + i32.const 1 + i32.or + i32.store offset=4 + local.get $7 + local.get $4 + i32.store + local.get $4 + i32.const 3 + i32.shr_u + local.set $1 + local.get $4 + i32.const 256 + i32.lt_u + if ;; label = @8 + block ;; label = @9 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.set $2 + i32.const 4176 + i32.load + local.tee $3 + i32.const 1 + local.get $1 + i32.shl + local.tee $1 + i32.and + if ;; label = @10 + local.get $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $1 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$8 + else + block ;; label = @12 + local.get $3 + local.set $15 + local.get $1 + local.set $11 + end + end + else + block ;; label = @11 + i32.const 4176 + local.get $3 + local.get $1 + i32.or + i32.store + local.get $2 + i32.const 8 + i32.add + local.set $15 + local.get $2 + local.set $11 + end + end + local.get $15 + local.get $8 + i32.store + local.get $11 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $11 + i32.store offset=8 + local.get $8 + local.get $2 + i32.store offset=12 + br 6 (;@3;) + end + end + local.get $4 + i32.const 8 + i32.shr_u + local.tee $1 + if (result i32) ;; label = @8 + local.get $4 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @9 + i32.const 31 + else + local.get $4 + i32.const 14 + local.get $1 + local.get $1 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $2 + i32.shl + local.tee $3 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $1 + local.get $2 + i32.or + local.get $3 + local.get $1 + i32.shl + local.tee $3 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $1 + i32.or + i32.sub + local.get $3 + local.get $1 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $1 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $1 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $5 + i32.const 2 + i32.shl + i32.const 4480 + i32.add + local.set $2 + local.get $8 + local.get $5 + i32.store offset=28 + local.get $8 + i32.const 0 + i32.store offset=20 + local.get $12 + i32.const 0 + i32.store + i32.const 4180 + i32.load + local.tee $3 + i32.const 1 + local.get $5 + i32.shl + local.tee $1 + i32.and + i32.eqz + if ;; label = @8 + block ;; label = @9 + i32.const 4180 + local.get $3 + local.get $1 + i32.or + i32.store + local.get $2 + local.get $8 + i32.store + local.get $8 + local.get $2 + i32.store offset=24 + local.get $8 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $8 + i32.store offset=8 + br 6 (;@3;) + end + end + local.get $2 + i32.load + local.set $1 + i32.const 25 + local.get $5 + i32.const 1 + i32.shr_u + i32.sub + local.set $3 + local.get $4 + local.get $5 + i32.const 31 + i32.eq + if (result i32) ;; label = @8 + i32.const 0 + else + local.get $3 + end + i32.shl + local.set $5 + block $label$304 ;; label = @8 + block $label$305 ;; label = @9 + block $label$306 ;; label = @10 + loop $label$307 ;; label = @11 + local.get $1 + i32.load offset=4 + i32.const -8 + i32.and + local.get $4 + i32.eq + br_if 2 (;@9;) + local.get $5 + i32.const 1 + i32.shl + local.set $2 + local.get $1 + i32.const 16 + i32.add + local.get $5 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $5 + i32.load + local.tee $3 + i32.eqz + br_if 1 (;@10;) + local.get $2 + local.set $5 + local.get $3 + local.set $1 + br 0 (;@11;) + end + end + local.get $5 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$8 + else + block ;; label = @11 + local.get $5 + local.get $8 + i32.store + local.get $8 + local.get $1 + i32.store offset=24 + local.get $8 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $8 + i32.store offset=8 + br 8 (;@3;) + end + end + br 1 (;@8;) + end + local.get $1 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $5 + i32.const 4192 + i32.load + local.tee $3 + i32.ge_u + local.get $1 + local.get $3 + i32.ge_u + i32.and + if ;; label = @9 + block ;; label = @10 + local.get $5 + local.get $8 + i32.store offset=12 + local.get $2 + local.get $8 + i32.store + local.get $8 + local.get $5 + i32.store offset=8 + local.get $8 + local.get $1 + i32.store offset=12 + local.get $8 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$8 + end + end + end + end + end + else + block ;; label = @5 + i32.const 4192 + i32.load + local.tee $2 + i32.eqz + local.get $1 + local.get $2 + i32.lt_u + i32.or + if ;; label = @6 + i32.const 4192 + local.get $1 + i32.store + end + i32.const 4624 + local.get $1 + i32.store + i32.const 4628 + local.get $3 + i32.store + i32.const 4636 + i32.const 0 + i32.store + i32.const 4212 + i32.const 4648 + i32.load + i32.store + i32.const 4208 + i32.const -1 + i32.store + i32.const 0 + local.set $2 + loop $label$314 ;; label = @6 + local.get $2 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.tee $5 + local.get $5 + i32.store offset=12 + local.get $5 + local.get $5 + i32.store offset=8 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 32 + i32.ne + br_if 0 (;@6;) + end + local.get $3 + i32.const -40 + i32.add + local.set $5 + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $2 + i32.sub + i32.const 7 + i32.and + local.set $3 + i32.const 4200 + local.get $1 + local.get $2 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $3 + else + i32.const 0 + end + local.tee $1 + i32.add + local.tee $3 + i32.store + i32.const 4188 + local.get $5 + local.get $1 + i32.sub + local.tee $1 + i32.store + local.get $3 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $1 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 4204 + i32.const 4664 + i32.load + i32.store + end + end + end + i32.const 4188 + i32.load + local.tee $1 + local.get $0 + i32.gt_u + if ;; label = @3 + block ;; label = @4 + i32.const 4188 + local.get $1 + local.get $0 + i32.sub + local.tee $3 + i32.store + i32.const 4200 + i32.const 4200 + i32.load + local.tee $2 + local.get $0 + i32.add + local.tee $1 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + end + call $12 + i32.const 12 + i32.store + local.get $14 + global.set $global$1 + i32.const 0 + end ) - ) - (func $43 (; 56 ;) (type $4) (result i32) - (local $0 i32) - (block $label$1 (result i32) - (i32.store - (i32.const 4672) - (i32.add - (local.tee $0 - (i32.load - (i32.const 4672) - ) - ) - (i32.const 0) - ) - ) - (local.get $0) + (func $38 (;51;) (type $3) (param $0 i32) + (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) + block $label$1 ;; label = @1 + local.get $0 + i32.eqz + if ;; label = @2 + return + end + local.get $0 + i32.const -8 + i32.add + local.tee $1 + i32.const 4192 + i32.load + local.tee $11 + i32.lt_u + if ;; label = @2 + call $fimport$8 + end + local.get $0 + i32.const -4 + i32.add + i32.load + local.tee $0 + i32.const 3 + i32.and + local.tee $8 + i32.const 1 + i32.eq + if ;; label = @2 + call $fimport$8 + end + local.get $1 + local.get $0 + i32.const -8 + i32.and + local.tee $4 + i32.add + local.set $6 + block $label$5 ;; label = @2 + local.get $0 + i32.const 1 + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $1 + local.set $3 + local.get $4 + local.set $2 + end + else + block ;; label = @4 + local.get $8 + i32.eqz + if ;; label = @5 + return + end + local.get $1 + i32.const 0 + local.get $1 + i32.load + local.tee $8 + i32.sub + i32.add + local.tee $0 + local.get $11 + i32.lt_u + if ;; label = @5 + call $fimport$8 + end + local.get $8 + local.get $4 + i32.add + local.set $1 + local.get $0 + i32.const 4196 + i32.load + i32.eq + if ;; label = @5 + block ;; label = @6 + local.get $6 + i32.const 4 + i32.add + local.tee $2 + i32.load + local.tee $3 + i32.const 3 + i32.and + i32.const 3 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 6 (;@2;) + end + end + i32.const 4184 + local.get $1 + i32.store + local.get $2 + local.get $3 + i32.const -2 + i32.and + i32.store + local.get $0 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $0 + local.get $1 + i32.add + local.get $1 + i32.store + return + end + end + local.get $8 + i32.const 3 + i32.shr_u + local.set $10 + local.get $8 + i32.const 256 + i32.lt_u + if ;; label = @5 + block ;; label = @6 + local.get $0 + i32.load offset=12 + local.set $3 + local.get $0 + i32.load offset=8 + local.tee $4 + local.get $10 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.tee $2 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $4 + local.get $11 + i32.lt_u + if ;; label = @9 + call $fimport$8 + end + local.get $4 + i32.load offset=12 + local.get $0 + i32.ne + if ;; label = @9 + call $fimport$8 + end + end + end + local.get $3 + local.get $4 + i32.eq + if ;; label = @7 + block ;; label = @8 + i32.const 4176 + i32.const 4176 + i32.load + i32.const 1 + local.get $10 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 6 (;@2;) + end + end + local.get $3 + local.get $2 + i32.eq + if ;; label = @7 + local.get $3 + i32.const 8 + i32.add + local.set $5 + else + block ;; label = @8 + local.get $3 + local.get $11 + i32.lt_u + if ;; label = @9 + call $fimport$8 + end + local.get $3 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.get $0 + i32.eq + if ;; label = @9 + local.get $2 + local.set $5 + else + call $fimport$8 + end + end + end + local.get $4 + local.get $3 + i32.store offset=12 + local.get $5 + local.get $4 + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 4 (;@2;) + end + end + local.get $0 + i32.load offset=24 + local.set $12 + block $label$22 ;; label = @5 + local.get $0 + i32.load offset=12 + local.tee $4 + local.get $0 + i32.eq + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 16 + i32.add + local.tee $5 + i32.const 4 + i32.add + local.tee $8 + i32.load + local.tee $4 + if ;; label = @8 + local.get $8 + local.set $5 + else + local.get $5 + i32.load + local.tee $4 + i32.eqz + if ;; label = @9 + block ;; label = @10 + i32.const 0 + local.set $7 + br 5 (;@5;) + end + end + end + loop $label$27 ;; label = @8 + local.get $4 + i32.const 20 + i32.add + local.tee $8 + i32.load + local.tee $10 + if ;; label = @9 + block ;; label = @10 + local.get $10 + local.set $4 + local.get $8 + local.set $5 + br 2 (;@8;) + end + end + local.get $4 + i32.const 16 + i32.add + local.tee $8 + i32.load + local.tee $10 + if ;; label = @9 + block ;; label = @10 + local.get $10 + local.set $4 + local.get $8 + local.set $5 + br 2 (;@8;) + end + end + end + local.get $5 + local.get $11 + i32.lt_u + if ;; label = @8 + call $fimport$8 + else + block ;; label = @9 + local.get $5 + i32.const 0 + i32.store + local.get $4 + local.set $7 + end + end + end + else + block ;; label = @7 + local.get $0 + i32.load offset=8 + local.tee $5 + local.get $11 + i32.lt_u + if ;; label = @8 + call $fimport$8 + end + local.get $5 + i32.const 12 + i32.add + local.tee $8 + i32.load + local.get $0 + i32.ne + if ;; label = @8 + call $fimport$8 + end + local.get $4 + i32.const 8 + i32.add + local.tee $10 + i32.load + local.get $0 + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $8 + local.get $4 + i32.store + local.get $10 + local.get $5 + i32.store + local.get $4 + local.set $7 + end + else + call $fimport$8 + end + end + end + end + local.get $12 + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $0 + i32.load offset=28 + local.tee $4 + i32.const 2 + i32.shl + i32.const 4480 + i32.add + local.tee $5 + i32.load + i32.eq + if ;; label = @7 + block ;; label = @8 + local.get $5 + local.get $7 + i32.store + local.get $7 + i32.eqz + if ;; label = @9 + block ;; label = @10 + i32.const 4180 + i32.const 4180 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 8 (;@2;) + end + end + end + else + block ;; label = @8 + local.get $12 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$8 + end + local.get $12 + i32.const 16 + i32.add + local.tee $4 + i32.load + local.get $0 + i32.eq + if ;; label = @9 + local.get $4 + local.get $7 + i32.store + else + local.get $12 + local.get $7 + i32.store offset=20 + end + local.get $7 + i32.eqz + if ;; label = @9 + block ;; label = @10 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 8 (;@2;) + end + end + end + end + local.get $7 + i32.const 4192 + i32.load + local.tee $5 + i32.lt_u + if ;; label = @7 + call $fimport$8 + end + local.get $7 + local.get $12 + i32.store offset=24 + local.get $0 + i32.const 16 + i32.add + local.tee $8 + i32.load + local.tee $4 + if ;; label = @7 + local.get $4 + local.get $5 + i32.lt_u + if ;; label = @8 + call $fimport$8 + else + block ;; label = @9 + local.get $7 + local.get $4 + i32.store offset=16 + local.get $4 + local.get $7 + i32.store offset=24 + end + end + end + local.get $8 + i32.load offset=4 + local.tee $4 + if ;; label = @7 + local.get $4 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @8 + call $fimport$8 + else + block ;; label = @9 + local.get $7 + local.get $4 + i32.store offset=20 + local.get $4 + local.get $7 + i32.store offset=24 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + else + block ;; label = @8 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + end + else + block ;; label = @6 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + end + end + end + local.get $3 + local.get $6 + i32.ge_u + if ;; label = @2 + call $fimport$8 + end + local.get $6 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 1 + i32.and + i32.eqz + if ;; label = @2 + call $fimport$8 + end + local.get $0 + i32.const 2 + i32.and + if ;; label = @2 + block ;; label = @3 + local.get $1 + local.get $0 + i32.const -2 + i32.and + i32.store + local.get $3 + local.get $2 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $2 + i32.add + local.get $2 + i32.store + end + else + block ;; label = @3 + local.get $6 + i32.const 4200 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 4188 + i32.const 4188 + i32.load + local.get $2 + i32.add + local.tee $0 + i32.store + i32.const 4200 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + i32.const 4196 + i32.load + i32.ne + if ;; label = @6 + return + end + i32.const 4196 + i32.const 0 + i32.store + i32.const 4184 + i32.const 0 + i32.store + return + end + end + local.get $6 + i32.const 4196 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 4184 + i32.const 4184 + i32.load + local.get $2 + i32.add + local.tee $0 + i32.store + i32.const 4196 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $0 + i32.add + local.get $0 + i32.store + return + end + end + local.get $0 + i32.const -8 + i32.and + local.get $2 + i32.add + local.set $5 + local.get $0 + i32.const 3 + i32.shr_u + local.set $4 + block $label$61 ;; label = @4 + local.get $0 + i32.const 256 + i32.lt_u + if ;; label = @5 + block ;; label = @6 + local.get $6 + i32.load offset=12 + local.set $2 + local.get $6 + i32.load offset=8 + local.tee $1 + local.get $4 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.tee $0 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $1 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$8 + end + local.get $1 + i32.load offset=12 + local.get $6 + i32.ne + if ;; label = @9 + call $fimport$8 + end + end + end + local.get $2 + local.get $1 + i32.eq + if ;; label = @7 + block ;; label = @8 + i32.const 4176 + i32.const 4176 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 4 (;@4;) + end + end + local.get $2 + local.get $0 + i32.eq + if ;; label = @7 + local.get $2 + i32.const 8 + i32.add + local.set $14 + else + block ;; label = @8 + local.get $2 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$8 + end + local.get $2 + i32.const 8 + i32.add + local.tee $0 + i32.load + local.get $6 + i32.eq + if ;; label = @9 + local.get $0 + local.set $14 + else + call $fimport$8 + end + end + end + local.get $1 + local.get $2 + i32.store offset=12 + local.get $14 + local.get $1 + i32.store + end + else + block ;; label = @6 + local.get $6 + i32.load offset=24 + local.set $7 + block $label$73 ;; label = @7 + local.get $6 + i32.load offset=12 + local.tee $0 + local.get $6 + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $6 + i32.const 16 + i32.add + local.tee $2 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + if ;; label = @10 + local.get $1 + local.set $2 + else + local.get $2 + i32.load + local.tee $0 + i32.eqz + if ;; label = @11 + block ;; label = @12 + i32.const 0 + local.set $9 + br 5 (;@7;) + end + end + end + loop $label$78 ;; label = @10 + local.get $0 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $4 + if ;; label = @11 + block ;; label = @12 + local.get $4 + local.set $0 + local.get $1 + local.set $2 + br 2 (;@10;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $4 + if ;; label = @11 + block ;; label = @12 + local.get $4 + local.set $0 + local.get $1 + local.set $2 + br 2 (;@10;) + end + end + end + local.get $2 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$8 + else + block ;; label = @11 + local.get $2 + i32.const 0 + i32.store + local.get $0 + local.set $9 + end + end + end + else + block ;; label = @9 + local.get $6 + i32.load offset=8 + local.tee $2 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$8 + end + local.get $2 + i32.const 12 + i32.add + local.tee $1 + i32.load + local.get $6 + i32.ne + if ;; label = @10 + call $fimport$8 + end + local.get $0 + i32.const 8 + i32.add + local.tee $4 + i32.load + local.get $6 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $1 + local.get $0 + i32.store + local.get $4 + local.get $2 + i32.store + local.get $0 + local.set $9 + end + else + call $fimport$8 + end + end + end + end + local.get $7 + if ;; label = @7 + block ;; label = @8 + local.get $6 + local.get $6 + i32.load offset=28 + local.tee $0 + i32.const 2 + i32.shl + i32.const 4480 + i32.add + local.tee $2 + i32.load + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $2 + local.get $9 + i32.store + local.get $9 + i32.eqz + if ;; label = @11 + block ;; label = @12 + i32.const 4180 + i32.const 4180 + i32.load + i32.const 1 + local.get $0 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 8 (;@4;) + end + end + end + else + block ;; label = @10 + local.get $7 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$8 + end + local.get $7 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $6 + i32.eq + if ;; label = @11 + local.get $0 + local.get $9 + i32.store + else + local.get $7 + local.get $9 + i32.store offset=20 + end + local.get $9 + i32.eqz + br_if 6 (;@4;) + end + end + local.get $9 + i32.const 4192 + i32.load + local.tee $2 + i32.lt_u + if ;; label = @9 + call $fimport$8 + end + local.get $9 + local.get $7 + i32.store offset=24 + local.get $6 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + if ;; label = @9 + local.get $0 + local.get $2 + i32.lt_u + if ;; label = @10 + call $fimport$8 + else + block ;; label = @11 + local.get $9 + local.get $0 + i32.store offset=16 + local.get $0 + local.get $9 + i32.store offset=24 + end + end + end + local.get $1 + i32.load offset=4 + local.tee $0 + if ;; label = @9 + local.get $0 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$8 + else + block ;; label = @11 + local.get $9 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $9 + i32.store offset=24 + end + end + end + end + end + end + end + end + local.get $3 + local.get $5 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $5 + i32.add + local.get $5 + i32.store + local.get $3 + i32.const 4196 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 4184 + local.get $5 + i32.store + return + end + else + local.get $5 + local.set $2 + end + end + end + local.get $2 + i32.const 3 + i32.shr_u + local.set $1 + local.get $2 + i32.const 256 + i32.lt_u + if ;; label = @2 + block ;; label = @3 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 4216 + i32.add + local.set $0 + i32.const 4176 + i32.load + local.tee $2 + i32.const 1 + local.get $1 + i32.shl + local.tee $1 + i32.and + if ;; label = @4 + local.get $0 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $1 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @5 + call $fimport$8 + else + block ;; label = @6 + local.get $2 + local.set $15 + local.get $1 + local.set $13 + end + end + else + block ;; label = @5 + i32.const 4176 + local.get $2 + local.get $1 + i32.or + i32.store + local.get $0 + i32.const 8 + i32.add + local.set $15 + local.get $0 + local.set $13 + end + end + local.get $15 + local.get $3 + i32.store + local.get $13 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $13 + i32.store offset=8 + local.get $3 + local.get $0 + i32.store offset=12 + return + end + end + local.get $2 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @2 + local.get $2 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @3 + i32.const 31 + else + local.get $2 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $4 + local.get $0 + i32.or + local.get $1 + local.get $4 + i32.shl + local.tee $0 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $1 + i32.or + i32.sub + local.get $0 + local.get $1 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $1 + i32.const 2 + i32.shl + i32.const 4480 + i32.add + local.set $0 + local.get $3 + local.get $1 + i32.store offset=28 + local.get $3 + i32.const 0 + i32.store offset=20 + local.get $3 + i32.const 0 + i32.store offset=16 + block $label$113 ;; label = @2 + i32.const 4180 + i32.load + local.tee $4 + i32.const 1 + local.get $1 + i32.shl + local.tee $5 + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $0 + i32.load + local.set $0 + i32.const 25 + local.get $1 + i32.const 1 + i32.shr_u + i32.sub + local.set $4 + local.get $2 + local.get $1 + i32.const 31 + i32.eq + if (result i32) ;; label = @5 + i32.const 0 + else + local.get $4 + end + i32.shl + local.set $1 + block $label$117 ;; label = @5 + block $label$118 ;; label = @6 + block $label$119 ;; label = @7 + loop $label$120 ;; label = @8 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $2 + i32.eq + br_if 2 (;@6;) + local.get $1 + i32.const 1 + i32.shl + local.set $4 + local.get $0 + i32.const 16 + i32.add + local.get $1 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $1 + i32.load + local.tee $5 + i32.eqz + br_if 1 (;@7;) + local.get $4 + local.set $1 + local.get $5 + local.set $0 + br 0 (;@8;) + end + end + local.get $1 + i32.const 4192 + i32.load + i32.lt_u + if ;; label = @7 + call $fimport$8 + else + block ;; label = @8 + local.get $1 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.store offset=24 + local.get $3 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $3 + i32.store offset=8 + br 6 (;@2;) + end + end + br 1 (;@5;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $2 + i32.const 4192 + i32.load + local.tee $4 + i32.ge_u + local.get $0 + local.get $4 + i32.ge_u + i32.and + if ;; label = @6 + block ;; label = @7 + local.get $2 + local.get $3 + i32.store offset=12 + local.get $1 + local.get $3 + i32.store + local.get $3 + local.get $2 + i32.store offset=8 + local.get $3 + local.get $0 + i32.store offset=12 + local.get $3 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$8 + end + end + end + else + block ;; label = @4 + i32.const 4180 + local.get $4 + local.get $5 + i32.or + i32.store + local.get $0 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.store offset=24 + local.get $3 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $3 + i32.store offset=8 + end + end + end + i32.const 4208 + i32.const 4208 + i32.load + i32.const -1 + i32.add + local.tee $0 + i32.store + local.get $0 + if ;; label = @2 + return + else + i32.const 4632 + local.set $0 + end + loop $label$128 ;; label = @2 + local.get $0 + i32.load + local.tee $2 + i32.const 8 + i32.add + local.set $0 + local.get $2 + br_if 0 (;@2;) + end + i32.const 4208 + i32.const -1 + i32.store + end ) - ) - (func $44 (; 57 ;) (type $1) - (nop) - ) - (func $45 (; 58 ;) (type $2) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.add - (local.tee $2 - (i32.load - (global.get $global$0) - ) - ) - (local.tee $0 - (i32.and - (i32.add - (local.get $0) - (i32.const 15) - ) - (i32.const -16) - ) - ) - ) - ) - (if - (i32.or - (i32.and - (i32.gt_s - (local.get $0) - (i32.const 0) - ) - (i32.lt_s - (local.get $1) - (local.get $2) - ) - ) - (i32.lt_s - (local.get $1) - (i32.const 0) - ) - ) - (block - (drop - (call $fimport$6) - ) - (call $fimport$11 - (i32.const 12) - ) - (return - (i32.const -1) - ) - ) - ) - (i32.store - (global.get $global$0) - (local.get $1) - ) - (if - (i32.gt_s - (local.get $1) - (call $fimport$5) - ) - (if - (i32.eqz - (call $fimport$4) - ) - (block - (call $fimport$11 - (i32.const 12) - ) - (i32.store - (global.get $global$0) - (local.get $2) - ) - (return - (i32.const -1) - ) - ) - ) - ) - (local.get $2) + (func $39 (;52;) (type $2) (param $0 i32) (result i32) + (local $1 i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + i32.eqz + if ;; label = @2 + i32.const 1 + local.set $0 + end + loop $label$3 ;; label = @2 + block $label$4 ;; label = @3 + local.get $0 + call $37 + local.tee $1 + if ;; label = @4 + block ;; label = @5 + local.get $1 + local.set $0 + br 2 (;@3;) + end + end + call $43 + local.tee $1 + if ;; label = @4 + block ;; label = @5 + local.get $1 + i32.const 0 + i32.and + i32.const 8 + i32.add + call_indirect (type $1) + br 3 (;@2;) + end + else + i32.const 0 + local.set $0 + end + end + end + local.get $0 + end ) - ) - (func $46 (; 59 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $4 - (i32.add - (local.get $0) - (local.get $2) - ) - ) - (if - (i32.ge_s - (local.get $2) - (i32.const 20) - ) - (block - (local.set $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (if - (local.tee $3 - (i32.and - (local.get $0) - (i32.const 3) - ) - ) - (block - (local.set $3 - (i32.sub - (i32.add - (local.get $0) - (i32.const 4) - ) - (local.get $3) - ) - ) - (loop $label$4 - (if - (i32.lt_s - (local.get $0) - (local.get $3) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (br $label$4) - ) - ) - ) - ) - ) - (local.set $3 - (i32.or - (i32.or - (i32.or - (local.get $1) - (i32.shl - (local.get $1) - (i32.const 8) - ) - ) - (i32.shl - (local.get $1) - (i32.const 16) - ) - ) - (i32.shl - (local.get $1) - (i32.const 24) - ) - ) - ) - (local.set $5 - (i32.and - (local.get $4) - (i32.const -4) - ) - ) - (loop $label$6 - (if - (i32.lt_s - (local.get $0) - (local.get $5) - ) - (block - (i32.store - (local.get $0) - (local.get $3) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - (br $label$6) - ) - ) - ) - ) - ) - (loop $label$8 - (if - (i32.lt_s - (local.get $0) - (local.get $4) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (br $label$8) - ) - ) - ) - (i32.sub - (local.get $0) - (local.get $2) - ) + (func $40 (;53;) (type $2) (param $0 i32) (result i32) + local.get $0 + call $39 ) - ) - (func $47 (; 60 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (block $label$1 (result i32) - (if - (i32.ge_s - (local.get $2) - (i32.const 4096) - ) - (return - (call $fimport$12 - (local.get $0) - (local.get $1) - (local.get $2) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (if - (i32.eq - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.and - (local.get $1) - (i32.const 3) - ) - ) - (block - (loop $label$4 - (if - (i32.and - (local.get $0) - (i32.const 3) - ) - (block - (if - (i32.eqz - (local.get $2) - ) - (return - (local.get $3) - ) - ) - (i32.store8 - (local.get $0) - (i32.load8_s - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 1) - ) - ) - (br $label$4) - ) - ) - ) - (loop $label$7 - (if - (i32.ge_s - (local.get $2) - (i32.const 4) - ) - (block - (i32.store - (local.get $0) - (i32.load - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 4) - ) - ) - (br $label$7) - ) - ) - ) - ) - ) - (loop $label$9 - (if - (i32.gt_s - (local.get $2) - (i32.const 0) - ) - (block - (i32.store8 - (local.get $0) - (i32.load8_s - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 1) - ) - ) - (br $label$9) - ) - ) - ) - (local.get $3) + (func $41 (;54;) (type $3) (param $0 i32) + local.get $0 + call $38 ) - ) - (func $48 (; 61 ;) (type $4) (result i32) - (i32.const 0) - ) - (func $49 (; 62 ;) (type $6) (param $0 i32) (param $1 i32) (result i32) - (call_indirect (type $2) - (local.get $1) - (i32.add - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 0) - ) + (func $42 (;55;) (type $3) (param $0 i32) + local.get $0 + call $41 ) - ) - (func $50 (; 63 ;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - (call_indirect (type $0) - (local.get $1) - (local.get $2) - (local.get $3) - (i32.add - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.const 2) - ) + (func $43 (;56;) (type $4) (result i32) + (local $0 i32) + block $label$1 (result i32) ;; label = @1 + i32.const 4672 + i32.const 4672 + i32.load + local.tee $0 + i32.const 0 + i32.add + i32.store + local.get $0 + end ) - ) - (func $51 (; 64 ;) (type $5) (param $0 i32) (param $1 i32) - (call_indirect (type $3) - (local.get $1) - (i32.add - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 6) - ) + (func $44 (;57;) (type $1) + nop ) - ) - (func $52 (; 65 ;) (type $3) (param $0 i32) - (call_indirect (type $1) - (i32.add - (i32.and - (local.get $0) - (i32.const 0) - ) - (i32.const 8) - ) + (func $45 (;58;) (type $2) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$0 + i32.load + local.tee $2 + local.get $0 + i32.const 15 + i32.add + i32.const -16 + i32.and + local.tee $0 + i32.add + local.set $1 + local.get $0 + i32.const 0 + i32.gt_s + local.get $1 + local.get $2 + i32.lt_s + i32.and + local.get $1 + i32.const 0 + i32.lt_s + i32.or + if ;; label = @2 + block ;; label = @3 + call $fimport$6 + drop + i32.const 12 + call $fimport$11 + i32.const -1 + return + end + end + global.get $global$0 + local.get $1 + i32.store + local.get $1 + call $fimport$5 + i32.gt_s + if ;; label = @2 + call $fimport$4 + i32.eqz + if ;; label = @3 + block ;; label = @4 + i32.const 12 + call $fimport$11 + global.get $global$0 + local.get $2 + i32.store + i32.const -1 + return + end + end + end + local.get $2 + end ) - ) - (func $53 (; 66 ;) (type $2) (param $0 i32) (result i32) - (block $label$1 (result i32) - (call $fimport$3 - (i32.const 0) - ) - (i32.const 0) + (func $46 (;59;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + local.get $2 + i32.add + local.set $4 + local.get $2 + i32.const 20 + i32.ge_s + if ;; label = @2 + block ;; label = @3 + local.get $1 + i32.const 255 + i32.and + local.set $1 + local.get $0 + i32.const 3 + i32.and + local.tee $3 + if ;; label = @4 + block ;; label = @5 + local.get $0 + i32.const 4 + i32.add + local.get $3 + i32.sub + local.set $3 + loop $label$4 ;; label = @6 + local.get $0 + local.get $3 + i32.lt_s + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.get $1 + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br 2 (;@6;) + end + end + end + end + end + local.get $1 + local.get $1 + i32.const 8 + i32.shl + i32.or + local.get $1 + i32.const 16 + i32.shl + i32.or + local.get $1 + i32.const 24 + i32.shl + i32.or + local.set $3 + local.get $4 + i32.const -4 + i32.and + local.set $5 + loop $label$6 ;; label = @4 + local.get $0 + local.get $5 + i32.lt_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $3 + i32.store + local.get $0 + i32.const 4 + i32.add + local.set $0 + br 2 (;@4;) + end + end + end + end + end + loop $label$8 ;; label = @2 + local.get $0 + local.get $4 + i32.lt_s + if ;; label = @3 + block ;; label = @4 + local.get $0 + local.get $1 + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br 2 (;@2;) + end + end + end + local.get $0 + local.get $2 + i32.sub + end ) - ) - (func $54 (; 67 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (block $label$1 (result i32) - (call $fimport$3 - (i32.const 1) - ) - (i32.const 0) + (func $47 (;60;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + block $label$1 (result i32) ;; label = @1 + local.get $2 + i32.const 4096 + i32.ge_s + if ;; label = @2 + local.get $0 + local.get $1 + local.get $2 + call $fimport$12 + return + end + local.get $0 + local.set $3 + local.get $0 + i32.const 3 + i32.and + local.get $1 + i32.const 3 + i32.and + i32.eq + if ;; label = @2 + block ;; label = @3 + loop $label$4 ;; label = @4 + local.get $0 + i32.const 3 + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $2 + i32.eqz + if ;; label = @7 + local.get $3 + return + end + local.get $0 + local.get $1 + i32.load8_s + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + br 2 (;@4;) + end + end + end + loop $label$7 ;; label = @4 + local.get $2 + i32.const 4 + i32.ge_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $1 + i32.load + i32.store + local.get $0 + i32.const 4 + i32.add + local.set $0 + local.get $1 + i32.const 4 + i32.add + local.set $1 + local.get $2 + i32.const 4 + i32.sub + local.set $2 + br 2 (;@4;) + end + end + end + end + end + loop $label$9 ;; label = @2 + local.get $2 + i32.const 0 + i32.gt_s + if ;; label = @3 + block ;; label = @4 + local.get $0 + local.get $1 + i32.load8_s + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + br 2 (;@2;) + end + end + end + local.get $3 + end ) - ) - (func $55 (; 68 ;) (type $3) (param $0 i32) - (call $fimport$3 - (i32.const 2) + (func $48 (;61;) (type $4) (result i32) + i32.const 0 ) - ) - (func $56 (; 69 ;) (type $1) - (call $fimport$3 - (i32.const 3) + (func $49 (;62;) (type $6) (param $0 i32) (param $1 i32) (result i32) + local.get $1 + local.get $0 + i32.const 1 + i32.and + i32.const 0 + i32.add + call_indirect (type $2) ) - ) -) - + (func $50 (;63;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) + local.get $1 + local.get $2 + local.get $3 + local.get $0 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + ) + (func $51 (;64;) (type $5) (param $0 i32) (param $1 i32) + local.get $1 + local.get $0 + i32.const 1 + i32.and + i32.const 6 + i32.add + call_indirect (type $3) + ) + (func $52 (;65;) (type $3) (param $0 i32) + local.get $0 + i32.const 0 + i32.and + i32.const 8 + i32.add + call_indirect (type $1) + ) + (func $53 (;66;) (type $2) (param $0 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + i32.const 0 + call $fimport$3 + i32.const 0 + end + ) + (func $54 (;67;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + i32.const 1 + call $fimport$3 + i32.const 0 + end + ) + (func $55 (;68;) (type $3) (param $0 i32) + i32.const 2 + call $fimport$3 + ) + (func $56 (;69;) (type $1) + i32.const 3 + call $fimport$3 + ) + (global $global$0 (;5;) (mut i32) global.get $gimport$0) + (global $global$1 (;6;) (mut i32) global.get $gimport$1) + (global $global$2 (;7;) (mut i32) global.get $gimport$2) + (global $global$3 (;8;) (mut i32) i32.const 0) + (global $global$4 (;9;) (mut i32) i32.const 0) + (global $global$5 (;10;) (mut i32) i32.const 0) + (export "_sbrk" (func $45)) + (export "_free" (func $38)) + (export "_main" (func $7)) + (export "_pthread_self" (func $48)) + (export "_memset" (func $46)) + (export "_malloc" (func $37)) + (export "_memcpy" (func $47)) + (export "___errno_location" (func $12)) + (export "runPostSets" (func $44)) + (export "stackAlloc" (func $0)) + (export "stackSave" (func $1)) + (export "stackRestore" (func $2)) + (export "establishStackSpace" (func $3)) + (export "setThrew" (func $4)) + (export "setTempRet0" (func $5)) + (export "getTempRet0" (func $6)) + (export "dynCall_ii" (func $49)) + (export "dynCall_iiii" (func $50)) + (export "dynCall_vi" (func $51)) + (export "dynCall_v" (func $52)) + (elem (;0;) (global.get $gimport$19) func $53 $9 $54 $14 $10 $15 $55 $16 $56) + (data (;0;) (i32.const 1024) "&\02\00\00a\00\00\00q=\8a>\00\00\00\00c\00\00\00\8f\c2\f5=\00\00\00\00g\00\00\00\8f\c2\f5=\00\00\00\00t\00\00\00q=\8a>\00\00\00\00B\00\00\00\0a\d7\a3<\00\00\00\00D\00\00\00\0a\d7\a3<\00\00\00\00H\00\00\00\0a\d7\a3<\00\00\00\00K\00\00\00\0a\d7\a3<\00\00\00\00M\00\00\00\0a\d7\a3<\00\00\00\00N\00\00\00\0a\d7\a3<\00\00\00\00R\00\00\00\0a\d7\a3<\00\00\00\00S\00\00\00\0a\d7\a3<\00\00\00\00V\00\00\00\0a\d7\a3<\00\00\00\00W\00\00\00\0a\d7\a3<\00\00\00\00Y\00\00\00\0a\d7\a3<") + (data (;1;) (i32.const 1220) "a\00\00\00\e9\1c\9b>\00\00\00\00c\00\00\00r\bdJ>\00\00\00\00g\00\00\00\d7IJ>\00\00\00\00t\00\00\00r_\9a>") + (data (;2;) (i32.const 1280) "\04\05\00\00\05") + (data (;3;) (i32.const 1296) "\01") + (data (;4;) (i32.const 1320) "\01\00\00\00\02\00\00\00L\12\00\00\00\04") + (data (;5;) (i32.const 1344) "\01") + (data (;6;) (i32.const 1359) "\0a\ff\ff\ff\ff") + (data (;7;) (i32.const 1396) "*\00\00\00error: %d\0a\00GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA\00\11\00\0a\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\09\00\00\00\00\0b") + (data (;8;) (i32.const 1731) "\11\00\0f\0a\11\11\11\03\0a\07\00\01\13\09\0b\0b\00\00\09\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") + (data (;9;) (i32.const 1780) "\0b") + (data (;10;) (i32.const 1789) "\11\00\0a\0a\11\11\11\00\0a\00\00\02\00\09\0b\00\00\00\09\00\0b\00\00\0b") + (data (;11;) (i32.const 1838) "\0c") + (data (;12;) (i32.const 1850) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c") + (data (;13;) (i32.const 1896) "\0e") + (data (;14;) (i32.const 1908) "\0d\00\00\00\04\0d\00\00\00\00\09\0e\00\00\00\00\00\0e\00\00\0e") + (data (;15;) (i32.const 1954) "\10") + (data (;16;) (i32.const 1966) "\0f\00\00\00\00\0f\00\00\00\00\09\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") + (data (;17;) (i32.const 2021) "\12\00\00\00\12\12\12\00\00\00\00\00\00\09") + (data (;18;) (i32.const 2070) "\0b") + (data (;19;) (i32.const 2082) "\0a\00\00\00\00\0a\00\00\00\00\09\0b\00\00\00\00\00\0b\00\00\0b") + (data (;20;) (i32.const 2128) "\0c") + (data (;21;) (i32.const 2140) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\22\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\09\0a\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\5c]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") +) \ No newline at end of file diff --git a/cranelift/wasm/wasmtests/embenchen_ifs.wat b/cranelift/wasm/wasmtests/embenchen_ifs.wat index e5ada5702fef..dc466513c7e6 100644 --- a/cranelift/wasm/wasmtests/embenchen_ifs.wat +++ b/cranelift/wasm/wasmtests/embenchen_ifs.wat @@ -1,15771 +1,11505 @@ (module - (type $0 (func (param i32 i32 i32) (result i32))) - (type $1 (func (param i32) (result i32))) - (type $2 (func (param i32))) - (type $3 (func (result i32))) - (type $4 (func (param i32 i32) (result i32))) - (type $5 (func (param i32 i32))) - (type $6 (func)) - (type $7 (func (param i32 i32 i32 i32 i32) (result i32))) - (type $8 (func (param i32 i32 i32))) - (type $9 (func (param i64 i32) (result i32))) - (type $10 (func (param i32 i32 i32 i32 i32))) - (type $11 (func (param f64 i32) (result f64))) - (type $12 (func (param i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory $16 2048 2048)) - (data (i32.const 1024) "\04\04\00\00\05") - (data (i32.const 1040) "\01") - (data (i32.const 1064) "\01\00\00\00\02\00\00\00,\10\00\00\00\04") - (data (i32.const 1088) "\01") - (data (i32.const 1103) "\n\ff\ff\ff\ff") - (data (i32.const 1140) "error: %d\\n\00ok\00\11\00\n\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\t\00\00\00\00\0b") - (data (i32.const 1187) "\11\00\0f\n\11\11\11\03\n\07\00\01\13\t\0b\0b\00\00\t\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") - (data (i32.const 1236) "\0b") - (data (i32.const 1245) "\11\00\n\n\11\11\11\00\n\00\00\02\00\t\0b\00\00\00\t\00\0b\00\00\0b") - (data (i32.const 1294) "\0c") - (data (i32.const 1306) "\0c\00\00\00\00\0c\00\00\00\00\t\0c\00\00\00\00\00\0c\00\00\0c") - (data (i32.const 1352) "\0e") - (data (i32.const 1364) "\0d\00\00\00\04\0d\00\00\00\00\t\0e\00\00\00\00\00\0e\00\00\0e") - (data (i32.const 1410) "\10") - (data (i32.const 1422) "\0f\00\00\00\00\0f\00\00\00\00\t\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") - (data (i32.const 1477) "\12\00\00\00\12\12\12\00\00\00\00\00\00\t") - (data (i32.const 1526) "\0b") - (data (i32.const 1538) "\n\00\00\00\00\n\00\00\00\00\t\0b\00\00\00\00\00\0b\00\00\0b") - (data (i32.const 1584) "\0c") - (data (i32.const 1596) "\0c\00\00\00\00\0c\00\00\00\00\t\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\"\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e\'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\t\n\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\\]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") - (import "env" "table" (table $timport$17 8 8 funcref)) - (elem (global.get $gimport$19) $47 $9 $48 $14 $10 $15 $49 $16) - (import "env" "DYNAMICTOP_PTR" (global $gimport$0 i32)) - (import "env" "STACKTOP" (global $gimport$1 i32)) - (import "env" "STACK_MAX" (global $gimport$2 i32)) - (import "env" "memoryBase" (global $gimport$18 i32)) - (import "env" "tableBase" (global $gimport$19 i32)) - (import "env" "abort" (func $fimport$3 (param i32))) - (import "env" "enlargeMemory" (func $fimport$4 (result i32))) - (import "env" "getTotalMemory" (func $fimport$5 (result i32))) - (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (result i32))) - (import "env" "_pthread_cleanup_pop" (func $fimport$7 (param i32))) - (import "env" "___syscall6" (func $fimport$8 (param i32 i32) (result i32))) - (import "env" "_pthread_cleanup_push" (func $fimport$9 (param i32 i32))) - (import "env" "_abort" (func $fimport$10)) - (import "env" "___setErrNo" (func $fimport$11 (param i32))) - (import "env" "_emscripten_memcpy_big" (func $fimport$12 (param i32 i32 i32) (result i32))) - (import "env" "___syscall54" (func $fimport$13 (param i32 i32) (result i32))) - (import "env" "___syscall140" (func $fimport$14 (param i32 i32) (result i32))) - (import "env" "___syscall146" (func $fimport$15 (param i32 i32) (result i32))) - (global $global$0 (mut i32) (global.get $gimport$0)) - (global $global$1 (mut i32) (global.get $gimport$1)) - (global $global$2 (mut i32) (global.get $gimport$2)) - (global $global$3 (mut i32) (i32.const 0)) - (global $global$4 (mut i32) (i32.const 0)) - (global $global$5 (mut i32) (i32.const 0)) - (export "_sbrk" (func $40)) - (export "_free" (func $38)) - (export "_main" (func $8)) - (export "_pthread_self" (func $43)) - (export "_memset" (func $41)) - (export "_malloc" (func $37)) - (export "_memcpy" (func $42)) - (export "___errno_location" (func $12)) - (export "runPostSets" (func $39)) - (export "stackAlloc" (func $0)) - (export "stackSave" (func $1)) - (export "stackRestore" (func $2)) - (export "establishStackSpace" (func $3)) - (export "setThrew" (func $4)) - (export "setTempRet0" (func $5)) - (export "getTempRet0" (func $6)) - (export "dynCall_ii" (func $44)) - (export "dynCall_iiii" (func $45)) - (export "dynCall_vi" (func $46)) - (func $0 (; 13 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (block $label$1 (result i32) - (local.set $1 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (local.get $0) - ) - ) - (global.set $global$1 - (i32.and - (i32.add - (global.get $global$1) - (i32.const 15) - ) - (i32.const -16) - ) - ) - (local.get $1) + (type $0 (;0;) (func (param i32 i32 i32) (result i32))) + (type $1 (;1;) (func (param i32) (result i32))) + (type $2 (;2;) (func (param i32))) + (type $3 (;3;) (func (result i32))) + (type $4 (;4;) (func (param i32 i32) (result i32))) + (type $5 (;5;) (func (param i32 i32))) + (type $6 (;6;) (func)) + (type $7 (;7;) (func (param i32 i32 i32 i32 i32) (result i32))) + (type $8 (;8;) (func (param i32 i32 i32))) + (type $9 (;9;) (func (param i64 i32) (result i32))) + (type $10 (;10;) (func (param i32 i32 i32 i32 i32))) + (type $11 (;11;) (func (param f64 i32) (result f64))) + (type $12 (;12;) (func (param i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory $16 (;0;) 2048 2048)) + (import "env" "table" (table $timport$17 (;0;) 8 8 funcref)) + (import "env" "DYNAMICTOP_PTR" (global $gimport$0 (;0;) i32)) + (import "env" "STACKTOP" (global $gimport$1 (;1;) i32)) + (import "env" "STACK_MAX" (global $gimport$2 (;2;) i32)) + (import "env" "memoryBase" (global $gimport$18 (;3;) i32)) + (import "env" "tableBase" (global $gimport$19 (;4;) i32)) + (import "env" "abort" (func $fimport$3 (;0;) (type $2))) + (import "env" "enlargeMemory" (func $fimport$4 (;1;) (type $3))) + (import "env" "getTotalMemory" (func $fimport$5 (;2;) (type $3))) + (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (;3;) (type $3))) + (import "env" "_pthread_cleanup_pop" (func $fimport$7 (;4;) (type $2))) + (import "env" "___syscall6" (func $fimport$8 (;5;) (type $4))) + (import "env" "_pthread_cleanup_push" (func $fimport$9 (;6;) (type $5))) + (import "env" "_abort" (func $fimport$10 (;7;) (type $6))) + (import "env" "___setErrNo" (func $fimport$11 (;8;) (type $2))) + (import "env" "_emscripten_memcpy_big" (func $fimport$12 (;9;) (type $0))) + (import "env" "___syscall54" (func $fimport$13 (;10;) (type $4))) + (import "env" "___syscall140" (func $fimport$14 (;11;) (type $4))) + (import "env" "___syscall146" (func $fimport$15 (;12;) (type $4))) + (func $0 (;13;) (type $1) (param $0 i32) (result i32) + (local $1 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $1 + global.get $global$1 + local.get $0 + i32.add + global.set $global$1 + global.get $global$1 + i32.const 15 + i32.add + i32.const -16 + i32.and + global.set $global$1 + local.get $1 + end ) - ) - (func $1 (; 14 ;) (type $3) (result i32) - (global.get $global$1) - ) - (func $2 (; 15 ;) (type $2) (param $0 i32) - (global.set $global$1 - (local.get $0) + (func $1 (;14;) (type $3) (result i32) + global.get $global$1 ) - ) - (func $3 (; 16 ;) (type $5) (param $0 i32) (param $1 i32) - (block $label$1 - (global.set $global$1 - (local.get $0) - ) - (global.set $global$2 - (local.get $1) - ) + (func $2 (;15;) (type $2) (param $0 i32) + local.get $0 + global.set $global$1 ) - ) - (func $4 (; 17 ;) (type $5) (param $0 i32) (param $1 i32) - (if - (i32.eqz - (global.get $global$3) - ) - (block - (global.set $global$3 - (local.get $0) - ) - (global.set $global$4 - (local.get $1) - ) - ) + (func $3 (;16;) (type $5) (param $0 i32) (param $1 i32) + block $label$1 ;; label = @1 + local.get $0 + global.set $global$1 + local.get $1 + global.set $global$2 + end ) - ) - (func $5 (; 18 ;) (type $2) (param $0 i32) - (global.set $global$5 - (local.get $0) + (func $4 (;17;) (type $5) (param $0 i32) (param $1 i32) + global.get $global$3 + i32.eqz + if ;; label = @1 + block ;; label = @2 + local.get $0 + global.set $global$3 + local.get $1 + global.set $global$4 + end + end ) - ) - (func $6 (; 19 ;) (type $3) (result i32) - (global.get $global$5) - ) - (func $7 (; 20 ;) (type $3) (result i32) - (local $0 i32) - (block $label$1 (result i32) - (i32.store - (i32.const 3584) - (i32.add - (local.tee $0 - (i32.load - (i32.const 3584) - ) - ) - (i32.const 1) - ) - ) - (i32.and - (local.get $0) - (i32.const 16384) - ) + (func $5 (;18;) (type $2) (param $0 i32) + local.get $0 + global.set $global$5 ) - ) - (func $8 (; 21 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (local.set $2 - (local.get $4) - ) - (block $label$2 - (block $label$3 - (br_if $label$3 - (i32.le_s - (local.get $0) - (i32.const 1) - ) - ) - (block $label$4 - (block $label$5 - (block $label$6 - (block $label$7 - (block $label$8 - (block $label$9 - (block $label$10 - (br_table $label$5 $label$10 $label$8 $label$9 $label$7 $label$6 $label$4 - (i32.sub - (local.tee $0 - (i32.load8_s - (i32.load offset=4 - (local.get $1) - ) - ) - ) - (i32.const 48) - ) - ) - ) - (local.set $3 - (i32.const 75) - ) - (br $label$2) - ) - (br $label$3) - ) - (local.set $3 - (i32.const 625) - ) - (br $label$2) - ) - (local.set $3 - (i32.const 6250) - ) - (br $label$2) - ) - (local.set $3 - (i32.const 12500) - ) - (br $label$2) - ) - (global.set $global$1 - (local.get $4) - ) - (return - (i32.const 0) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $0) - (i32.const -48) - ) - ) - (drop - (call $34 - (i32.const 1140) - (local.get $2) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (return - (i32.const -1) - ) - ) - (local.set $3 - (i32.const 1250) - ) - ) - (local.set $1 - (i32.const 0) - ) - (local.set $0 - (i32.const 0) - ) - (loop $label$11 - (local.set $2 - (i32.const 0) - ) - (loop $label$12 - (local.set $0 - (block $label$13 (result i32) - (block $label$14 - (br_if $label$14 - (i32.eqz - (call $7) - ) - ) - (br_if $label$14 - (i32.eqz - (call $7) - ) - ) - (br $label$13 - (i32.add - (local.get $0) - (i32.const 17) - ) - ) - ) - (i32.add - (local.get $0) - (i32.const 19) - ) - ) - ) - (block $label$15 - (block $label$16 - (br_if $label$16 - (call $7) - ) - (br_if $label$16 - (call $7) - ) - (br $label$15) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 23) - ) - ) - ) - (br_if $label$12 - (i32.lt_s - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (local.get $3) - ) - ) - ) - (br_if $label$11 - (i32.ne - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (i32.const 27000) - ) - ) - ) - (drop - (call $35 - (i32.const 1152) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $0) + (func $6 (;19;) (type $3) (result i32) + global.get $global$5 ) - ) - (func $9 (; 22 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store - (local.tee $2 - (local.get $1) - ) - (i32.load offset=60 - (local.get $0) - ) - ) - (local.set $0 - (call $11 - (call $fimport$8 - (i32.const 6) - (local.get $2) - ) - ) - ) - (global.set $global$1 - (local.get $1) - ) - (local.get $0) + (func $7 (;20;) (type $3) (result i32) + (local $0 i32) + block $label$1 (result i32) ;; label = @1 + i32.const 3584 + i32.const 3584 + i32.load + local.tee $0 + i32.const 1 + i32.add + i32.store + local.get $0 + i32.const 16384 + i32.and + end ) - ) - (func $10 (; 23 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 32) - ) - ) - (i32.store - (local.tee $3 - (local.get $4) - ) - (i32.load offset=60 - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.const 0) - ) - (i32.store offset=8 - (local.get $3) - (local.get $1) - ) - (i32.store offset=12 - (local.get $3) - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - (i32.store offset=16 - (local.get $3) - (local.get $2) - ) - (local.set $0 - (if (result i32) - (i32.lt_s - (call $11 - (call $fimport$14 - (i32.const 140) - (local.get $3) - ) - ) - (i32.const 0) - ) - (block (result i32) - (i32.store - (local.get $0) - (i32.const -1) - ) - (i32.const -1) - ) - (i32.load - (local.get $0) - ) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $0) + (func $8 (;21;) (type $4) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $4 + local.set $2 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $0 + i32.const 1 + i32.le_s + br_if 0 (;@3;) + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + block $label$6 ;; label = @6 + block $label$7 ;; label = @7 + block $label$8 ;; label = @8 + block $label$9 ;; label = @9 + block $label$10 ;; label = @10 + local.get $1 + i32.load offset=4 + i32.load8_s + local.tee $0 + i32.const 48 + i32.sub + br_table 5 (;@5;) 0 (;@10;) 2 (;@8;) 1 (;@9;) 3 (;@7;) 4 (;@6;) 6 (;@4;) + end + i32.const 75 + local.set $3 + br 7 (;@2;) + end + br 5 (;@3;) + end + i32.const 625 + local.set $3 + br 5 (;@2;) + end + i32.const 6250 + local.set $3 + br 4 (;@2;) + end + i32.const 12500 + local.set $3 + br 3 (;@2;) + end + local.get $4 + global.set $global$1 + i32.const 0 + return + end + local.get $2 + local.get $0 + i32.const -48 + i32.add + i32.store + i32.const 1140 + local.get $2 + call $34 + drop + local.get $4 + global.set $global$1 + i32.const -1 + return + end + i32.const 1250 + local.set $3 + end + i32.const 0 + local.set $1 + i32.const 0 + local.set $0 + loop $label$11 ;; label = @2 + i32.const 0 + local.set $2 + loop $label$12 ;; label = @3 + block $label$13 (result i32) ;; label = @4 + block $label$14 ;; label = @5 + call $7 + i32.eqz + br_if 0 (;@5;) + call $7 + i32.eqz + br_if 0 (;@5;) + local.get $0 + i32.const 17 + i32.add + br 1 (;@4;) + end + local.get $0 + i32.const 19 + i32.add + end + local.set $0 + block $label$15 ;; label = @4 + block $label$16 ;; label = @5 + call $7 + br_if 0 (;@5;) + call $7 + br_if 0 (;@5;) + br 1 (;@4;) + end + local.get $0 + i32.const 23 + i32.add + local.set $0 + end + local.get $2 + i32.const 1 + i32.add + local.tee $2 + local.get $3 + i32.lt_s + br_if 0 (;@3;) + end + local.get $1 + i32.const 1 + i32.add + local.tee $1 + i32.const 27000 + i32.ne + br_if 0 (;@2;) + end + i32.const 1152 + call $35 + drop + local.get $4 + global.set $global$1 + local.get $0 + end ) - ) - (func $11 (; 24 ;) (type $1) (param $0 i32) (result i32) - (if (result i32) - (i32.gt_u - (local.get $0) - (i32.const -4096) - ) - (block (result i32) - (i32.store - (call $12) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - (local.get $0) + (func $9 (;22;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $1 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $1 + local.tee $2 + local.get $0 + i32.load offset=60 + i32.store + i32.const 6 + local.get $2 + call $fimport$8 + call $11 + local.set $0 + local.get $1 + global.set $global$1 + local.get $0 + end ) - ) - (func $12 (; 25 ;) (type $3) (result i32) - (i32.const 3632) - ) - (func $13 (; 26 ;) (type $2) (param $0 i32) - (nop) - ) - (func $14 (; 27 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 80) - ) - ) - (local.set $3 - (local.get $4) - ) - (local.set $5 - (i32.add - (local.get $4) - (i32.const 12) - ) - ) - (i32.store offset=36 - (local.get $0) - (i32.const 3) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 64) - ) - ) - (block - (i32.store - (local.get $3) - (i32.load offset=60 - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.const 21505) - ) - (i32.store offset=8 - (local.get $3) - (local.get $5) - ) - (if - (call $fimport$13 - (i32.const 54) - (local.get $3) - ) - (i32.store8 offset=75 - (local.get $0) - (i32.const -1) - ) - ) - ) - ) - (local.set $0 - (call $15 - (local.get $0) - (local.get $1) - (local.get $2) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $0) + (func $10 (;23;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 32 + i32.add + global.set $global$1 + local.get $4 + local.tee $3 + local.get $0 + i32.load offset=60 + i32.store + local.get $3 + i32.const 0 + i32.store offset=4 + local.get $3 + local.get $1 + i32.store offset=8 + local.get $3 + local.get $4 + i32.const 20 + i32.add + local.tee $0 + i32.store offset=12 + local.get $3 + local.get $2 + i32.store offset=16 + i32.const 140 + local.get $3 + call $fimport$14 + call $11 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $0 + i32.const -1 + i32.store + i32.const -1 + end + else + local.get $0 + i32.load + end + local.set $0 + local.get $4 + global.set $global$1 + local.get $0 + end ) - ) - (func $15 (; 28 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (block $label$1 (result i32) - (local.set $8 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 48) - ) - ) - (local.set $9 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - (local.set $10 - (local.get $8) - ) - (i32.store - (local.tee $3 - (i32.add - (local.get $8) - (i32.const 32) - ) - ) - (local.tee $4 - (i32.load - (local.tee $6 - (i32.add - (local.get $0) - (i32.const 28) - ) - ) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (local.tee $5 - (i32.sub - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - (local.get $4) - ) - ) - ) - (i32.store offset=8 - (local.get $3) - (local.get $1) - ) - (i32.store offset=12 - (local.get $3) - (local.get $2) - ) - (local.set $13 - (i32.add - (local.get $0) - (i32.const 60) - ) - ) - (local.set $14 - (i32.add - (local.get $0) - (i32.const 44) - ) - ) - (local.set $1 - (local.get $3) - ) - (local.set $4 - (i32.const 2) - ) - (local.set $12 - (i32.add - (local.get $5) - (local.get $2) - ) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (loop $label$5 - (if - (i32.load - (i32.const 3588) - ) - (block - (call $fimport$9 - (i32.const 1) - (local.get $0) - ) - (i32.store - (local.get $10) - (i32.load - (local.get $13) - ) - ) - (i32.store offset=4 - (local.get $10) - (local.get $1) - ) - (i32.store offset=8 - (local.get $10) - (local.get $4) - ) - (local.set $3 - (call $11 - (call $fimport$15 - (i32.const 146) - (local.get $10) - ) - ) - ) - (call $fimport$7 - (i32.const 0) - ) - ) - (block - (i32.store - (local.get $9) - (i32.load - (local.get $13) - ) - ) - (i32.store offset=4 - (local.get $9) - (local.get $1) - ) - (i32.store offset=8 - (local.get $9) - (local.get $4) - ) - (local.set $3 - (call $11 - (call $fimport$15 - (i32.const 146) - (local.get $9) - ) - ) - ) - ) - ) - (br_if $label$4 - (i32.eq - (local.get $12) - (local.get $3) - ) - ) - (br_if $label$3 - (i32.lt_s - (local.get $3) - (i32.const 0) - ) - ) - (local.set $5 - (if (result i32) - (i32.gt_u - (local.get $3) - (local.tee $5 - (i32.load offset=4 - (local.get $1) - ) - ) - ) - (block (result i32) - (i32.store - (local.get $6) - (local.tee $7 - (i32.load - (local.get $14) - ) - ) - ) - (i32.store - (local.get $11) - (local.get $7) - ) - (local.set $7 - (i32.load offset=12 - (local.get $1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (local.set $4 - (i32.add - (local.get $4) - (i32.const -1) - ) - ) - (i32.sub - (local.get $3) - (local.get $5) - ) - ) - (if (result i32) - (i32.eq - (local.get $4) - (i32.const 2) - ) - (block (result i32) - (i32.store - (local.get $6) - (i32.add - (i32.load - (local.get $6) - ) - (local.get $3) - ) - ) - (local.set $7 - (local.get $5) - ) - (local.set $4 - (i32.const 2) - ) - (local.get $3) - ) - (block (result i32) - (local.set $7 - (local.get $5) - ) - (local.get $3) - ) - ) - ) - ) - (i32.store - (local.get $1) - (i32.add - (i32.load - (local.get $1) - ) - (local.get $5) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.sub - (local.get $7) - (local.get $5) - ) - ) - (local.set $12 - (i32.sub - (local.get $12) - (local.get $3) - ) - ) - (br $label$5) - ) - ) - (i32.store offset=16 - (local.get $0) - (i32.add - (local.tee $1 - (i32.load - (local.get $14) - ) - ) - (i32.load offset=48 - (local.get $0) - ) - ) - ) - (i32.store - (local.get $6) - (local.get $1) - ) - (i32.store - (local.get $11) - (local.get $1) - ) - (br $label$2) - ) - (i32.store offset=16 - (local.get $0) - (i32.const 0) - ) - (i32.store - (local.get $6) - (i32.const 0) - ) - (i32.store - (local.get $11) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (local.set $2 - (if (result i32) - (i32.eq - (local.get $4) - (i32.const 2) - ) - (i32.const 0) - (i32.sub - (local.get $2) - (i32.load offset=4 - (local.get $1) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $8) - ) - (local.get $2) + (func $11 (;24;) (type $1) (param $0 i32) (result i32) + local.get $0 + i32.const -4096 + i32.gt_u + if (result i32) ;; label = @1 + block (result i32) ;; label = @2 + call $12 + i32.const 0 + local.get $0 + i32.sub + i32.store + i32.const -1 + end + else + local.get $0 + end ) - ) - (func $16 (; 29 ;) (type $2) (param $0 i32) - (if - (i32.eqz - (i32.load offset=68 - (local.get $0) - ) - ) - (call $13 - (local.get $0) - ) + (func $12 (;25;) (type $3) (result i32) + i32.const 3632 ) - ) - (func $17 (; 30 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $5 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (if - (i32.and - (local.tee $4 - (i32.ne - (local.get $2) - (i32.const 0) - ) - ) - (i32.ne - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.const 0) - ) - ) - (block - (local.set $4 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (local.set $3 - (local.get $2) - ) - (local.set $2 - (local.get $0) - ) - (loop $label$6 - (if - (i32.eq - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.get $4) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (br $label$3) - ) - ) - (br_if $label$6 - (i32.and - (local.tee $0 - (i32.ne - (local.tee $3 - (i32.add - (local.get $3) - (i32.const -1) - ) - ) - (i32.const 0) - ) - ) - (i32.ne - (i32.and - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 3) - ) - (i32.const 0) - ) - ) - ) - (br $label$4) - ) - ) - (block - (local.set $3 - (local.get $2) - ) - (local.set $2 - (local.get $0) - ) - (local.set $0 - (local.get $4) - ) - ) - ) - ) - (if - (local.get $0) - (block - (local.set $0 - (local.get $3) - ) - (br $label$3) - ) - (local.set $0 - (i32.const 0) - ) - ) - (br $label$2) - ) - (if - (i32.ne - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.tee $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $3 - (i32.mul - (local.get $5) - (i32.const 16843009) - ) - ) - (block $label$12 - (block $label$13 - (br_if $label$13 - (i32.le_u - (local.get $0) - (i32.const 3) - ) - ) - (loop $label$14 - (if - (i32.eqz - (i32.and - (i32.xor - (i32.and - (local.tee $4 - (i32.xor - (i32.load - (local.get $2) - ) - (local.get $3) - ) - ) - (i32.const -2139062144) - ) - (i32.const -2139062144) - ) - (i32.add - (local.get $4) - (i32.const -16843009) - ) - ) - ) - (block - (local.set $2 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - (br_if $label$14 - (i32.gt_u - (local.tee $0 - (i32.add - (local.get $0) - (i32.const -4) - ) - ) - (i32.const 3) - ) - ) - (br $label$13) - ) - ) - ) - (br $label$12) - ) - (if - (i32.eqz - (local.get $0) - ) - (block - (local.set $0 - (i32.const 0) - ) - (br $label$2) - ) - ) - ) - (loop $label$17 - (br_if $label$2 - (i32.eq - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.get $1) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (local.set $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (br_if $label$17 - (local.tee $0 - (i32.add - (local.get $0) - (i32.const -1) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - ) - ) - ) - ) - (if (result i32) - (local.get $0) - (local.get $2) - (i32.const 0) - ) + (func $13 (;26;) (type $2) (param $0 i32) + nop ) - ) - (func $18 (; 31 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 224) - ) - ) - (local.set $5 - (i32.add - (local.get $4) - (i32.const 136) - ) - ) - (i64.store align=4 - (local.tee $3 - (i32.add - (local.get $4) - (i32.const 80) - ) - ) - (i64.const 0) - ) - (i64.store offset=8 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=16 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=24 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=32 align=4 - (local.get $3) - (i64.const 0) - ) - (i32.store - (local.tee $6 - (i32.add - (local.get $4) - (i32.const 120) - ) - ) - (i32.load - (local.get $2) - ) - ) - (if - (i32.lt_s - (call $19 - (i32.const 0) - (local.get $1) - (local.get $6) - (local.tee $2 - (local.get $4) - ) - (local.get $3) - ) - (i32.const 0) - ) - (local.set $1 - (i32.const -1) - ) - (block - (local.set $12 - (if (result i32) - (i32.gt_s - (i32.load offset=76 - (local.get $0) - ) - (i32.const -1) - ) - (call $20 - (local.get $0) - ) - (i32.const 0) - ) - ) - (local.set $7 - (i32.load - (local.get $0) - ) - ) - (if - (i32.lt_s - (i32.load8_s offset=74 - (local.get $0) - ) - (i32.const 1) - ) - (i32.store - (local.get $0) - (i32.and - (local.get $7) - (i32.const -33) - ) - ) - ) - (if - (i32.load - (local.tee $8 - (i32.add - (local.get $0) - (i32.const 48) - ) - ) - ) - (local.set $1 - (call $19 - (local.get $0) - (local.get $1) - (local.get $6) - (local.get $2) - (local.get $3) - ) - ) - (block - (local.set $10 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 44) - ) - ) - ) - ) - (i32.store - (local.get $9) - (local.get $5) - ) - (i32.store - (local.tee $13 - (i32.add - (local.get $0) - (i32.const 28) - ) - ) - (local.get $5) - ) - (i32.store - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - (local.get $5) - ) - (i32.store - (local.get $8) - (i32.const 80) - ) - (i32.store - (local.tee $14 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - (i32.add - (local.get $5) - (i32.const 80) - ) - ) - (local.set $1 - (call $19 - (local.get $0) - (local.get $1) - (local.get $6) - (local.get $2) - (local.get $3) - ) - ) - (if - (local.get $10) - (block - (drop - (call_indirect (type $0) - (local.get $0) - (i32.const 0) - (i32.const 0) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $0) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $11) - ) - ) - (local.set $1 - (i32.const -1) - ) - ) - (i32.store - (local.get $9) - (local.get $10) - ) - (i32.store - (local.get $8) - (i32.const 0) - ) - (i32.store - (local.get $14) - (i32.const 0) - ) - (i32.store - (local.get $13) - (i32.const 0) - ) - (i32.store - (local.get $11) - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (local.get $0) - (i32.or - (local.tee $2 - (i32.load - (local.get $0) - ) - ) - (i32.and - (local.get $7) - (i32.const 32) - ) - ) - ) - (if - (local.get $12) - (call $13 - (local.get $0) - ) - ) - (if - (i32.and - (local.get $2) - (i32.const 32) - ) - (local.set $1 - (i32.const -1) - ) - ) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $1) + (func $14 (;27;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 80 + i32.add + global.set $global$1 + local.get $4 + local.set $3 + local.get $4 + i32.const 12 + i32.add + local.set $5 + local.get $0 + i32.const 3 + i32.store offset=36 + local.get $0 + i32.load + i32.const 64 + i32.and + i32.eqz + if ;; label = @2 + block ;; label = @3 + local.get $3 + local.get $0 + i32.load offset=60 + i32.store + local.get $3 + i32.const 21505 + i32.store offset=4 + local.get $3 + local.get $5 + i32.store offset=8 + i32.const 54 + local.get $3 + call $fimport$13 + if ;; label = @4 + local.get $0 + i32.const -1 + i32.store8 offset=75 + end + end + end + local.get $0 + local.get $1 + local.get $2 + call $15 + local.set $0 + local.get $4 + global.set $global$1 + local.get $0 + end ) - ) - (func $19 (; 32 ;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 i32) - (local $17 i32) - (local $18 i32) - (local $19 i32) - (local $20 i32) - (local $21 i32) - (local $22 i32) - (local $23 i32) - (local $24 i32) - (local $25 i32) - (local $26 i32) - (local $27 i32) - (local $28 i32) - (local $29 i32) - (local $30 i32) - (local $31 i32) - (local $32 i32) - (local $33 i32) - (local $34 i32) - (local $35 i32) - (local $36 i32) - (local $37 i32) - (local $38 i32) - (local $39 i32) - (local $40 i32) - (local $41 i32) - (local $42 i32) - (local $43 i32) - (local $44 i32) - (local $45 i32) - (local $46 i32) - (local $47 i32) - (local $48 i32) - (local $49 i32) - (local $50 i64) - (local $51 i64) - (local $52 f64) - (local $53 f64) - (block $label$1 (result i32) - (local.set $23 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 624) - ) - ) - (local.set $20 - (i32.add - (local.get $23) - (i32.const 16) - ) - ) - (local.set $16 - (local.get $23) - ) - (local.set $36 - (i32.add - (local.get $23) - (i32.const 528) - ) - ) - (local.set $30 - (i32.ne - (local.get $0) - (i32.const 0) - ) - ) - (local.set $38 - (local.tee $21 - (i32.add - (local.tee $17 - (i32.add - (local.get $23) - (i32.const 536) - ) - ) - (i32.const 40) - ) - ) - ) - (local.set $39 - (i32.add - (local.get $17) - (i32.const 39) - ) - ) - (local.set $42 - (i32.add - (local.tee $37 - (i32.add - (local.get $23) - (i32.const 8) - ) - ) - (i32.const 4) - ) - ) - (local.set $43 - (i32.sub - (i32.const 0) - (local.tee $27 - (local.tee $19 - (i32.add - (local.get $23) - (i32.const 588) - ) - ) - ) - ) - ) - (local.set $33 - (i32.add - (local.tee $17 - (i32.add - (local.get $23) - (i32.const 576) - ) - ) - (i32.const 12) - ) - ) - (local.set $40 - (i32.add - (local.get $17) - (i32.const 11) - ) - ) - (local.set $44 - (i32.sub - (local.tee $28 - (local.get $33) - ) - (local.get $27) - ) - ) - (local.set $45 - (i32.sub - (i32.const -2) - (local.get $27) - ) - ) - (local.set $46 - (i32.add - (local.get $28) - (i32.const 2) - ) - ) - (local.set $48 - (i32.add - (local.tee $47 - (i32.add - (local.get $23) - (i32.const 24) - ) - ) - (i32.const 288) - ) - ) - (local.set $41 - (local.tee $31 - (i32.add - (local.get $19) - (i32.const 9) - ) - ) - ) - (local.set $34 - (i32.add - (local.get $19) - (i32.const 8) - ) - ) - (local.set $15 - (i32.const 0) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $17 - (i32.const 0) - ) - (block $label$2 - (block $label$3 - (loop $label$4 - (block $label$5 - (if - (i32.gt_s - (local.get $15) - (i32.const -1) - ) - (local.set $15 - (if (result i32) - (i32.gt_s - (local.get $10) - (i32.sub - (i32.const 2147483647) - (local.get $15) - ) - ) - (block (result i32) - (i32.store - (call $12) - (i32.const 75) - ) - (i32.const -1) - ) - (i32.add - (local.get $10) - (local.get $15) - ) - ) - ) - ) - (br_if $label$3 - (i32.eqz - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.get $1) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (local.set $11 - (local.get $1) - ) - (block $label$9 - (block $label$10 - (loop $label$11 - (block $label$12 - (block $label$13 - (block $label$14 - (block $label$15 - (br_table $label$14 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$15 $label$13 - (i32.sub - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 0) - ) - ) - ) - (local.set $5 - (local.get $11) - ) - (br $label$10) - ) - (local.set $5 - (local.get $11) - ) - (br $label$12) - ) - (local.set $5 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (br $label$11) - ) - ) - (br $label$9) - ) - (loop $label$16 - (br_if $label$9 - (i32.ne - (i32.load8_s offset=1 - (local.get $5) - ) - (i32.const 37) - ) - ) - (local.set $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - (br_if $label$16 - (i32.eq - (i32.load8_s - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 2) - ) - ) - ) - (i32.const 37) - ) - ) - ) - ) - (local.set $10 - (i32.sub - (local.get $11) - (local.get $1) - ) - ) - (if - (local.get $30) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (local.get $10) - (local.get $0) - ) - ) - ) - ) - (if - (local.get $10) - (block - (local.set $1 - (local.get $5) - ) - (br $label$4) - ) - ) - (local.set $10 - (if (result i32) - (i32.lt_u - (local.tee $9 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $10 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block (result i32) - (local.set $10 - (i32.add - (local.get $5) - (i32.const 3) - ) - ) - (if - (local.tee $12 - (i32.eq - (i32.load8_s offset=2 - (local.get $5) - ) - (i32.const 36) - ) - ) - (local.set $11 - (local.get $10) - ) - ) - (if - (local.get $12) - (local.set $17 - (i32.const 1) - ) - ) - (local.set $5 - (i32.load8_s - (local.get $11) - ) - ) - (if - (i32.eqz - (local.get $12) - ) - (local.set $9 - (i32.const -1) - ) - ) - (local.get $17) - ) - (block (result i32) - (local.set $5 - (local.get $10) - ) - (local.set $9 - (i32.const -1) - ) - (local.get $17) - ) - ) - ) - (block $label$25 - (if - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (i32.const 32) - ) - (block - (local.set $17 - (i32.const 0) - ) - (loop $label$27 - (br_if $label$25 - (i32.eqz - (i32.and - (i32.shl - (i32.const 1) - (local.get $12) - ) - (i32.const 75913) - ) - ) - ) - (local.set $17 - (i32.or - (i32.shl - (i32.const 1) - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (local.get $17) - ) - ) - (br_if $label$27 - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (i32.const 32) - ) - ) - ) - ) - (local.set $17 - (i32.const 0) - ) - ) - ) - (block $label$29 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 42) - ) - (block - (local.set $11 - (block $label$31 (result i32) - (block $label$32 - (br_if $label$32 - (i32.ge_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - (br_if $label$32 - (i32.ne - (i32.load8_s offset=2 - (local.get $11) - ) - (i32.const 36) - ) - ) - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $12) - (i32.const 2) - ) - ) - (i32.const 10) - ) - (local.set $8 - (i32.const 1) - ) - (local.set $10 - (i32.wrap_i64 - (i64.load - (i32.add - (local.get $3) - (i32.shl - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -48) - ) - (i32.const 3) - ) - ) - ) - ) - ) - (br $label$31 - (i32.add - (local.get $11) - (i32.const 3) - ) - ) - ) - (if - (local.get $10) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $12 - (local.get $17) - ) - (local.set $17 - (i32.const 0) - ) - (local.set $11 - (local.get $7) - ) - (local.set $10 - (i32.const 0) - ) - (br $label$29) - ) - ) - (local.set $10 - (i32.load - (local.tee $11 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (local.set $8 - (i32.const 0) - ) - (local.get $7) - ) - ) - (local.set $12 - (i32.or - (local.get $17) - (i32.const 8192) - ) - ) - (local.set $7 - (i32.sub - (i32.const 0) - (local.get $10) - ) - ) - (local.set $5 - (i32.load8_s - (local.get $11) - ) - ) - (if - (i32.eqz - (local.tee $6 - (i32.lt_s - (local.get $10) - (i32.const 0) - ) - ) - ) - (local.set $12 - (local.get $17) - ) - ) - (local.set $17 - (local.get $8) - ) - (if - (local.get $6) - (local.set $10 - (local.get $7) - ) - ) - ) - (if - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block - (local.set $7 - (i32.const 0) - ) - (local.set $5 - (local.get $12) - ) - (loop $label$39 - (local.set $7 - (i32.add - (i32.mul - (local.get $7) - (i32.const 10) - ) - (local.get $5) - ) - ) - (br_if $label$39 - (i32.lt_u - (local.tee $5 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $12 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - ) - (if - (i32.lt_s - (local.get $7) - (i32.const 0) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - (block - (local.set $5 - (local.get $12) - ) - (local.set $12 - (local.get $17) - ) - (local.set $17 - (local.get $10) - ) - (local.set $10 - (local.get $7) - ) - ) - ) - ) - (block - (local.set $12 - (local.get $17) - ) - (local.set $17 - (local.get $10) - ) - (local.set $10 - (i32.const 0) - ) - ) - ) - ) - ) - (block $label$43 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 46) - ) - (block - (if - (i32.ne - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 42) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block - (local.set $11 - (local.get $7) - ) - (local.set $7 - (i32.const 0) - ) - ) - (block - (local.set $5 - (i32.const 0) - ) - (local.set $11 - (local.get $7) - ) - (br $label$43) - ) - ) - (loop $label$48 - (local.set $5 - (i32.add - (i32.mul - (local.get $7) - (i32.const 10) - ) - (local.get $5) - ) - ) - (br_if $label$43 - (i32.ge_u - (local.tee $8 - (i32.add - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - (local.set $7 - (local.get $5) - ) - (local.set $5 - (local.get $8) - ) - (br $label$48) - ) - ) - ) - (if - (i32.lt_u - (local.tee $5 - (i32.add - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 2) - ) - ) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (if - (i32.eq - (i32.load8_s offset=3 - (local.get $11) - ) - (i32.const 36) - ) - (block - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $5) - (i32.const 2) - ) - ) - (i32.const 10) - ) - (local.set $5 - (i32.wrap_i64 - (i64.load - (i32.add - (local.get $3) - (i32.shl - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -48) - ) - (i32.const 3) - ) - ) - ) - ) - ) - (local.set $11 - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (br $label$43) - ) - ) - ) - (if - (local.get $17) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $11 - (if (result i32) - (local.get $30) - (block (result i32) - (local.set $5 - (i32.load - (local.tee $11 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (local.get $7) - ) - (block (result i32) - (local.set $5 - (i32.const 0) - ) - (local.get $7) - ) - ) - ) - ) - (local.set $5 - (i32.const -1) - ) - ) - ) - (local.set $7 - (local.get $11) - ) - (local.set $8 - (i32.const 0) - ) - (loop $label$55 - (if - (i32.gt_u - (local.tee $6 - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -65) - ) - ) - (i32.const 57) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $11 - (i32.add - (local.get $7) - (i32.const 1) - ) - ) - (if - (i32.lt_u - (i32.add - (local.tee $6 - (i32.and - (local.tee $13 - (i32.load8_s - (i32.add - (i32.add - (i32.mul - (local.get $8) - (i32.const 58) - ) - (i32.const 1155) - ) - (local.get $6) - ) - ) - ) - (i32.const 255) - ) - ) - (i32.const -1) - ) - (i32.const 8) - ) - (block - (local.set $7 - (local.get $11) - ) - (local.set $8 - (local.get $6) - ) - (br $label$55) - ) - ) - ) - (if - (i32.eqz - (i32.shr_s - (i32.shl - (local.get $13) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $14 - (i32.gt_s - (local.get $9) - (i32.const -1) - ) - ) - (block $label$59 - (block $label$60 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $13) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 19) - ) - (if - (local.get $14) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - (br $label$60) - ) - (block - (if - (local.get $14) - (block - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $9) - (i32.const 2) - ) - ) - (local.get $6) - ) - (i64.store - (local.get $16) - (i64.load - (i32.add - (local.get $3) - (i32.shl - (local.get $9) - (i32.const 3) - ) - ) - ) - ) - (br $label$60) - ) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $15 - (i32.const 0) - ) - (br $label$5) - ) - ) - (call $22 - (local.get $16) - (local.get $6) - (local.get $2) - ) - ) - ) - (br $label$59) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - ) - ) - (local.set $9 - (i32.and - (local.tee $7 - (i32.load8_s - (local.get $7) - ) - ) - (i32.const -33) - ) - ) - (if - (i32.eqz - (i32.and - (i32.ne - (local.get $8) - (i32.const 0) - ) - (i32.eq - (i32.and - (local.get $7) - (i32.const 15) - ) - (i32.const 3) - ) - ) - ) - (local.set $9 - (local.get $7) - ) - ) - (local.set $7 - (i32.and - (local.get $12) - (i32.const -65537) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 8192) - ) - (local.set $12 - (local.get $7) - ) - ) - (block $label$70 - (block $label$71 - (block $label$72 - (block $label$73 - (block $label$74 - (block $label$75 - (block $label$76 - (block $label$77 - (block $label$78 - (block $label$79 - (block $label$80 - (block $label$81 - (block $label$82 - (block $label$83 - (block $label$84 - (block $label$85 - (block $label$86 - (block $label$87 - (block $label$88 - (block $label$89 - (br_table $label$78 $label$77 $label$80 $label$77 $label$78 $label$78 $label$78 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$79 $label$77 $label$77 $label$77 $label$77 $label$87 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$78 $label$77 $label$83 $label$85 $label$78 $label$78 $label$78 $label$77 $label$85 $label$77 $label$77 $label$77 $label$82 $label$89 $label$86 $label$88 $label$77 $label$77 $label$81 $label$77 $label$84 $label$77 $label$77 $label$87 $label$77 - (i32.sub - (local.get $9) - (i32.const 65) - ) - ) - ) - (block $label$90 - (block $label$91 - (block $label$92 - (block $label$93 - (block $label$94 - (block $label$95 - (block $label$96 - (block $label$97 - (br_table $label$97 $label$96 $label$95 $label$94 $label$93 $label$90 $label$92 $label$91 $label$90 - (i32.sub - (i32.shr_s - (i32.shl - (i32.and - (local.get $8) - (i32.const 255) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 0) - ) - ) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i64.store - (i32.load - (local.get $16) - ) - (i64.extend_i32_s - (local.get $15) - ) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store16 - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store8 - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i64.store - (i32.load - (local.get $16) - ) - (i64.extend_i32_s - (local.get $15) - ) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $12 - (i32.or - (local.get $12) - (i32.const 8) - ) - ) - (if - (i32.le_u - (local.get $5) - (i32.const 8) - ) - (local.set $5 - (i32.const 8) - ) - ) - (local.set $9 - (i32.const 120) - ) - (br $label$76) - ) - (br $label$76) - ) - (if - (i64.eq - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (local.set $7 - (local.get $21) - ) - (block - (local.set $1 - (local.get $21) - ) - (loop $label$101 - (i64.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i64.or - (i64.and - (local.get $50) - (i64.const 7) - ) - (i64.const 48) - ) - ) - (br_if $label$101 - (i64.ne - (local.tee $50 - (i64.shr_u - (local.get $50) - (i64.const 3) - ) - ) - (i64.const 0) - ) - ) - (local.set $7 - (local.get $1) - ) - ) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 8) - ) - (block - (local.set $8 - (i32.add - (local.tee $1 - (i32.sub - (local.get $38) - (local.get $7) - ) - ) - (i32.const 1) - ) - ) - (if - (i32.le_s - (local.get $5) - (local.get $1) - ) - (local.set $5 - (local.get $8) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1635) - ) - (br $label$71) - ) - (block - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1635) - ) - (br $label$71) - ) - ) - ) - (if - (i64.lt_s - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (block - (i64.store - (local.get $16) - (local.tee $50 - (i64.sub - (i64.const 0) - (local.get $50) - ) - ) - ) - (local.set $6 - (i32.const 1) - ) - (local.set $8 - (i32.const 1635) - ) - (br $label$75) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 2048) - ) - (block - (local.set $6 - (i32.const 1) - ) - (local.set $8 - (i32.const 1636) - ) - (br $label$75) - ) - (block - (local.set $6 - (local.tee $1 - (i32.and - (local.get $12) - (i32.const 1) - ) - ) - ) - (local.set $8 - (if (result i32) - (local.get $1) - (i32.const 1637) - (i32.const 1635) - ) - ) - (br $label$75) - ) - ) - ) - (local.set $50 - (i64.load - (local.get $16) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1635) - ) - (br $label$75) - ) - (i64.store8 - (local.get $39) - (i64.load - (local.get $16) - ) - ) - (local.set $1 - (local.get $39) - ) - (local.set $12 - (local.get $7) - ) - (local.set $7 - (i32.const 1) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1635) - ) - (local.set $5 - (local.get $21) - ) - (br $label$70) - ) - (local.set $1 - (call $24 - (i32.load - (call $12) - ) - ) - ) - (br $label$74) - ) - (if - (i32.eqz - (local.tee $1 - (i32.load - (local.get $16) - ) - ) - ) - (local.set $1 - (i32.const 1645) - ) - ) - (br $label$74) - ) - (i64.store32 - (local.get $37) - (i64.load - (local.get $16) - ) - ) - (i32.store - (local.get $42) - (i32.const 0) - ) - (i32.store - (local.get $16) - (local.get $37) - ) - (local.set $7 - (local.get $37) - ) - (local.set $6 - (i32.const -1) - ) - (br $label$73) - ) - (local.set $7 - (i32.load - (local.get $16) - ) - ) - (if - (local.get $5) - (block - (local.set $6 - (local.get $5) - ) - (br $label$73) - ) - (block - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (i32.const 0) - (local.get $12) - ) - (local.set $1 - (i32.const 0) - ) - (br $label$72) - ) - ) - ) - (local.set $52 - (f64.load - (local.get $16) - ) - ) - (i32.store - (local.get $20) - (i32.const 0) - ) - (local.set $26 - (if (result i32) - (i64.lt_s - (i64.reinterpret_f64 - (local.get $52) - ) - (i64.const 0) - ) - (block (result i32) - (local.set $24 - (i32.const 1) - ) - (local.set $52 - (f64.neg - (local.get $52) - ) - ) - (i32.const 1652) - ) - (block (result i32) - (local.set $1 - (i32.and - (local.get $12) - (i32.const 1) - ) - ) - (if (result i32) - (i32.and - (local.get $12) - (i32.const 2048) - ) - (block (result i32) - (local.set $24 - (i32.const 1) - ) - (i32.const 1655) - ) - (block (result i32) - (local.set $24 - (local.get $1) - ) - (if (result i32) - (local.get $1) - (i32.const 1658) - (i32.const 1653) - ) - ) - ) - ) - ) - ) - (block $label$119 - (if - (i64.lt_u - (i64.and - (i64.reinterpret_f64 - (local.get $52) - ) - (i64.const 9218868437227405312) - ) - (i64.const 9218868437227405312) - ) - (block - (if - (local.tee $1 - (f64.ne - (local.tee $52 - (f64.mul - (call $27 - (local.get $52) - (local.get $20) - ) - (f64.const 2) - ) - ) - (f64.const 0) - ) - ) - (i32.store - (local.get $20) - (i32.add - (i32.load - (local.get $20) - ) - (i32.const -1) - ) - ) - ) - (if - (i32.eq - (local.tee $22 - (i32.or - (local.get $9) - (i32.const 32) - ) - ) - (i32.const 97) - ) - (block - (local.set $1 - (i32.add - (local.get $26) - (i32.const 9) - ) - ) - (if - (local.tee $6 - (i32.and - (local.get $9) - (i32.const 32) - ) - ) - (local.set $26 - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.or - (i32.gt_u - (local.get $5) - (i32.const 11) - ) - (i32.eqz - (local.tee $1 - (i32.sub - (i32.const 12) - (local.get $5) - ) - ) - ) - ) - ) - (block - (local.set $53 - (f64.const 8) - ) - (loop $label$125 - (local.set $53 - (f64.mul - (local.get $53) - (f64.const 16) - ) - ) - (br_if $label$125 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - (local.set $52 - (if (result f64) - (i32.eq - (i32.load8_s - (local.get $26) - ) - (i32.const 45) - ) - (f64.neg - (f64.add - (local.get $53) - (f64.sub - (f64.neg - (local.get $52) - ) - (local.get $53) - ) - ) - ) - (f64.sub - (f64.add - (local.get $52) - (local.get $53) - ) - (local.get $53) - ) - ) - ) - ) - ) - (local.set $1 - (i32.sub - (i32.const 0) - (local.tee $7 - (i32.load - (local.get $20) - ) - ) - ) - ) - (if - (i32.eq - (local.tee $1 - (call $23 - (i64.extend_i32_s - (if (result i32) - (i32.lt_s - (local.get $7) - (i32.const 0) - ) - (local.get $1) - (local.get $7) - ) - ) - (local.get $33) - ) - ) - (local.get $33) - ) - (block - (i32.store8 - (local.get $40) - (i32.const 48) - ) - (local.set $1 - (local.get $40) - ) - ) - ) - (local.set $13 - (i32.or - (local.get $24) - (i32.const 2) - ) - ) - (i32.store8 - (i32.add - (local.get $1) - (i32.const -1) - ) - (i32.add - (i32.and - (i32.shr_s - (local.get $7) - (i32.const 31) - ) - (i32.const 2) - ) - (i32.const 43) - ) - ) - (i32.store8 - (local.tee $8 - (i32.add - (local.get $1) - (i32.const -2) - ) - ) - (i32.add - (local.get $9) - (i32.const 15) - ) - ) - (local.set $9 - (i32.lt_s - (local.get $5) - (i32.const 1) - ) - ) - (local.set $14 - (i32.eqz - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - ) - (local.set $1 - (local.get $19) - ) - (loop $label$131 - (i32.store8 - (local.get $1) - (i32.or - (i32.load8_u - (i32.add - (local.tee $7 - (i32.trunc_f64_s - (local.get $52) - ) - ) - (i32.const 1619) - ) - ) - (local.get $6) - ) - ) - (local.set $52 - (f64.mul - (f64.sub - (local.get $52) - (f64.convert_i32_s - (local.get $7) - ) - ) - (f64.const 16) - ) - ) - (local.set $1 - (block $label$132 (result i32) - (if (result i32) - (i32.eq - (i32.sub - (local.tee $7 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.get $27) - ) - (i32.const 1) - ) - (block (result i32) - (drop - (br_if $label$132 - (local.get $7) - (i32.and - (local.get $14) - (i32.and - (local.get $9) - (f64.eq - (local.get $52) - (f64.const 0) - ) - ) - ) - ) - ) - (i32.store8 - (local.get $7) - (i32.const 46) - ) - (i32.add - (local.get $1) - (i32.const 2) - ) - ) - (local.get $7) - ) - ) - ) - (br_if $label$131 - (f64.ne - (local.get $52) - (f64.const 0) - ) - ) - ) - (local.set $6 - (i32.sub - (i32.add - (local.get $46) - (local.get $5) - ) - (local.tee $7 - (local.get $8) - ) - ) - ) - (local.set $9 - (i32.add - (i32.sub - (local.get $44) - (local.get $7) - ) - (local.get $1) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $5 - (i32.add - (if (result i32) - (i32.and - (i32.ne - (local.get $5) - (i32.const 0) - ) - (i32.lt_s - (i32.add - (local.get $45) - (local.get $1) - ) - (local.get $5) - ) - ) - (local.get $6) - (local.tee $6 - (local.get $9) - ) - ) - (local.get $13) - ) - ) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $26) - (local.get $13) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (local.set $1 - (i32.sub - (local.get $1) - (local.get $27) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $19) - (local.get $1) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (i32.sub - (local.get $6) - (i32.add - (local.get $1) - (local.tee $1 - (i32.sub - (local.get $28) - (local.get $7) - ) - ) - ) - ) - (i32.const 0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $8) - (local.get $1) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $5) - (local.get $10) - ) - (local.set $10 - (local.get $5) - ) - ) - (br $label$119) - ) - ) - (if - (local.get $1) - (block - (i32.store - (local.get $20) - (local.tee $6 - (i32.add - (i32.load - (local.get $20) - ) - (i32.const -28) - ) - ) - ) - (local.set $52 - (f64.mul - (local.get $52) - (f64.const 268435456) - ) - ) - ) - (local.set $6 - (i32.load - (local.get $20) - ) - ) - ) - (local.set $8 - (local.tee $7 - (if (result i32) - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (local.get $47) - (local.get $48) - ) - ) - ) - (loop $label$145 - (i32.store - (local.get $8) - (local.tee $1 - (i32.trunc_f64_s - (local.get $52) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (br_if $label$145 - (f64.ne - (local.tee $52 - (f64.mul - (f64.sub - (local.get $52) - (f64.convert_i32_u - (local.get $1) - ) - ) - (f64.const 1e9) - ) - ) - (f64.const 0) - ) - ) - ) - (if - (i32.gt_s - (local.get $6) - (i32.const 0) - ) - (block - (local.set $1 - (local.get $7) - ) - (loop $label$147 - (local.set $14 - (if (result i32) - (i32.gt_s - (local.get $6) - (i32.const 29) - ) - (i32.const 29) - (local.get $6) - ) - ) - (block $label$150 - (if - (i32.ge_u - (local.tee $6 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - (local.get $1) - ) - (block - (local.set $50 - (i64.extend_i32_u - (local.get $14) - ) - ) - (local.set $13 - (i32.const 0) - ) - (loop $label$152 - (i64.store32 - (local.get $6) - (i64.rem_u - (local.tee $51 - (i64.add - (i64.shl - (i64.extend_i32_u - (i32.load - (local.get $6) - ) - ) - (local.get $50) - ) - (i64.extend_i32_u - (local.get $13) - ) - ) - ) - (i64.const 1000000000) - ) - ) - (local.set $13 - (i32.wrap_i64 - (i64.div_u - (local.get $51) - (i64.const 1000000000) - ) - ) - ) - (br_if $label$152 - (i32.ge_u - (local.tee $6 - (i32.add - (local.get $6) - (i32.const -4) - ) - ) - (local.get $1) - ) - ) - ) - (br_if $label$150 - (i32.eqz - (local.get $13) - ) - ) - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -4) - ) - ) - (local.get $13) - ) - ) - ) - ) - (loop $label$153 - (if - (i32.gt_u - (local.get $8) - (local.get $1) - ) - (if - (i32.eqz - (i32.load - (local.tee $6 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - ) - (block - (local.set $8 - (local.get $6) - ) - (br $label$153) - ) - ) - ) - ) - (i32.store - (local.get $20) - (local.tee $6 - (i32.sub - (i32.load - (local.get $20) - ) - (local.get $14) - ) - ) - ) - (br_if $label$147 - (i32.gt_s - (local.get $6) - (i32.const 0) - ) - ) - ) - ) - (local.set $1 - (local.get $7) - ) - ) - (local.set $18 - (if (result i32) - (i32.lt_s - (local.get $5) - (i32.const 0) - ) - (i32.const 6) - (local.get $5) - ) - ) - (if - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (block - (local.set $14 - (i32.add - (i32.div_s - (i32.add - (local.get $18) - (i32.const 25) - ) - (i32.const 9) - ) - (i32.const 1) - ) - ) - (local.set $25 - (i32.eq - (local.get $22) - (i32.const 102) - ) - ) - (local.set $5 - (local.get $8) - ) - (loop $label$160 - (if - (i32.gt_s - (local.tee $13 - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (i32.const 9) - ) - (local.set $13 - (i32.const 9) - ) - ) - (block $label$162 - (if - (i32.lt_u - (local.get $1) - (local.get $5) - ) - (block - (local.set $29 - (i32.add - (i32.shl - (i32.const 1) - (local.get $13) - ) - (i32.const -1) - ) - ) - (local.set $35 - (i32.shr_u - (i32.const 1000000000) - (local.get $13) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (local.get $1) - ) - (loop $label$164 - (i32.store - (local.get $8) - (i32.add - (i32.shr_u - (local.tee $32 - (i32.load - (local.get $8) - ) - ) - (local.get $13) - ) - (local.get $6) - ) - ) - (local.set $6 - (i32.mul - (i32.and - (local.get $32) - (local.get $29) - ) - (local.get $35) - ) - ) - (br_if $label$164 - (i32.lt_u - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (local.get $5) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (local.get $8) - ) - ) - (br_if $label$162 - (i32.eqz - (local.get $6) - ) - ) - (i32.store - (local.get $5) - (local.get $6) - ) - (local.set $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - ) - (block - (local.set $8 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (local.get $8) - ) - ) - ) - ) - ) - (local.set $6 - (i32.add - (local.tee $8 - (if (result i32) - (local.get $25) - (local.get $7) - (local.get $1) - ) - ) - (i32.shl - (local.get $14) - (i32.const 2) - ) - ) - ) - (if - (i32.gt_s - (i32.shr_s - (i32.sub - (local.get $5) - (local.get $8) - ) - (i32.const 2) - ) - (local.get $14) - ) - (local.set $5 - (local.get $6) - ) - ) - (i32.store - (local.get $20) - (local.tee $6 - (i32.add - (i32.load - (local.get $20) - ) - (local.get $13) - ) - ) - ) - (br_if $label$160 - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - ) - (local.set $13 - (local.get $5) - ) - ) - ) - (local.set $13 - (local.get $8) - ) - ) - (local.set $25 - (local.get $7) - ) - (block $label$172 - (if - (i32.lt_u - (local.get $1) - (local.get $13) - ) - (block - (local.set $5 - (i32.mul - (i32.shr_s - (i32.sub - (local.get $25) - (local.get $1) - ) - (i32.const 2) - ) - (i32.const 9) - ) - ) - (br_if $label$172 - (i32.lt_u - (local.tee $6 - (i32.load - (local.get $1) - ) - ) - (i32.const 10) - ) - ) - (local.set $8 - (i32.const 10) - ) - (loop $label$174 - (local.set $5 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$174 - (i32.ge_u - (local.get $6) - (local.tee $8 - (i32.mul - (local.get $8) - (i32.const 10) - ) - ) - ) - ) - ) - ) - (local.set $5 - (i32.const 0) - ) - ) - ) - (local.set $29 - (i32.eq - (local.get $22) - (i32.const 103) - ) - ) - (local.set $35 - (i32.ne - (local.get $18) - (i32.const 0) - ) - ) - (if - (i32.lt_s - (local.tee $8 - (i32.add - (i32.sub - (local.get $18) - (if (result i32) - (i32.ne - (local.get $22) - (i32.const 102) - ) - (local.get $5) - (i32.const 0) - ) - ) - (i32.shr_s - (i32.shl - (i32.and - (local.get $35) - (local.get $29) - ) - (i32.const 31) - ) - (i32.const 31) - ) - ) - ) - (i32.add - (i32.mul - (i32.shr_s - (i32.sub - (local.get $13) - (local.get $25) - ) - (i32.const 2) - ) - (i32.const 9) - ) - (i32.const -9) - ) - ) - (block - (if - (i32.lt_s - (local.tee $8 - (i32.add - (i32.rem_s - (local.tee $14 - (i32.add - (local.get $8) - (i32.const 9216) - ) - ) - (i32.const 9) - ) - (i32.const 1) - ) - ) - (i32.const 9) - ) - (block - (local.set $6 - (i32.const 10) - ) - (loop $label$180 - (local.set $6 - (i32.mul - (local.get $6) - (i32.const 10) - ) - ) - (br_if $label$180 - (i32.ne - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 1) - ) - ) - (i32.const 9) - ) - ) - ) - ) - (local.set $6 - (i32.const 10) - ) - ) - (local.set $14 - (i32.rem_u - (local.tee $22 - (i32.load - (local.tee $8 - (i32.add - (i32.add - (local.get $7) - (i32.const 4) - ) - (i32.shl - (i32.add - (i32.div_s - (local.get $14) - (i32.const 9) - ) - (i32.const -1024) - ) - (i32.const 2) - ) - ) - ) - ) - ) - (local.get $6) - ) - ) - (block $label$182 - (if - (i32.eqz - (i32.and - (local.tee $32 - (i32.eq - (i32.add - (local.get $8) - (i32.const 4) - ) - (local.get $13) - ) - ) - (i32.eqz - (local.get $14) - ) - ) - ) - (block - (local.set $52 - (if (result f64) - (i32.lt_u - (local.get $14) - (local.tee $49 - (i32.div_s - (local.get $6) - (i32.const 2) - ) - ) - ) - (f64.const 0.5) - (if (result f64) - (i32.and - (local.get $32) - (i32.eq - (local.get $14) - (local.get $49) - ) - ) - (f64.const 1) - (f64.const 1.5) - ) - ) - ) - (local.set $53 - (if (result f64) - (i32.and - (i32.div_u - (local.get $22) - (local.get $6) - ) - (i32.const 1) - ) - (f64.const 9007199254740994) - (f64.const 9007199254740992) - ) - ) - (block $label$190 - (if - (local.get $24) - (block - (br_if $label$190 - (i32.ne - (i32.load8_s - (local.get $26) - ) - (i32.const 45) - ) - ) - (local.set $53 - (f64.neg - (local.get $53) - ) - ) - (local.set $52 - (f64.neg - (local.get $52) - ) - ) - ) - ) - ) - (i32.store - (local.get $8) - (local.tee $14 - (i32.sub - (local.get $22) - (local.get $14) - ) - ) - ) - (br_if $label$182 - (f64.eq - (f64.add - (local.get $53) - (local.get $52) - ) - (local.get $53) - ) - ) - (i32.store - (local.get $8) - (local.tee $5 - (i32.add - (local.get $14) - (local.get $6) - ) - ) - ) - (if - (i32.gt_u - (local.get $5) - (i32.const 999999999) - ) - (loop $label$193 - (i32.store - (local.get $8) - (i32.const 0) - ) - (if - (i32.lt_u - (local.tee $8 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - (local.get $1) - ) - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -4) - ) - ) - (i32.const 0) - ) - ) - (i32.store - (local.get $8) - (local.tee $5 - (i32.add - (i32.load - (local.get $8) - ) - (i32.const 1) - ) - ) - ) - (br_if $label$193 - (i32.gt_u - (local.get $5) - (i32.const 999999999) - ) - ) - ) - ) - (local.set $5 - (i32.mul - (i32.shr_s - (i32.sub - (local.get $25) - (local.get $1) - ) - (i32.const 2) - ) - (i32.const 9) - ) - ) - (br_if $label$182 - (i32.lt_u - (local.tee $14 - (i32.load - (local.get $1) - ) - ) - (i32.const 10) - ) - ) - (local.set $6 - (i32.const 10) - ) - (loop $label$195 - (local.set $5 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$195 - (i32.ge_u - (local.get $14) - (local.tee $6 - (i32.mul - (local.get $6) - (i32.const 10) - ) - ) - ) - ) - ) - ) - ) - ) - (local.set $14 - (local.get $1) - ) - (local.set $6 - (local.get $5) - ) - (if - (i32.le_u - (local.get $13) - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - ) - (local.set $8 - (local.get $13) - ) - ) - ) - (block - (local.set $14 - (local.get $1) - ) - (local.set $6 - (local.get $5) - ) - (local.set $8 - (local.get $13) - ) - ) - ) - (local.set $32 - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (loop $label$198 - (block $label$199 - (if - (i32.le_u - (local.get $8) - (local.get $14) - ) - (block - (local.set $22 - (i32.const 0) - ) - (br $label$199) - ) - ) - (if - (i32.load - (local.tee $1 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - (local.set $22 - (i32.const 1) - ) - (block - (local.set $8 - (local.get $1) - ) - (br $label$198) - ) - ) - ) - ) - (block $label$203 - (if - (local.get $29) - (block - (local.set $1 - (if (result i32) - (i32.and - (i32.gt_s - (local.tee $1 - (i32.add - (i32.xor - (i32.and - (local.get $35) - (i32.const 1) - ) - (i32.const 1) - ) - (local.get $18) - ) - ) - (local.get $6) - ) - (i32.gt_s - (local.get $6) - (i32.const -5) - ) - ) - (block (result i32) - (local.set $5 - (i32.add - (local.get $9) - (i32.const -1) - ) - ) - (i32.sub - (i32.add - (local.get $1) - (i32.const -1) - ) - (local.get $6) - ) - ) - (block (result i32) - (local.set $5 - (i32.add - (local.get $9) - (i32.const -2) - ) - ) - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - (br_if $label$203 - (local.tee $13 - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - ) - (block $label$207 - (if - (local.get $22) - (block - (if - (i32.eqz - (local.tee $18 - (i32.load - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - ) - (block - (local.set $9 - (i32.const 9) - ) - (br $label$207) - ) - ) - (if - (i32.rem_u - (local.get $18) - (i32.const 10) - ) - (block - (local.set $9 - (i32.const 0) - ) - (br $label$207) - ) - (block - (local.set $13 - (i32.const 10) - ) - (local.set $9 - (i32.const 0) - ) - ) - ) - (loop $label$212 - (local.set $9 - (i32.add - (local.get $9) - (i32.const 1) - ) - ) - (br_if $label$212 - (i32.eqz - (i32.rem_u - (local.get $18) - (local.tee $13 - (i32.mul - (local.get $13) - (i32.const 10) - ) - ) - ) - ) - ) - ) - ) - (local.set $9 - (i32.const 9) - ) - ) - ) - (local.set $18 - (i32.add - (i32.mul - (i32.shr_s - (i32.sub - (local.get $8) - (local.get $25) - ) - (i32.const 2) - ) - (i32.const 9) - ) - (i32.const -9) - ) - ) - (if - (i32.eq - (i32.or - (local.get $5) - (i32.const 32) - ) - (i32.const 102) - ) - (block - (local.set $13 - (i32.const 0) - ) - (if - (i32.ge_s - (local.get $1) - (if (result i32) - (i32.lt_s - (local.tee $9 - (i32.sub - (local.get $18) - (local.get $9) - ) - ) - (i32.const 0) - ) - (local.tee $9 - (i32.const 0) - ) - (local.get $9) - ) - ) - (local.set $1 - (local.get $9) - ) - ) - ) - (block - (local.set $13 - (i32.const 0) - ) - (if - (i32.ge_s - (local.get $1) - (if (result i32) - (i32.lt_s - (local.tee $9 - (i32.sub - (i32.add - (local.get $18) - (local.get $6) - ) - (local.get $9) - ) - ) - (i32.const 0) - ) - (local.tee $9 - (i32.const 0) - ) - (local.get $9) - ) - ) - (local.set $1 - (local.get $9) - ) - ) - ) - ) - ) - (block - (local.set $13 - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - (local.set $1 - (local.get $18) - ) - (local.set $5 - (local.get $9) - ) - ) - ) - ) - (if - (local.tee $25 - (i32.eq - (i32.or - (local.get $5) - (i32.const 32) - ) - (i32.const 102) - ) - ) - (block - (local.set $9 - (i32.const 0) - ) - (if - (i32.le_s - (local.get $6) - (i32.const 0) - ) - (local.set $6 - (i32.const 0) - ) - ) - ) - (block - (if - (i32.lt_s - (i32.sub - (local.get $28) - (local.tee $9 - (call $23 - (i64.extend_i32_s - (if (result i32) - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (local.get $32) - (local.get $6) - ) - ) - (local.get $33) - ) - ) - ) - (i32.const 2) - ) - (loop $label$229 - (i32.store8 - (local.tee $9 - (i32.add - (local.get $9) - (i32.const -1) - ) - ) - (i32.const 48) - ) - (br_if $label$229 - (i32.lt_s - (i32.sub - (local.get $28) - (local.get $9) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.store8 - (i32.add - (local.get $9) - (i32.const -1) - ) - (i32.add - (i32.and - (i32.shr_s - (local.get $6) - (i32.const 31) - ) - (i32.const 2) - ) - (i32.const 43) - ) - ) - (i32.store8 - (local.tee $6 - (i32.add - (local.get $9) - (i32.const -2) - ) - ) - (local.get $5) - ) - (local.set $9 - (local.get $6) - ) - (local.set $6 - (i32.sub - (local.get $28) - (local.get $6) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $18 - (i32.add - (i32.add - (i32.add - (i32.add - (local.get $24) - (i32.const 1) - ) - (local.get $1) - ) - (i32.ne - (local.tee $29 - (i32.or - (local.get $1) - (local.get $13) - ) - ) - (i32.const 0) - ) - ) - (local.get $6) - ) - ) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $26) - (local.get $24) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $18) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (block $label$231 - (if - (local.get $25) - (block - (local.set $6 - (local.tee $9 - (if (result i32) - (i32.gt_u - (local.get $14) - (local.get $7) - ) - (local.get $7) - (local.get $14) - ) - ) - ) - (loop $label$235 - (local.set $5 - (call $23 - (i64.extend_i32_u - (i32.load - (local.get $6) - ) - ) - (local.get $31) - ) - ) - (block $label$236 - (if - (i32.eq - (local.get $6) - (local.get $9) - ) - (block - (br_if $label$236 - (i32.ne - (local.get $5) - (local.get $31) - ) - ) - (i32.store8 - (local.get $34) - (i32.const 48) - ) - (local.set $5 - (local.get $34) - ) - ) - (block - (br_if $label$236 - (i32.le_u - (local.get $5) - (local.get $19) - ) - ) - (drop - (call $41 - (local.get $19) - (i32.const 48) - (i32.sub - (local.get $5) - (local.get $27) - ) - ) - ) - (loop $label$239 - (br_if $label$239 - (i32.gt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $5) - (i32.sub - (local.get $41) - (local.get $5) - ) - (local.get $0) - ) - ) - ) - (if - (i32.le_u - (local.tee $5 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - (local.get $7) - ) - (block - (local.set $6 - (local.get $5) - ) - (br $label$235) - ) - ) - ) - (block $label$242 - (if - (local.get $29) - (block - (br_if $label$242 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (i32.const 1687) - (i32.const 1) - (local.get $0) - ) - ) - ) - ) - ) - (if - (i32.and - (i32.gt_s - (local.get $1) - (i32.const 0) - ) - (i32.lt_u - (local.get $5) - (local.get $8) - ) - ) - (loop $label$245 - (if - (i32.gt_u - (local.tee $7 - (call $23 - (i64.extend_i32_u - (i32.load - (local.get $5) - ) - ) - (local.get $31) - ) - ) - (local.get $19) - ) - (block - (drop - (call $41 - (local.get $19) - (i32.const 48) - (i32.sub - (local.get $7) - (local.get $27) - ) - ) - ) - (loop $label$247 - (br_if $label$247 - (i32.gt_u - (local.tee $7 - (i32.add - (local.get $7) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $7) - (if (result i32) - (i32.gt_s - (local.get $1) - (i32.const 9) - ) - (i32.const 9) - (local.get $1) - ) - (local.get $0) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $1) - (i32.const -9) - ) - ) - (if - (i32.and - (i32.gt_s - (local.get $1) - (i32.const 9) - ) - (i32.lt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - (local.get $8) - ) - ) - (block - (local.set $1 - (local.get $7) - ) - (br $label$245) - ) - (local.set $1 - (local.get $7) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (i32.add - (local.get $1) - (i32.const 9) - ) - (i32.const 9) - (i32.const 0) - ) - ) - (block - (local.set $5 - (i32.add - (local.get $14) - (i32.const 4) - ) - ) - (if - (i32.eqz - (local.get $22) - ) - (local.set $8 - (local.get $5) - ) - ) - (if - (i32.gt_s - (local.get $1) - (i32.const -1) - ) - (block - (local.set $13 - (i32.eqz - (local.get $13) - ) - ) - (local.set $7 - (local.get $14) - ) - (local.set $5 - (local.get $1) - ) - (loop $label$256 - (if - (i32.eq - (local.tee $1 - (call $23 - (i64.extend_i32_u - (i32.load - (local.get $7) - ) - ) - (local.get $31) - ) - ) - (local.get $31) - ) - (block - (i32.store8 - (local.get $34) - (i32.const 48) - ) - (local.set $1 - (local.get $34) - ) - ) - ) - (block $label$258 - (if - (i32.eq - (local.get $7) - (local.get $14) - ) - (block - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (i32.const 1) - (local.get $0) - ) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (br_if $label$258 - (i32.and - (local.get $13) - (i32.lt_s - (local.get $5) - (i32.const 1) - ) - ) - ) - (br_if $label$258 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (i32.const 1687) - (i32.const 1) - (local.get $0) - ) - ) - ) - (block - (br_if $label$258 - (i32.le_u - (local.get $1) - (local.get $19) - ) - ) - (drop - (call $41 - (local.get $19) - (i32.const 48) - (i32.add - (local.get $1) - (local.get $43) - ) - ) - ) - (loop $label$262 - (br_if $label$262 - (i32.gt_u - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - ) - (local.set $6 - (i32.sub - (local.get $41) - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (if (result i32) - (i32.gt_s - (local.get $5) - (local.get $6) - ) - (local.get $6) - (local.get $5) - ) - (local.get $0) - ) - ) - ) - (br_if $label$256 - (i32.and - (i32.lt_u - (local.tee $7 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (local.get $8) - ) - (i32.gt_s - (local.tee $5 - (i32.sub - (local.get $5) - (local.get $6) - ) - ) - (i32.const -1) - ) - ) - ) - (local.set $1 - (local.get $5) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (i32.add - (local.get $1) - (i32.const 18) - ) - (i32.const 18) - (i32.const 0) - ) - (br_if $label$231 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $9) - (i32.sub - (local.get $28) - (local.get $9) - ) - (local.get $0) - ) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $18) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $18) - (local.get $10) - ) - (local.set $10 - (local.get $18) - ) - ) - ) - (block - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $8 - (i32.add - (if (result i32) - (local.tee $6 - (i32.or - (f64.ne - (local.get $52) - (local.get $52) - ) - (i32.const 0) - ) - ) - (local.tee $24 - (i32.const 0) - ) - (local.get $24) - ) - (i32.const 3) - ) - ) - (local.get $7) - ) - (if - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 32) - ) - ) - (block - (drop - (call $21 - (local.get $26) - (local.get $24) - (local.get $0) - ) - ) - (local.set $1 - (i32.load - (local.get $0) - ) - ) - ) - ) - (local.set $7 - (if (result i32) - (local.tee $5 - (i32.ne - (i32.and - (local.get $9) - (i32.const 32) - ) - (i32.const 0) - ) - ) - (i32.const 1671) - (i32.const 1675) - ) - ) - (local.set $5 - (if (result i32) - (local.get $5) - (i32.const 1679) - (i32.const 1683) - ) - ) - (if - (i32.eqz - (local.get $6) - ) - (local.set $5 - (local.get $7) - ) - ) - (if - (i32.eqz - (i32.and - (local.get $1) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $5) - (i32.const 3) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $8) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $8) - (local.get $10) - ) - (local.set $10 - (local.get $8) - ) - ) - ) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $7 - (local.get $5) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1635) - ) - (local.set $5 - (local.get $21) - ) - (br $label$70) - ) - (local.set $7 - (i32.and - (local.get $9) - (i32.const 32) - ) - ) - (local.set $7 - (if (result i32) - (i64.eq - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (block (result i32) - (local.set $50 - (i64.const 0) - ) - (local.get $21) - ) - (block (result i32) - (local.set $1 - (local.get $21) - ) - (loop $label$280 - (i32.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i32.or - (i32.load8_u - (i32.add - (i32.and - (i32.wrap_i64 - (local.get $50) - ) - (i32.const 15) - ) - (i32.const 1619) - ) - ) - (local.get $7) - ) - ) - (br_if $label$280 - (i64.ne - (local.tee $50 - (i64.shr_u - (local.get $50) - (i64.const 4) - ) - ) - (i64.const 0) - ) - ) - ) - (local.set $50 - (i64.load - (local.get $16) - ) - ) - (local.get $1) - ) - ) - ) - (local.set $8 - (i32.add - (i32.shr_s - (local.get $9) - (i32.const 4) - ) - (i32.const 1635) - ) - ) - (if - (local.tee $1 - (i32.or - (i32.eqz - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - (i64.eq - (local.get $50) - (i64.const 0) - ) - ) - ) - (local.set $8 - (i32.const 1635) - ) - ) - (local.set $6 - (if (result i32) - (local.get $1) - (i32.const 0) - (i32.const 2) - ) - ) - (br $label$71) - ) - (local.set $7 - (call $23 - (local.get $50) - (local.get $21) - ) - ) - (br $label$71) - ) - (local.set $14 - (i32.eqz - (local.tee $13 - (call $17 - (local.get $1) - (i32.const 0) - (local.get $5) - ) - ) - ) - ) - (local.set $8 - (i32.sub - (local.get $13) - (local.get $1) - ) - ) - (local.set $9 - (i32.add - (local.get $1) - (local.get $5) - ) - ) - (local.set $12 - (local.get $7) - ) - (local.set $7 - (if (result i32) - (local.get $14) - (local.get $5) - (local.get $8) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1635) - ) - (local.set $5 - (if (result i32) - (local.get $14) - (local.get $9) - (local.get $13) - ) - ) - (br $label$70) - ) - (local.set $1 - (i32.const 0) - ) - (local.set $5 - (i32.const 0) - ) - (local.set $8 - (local.get $7) - ) - (loop $label$288 - (block $label$289 - (br_if $label$289 - (i32.eqz - (local.tee $9 - (i32.load - (local.get $8) - ) - ) - ) - ) - (br_if $label$289 - (i32.or - (i32.lt_s - (local.tee $5 - (call $26 - (local.get $36) - (local.get $9) - ) - ) - (i32.const 0) - ) - (i32.gt_u - (local.get $5) - (i32.sub - (local.get $6) - (local.get $1) - ) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (br_if $label$288 - (i32.gt_u - (local.get $6) - (local.tee $1 - (i32.add - (local.get $5) - (local.get $1) - ) - ) - ) - ) - ) - ) - (if - (i32.lt_s - (local.get $5) - (i32.const 0) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $1) - (local.get $12) - ) - (if - (local.get $1) - (block - (local.set $5 - (i32.const 0) - ) - (loop $label$292 - (br_if $label$72 - (i32.eqz - (local.tee $8 - (i32.load - (local.get $7) - ) - ) - ) - ) - (br_if $label$72 - (i32.gt_s - (local.tee $5 - (i32.add - (local.tee $8 - (call $26 - (local.get $36) - (local.get $8) - ) - ) - (local.get $5) - ) - ) - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $36) - (local.get $8) - (local.get $0) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (br_if $label$292 - (i32.lt_u - (local.get $5) - (local.get $1) - ) - ) - (br $label$72) - ) - ) - (block - (local.set $1 - (i32.const 0) - ) - (br $label$72) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $1) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.le_s - (local.get $10) - (local.get $1) - ) - (local.set $10 - (local.get $1) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $1 - (i32.and - (local.get $12) - (i32.const -65537) - ) - ) - (if - (i32.gt_s - (local.get $5) - (i32.const -1) - ) - (local.set $12 - (local.get $1) - ) - ) - (local.set $5 - (if (result i32) - (i32.or - (local.get $5) - (local.tee $9 - (i64.ne - (i64.load - (local.get $16) - ) - (i64.const 0) - ) - ) - ) - (block (result i32) - (local.set $1 - (local.get $7) - ) - (if - (i32.gt_s - (local.get $5) - (local.tee $7 - (i32.add - (i32.xor - (i32.and - (local.get $9) - (i32.const 1) - ) - (i32.const 1) - ) - (i32.sub - (local.get $38) - (local.get $7) - ) - ) - ) - ) - (local.set $7 - (local.get $5) - ) - ) - (local.get $21) - ) - (block (result i32) - (local.set $1 - (local.get $21) - ) - (local.set $7 - (i32.const 0) - ) - (local.get $21) - ) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (if (result i32) - (i32.lt_s - (local.get $10) - (local.tee $5 - (i32.add - (if (result i32) - (i32.lt_s - (local.get $7) - (local.tee $9 - (i32.sub - (local.get $5) - (local.get $1) - ) - ) - ) - (local.tee $7 - (local.get $9) - ) - (local.get $7) - ) - (local.get $6) - ) - ) - ) - (local.tee $10 - (local.get $5) - ) - (local.get $10) - ) - (local.get $5) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $8) - (local.get $6) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (call $25 - (local.get $0) - (i32.const 48) - (local.get $7) - (local.get $9) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $21 - (local.get $1) - (local.get $9) - (local.get $0) - ) - ) - ) - (call $25 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - ) - (br $label$2) - ) - (if - (i32.eqz - (local.get $0) - ) - (if - (local.get $17) - (block - (local.set $0 - (i32.const 1) - ) - (loop $label$308 - (if - (local.tee $1 - (i32.load - (i32.add - (local.get $4) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - ) - ) - (block - (call $22 - (i32.add - (local.get $3) - (i32.shl - (local.get $0) - (i32.const 3) - ) - ) - (local.get $1) - (local.get $2) - ) - (br_if $label$308 - (i32.lt_s - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.const 10) - ) - ) - (local.set $15 - (i32.const 1) - ) - (br $label$2) - ) - ) - ) - (loop $label$310 - (if - (i32.load - (i32.add - (local.get $4) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$2) - ) - ) - (br_if $label$310 - (i32.lt_s - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.const 10) - ) - ) - (local.set $15 - (i32.const 1) - ) - ) - ) - (local.set $15 - (i32.const 0) - ) - ) - ) - ) - (global.set $global$1 - (local.get $23) - ) - (local.get $15) + (func $15 (;28;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $8 + global.get $global$1 + i32.const 48 + i32.add + global.set $global$1 + local.get $8 + i32.const 16 + i32.add + local.set $9 + local.get $8 + local.set $10 + local.get $8 + i32.const 32 + i32.add + local.tee $3 + local.get $0 + i32.const 28 + i32.add + local.tee $6 + i32.load + local.tee $4 + i32.store + local.get $3 + local.get $0 + i32.const 20 + i32.add + local.tee $11 + i32.load + local.get $4 + i32.sub + local.tee $5 + i32.store offset=4 + local.get $3 + local.get $1 + i32.store offset=8 + local.get $3 + local.get $2 + i32.store offset=12 + local.get $0 + i32.const 60 + i32.add + local.set $13 + local.get $0 + i32.const 44 + i32.add + local.set $14 + local.get $3 + local.set $1 + i32.const 2 + local.set $4 + local.get $5 + local.get $2 + i32.add + local.set $12 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + loop $label$5 ;; label = @5 + i32.const 3588 + i32.load + if ;; label = @6 + block ;; label = @7 + i32.const 1 + local.get $0 + call $fimport$9 + local.get $10 + local.get $13 + i32.load + i32.store + local.get $10 + local.get $1 + i32.store offset=4 + local.get $10 + local.get $4 + i32.store offset=8 + i32.const 146 + local.get $10 + call $fimport$15 + call $11 + local.set $3 + i32.const 0 + call $fimport$7 + end + else + block ;; label = @7 + local.get $9 + local.get $13 + i32.load + i32.store + local.get $9 + local.get $1 + i32.store offset=4 + local.get $9 + local.get $4 + i32.store offset=8 + i32.const 146 + local.get $9 + call $fimport$15 + call $11 + local.set $3 + end + end + local.get $12 + local.get $3 + i32.eq + br_if 1 (;@4;) + local.get $3 + i32.const 0 + i32.lt_s + br_if 2 (;@3;) + local.get $3 + local.get $1 + i32.load offset=4 + local.tee $5 + i32.gt_u + if (result i32) ;; label = @6 + block (result i32) ;; label = @7 + local.get $6 + local.get $14 + i32.load + local.tee $7 + i32.store + local.get $11 + local.get $7 + i32.store + local.get $1 + i32.load offset=12 + local.set $7 + local.get $1 + i32.const 8 + i32.add + local.set $1 + local.get $4 + i32.const -1 + i32.add + local.set $4 + local.get $3 + local.get $5 + i32.sub + end + else + local.get $4 + i32.const 2 + i32.eq + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + local.get $6 + local.get $6 + i32.load + local.get $3 + i32.add + i32.store + local.get $5 + local.set $7 + i32.const 2 + local.set $4 + local.get $3 + end + else + block (result i32) ;; label = @8 + local.get $5 + local.set $7 + local.get $3 + end + end + end + local.set $5 + local.get $1 + local.get $1 + i32.load + local.get $5 + i32.add + i32.store + local.get $1 + local.get $7 + local.get $5 + i32.sub + i32.store offset=4 + local.get $12 + local.get $3 + i32.sub + local.set $12 + br 0 (;@5;) + end + end + local.get $0 + local.get $14 + i32.load + local.tee $1 + local.get $0 + i32.load offset=48 + i32.add + i32.store offset=16 + local.get $6 + local.get $1 + i32.store + local.get $11 + local.get $1 + i32.store + br 1 (;@2;) + end + local.get $0 + i32.const 0 + i32.store offset=16 + local.get $6 + i32.const 0 + i32.store + local.get $11 + i32.const 0 + i32.store + local.get $0 + local.get $0 + i32.load + i32.const 32 + i32.or + i32.store + local.get $4 + i32.const 2 + i32.eq + if (result i32) ;; label = @3 + i32.const 0 + else + local.get $2 + local.get $1 + i32.load offset=4 + i32.sub + end + local.set $2 + end + local.get $8 + global.set $global$1 + local.get $2 + end ) - ) - (func $20 (; 33 ;) (type $1) (param $0 i32) (result i32) - (i32.const 0) - ) - (func $21 (; 34 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (block $label$1 (result i32) - (block $label$2 - (block $label$3 - (br_if $label$3 - (local.tee $3 - (i32.load - (local.tee $4 - (i32.add - (local.get $2) - (i32.const 16) - ) - ) - ) - ) - ) - (if - (call $30 - (local.get $2) - ) - (local.set $3 - (i32.const 0) - ) - (block - (local.set $3 - (i32.load - (local.get $4) - ) - ) - (br $label$3) - ) - ) - (br $label$2) - ) - (if - (i32.lt_u - (i32.sub - (local.get $3) - (local.tee $4 - (i32.load - (local.tee $5 - (i32.add - (local.get $2) - (i32.const 20) - ) - ) - ) - ) - ) - (local.get $1) - ) - (block - (local.set $3 - (call_indirect (type $0) - (local.get $2) - (local.get $0) - (local.get $1) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $2) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - ) - (br $label$2) - ) - ) - (local.set $2 - (block $label$7 (result i32) - (if (result i32) - (i32.gt_s - (i32.load8_s offset=75 - (local.get $2) - ) - (i32.const -1) - ) - (block (result i32) - (local.set $3 - (local.get $1) - ) - (loop $label$9 - (drop - (br_if $label$7 - (i32.const 0) - (i32.eqz - (local.get $3) - ) - ) - ) - (if - (i32.ne - (i32.load8_s - (i32.add - (local.get $0) - (local.tee $6 - (i32.add - (local.get $3) - (i32.const -1) - ) - ) - ) - ) - (i32.const 10) - ) - (block - (local.set $3 - (local.get $6) - ) - (br $label$9) - ) - ) - ) - (br_if $label$2 - (i32.lt_u - (call_indirect (type $0) - (local.get $2) - (local.get $0) - (local.get $3) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $2) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - (local.get $3) - ) - ) - (local.set $4 - (i32.load - (local.get $5) - ) - ) - (local.set $1 - (i32.sub - (local.get $1) - (local.get $3) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (local.get $3) - ) - ) - (local.get $3) - ) - (i32.const 0) - ) - ) - ) - (drop - (call $42 - (local.get $4) - (local.get $0) - (local.get $1) - ) - ) - (i32.store - (local.get $5) - (i32.add - (i32.load - (local.get $5) - ) - (local.get $1) - ) - ) - (local.set $3 - (i32.add - (local.get $2) - (local.get $1) - ) - ) - ) - (local.get $3) + (func $16 (;29;) (type $2) (param $0 i32) + local.get $0 + i32.load offset=68 + i32.eqz + if ;; label = @1 + local.get $0 + call $13 + end ) - ) - (func $22 (; 35 ;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i64) - (local $5 f64) - (block $label$1 - (if - (i32.le_u - (local.get $1) - (i32.const 20) - ) - (block $label$3 - (block $label$4 - (block $label$5 - (block $label$6 - (block $label$7 - (block $label$8 - (block $label$9 - (block $label$10 - (block $label$11 - (block $label$12 - (block $label$13 - (br_table $label$13 $label$12 $label$11 $label$10 $label$9 $label$8 $label$7 $label$6 $label$5 $label$4 $label$3 - (i32.sub - (local.get $1) - (i32.const 9) - ) - ) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i32.store - (local.get $0) - (local.get $3) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (local.get $3) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (local.get $3) - ) - ) - (br $label$1) - ) - (local.set $4 - (i64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (i64.store - (local.get $0) - (local.get $4) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (i32.shr_s - (i32.shl - (i32.and - (local.get $3) - (i32.const 65535) - ) - (i32.const 16) - ) - (i32.const 16) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (i32.and - (local.get $3) - (i32.const 65535) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (i32.shr_s - (i32.shl - (i32.and - (local.get $3) - (i32.const 255) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (i32.and - (local.get $3) - (i32.const 255) - ) - ) - ) - (br $label$1) - ) - (local.set $5 - (f64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (f64.store - (local.get $0) - (local.get $5) - ) - (br $label$1) - ) - (local.set $5 - (f64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (f64.store - (local.get $0) - (local.get $5) - ) - ) - ) + (func $17 (;30;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + local.get $1 + i32.const 255 + i32.and + local.set $5 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + local.get $2 + i32.const 0 + i32.ne + local.tee $4 + local.get $0 + i32.const 3 + i32.and + i32.const 0 + i32.ne + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $1 + i32.const 255 + i32.and + local.set $4 + local.get $2 + local.set $3 + local.get $0 + local.set $2 + loop $label$6 ;; label = @7 + local.get $2 + i32.load8_s + local.get $4 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $3 + local.set $0 + br 6 (;@3;) + end + end + local.get $3 + i32.const -1 + i32.add + local.tee $3 + i32.const 0 + i32.ne + local.tee $0 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 3 + i32.and + i32.const 0 + i32.ne + i32.and + br_if 0 (;@7;) + br 3 (;@4;) + end + end + else + block ;; label = @6 + local.get $2 + local.set $3 + local.get $0 + local.set $2 + local.get $4 + local.set $0 + end + end + end + local.get $0 + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.set $0 + br 2 (;@3;) + end + else + i32.const 0 + local.set $0 + end + br 1 (;@2;) + end + local.get $2 + i32.load8_s + local.get $1 + i32.const 255 + i32.and + local.tee $1 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.ne + if ;; label = @3 + block ;; label = @4 + local.get $5 + i32.const 16843009 + i32.mul + local.set $3 + block $label$12 ;; label = @5 + block $label$13 ;; label = @6 + local.get $0 + i32.const 3 + i32.le_u + br_if 0 (;@6;) + loop $label$14 ;; label = @7 + local.get $2 + i32.load + local.get $3 + i32.xor + local.tee $4 + i32.const -2139062144 + i32.and + i32.const -2139062144 + i32.xor + local.get $4 + i32.const -16843009 + i32.add + i32.and + i32.eqz + if ;; label = @8 + block ;; label = @9 + local.get $2 + i32.const 4 + i32.add + local.set $2 + local.get $0 + i32.const -4 + i32.add + local.tee $0 + i32.const 3 + i32.gt_u + br_if 2 (;@7;) + br 3 (;@6;) + end + end + end + br 1 (;@5;) + end + local.get $0 + i32.eqz + if ;; label = @6 + block ;; label = @7 + i32.const 0 + local.set $0 + br 5 (;@2;) + end + end + end + loop $label$17 ;; label = @5 + local.get $2 + i32.load8_s + local.get $1 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eq + br_if 3 (;@2;) + local.get $2 + i32.const 1 + i32.add + local.set $2 + local.get $0 + i32.const -1 + i32.add + local.tee $0 + br_if 0 (;@5;) + i32.const 0 + local.set $0 + end + end + end + end + local.get $0 + if (result i32) ;; label = @2 + local.get $2 + else + i32.const 0 + end + end ) - ) - (func $23 (; 36 ;) (type $9) (param $0 i64) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i64) - (block $label$1 (result i32) - (local.set $2 - (i32.wrap_i64 - (local.get $0) - ) - ) - (if - (i64.gt_u - (local.get $0) - (i64.const 4294967295) - ) - (block - (loop $label$3 - (i64.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i64.or - (i64.rem_u - (local.get $0) - (i64.const 10) - ) - (i64.const 48) - ) - ) - (local.set $4 - (i64.div_u - (local.get $0) - (i64.const 10) - ) - ) - (if - (i64.gt_u - (local.get $0) - (i64.const 42949672959) - ) - (block - (local.set $0 - (local.get $4) - ) - (br $label$3) - ) - ) - ) - (local.set $2 - (i32.wrap_i64 - (local.get $4) - ) - ) - ) - ) - (if - (local.get $2) - (loop $label$6 - (i32.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i32.or - (i32.rem_u - (local.get $2) - (i32.const 10) - ) - (i32.const 48) - ) - ) - (local.set $3 - (i32.div_u - (local.get $2) - (i32.const 10) - ) - ) - (if - (i32.ge_u - (local.get $2) - (i32.const 10) - ) - (block - (local.set $2 - (local.get $3) - ) - (br $label$6) - ) - ) - ) - ) - (local.get $1) + (func $18 (;31;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 224 + i32.add + global.set $global$1 + local.get $4 + i32.const 136 + i32.add + local.set $5 + local.get $4 + i32.const 80 + i32.add + local.tee $3 + i64.const 0 + i64.store align=4 + local.get $3 + i64.const 0 + i64.store offset=8 align=4 + local.get $3 + i64.const 0 + i64.store offset=16 align=4 + local.get $3 + i64.const 0 + i64.store offset=24 align=4 + local.get $3 + i64.const 0 + i64.store offset=32 align=4 + local.get $4 + i32.const 120 + i32.add + local.tee $6 + local.get $2 + i32.load + i32.store + i32.const 0 + local.get $1 + local.get $6 + local.get $4 + local.tee $2 + local.get $3 + call $19 + i32.const 0 + i32.lt_s + if ;; label = @2 + i32.const -1 + local.set $1 + else + block ;; label = @3 + local.get $0 + i32.load offset=76 + i32.const -1 + i32.gt_s + if (result i32) ;; label = @4 + local.get $0 + call $20 + else + i32.const 0 + end + local.set $12 + local.get $0 + i32.load + local.set $7 + local.get $0 + i32.load8_s offset=74 + i32.const 1 + i32.lt_s + if ;; label = @4 + local.get $0 + local.get $7 + i32.const -33 + i32.and + i32.store + end + local.get $0 + i32.const 48 + i32.add + local.tee $8 + i32.load + if ;; label = @4 + local.get $0 + local.get $1 + local.get $6 + local.get $2 + local.get $3 + call $19 + local.set $1 + else + block ;; label = @5 + local.get $0 + i32.const 44 + i32.add + local.tee $9 + i32.load + local.set $10 + local.get $9 + local.get $5 + i32.store + local.get $0 + i32.const 28 + i32.add + local.tee $13 + local.get $5 + i32.store + local.get $0 + i32.const 20 + i32.add + local.tee $11 + local.get $5 + i32.store + local.get $8 + i32.const 80 + i32.store + local.get $0 + i32.const 16 + i32.add + local.tee $14 + local.get $5 + i32.const 80 + i32.add + i32.store + local.get $0 + local.get $1 + local.get $6 + local.get $2 + local.get $3 + call $19 + local.set $1 + local.get $10 + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 0 + i32.const 0 + local.get $0 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + drop + local.get $11 + i32.load + i32.eqz + if ;; label = @8 + i32.const -1 + local.set $1 + end + local.get $9 + local.get $10 + i32.store + local.get $8 + i32.const 0 + i32.store + local.get $14 + i32.const 0 + i32.store + local.get $13 + i32.const 0 + i32.store + local.get $11 + i32.const 0 + i32.store + end + end + end + end + local.get $0 + local.get $0 + i32.load + local.tee $2 + local.get $7 + i32.const 32 + i32.and + i32.or + i32.store + local.get $12 + if ;; label = @4 + local.get $0 + call $13 + end + local.get $2 + i32.const 32 + i32.and + if ;; label = @4 + i32.const -1 + local.set $1 + end + end + end + local.get $4 + global.set $global$1 + local.get $1 + end ) - ) - (func $24 (; 37 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.const 0) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (loop $label$5 - (br_if $label$4 - (i32.eq - (i32.load8_u - (i32.add - (local.get $1) - (i32.const 1689) - ) - ) - (local.get $0) - ) - ) - (br_if $label$5 - (i32.ne - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (i32.const 87) - ) - ) - (local.set $1 - (i32.const 87) - ) - (local.set $0 - (i32.const 1777) - ) - (br $label$3) - ) - ) - (if - (local.get $1) - (block - (local.set $0 - (i32.const 1777) - ) - (br $label$3) - ) - (local.set $0 - (i32.const 1777) - ) - ) - (br $label$2) - ) - (loop $label$8 - (local.set $2 - (local.get $0) - ) - (loop $label$9 - (local.set $0 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (if - (i32.load8_s - (local.get $2) - ) - (block - (local.set $2 - (local.get $0) - ) - (br $label$9) - ) - ) - ) - (br_if $label$8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - ) - (local.get $0) + (func $19 (;32;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) + (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) (local $22 i32) (local $23 i32) (local $24 i32) (local $25 i32) (local $26 i32) (local $27 i32) (local $28 i32) (local $29 i32) (local $30 i32) (local $31 i32) (local $32 i32) (local $33 i32) (local $34 i32) (local $35 i32) (local $36 i32) (local $37 i32) (local $38 i32) (local $39 i32) (local $40 i32) (local $41 i32) (local $42 i32) (local $43 i32) (local $44 i32) (local $45 i32) (local $46 i32) (local $47 i32) (local $48 i32) (local $49 i32) (local $50 i64) (local $51 i64) (local $52 f64) (local $53 f64) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $23 + global.get $global$1 + i32.const 624 + i32.add + global.set $global$1 + local.get $23 + i32.const 16 + i32.add + local.set $20 + local.get $23 + local.set $16 + local.get $23 + i32.const 528 + i32.add + local.set $36 + local.get $0 + i32.const 0 + i32.ne + local.set $30 + local.get $23 + i32.const 536 + i32.add + local.tee $17 + i32.const 40 + i32.add + local.tee $21 + local.set $38 + local.get $17 + i32.const 39 + i32.add + local.set $39 + local.get $23 + i32.const 8 + i32.add + local.tee $37 + i32.const 4 + i32.add + local.set $42 + i32.const 0 + local.get $23 + i32.const 588 + i32.add + local.tee $19 + local.tee $27 + i32.sub + local.set $43 + local.get $23 + i32.const 576 + i32.add + local.tee $17 + i32.const 12 + i32.add + local.set $33 + local.get $17 + i32.const 11 + i32.add + local.set $40 + local.get $33 + local.tee $28 + local.get $27 + i32.sub + local.set $44 + i32.const -2 + local.get $27 + i32.sub + local.set $45 + local.get $28 + i32.const 2 + i32.add + local.set $46 + local.get $23 + i32.const 24 + i32.add + local.tee $47 + i32.const 288 + i32.add + local.set $48 + local.get $19 + i32.const 9 + i32.add + local.tee $31 + local.set $41 + local.get $19 + i32.const 8 + i32.add + local.set $34 + i32.const 0 + local.set $15 + i32.const 0 + local.set $10 + i32.const 0 + local.set $17 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + loop $label$4 ;; label = @4 + block $label$5 ;; label = @5 + local.get $15 + i32.const -1 + i32.gt_s + if ;; label = @6 + local.get $10 + i32.const 2147483647 + local.get $15 + i32.sub + i32.gt_s + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + call $12 + i32.const 75 + i32.store + i32.const -1 + end + else + local.get $10 + local.get $15 + i32.add + end + local.set $15 + end + local.get $1 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eqz + br_if 2 (;@3;) + local.get $1 + local.set $11 + block $label$9 ;; label = @6 + block $label$10 ;; label = @7 + loop $label$11 ;; label = @8 + block $label$12 ;; label = @9 + block $label$13 ;; label = @10 + block $label$14 ;; label = @11 + block $label$15 ;; label = @12 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 0 + i32.sub + br_table 1 (;@11;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 0 (;@12;) 2 (;@10;) + end + local.get $11 + local.set $5 + br 4 (;@7;) + end + local.get $11 + local.set $5 + br 1 (;@9;) + end + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.set $5 + br 1 (;@8;) + end + end + br 1 (;@6;) + end + loop $label$16 ;; label = @7 + local.get $5 + i32.load8_s offset=1 + i32.const 37 + i32.ne + br_if 1 (;@6;) + local.get $11 + i32.const 1 + i32.add + local.set $11 + local.get $5 + i32.const 2 + i32.add + local.tee $5 + i32.load8_s + i32.const 37 + i32.eq + br_if 0 (;@7;) + end + end + local.get $11 + local.get $1 + i32.sub + local.set $10 + local.get $30 + if ;; label = @6 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @7 + local.get $1 + local.get $10 + local.get $0 + call $21 + drop + end + end + local.get $10 + if ;; label = @6 + block ;; label = @7 + local.get $5 + local.set $1 + br 3 (;@4;) + end + end + local.get $5 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $10 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $9 + i32.const 10 + i32.lt_u + if (result i32) ;; label = @6 + block (result i32) ;; label = @7 + local.get $5 + i32.const 3 + i32.add + local.set $10 + local.get $5 + i32.load8_s offset=2 + i32.const 36 + i32.eq + local.tee $12 + if ;; label = @8 + local.get $10 + local.set $11 + end + local.get $12 + if ;; label = @8 + i32.const 1 + local.set $17 + end + local.get $11 + i32.load8_s + local.set $5 + local.get $12 + i32.eqz + if ;; label = @8 + i32.const -1 + local.set $9 + end + local.get $17 + end + else + block (result i32) ;; label = @7 + local.get $10 + local.set $5 + i32.const -1 + local.set $9 + local.get $17 + end + end + local.set $10 + block $label$25 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + local.tee $12 + i32.const 32 + i32.lt_u + if ;; label = @7 + block ;; label = @8 + i32.const 0 + local.set $17 + loop $label$27 ;; label = @9 + i32.const 1 + local.get $12 + i32.shl + i32.const 75913 + i32.and + i32.eqz + br_if 3 (;@6;) + i32.const 1 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + i32.shl + local.get $17 + i32.or + local.set $17 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + local.tee $12 + i32.const 32 + i32.lt_u + br_if 0 (;@9;) + end + end + else + i32.const 0 + local.set $17 + end + end + block $label$29 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 42 + i32.eq + if ;; label = @7 + block ;; label = @8 + block $label$31 (result i32) ;; label = @9 + block $label$32 ;; label = @10 + local.get $11 + i32.const 1 + i32.add + local.tee $7 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $12 + i32.const 10 + i32.ge_u + br_if 0 (;@10;) + local.get $11 + i32.load8_s offset=2 + i32.const 36 + i32.ne + br_if 0 (;@10;) + local.get $4 + local.get $12 + i32.const 2 + i32.shl + i32.add + i32.const 10 + i32.store + i32.const 1 + local.set $8 + local.get $3 + local.get $7 + i32.load8_s + i32.const -48 + i32.add + i32.const 3 + i32.shl + i32.add + i64.load + i32.wrap_i64 + local.set $10 + local.get $11 + i32.const 3 + i32.add + br 1 (;@9;) + end + local.get $10 + if ;; label = @10 + block ;; label = @11 + i32.const -1 + local.set $15 + br 6 (;@5;) + end + end + local.get $30 + i32.eqz + if ;; label = @10 + block ;; label = @11 + local.get $17 + local.set $12 + i32.const 0 + local.set $17 + local.get $7 + local.set $11 + i32.const 0 + local.set $10 + br 5 (;@6;) + end + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $11 + i32.load + local.set $10 + local.get $2 + local.get $11 + i32.const 4 + i32.add + i32.store + i32.const 0 + local.set $8 + local.get $7 + end + local.set $11 + local.get $17 + i32.const 8192 + i32.or + local.set $12 + i32.const 0 + local.get $10 + i32.sub + local.set $7 + local.get $11 + i32.load8_s + local.set $5 + local.get $10 + i32.const 0 + i32.lt_s + local.tee $6 + i32.eqz + if ;; label = @9 + local.get $17 + local.set $12 + end + local.get $8 + local.set $17 + local.get $6 + if ;; label = @9 + local.get $7 + local.set $10 + end + end + else + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $12 + i32.const 10 + i32.lt_u + if ;; label = @8 + block ;; label = @9 + i32.const 0 + local.set $7 + local.get $12 + local.set $5 + loop $label$39 ;; label = @10 + local.get $7 + i32.const 10 + i32.mul + local.get $5 + i32.add + local.set $7 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $12 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + br_if 0 (;@10;) + end + local.get $7 + i32.const 0 + i32.lt_s + if ;; label = @10 + block ;; label = @11 + i32.const -1 + local.set $15 + br 6 (;@5;) + end + else + block ;; label = @11 + local.get $12 + local.set $5 + local.get $17 + local.set $12 + local.get $10 + local.set $17 + local.get $7 + local.set $10 + end + end + end + else + block ;; label = @9 + local.get $17 + local.set $12 + local.get $10 + local.set $17 + i32.const 0 + local.set $10 + end + end + end + end + block $label$43 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 46 + i32.eq + if ;; label = @7 + block ;; label = @8 + local.get $11 + i32.const 1 + i32.add + local.tee $7 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 42 + i32.ne + if ;; label = @9 + block ;; label = @10 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + if ;; label = @11 + block ;; label = @12 + local.get $7 + local.set $11 + i32.const 0 + local.set $7 + end + else + block ;; label = @12 + i32.const 0 + local.set $5 + local.get $7 + local.set $11 + br 6 (;@6;) + end + end + loop $label$48 ;; label = @11 + local.get $7 + i32.const 10 + i32.mul + local.get $5 + i32.add + local.set $5 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + i32.const -48 + i32.add + local.tee $8 + i32.const 10 + i32.ge_u + br_if 5 (;@6;) + local.get $5 + local.set $7 + local.get $8 + local.set $5 + br 0 (;@11;) + end + end + end + local.get $11 + i32.const 2 + i32.add + local.tee $7 + i32.load8_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + if ;; label = @9 + local.get $11 + i32.load8_s offset=3 + i32.const 36 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.get $5 + i32.const 2 + i32.shl + i32.add + i32.const 10 + i32.store + local.get $3 + local.get $7 + i32.load8_s + i32.const -48 + i32.add + i32.const 3 + i32.shl + i32.add + i64.load + i32.wrap_i64 + local.set $5 + local.get $11 + i32.const 4 + i32.add + local.set $11 + br 5 (;@6;) + end + end + end + local.get $17 + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + end + local.get $30 + if (result i32) ;; label = @9 + block (result i32) ;; label = @10 + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $11 + i32.load + local.set $5 + local.get $2 + local.get $11 + i32.const 4 + i32.add + i32.store + local.get $7 + end + else + block (result i32) ;; label = @10 + i32.const 0 + local.set $5 + local.get $7 + end + end + local.set $11 + end + else + i32.const -1 + local.set $5 + end + end + local.get $11 + local.set $7 + i32.const 0 + local.set $8 + loop $label$55 ;; label = @6 + local.get $7 + i32.load8_s + i32.const -65 + i32.add + local.tee $6 + i32.const 57 + i32.gt_u + if ;; label = @7 + block ;; label = @8 + i32.const -1 + local.set $15 + br 3 (;@5;) + end + end + local.get $7 + i32.const 1 + i32.add + local.set $11 + local.get $8 + i32.const 58 + i32.mul + i32.const 1155 + i32.add + local.get $6 + i32.add + i32.load8_s + local.tee $13 + i32.const 255 + i32.and + local.tee $6 + i32.const -1 + i32.add + i32.const 8 + i32.lt_u + if ;; label = @7 + block ;; label = @8 + local.get $11 + local.set $7 + local.get $6 + local.set $8 + br 2 (;@6;) + end + end + end + local.get $13 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eqz + if ;; label = @6 + block ;; label = @7 + i32.const -1 + local.set $15 + br 2 (;@5;) + end + end + local.get $9 + i32.const -1 + i32.gt_s + local.set $14 + block $label$59 ;; label = @6 + block $label$60 ;; label = @7 + local.get $13 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 19 + i32.eq + if ;; label = @8 + local.get $14 + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + else + br 2 (;@7;) + end + else + block ;; label = @9 + local.get $14 + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.get $9 + i32.const 2 + i32.shl + i32.add + local.get $6 + i32.store + local.get $16 + local.get $3 + local.get $9 + i32.const 3 + i32.shl + i32.add + i64.load + i64.store + br 4 (;@7;) + end + end + local.get $30 + i32.eqz + if ;; label = @10 + block ;; label = @11 + i32.const 0 + local.set $15 + br 6 (;@5;) + end + end + local.get $16 + local.get $6 + local.get $2 + call $22 + end + end + br 1 (;@6;) + end + local.get $30 + i32.eqz + if ;; label = @7 + block ;; label = @8 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 4 (;@4;) + end + end + end + local.get $7 + i32.load8_s + local.tee $7 + i32.const -33 + i32.and + local.set $9 + local.get $8 + i32.const 0 + i32.ne + local.get $7 + i32.const 15 + i32.and + i32.const 3 + i32.eq + i32.and + i32.eqz + if ;; label = @6 + local.get $7 + local.set $9 + end + local.get $12 + i32.const -65537 + i32.and + local.set $7 + local.get $12 + i32.const 8192 + i32.and + if ;; label = @6 + local.get $7 + local.set $12 + end + block $label$70 ;; label = @6 + block $label$71 ;; label = @7 + block $label$72 ;; label = @8 + block $label$73 ;; label = @9 + block $label$74 ;; label = @10 + block $label$75 ;; label = @11 + block $label$76 ;; label = @12 + block $label$77 ;; label = @13 + block $label$78 ;; label = @14 + block $label$79 ;; label = @15 + block $label$80 ;; label = @16 + block $label$81 ;; label = @17 + block $label$82 ;; label = @18 + block $label$83 ;; label = @19 + block $label$84 ;; label = @20 + block $label$85 ;; label = @21 + block $label$86 ;; label = @22 + block $label$87 ;; label = @23 + block $label$88 ;; label = @24 + block $label$89 ;; label = @25 + local.get $9 + i32.const 65 + i32.sub + br_table 11 (;@14;) 12 (;@13;) 9 (;@16;) 12 (;@13;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 10 (;@15;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 11 (;@14;) 12 (;@13;) 6 (;@19;) 4 (;@21;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 4 (;@21;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 7 (;@18;) 0 (;@25;) 3 (;@22;) 1 (;@24;) 12 (;@13;) 12 (;@13;) 8 (;@17;) 12 (;@13;) 5 (;@20;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) + end + block $label$90 ;; label = @25 + block $label$91 ;; label = @26 + block $label$92 ;; label = @27 + block $label$93 ;; label = @28 + block $label$94 ;; label = @29 + block $label$95 ;; label = @30 + block $label$96 ;; label = @31 + block $label$97 ;; label = @32 + local.get $8 + i32.const 255 + i32.and + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 0 + i32.sub + br_table 0 (;@32;) 1 (;@31;) 2 (;@30;) 3 (;@29;) 4 (;@28;) 7 (;@25;) 5 (;@27;) 6 (;@26;) 7 (;@25;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 27 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 26 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i64.extend_i32_s + i64.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 25 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store16 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 24 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store8 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 23 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 22 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i64.extend_i32_s + i64.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 21 (;@4;) + end + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 20 (;@4;) + end + local.get $12 + i32.const 8 + i32.or + local.set $12 + local.get $5 + i32.const 8 + i32.le_u + if ;; label = @24 + i32.const 8 + local.set $5 + end + i32.const 120 + local.set $9 + br 11 (;@12;) + end + br 10 (;@12;) + end + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.eq + if ;; label = @22 + local.get $21 + local.set $7 + else + block ;; label = @23 + local.get $21 + local.set $1 + loop $label$101 ;; label = @24 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $50 + i64.const 7 + i64.and + i64.const 48 + i64.or + i64.store8 + local.get $50 + i64.const 3 + i64.shr_u + local.tee $50 + i64.const 0 + i64.ne + br_if 0 (;@24;) + local.get $1 + local.set $7 + end + end + end + local.get $12 + i32.const 8 + i32.and + if ;; label = @22 + block ;; label = @23 + local.get $38 + local.get $7 + i32.sub + local.tee $1 + i32.const 1 + i32.add + local.set $8 + local.get $5 + local.get $1 + i32.le_s + if ;; label = @24 + local.get $8 + local.set $5 + end + i32.const 0 + local.set $6 + i32.const 1635 + local.set $8 + br 16 (;@7;) + end + else + block ;; label = @23 + i32.const 0 + local.set $6 + i32.const 1635 + local.set $8 + br 16 (;@7;) + end + end + end + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.lt_s + if ;; label = @21 + block ;; label = @22 + local.get $16 + i64.const 0 + local.get $50 + i64.sub + local.tee $50 + i64.store + i32.const 1 + local.set $6 + i32.const 1635 + local.set $8 + br 11 (;@11;) + end + end + local.get $12 + i32.const 2048 + i32.and + if ;; label = @21 + block ;; label = @22 + i32.const 1 + local.set $6 + i32.const 1636 + local.set $8 + br 11 (;@11;) + end + else + block ;; label = @22 + local.get $12 + i32.const 1 + i32.and + local.tee $1 + local.set $6 + local.get $1 + if (result i32) ;; label = @23 + i32.const 1637 + else + i32.const 1635 + end + local.set $8 + br 11 (;@11;) + end + end + end + local.get $16 + i64.load + local.set $50 + i32.const 0 + local.set $6 + i32.const 1635 + local.set $8 + br 8 (;@11;) + end + local.get $39 + local.get $16 + i64.load + i64.store8 + local.get $39 + local.set $1 + local.get $7 + local.set $12 + i32.const 1 + local.set $7 + i32.const 0 + local.set $6 + i32.const 1635 + local.set $8 + local.get $21 + local.set $5 + br 12 (;@6;) + end + call $12 + i32.load + call $24 + local.set $1 + br 7 (;@10;) + end + local.get $16 + i32.load + local.tee $1 + i32.eqz + if ;; label = @17 + i32.const 1645 + local.set $1 + end + br 6 (;@10;) + end + local.get $37 + local.get $16 + i64.load + i64.store32 + local.get $42 + i32.const 0 + i32.store + local.get $16 + local.get $37 + i32.store + local.get $37 + local.set $7 + i32.const -1 + local.set $6 + br 6 (;@9;) + end + local.get $16 + i32.load + local.set $7 + local.get $5 + if ;; label = @15 + block ;; label = @16 + local.get $5 + local.set $6 + br 7 (;@9;) + end + else + block ;; label = @16 + local.get $0 + i32.const 32 + local.get $10 + i32.const 0 + local.get $12 + call $25 + i32.const 0 + local.set $1 + br 8 (;@8;) + end + end + end + local.get $16 + f64.load + local.set $52 + local.get $20 + i32.const 0 + i32.store + local.get $52 + i64.reinterpret_f64 + i64.const 0 + i64.lt_s + if (result i32) ;; label = @14 + block (result i32) ;; label = @15 + i32.const 1 + local.set $24 + local.get $52 + f64.neg + local.set $52 + i32.const 1652 + end + else + block (result i32) ;; label = @15 + local.get $12 + i32.const 1 + i32.and + local.set $1 + local.get $12 + i32.const 2048 + i32.and + if (result i32) ;; label = @16 + block (result i32) ;; label = @17 + i32.const 1 + local.set $24 + i32.const 1655 + end + else + block (result i32) ;; label = @17 + local.get $1 + local.set $24 + local.get $1 + if (result i32) ;; label = @18 + i32.const 1658 + else + i32.const 1653 + end + end + end + end + end + local.set $26 + block $label$119 ;; label = @14 + local.get $52 + i64.reinterpret_f64 + i64.const 9218868437227405312 + i64.and + i64.const 9218868437227405312 + i64.lt_u + if ;; label = @15 + block ;; label = @16 + local.get $52 + local.get $20 + call $27 + f64.const 0x1p+1 (;=2;) + f64.mul + local.tee $52 + f64.const 0x0p+0 (;=0;) + f64.ne + local.tee $1 + if ;; label = @17 + local.get $20 + local.get $20 + i32.load + i32.const -1 + i32.add + i32.store + end + local.get $9 + i32.const 32 + i32.or + local.tee $22 + i32.const 97 + i32.eq + if ;; label = @17 + block ;; label = @18 + local.get $26 + i32.const 9 + i32.add + local.set $1 + local.get $9 + i32.const 32 + i32.and + local.tee $6 + if ;; label = @19 + local.get $1 + local.set $26 + end + local.get $5 + i32.const 11 + i32.gt_u + i32.const 12 + local.get $5 + i32.sub + local.tee $1 + i32.eqz + i32.or + i32.eqz + if ;; label = @19 + block ;; label = @20 + f64.const 0x1p+3 (;=8;) + local.set $53 + loop $label$125 ;; label = @21 + local.get $53 + f64.const 0x1p+4 (;=16;) + f64.mul + local.set $53 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + br_if 0 (;@21;) + end + local.get $26 + i32.load8_s + i32.const 45 + i32.eq + if (result f64) ;; label = @21 + local.get $53 + local.get $52 + f64.neg + local.get $53 + f64.sub + f64.add + f64.neg + else + local.get $52 + local.get $53 + f64.add + local.get $53 + f64.sub + end + local.set $52 + end + end + i32.const 0 + local.get $20 + i32.load + local.tee $7 + i32.sub + local.set $1 + local.get $7 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @19 + local.get $1 + else + local.get $7 + end + i64.extend_i32_s + local.get $33 + call $23 + local.tee $1 + local.get $33 + i32.eq + if ;; label = @19 + block ;; label = @20 + local.get $40 + i32.const 48 + i32.store8 + local.get $40 + local.set $1 + end + end + local.get $24 + i32.const 2 + i32.or + local.set $13 + local.get $1 + i32.const -1 + i32.add + local.get $7 + i32.const 31 + i32.shr_s + i32.const 2 + i32.and + i32.const 43 + i32.add + i32.store8 + local.get $1 + i32.const -2 + i32.add + local.tee $8 + local.get $9 + i32.const 15 + i32.add + i32.store8 + local.get $5 + i32.const 1 + i32.lt_s + local.set $9 + local.get $12 + i32.const 8 + i32.and + i32.eqz + local.set $14 + local.get $19 + local.set $1 + loop $label$131 ;; label = @19 + local.get $1 + local.get $52 + i32.trunc_f64_s + local.tee $7 + i32.const 1619 + i32.add + i32.load8_u + local.get $6 + i32.or + i32.store8 + local.get $52 + local.get $7 + f64.convert_i32_s + f64.sub + f64.const 0x1p+4 (;=16;) + f64.mul + local.set $52 + block $label$132 (result i32) ;; label = @20 + local.get $1 + i32.const 1 + i32.add + local.tee $7 + local.get $27 + i32.sub + i32.const 1 + i32.eq + if (result i32) ;; label = @21 + block (result i32) ;; label = @22 + local.get $7 + local.get $14 + local.get $9 + local.get $52 + f64.const 0x0p+0 (;=0;) + f64.eq + i32.and + i32.and + br_if 2 (;@20;) + drop + local.get $7 + i32.const 46 + i32.store8 + local.get $1 + i32.const 2 + i32.add + end + else + local.get $7 + end + end + local.set $1 + local.get $52 + f64.const 0x0p+0 (;=0;) + f64.ne + br_if 0 (;@19;) + end + local.get $46 + local.get $5 + i32.add + local.get $8 + local.tee $7 + i32.sub + local.set $6 + local.get $44 + local.get $7 + i32.sub + local.get $1 + i32.add + local.set $9 + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + i32.const 0 + i32.ne + local.get $45 + local.get $1 + i32.add + local.get $5 + i32.lt_s + i32.and + if (result i32) ;; label = @19 + local.get $6 + else + local.get $9 + local.tee $6 + end + local.get $13 + i32.add + local.tee $5 + local.get $12 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $26 + local.get $13 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $5 + local.get $12 + i32.const 65536 + i32.xor + call $25 + local.get $1 + local.get $27 + i32.sub + local.set $1 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $19 + local.get $1 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $6 + local.get $1 + local.get $28 + local.get $7 + i32.sub + local.tee $1 + i32.add + i32.sub + i32.const 0 + i32.const 0 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $8 + local.get $1 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $5 + local.get $10 + i32.ge_s + if ;; label = @19 + local.get $5 + local.set $10 + end + br 4 (;@14;) + end + end + local.get $1 + if ;; label = @17 + block ;; label = @18 + local.get $20 + local.get $20 + i32.load + i32.const -28 + i32.add + local.tee $6 + i32.store + local.get $52 + f64.const 0x1p+28 (;=268435456;) + f64.mul + local.set $52 + end + else + local.get $20 + i32.load + local.set $6 + end + local.get $6 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @17 + local.get $47 + else + local.get $48 + end + local.tee $7 + local.set $8 + loop $label$145 ;; label = @17 + local.get $8 + local.get $52 + i32.trunc_f64_s + local.tee $1 + i32.store + local.get $8 + i32.const 4 + i32.add + local.set $8 + local.get $52 + local.get $1 + f64.convert_i32_u + f64.sub + f64.const 0x1.dcd65p+29 (;=1000000000;) + f64.mul + local.tee $52 + f64.const 0x0p+0 (;=0;) + f64.ne + br_if 0 (;@17;) + end + local.get $6 + i32.const 0 + i32.gt_s + if ;; label = @17 + block ;; label = @18 + local.get $7 + local.set $1 + loop $label$147 ;; label = @19 + local.get $6 + i32.const 29 + i32.gt_s + if (result i32) ;; label = @20 + i32.const 29 + else + local.get $6 + end + local.set $14 + block $label$150 ;; label = @20 + local.get $8 + i32.const -4 + i32.add + local.tee $6 + local.get $1 + i32.ge_u + if ;; label = @21 + block ;; label = @22 + local.get $14 + i64.extend_i32_u + local.set $50 + i32.const 0 + local.set $13 + loop $label$152 ;; label = @23 + local.get $6 + local.get $6 + i32.load + i64.extend_i32_u + local.get $50 + i64.shl + local.get $13 + i64.extend_i32_u + i64.add + local.tee $51 + i64.const 1000000000 + i64.rem_u + i64.store32 + local.get $51 + i64.const 1000000000 + i64.div_u + i32.wrap_i64 + local.set $13 + local.get $6 + i32.const -4 + i32.add + local.tee $6 + local.get $1 + i32.ge_u + br_if 0 (;@23;) + end + local.get $13 + i32.eqz + br_if 2 (;@20;) + local.get $1 + i32.const -4 + i32.add + local.tee $1 + local.get $13 + i32.store + end + end + end + loop $label$153 ;; label = @20 + local.get $8 + local.get $1 + i32.gt_u + if ;; label = @21 + local.get $8 + i32.const -4 + i32.add + local.tee $6 + i32.load + i32.eqz + if ;; label = @22 + block ;; label = @23 + local.get $6 + local.set $8 + br 3 (;@20;) + end + end + end + end + local.get $20 + local.get $20 + i32.load + local.get $14 + i32.sub + local.tee $6 + i32.store + local.get $6 + i32.const 0 + i32.gt_s + br_if 0 (;@19;) + end + end + else + local.get $7 + local.set $1 + end + local.get $5 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @17 + i32.const 6 + else + local.get $5 + end + local.set $18 + local.get $6 + i32.const 0 + i32.lt_s + if ;; label = @17 + block ;; label = @18 + local.get $18 + i32.const 25 + i32.add + i32.const 9 + i32.div_s + i32.const 1 + i32.add + local.set $14 + local.get $22 + i32.const 102 + i32.eq + local.set $25 + local.get $8 + local.set $5 + loop $label$160 ;; label = @19 + i32.const 0 + local.get $6 + i32.sub + local.tee $13 + i32.const 9 + i32.gt_s + if ;; label = @20 + i32.const 9 + local.set $13 + end + block $label$162 ;; label = @20 + local.get $1 + local.get $5 + i32.lt_u + if ;; label = @21 + block ;; label = @22 + i32.const 1 + local.get $13 + i32.shl + i32.const -1 + i32.add + local.set $29 + i32.const 1000000000 + local.get $13 + i32.shr_u + local.set $35 + i32.const 0 + local.set $6 + local.get $1 + local.set $8 + loop $label$164 ;; label = @23 + local.get $8 + local.get $8 + i32.load + local.tee $32 + local.get $13 + i32.shr_u + local.get $6 + i32.add + i32.store + local.get $32 + local.get $29 + i32.and + local.get $35 + i32.mul + local.set $6 + local.get $8 + i32.const 4 + i32.add + local.tee $8 + local.get $5 + i32.lt_u + br_if 0 (;@23;) + end + local.get $1 + i32.const 4 + i32.add + local.set $8 + local.get $1 + i32.load + i32.eqz + if ;; label = @23 + local.get $8 + local.set $1 + end + local.get $6 + i32.eqz + br_if 2 (;@20;) + local.get $5 + local.get $6 + i32.store + local.get $5 + i32.const 4 + i32.add + local.set $5 + end + else + block ;; label = @22 + local.get $1 + i32.const 4 + i32.add + local.set $8 + local.get $1 + i32.load + i32.eqz + if ;; label = @23 + local.get $8 + local.set $1 + end + end + end + end + local.get $25 + if (result i32) ;; label = @20 + local.get $7 + else + local.get $1 + end + local.tee $8 + local.get $14 + i32.const 2 + i32.shl + i32.add + local.set $6 + local.get $5 + local.get $8 + i32.sub + i32.const 2 + i32.shr_s + local.get $14 + i32.gt_s + if ;; label = @20 + local.get $6 + local.set $5 + end + local.get $20 + local.get $20 + i32.load + local.get $13 + i32.add + local.tee $6 + i32.store + local.get $6 + i32.const 0 + i32.lt_s + br_if 0 (;@19;) + local.get $5 + local.set $13 + end + end + else + local.get $8 + local.set $13 + end + local.get $7 + local.set $25 + block $label$172 ;; label = @17 + local.get $1 + local.get $13 + i32.lt_u + if ;; label = @18 + block ;; label = @19 + local.get $25 + local.get $1 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + local.set $5 + local.get $1 + i32.load + local.tee $6 + i32.const 10 + i32.lt_u + br_if 2 (;@17;) + i32.const 10 + local.set $8 + loop $label$174 ;; label = @20 + local.get $5 + i32.const 1 + i32.add + local.set $5 + local.get $6 + local.get $8 + i32.const 10 + i32.mul + local.tee $8 + i32.ge_u + br_if 0 (;@20;) + end + end + else + i32.const 0 + local.set $5 + end + end + local.get $22 + i32.const 103 + i32.eq + local.set $29 + local.get $18 + i32.const 0 + i32.ne + local.set $35 + local.get $18 + local.get $22 + i32.const 102 + i32.ne + if (result i32) ;; label = @17 + local.get $5 + else + i32.const 0 + end + i32.sub + local.get $35 + local.get $29 + i32.and + i32.const 31 + i32.shl + i32.const 31 + i32.shr_s + i32.add + local.tee $8 + local.get $13 + local.get $25 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + i32.const -9 + i32.add + i32.lt_s + if ;; label = @17 + block ;; label = @18 + local.get $8 + i32.const 9216 + i32.add + local.tee $14 + i32.const 9 + i32.rem_s + i32.const 1 + i32.add + local.tee $8 + i32.const 9 + i32.lt_s + if ;; label = @19 + block ;; label = @20 + i32.const 10 + local.set $6 + loop $label$180 ;; label = @21 + local.get $6 + i32.const 10 + i32.mul + local.set $6 + local.get $8 + i32.const 1 + i32.add + local.tee $8 + i32.const 9 + i32.ne + br_if 0 (;@21;) + end + end + else + i32.const 10 + local.set $6 + end + local.get $7 + i32.const 4 + i32.add + local.get $14 + i32.const 9 + i32.div_s + i32.const -1024 + i32.add + i32.const 2 + i32.shl + i32.add + local.tee $8 + i32.load + local.tee $22 + local.get $6 + i32.rem_u + local.set $14 + block $label$182 ;; label = @19 + local.get $8 + i32.const 4 + i32.add + local.get $13 + i32.eq + local.tee $32 + local.get $14 + i32.eqz + i32.and + i32.eqz + if ;; label = @20 + block ;; label = @21 + local.get $14 + local.get $6 + i32.const 2 + i32.div_s + local.tee $49 + i32.lt_u + if (result f64) ;; label = @22 + f64.const 0x1p-1 (;=0.5;) + else + local.get $32 + local.get $14 + local.get $49 + i32.eq + i32.and + if (result f64) ;; label = @23 + f64.const 0x1p+0 (;=1;) + else + f64.const 0x1.8p+0 (;=1.5;) + end + end + local.set $52 + local.get $22 + local.get $6 + i32.div_u + i32.const 1 + i32.and + if (result f64) ;; label = @22 + f64.const 0x1.0000000000001p+53 (;=9007199254740994;) + else + f64.const 0x1p+53 (;=9007199254740992;) + end + local.set $53 + block $label$190 ;; label = @22 + local.get $24 + if ;; label = @23 + block ;; label = @24 + local.get $26 + i32.load8_s + i32.const 45 + i32.ne + br_if 2 (;@22;) + local.get $53 + f64.neg + local.set $53 + local.get $52 + f64.neg + local.set $52 + end + end + end + local.get $8 + local.get $22 + local.get $14 + i32.sub + local.tee $14 + i32.store + local.get $53 + local.get $52 + f64.add + local.get $53 + f64.eq + br_if 2 (;@19;) + local.get $8 + local.get $14 + local.get $6 + i32.add + local.tee $5 + i32.store + local.get $5 + i32.const 999999999 + i32.gt_u + if ;; label = @22 + loop $label$193 ;; label = @23 + local.get $8 + i32.const 0 + i32.store + local.get $8 + i32.const -4 + i32.add + local.tee $8 + local.get $1 + i32.lt_u + if ;; label = @24 + local.get $1 + i32.const -4 + i32.add + local.tee $1 + i32.const 0 + i32.store + end + local.get $8 + local.get $8 + i32.load + i32.const 1 + i32.add + local.tee $5 + i32.store + local.get $5 + i32.const 999999999 + i32.gt_u + br_if 0 (;@23;) + end + end + local.get $25 + local.get $1 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + local.set $5 + local.get $1 + i32.load + local.tee $14 + i32.const 10 + i32.lt_u + br_if 2 (;@19;) + i32.const 10 + local.set $6 + loop $label$195 ;; label = @22 + local.get $5 + i32.const 1 + i32.add + local.set $5 + local.get $14 + local.get $6 + i32.const 10 + i32.mul + local.tee $6 + i32.ge_u + br_if 0 (;@22;) + end + end + end + end + local.get $1 + local.set $14 + local.get $5 + local.set $6 + local.get $13 + local.get $8 + i32.const 4 + i32.add + local.tee $8 + i32.le_u + if ;; label = @19 + local.get $13 + local.set $8 + end + end + else + block ;; label = @18 + local.get $1 + local.set $14 + local.get $5 + local.set $6 + local.get $13 + local.set $8 + end + end + i32.const 0 + local.get $6 + i32.sub + local.set $32 + loop $label$198 ;; label = @17 + block $label$199 ;; label = @18 + local.get $8 + local.get $14 + i32.le_u + if ;; label = @19 + block ;; label = @20 + i32.const 0 + local.set $22 + br 2 (;@18;) + end + end + local.get $8 + i32.const -4 + i32.add + local.tee $1 + i32.load + if ;; label = @19 + i32.const 1 + local.set $22 + else + block ;; label = @20 + local.get $1 + local.set $8 + br 3 (;@17;) + end + end + end + end + block $label$203 ;; label = @17 + local.get $29 + if ;; label = @18 + block ;; label = @19 + local.get $35 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $18 + i32.add + local.tee $1 + local.get $6 + i32.gt_s + local.get $6 + i32.const -5 + i32.gt_s + i32.and + if (result i32) ;; label = @20 + block (result i32) ;; label = @21 + local.get $9 + i32.const -1 + i32.add + local.set $5 + local.get $1 + i32.const -1 + i32.add + local.get $6 + i32.sub + end + else + block (result i32) ;; label = @21 + local.get $9 + i32.const -2 + i32.add + local.set $5 + local.get $1 + i32.const -1 + i32.add + end + end + local.set $1 + local.get $12 + i32.const 8 + i32.and + local.tee $13 + br_if 2 (;@17;) + block $label$207 ;; label = @20 + local.get $22 + if ;; label = @21 + block ;; label = @22 + local.get $8 + i32.const -4 + i32.add + i32.load + local.tee $18 + i32.eqz + if ;; label = @23 + block ;; label = @24 + i32.const 9 + local.set $9 + br 4 (;@20;) + end + end + local.get $18 + i32.const 10 + i32.rem_u + if ;; label = @23 + block ;; label = @24 + i32.const 0 + local.set $9 + br 4 (;@20;) + end + else + block ;; label = @24 + i32.const 10 + local.set $13 + i32.const 0 + local.set $9 + end + end + loop $label$212 ;; label = @23 + local.get $9 + i32.const 1 + i32.add + local.set $9 + local.get $18 + local.get $13 + i32.const 10 + i32.mul + local.tee $13 + i32.rem_u + i32.eqz + br_if 0 (;@23;) + end + end + else + i32.const 9 + local.set $9 + end + end + local.get $8 + local.get $25 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + i32.const -9 + i32.add + local.set $18 + local.get $5 + i32.const 32 + i32.or + i32.const 102 + i32.eq + if ;; label = @20 + block ;; label = @21 + i32.const 0 + local.set $13 + local.get $1 + local.get $18 + local.get $9 + i32.sub + local.tee $9 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @22 + i32.const 0 + local.tee $9 + else + local.get $9 + end + i32.ge_s + if ;; label = @22 + local.get $9 + local.set $1 + end + end + else + block ;; label = @21 + i32.const 0 + local.set $13 + local.get $1 + local.get $18 + local.get $6 + i32.add + local.get $9 + i32.sub + local.tee $9 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @22 + i32.const 0 + local.tee $9 + else + local.get $9 + end + i32.ge_s + if ;; label = @22 + local.get $9 + local.set $1 + end + end + end + end + else + block ;; label = @19 + local.get $12 + i32.const 8 + i32.and + local.set $13 + local.get $18 + local.set $1 + local.get $9 + local.set $5 + end + end + end + local.get $5 + i32.const 32 + i32.or + i32.const 102 + i32.eq + local.tee $25 + if ;; label = @17 + block ;; label = @18 + i32.const 0 + local.set $9 + local.get $6 + i32.const 0 + i32.le_s + if ;; label = @19 + i32.const 0 + local.set $6 + end + end + else + block ;; label = @18 + local.get $28 + local.get $6 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @19 + local.get $32 + else + local.get $6 + end + i64.extend_i32_s + local.get $33 + call $23 + local.tee $9 + i32.sub + i32.const 2 + i32.lt_s + if ;; label = @19 + loop $label$229 ;; label = @20 + local.get $9 + i32.const -1 + i32.add + local.tee $9 + i32.const 48 + i32.store8 + local.get $28 + local.get $9 + i32.sub + i32.const 2 + i32.lt_s + br_if 0 (;@20;) + end + end + local.get $9 + i32.const -1 + i32.add + local.get $6 + i32.const 31 + i32.shr_s + i32.const 2 + i32.and + i32.const 43 + i32.add + i32.store8 + local.get $9 + i32.const -2 + i32.add + local.tee $6 + local.get $5 + i32.store8 + local.get $6 + local.set $9 + local.get $28 + local.get $6 + i32.sub + local.set $6 + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $24 + i32.const 1 + i32.add + local.get $1 + i32.add + local.get $1 + local.get $13 + i32.or + local.tee $29 + i32.const 0 + i32.ne + i32.add + local.get $6 + i32.add + local.tee $18 + local.get $12 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + local.get $26 + local.get $24 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $18 + local.get $12 + i32.const 65536 + i32.xor + call $25 + block $label$231 ;; label = @17 + local.get $25 + if ;; label = @18 + block ;; label = @19 + local.get $14 + local.get $7 + i32.gt_u + if (result i32) ;; label = @20 + local.get $7 + else + local.get $14 + end + local.tee $9 + local.set $6 + loop $label$235 ;; label = @20 + local.get $6 + i32.load + i64.extend_i32_u + local.get $31 + call $23 + local.set $5 + block $label$236 ;; label = @21 + local.get $6 + local.get $9 + i32.eq + if ;; label = @22 + block ;; label = @23 + local.get $5 + local.get $31 + i32.ne + br_if 2 (;@21;) + local.get $34 + i32.const 48 + i32.store8 + local.get $34 + local.set $5 + end + else + block ;; label = @23 + local.get $5 + local.get $19 + i32.le_u + br_if 2 (;@21;) + local.get $19 + i32.const 48 + local.get $5 + local.get $27 + i32.sub + call $41 + drop + loop $label$239 ;; label = @24 + local.get $5 + i32.const -1 + i32.add + local.tee $5 + local.get $19 + i32.gt_u + br_if 0 (;@24;) + end + end + end + end + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @21 + local.get $5 + local.get $41 + local.get $5 + i32.sub + local.get $0 + call $21 + drop + end + local.get $6 + i32.const 4 + i32.add + local.tee $5 + local.get $7 + i32.le_u + if ;; label = @21 + block ;; label = @22 + local.get $5 + local.set $6 + br 2 (;@20;) + end + end + end + block $label$242 ;; label = @20 + local.get $29 + if ;; label = @21 + block ;; label = @22 + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@20;) + i32.const 1687 + i32.const 1 + local.get $0 + call $21 + drop + end + end + end + local.get $1 + i32.const 0 + i32.gt_s + local.get $5 + local.get $8 + i32.lt_u + i32.and + if ;; label = @20 + loop $label$245 ;; label = @21 + local.get $5 + i32.load + i64.extend_i32_u + local.get $31 + call $23 + local.tee $7 + local.get $19 + i32.gt_u + if ;; label = @22 + block ;; label = @23 + local.get $19 + i32.const 48 + local.get $7 + local.get $27 + i32.sub + call $41 + drop + loop $label$247 ;; label = @24 + local.get $7 + i32.const -1 + i32.add + local.tee $7 + local.get $19 + i32.gt_u + br_if 0 (;@24;) + end + end + end + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @22 + local.get $7 + local.get $1 + i32.const 9 + i32.gt_s + if (result i32) ;; label = @23 + i32.const 9 + else + local.get $1 + end + local.get $0 + call $21 + drop + end + local.get $1 + i32.const -9 + i32.add + local.set $7 + local.get $1 + i32.const 9 + i32.gt_s + local.get $5 + i32.const 4 + i32.add + local.tee $5 + local.get $8 + i32.lt_u + i32.and + if ;; label = @22 + block ;; label = @23 + local.get $7 + local.set $1 + br 2 (;@21;) + end + else + local.get $7 + local.set $1 + end + end + end + local.get $0 + i32.const 48 + local.get $1 + i32.const 9 + i32.add + i32.const 9 + i32.const 0 + call $25 + end + else + block ;; label = @19 + local.get $14 + i32.const 4 + i32.add + local.set $5 + local.get $22 + i32.eqz + if ;; label = @20 + local.get $5 + local.set $8 + end + local.get $1 + i32.const -1 + i32.gt_s + if ;; label = @20 + block ;; label = @21 + local.get $13 + i32.eqz + local.set $13 + local.get $14 + local.set $7 + local.get $1 + local.set $5 + loop $label$256 ;; label = @22 + local.get $7 + i32.load + i64.extend_i32_u + local.get $31 + call $23 + local.tee $1 + local.get $31 + i32.eq + if ;; label = @23 + block ;; label = @24 + local.get $34 + i32.const 48 + i32.store8 + local.get $34 + local.set $1 + end + end + block $label$258 ;; label = @23 + local.get $7 + local.get $14 + i32.eq + if ;; label = @24 + block ;; label = @25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @26 + local.get $1 + i32.const 1 + local.get $0 + call $21 + drop + end + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $13 + local.get $5 + i32.const 1 + i32.lt_s + i32.and + br_if 2 (;@23;) + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@23;) + i32.const 1687 + i32.const 1 + local.get $0 + call $21 + drop + end + else + block ;; label = @25 + local.get $1 + local.get $19 + i32.le_u + br_if 2 (;@23;) + local.get $19 + i32.const 48 + local.get $1 + local.get $43 + i32.add + call $41 + drop + loop $label$262 ;; label = @26 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $19 + i32.gt_u + br_if 0 (;@26;) + end + end + end + end + local.get $41 + local.get $1 + i32.sub + local.set $6 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @23 + local.get $1 + local.get $5 + local.get $6 + i32.gt_s + if (result i32) ;; label = @24 + local.get $6 + else + local.get $5 + end + local.get $0 + call $21 + drop + end + local.get $7 + i32.const 4 + i32.add + local.tee $7 + local.get $8 + i32.lt_u + local.get $5 + local.get $6 + i32.sub + local.tee $5 + i32.const -1 + i32.gt_s + i32.and + br_if 0 (;@22;) + local.get $5 + local.set $1 + end + end + end + local.get $0 + i32.const 48 + local.get $1 + i32.const 18 + i32.add + i32.const 18 + i32.const 0 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@17;) + local.get $9 + local.get $28 + local.get $9 + i32.sub + local.get $0 + call $21 + drop + end + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $18 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $18 + local.get $10 + i32.ge_s + if ;; label = @17 + local.get $18 + local.set $10 + end + end + else + block ;; label = @16 + local.get $0 + i32.const 32 + local.get $10 + local.get $52 + local.get $52 + f64.ne + i32.const 0 + i32.or + local.tee $6 + if (result i32) ;; label = @17 + i32.const 0 + local.tee $24 + else + local.get $24 + end + i32.const 3 + i32.add + local.tee $8 + local.get $7 + call $25 + local.get $0 + i32.load + local.tee $1 + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + block ;; label = @18 + local.get $26 + local.get $24 + local.get $0 + call $21 + drop + local.get $0 + i32.load + local.set $1 + end + end + local.get $9 + i32.const 32 + i32.and + i32.const 0 + i32.ne + local.tee $5 + if (result i32) ;; label = @17 + i32.const 1671 + else + i32.const 1675 + end + local.set $7 + local.get $5 + if (result i32) ;; label = @17 + i32.const 1679 + else + i32.const 1683 + end + local.set $5 + local.get $6 + i32.eqz + if ;; label = @17 + local.get $7 + local.set $5 + end + local.get $1 + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + local.get $5 + i32.const 3 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $8 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $8 + local.get $10 + i32.ge_s + if ;; label = @17 + local.get $8 + local.set $10 + end + end + end + end + local.get $11 + local.set $1 + br 9 (;@4;) + end + local.get $5 + local.set $7 + i32.const 0 + local.set $6 + i32.const 1635 + local.set $8 + local.get $21 + local.set $5 + br 6 (;@6;) + end + local.get $9 + i32.const 32 + i32.and + local.set $7 + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.eq + if (result i32) ;; label = @12 + block (result i32) ;; label = @13 + i64.const 0 + local.set $50 + local.get $21 + end + else + block (result i32) ;; label = @13 + local.get $21 + local.set $1 + loop $label$280 ;; label = @14 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $50 + i32.wrap_i64 + i32.const 15 + i32.and + i32.const 1619 + i32.add + i32.load8_u + local.get $7 + i32.or + i32.store8 + local.get $50 + i64.const 4 + i64.shr_u + local.tee $50 + i64.const 0 + i64.ne + br_if 0 (;@14;) + end + local.get $16 + i64.load + local.set $50 + local.get $1 + end + end + local.set $7 + local.get $9 + i32.const 4 + i32.shr_s + i32.const 1635 + i32.add + local.set $8 + local.get $12 + i32.const 8 + i32.and + i32.eqz + local.get $50 + i64.const 0 + i64.eq + i32.or + local.tee $1 + if ;; label = @12 + i32.const 1635 + local.set $8 + end + local.get $1 + if (result i32) ;; label = @12 + i32.const 0 + else + i32.const 2 + end + local.set $6 + br 4 (;@7;) + end + local.get $50 + local.get $21 + call $23 + local.set $7 + br 3 (;@7;) + end + local.get $1 + i32.const 0 + local.get $5 + call $17 + local.tee $13 + i32.eqz + local.set $14 + local.get $13 + local.get $1 + i32.sub + local.set $8 + local.get $1 + local.get $5 + i32.add + local.set $9 + local.get $7 + local.set $12 + local.get $14 + if (result i32) ;; label = @10 + local.get $5 + else + local.get $8 + end + local.set $7 + i32.const 0 + local.set $6 + i32.const 1635 + local.set $8 + local.get $14 + if (result i32) ;; label = @10 + local.get $9 + else + local.get $13 + end + local.set $5 + br 3 (;@6;) + end + i32.const 0 + local.set $1 + i32.const 0 + local.set $5 + local.get $7 + local.set $8 + loop $label$288 ;; label = @9 + block $label$289 ;; label = @10 + local.get $8 + i32.load + local.tee $9 + i32.eqz + br_if 0 (;@10;) + local.get $36 + local.get $9 + call $26 + local.tee $5 + i32.const 0 + i32.lt_s + local.get $5 + local.get $6 + local.get $1 + i32.sub + i32.gt_u + i32.or + br_if 0 (;@10;) + local.get $8 + i32.const 4 + i32.add + local.set $8 + local.get $6 + local.get $5 + local.get $1 + i32.add + local.tee $1 + i32.gt_u + br_if 1 (;@9;) + end + end + local.get $5 + i32.const 0 + i32.lt_s + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $1 + local.get $12 + call $25 + local.get $1 + if ;; label = @9 + block ;; label = @10 + i32.const 0 + local.set $5 + loop $label$292 ;; label = @11 + local.get $7 + i32.load + local.tee $8 + i32.eqz + br_if 3 (;@8;) + local.get $36 + local.get $8 + call $26 + local.tee $8 + local.get $5 + i32.add + local.tee $5 + local.get $1 + i32.gt_s + br_if 3 (;@8;) + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @12 + local.get $36 + local.get $8 + local.get $0 + call $21 + drop + end + local.get $7 + i32.const 4 + i32.add + local.set $7 + local.get $5 + local.get $1 + i32.lt_u + br_if 0 (;@11;) + br 3 (;@8;) + end + end + else + block ;; label = @10 + i32.const 0 + local.set $1 + br 2 (;@8;) + end + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $1 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $10 + local.get $1 + i32.le_s + if ;; label = @8 + local.get $1 + local.set $10 + end + local.get $11 + local.set $1 + br 3 (;@4;) + end + local.get $12 + i32.const -65537 + i32.and + local.set $1 + local.get $5 + i32.const -1 + i32.gt_s + if ;; label = @7 + local.get $1 + local.set $12 + end + local.get $5 + local.get $16 + i64.load + i64.const 0 + i64.ne + local.tee $9 + i32.or + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + local.get $7 + local.set $1 + local.get $5 + local.get $9 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $38 + local.get $7 + i32.sub + i32.add + local.tee $7 + i32.gt_s + if ;; label = @9 + local.get $5 + local.set $7 + end + local.get $21 + end + else + block (result i32) ;; label = @8 + local.get $21 + local.set $1 + i32.const 0 + local.set $7 + local.get $21 + end + end + local.set $5 + end + local.get $0 + i32.const 32 + local.get $10 + local.get $7 + local.get $5 + local.get $1 + i32.sub + local.tee $9 + i32.lt_s + if (result i32) ;; label = @6 + local.get $9 + local.tee $7 + else + local.get $7 + end + local.get $6 + i32.add + local.tee $5 + i32.lt_s + if (result i32) ;; label = @6 + local.get $5 + local.tee $10 + else + local.get $10 + end + local.get $5 + local.get $12 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @6 + local.get $8 + local.get $6 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $5 + local.get $12 + i32.const 65536 + i32.xor + call $25 + local.get $0 + i32.const 48 + local.get $7 + local.get $9 + i32.const 0 + call $25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @6 + local.get $1 + local.get $9 + local.get $0 + call $21 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + local.get $12 + i32.const 8192 + i32.xor + call $25 + local.get $11 + local.set $1 + br 1 (;@4;) + end + end + br 1 (;@2;) + end + local.get $0 + i32.eqz + if ;; label = @3 + local.get $17 + if ;; label = @4 + block ;; label = @5 + i32.const 1 + local.set $0 + loop $label$308 ;; label = @6 + local.get $4 + local.get $0 + i32.const 2 + i32.shl + i32.add + i32.load + local.tee $1 + if ;; label = @7 + block ;; label = @8 + local.get $3 + local.get $0 + i32.const 3 + i32.shl + i32.add + local.get $1 + local.get $2 + call $22 + local.get $0 + i32.const 1 + i32.add + local.tee $0 + i32.const 10 + i32.lt_s + br_if 2 (;@6;) + i32.const 1 + local.set $15 + br 6 (;@2;) + end + end + end + loop $label$310 ;; label = @6 + local.get $4 + local.get $0 + i32.const 2 + i32.shl + i32.add + i32.load + if ;; label = @7 + block ;; label = @8 + i32.const -1 + local.set $15 + br 6 (;@2;) + end + end + local.get $0 + i32.const 1 + i32.add + local.tee $0 + i32.const 10 + i32.lt_s + br_if 0 (;@6;) + i32.const 1 + local.set $15 + end + end + else + i32.const 0 + local.set $15 + end + end + end + local.get $23 + global.set $global$1 + local.get $15 + end ) - ) - (func $25 (; 38 ;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (block $label$1 - (local.set $7 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 256) - ) - ) - (local.set $6 - (local.get $7) - ) - (block $label$2 - (if - (i32.and - (i32.gt_s - (local.get $2) - (local.get $3) - ) - (i32.eqz - (i32.and - (local.get $4) - (i32.const 73728) - ) - ) - ) - (block - (drop - (call $41 - (local.get $6) - (local.get $1) - (if (result i32) - (i32.gt_u - (local.tee $5 - (i32.sub - (local.get $2) - (local.get $3) - ) - ) - (i32.const 256) - ) - (i32.const 256) - (local.get $5) - ) - ) - ) - (local.set $4 - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 32) - ) - ) - ) - (if - (i32.gt_u - (local.get $5) - (i32.const 255) - ) - (block - (loop $label$7 - (if - (local.get $4) - (block - (drop - (call $21 - (local.get $6) - (i32.const 256) - (local.get $0) - ) - ) - (local.set $1 - (i32.load - (local.get $0) - ) - ) - ) - ) - (local.set $4 - (i32.eqz - (i32.and - (local.get $1) - (i32.const 32) - ) - ) - ) - (br_if $label$7 - (i32.gt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const -256) - ) - ) - (i32.const 255) - ) - ) - ) - (br_if $label$2 - (i32.eqz - (local.get $4) - ) - ) - (local.set $5 - (i32.and - (i32.sub - (local.get $2) - (local.get $3) - ) - (i32.const 255) - ) - ) - ) - (br_if $label$2 - (i32.eqz - (local.get $4) - ) - ) - ) - (drop - (call $21 - (local.get $6) - (local.get $5) - (local.get $0) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $7) - ) + (func $20 (;33;) (type $1) (param $0 i32) (result i32) + i32.const 0 ) - ) - (func $26 (; 39 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (if (result i32) - (local.get $0) - (call $29 - (local.get $0) - (local.get $1) - (i32.const 0) - ) - (i32.const 0) + (func $21 (;34;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) + block $label$1 (result i32) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $2 + i32.const 16 + i32.add + local.tee $4 + i32.load + local.tee $3 + br_if 0 (;@3;) + local.get $2 + call $30 + if ;; label = @4 + i32.const 0 + local.set $3 + else + block ;; label = @5 + local.get $4 + i32.load + local.set $3 + br 2 (;@3;) + end + end + br 1 (;@2;) + end + local.get $3 + local.get $2 + i32.const 20 + i32.add + local.tee $5 + i32.load + local.tee $4 + i32.sub + local.get $1 + i32.lt_u + if ;; label = @3 + block ;; label = @4 + local.get $2 + local.get $0 + local.get $1 + local.get $2 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + local.set $3 + br 2 (;@2;) + end + end + block $label$7 (result i32) ;; label = @3 + local.get $2 + i32.load8_s offset=75 + i32.const -1 + i32.gt_s + if (result i32) ;; label = @4 + block (result i32) ;; label = @5 + local.get $1 + local.set $3 + loop $label$9 ;; label = @6 + i32.const 0 + local.get $3 + i32.eqz + br_if 3 (;@3;) + drop + local.get $0 + local.get $3 + i32.const -1 + i32.add + local.tee $6 + i32.add + i32.load8_s + i32.const 10 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $6 + local.set $3 + br 2 (;@6;) + end + end + end + local.get $2 + local.get $0 + local.get $3 + local.get $2 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + local.get $3 + i32.lt_u + br_if 3 (;@2;) + local.get $5 + i32.load + local.set $4 + local.get $1 + local.get $3 + i32.sub + local.set $1 + local.get $0 + local.get $3 + i32.add + local.set $0 + local.get $3 + end + else + i32.const 0 + end + end + local.set $2 + local.get $4 + local.get $0 + local.get $1 + call $42 + drop + local.get $5 + local.get $5 + i32.load + local.get $1 + i32.add + i32.store + local.get $2 + local.get $1 + i32.add + local.set $3 + end + local.get $3 + end ) - ) - (func $27 (; 40 ;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (call $28 - (local.get $0) - (local.get $1) + (func $22 (;35;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) (local $4 i64) (local $5 f64) + block $label$1 ;; label = @1 + local.get $1 + i32.const 20 + i32.le_u + if ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + block $label$6 ;; label = @6 + block $label$7 ;; label = @7 + block $label$8 ;; label = @8 + block $label$9 ;; label = @9 + block $label$10 ;; label = @10 + block $label$11 ;; label = @11 + block $label$12 ;; label = @12 + block $label$13 ;; label = @13 + local.get $1 + i32.const 9 + i32.sub + br_table 0 (;@13;) 1 (;@12;) 2 (;@11;) 3 (;@10;) 4 (;@9;) 5 (;@8;) 6 (;@7;) 7 (;@6;) 8 (;@5;) 9 (;@4;) 10 (;@3;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.store + br 11 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i64.extend_i32_s + i64.store + br 10 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i64.extend_i32_u + i64.store + br 9 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + i64.load + local.set $4 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $4 + i64.store + br 8 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 65535 + i32.and + i32.const 16 + i32.shl + i32.const 16 + i32.shr_s + i64.extend_i32_s + i64.store + br 7 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 65535 + i32.and + i64.extend_i32_u + i64.store + br 6 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 255 + i32.and + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i64.extend_i32_s + i64.store + br 5 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 255 + i32.and + i64.extend_i32_u + i64.store + br 4 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + f64.load + local.set $5 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $5 + f64.store + br 3 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + f64.load + local.set $5 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $5 + f64.store + end + end + end ) - ) - (func $28 (; 41 ;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (local $2 i64) - (local $3 i64) - (block $label$1 (result f64) - (block $label$2 - (block $label$3 - (block $label$4 - (block $label$5 - (br_table $label$5 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$4 $label$3 - (i32.sub - (i32.shr_s - (i32.shl - (i32.and - (i32.and - (i32.wrap_i64 - (local.tee $3 - (i64.shr_u - (local.tee $2 - (i64.reinterpret_f64 - (local.get $0) - ) - ) - (i64.const 52) - ) - ) - ) - (i32.const 65535) - ) - (i32.const 2047) - ) - (i32.const 16) - ) - (i32.const 16) - ) - (i32.const 0) - ) - ) - ) - (i32.store - (local.get $1) - (if (result i32) - (f64.ne - (local.get $0) - (f64.const 0) - ) - (block (result i32) - (local.set $0 - (call $28 - (f64.mul - (local.get $0) - (f64.const 18446744073709551615) - ) - (local.get $1) - ) - ) - (i32.add - (i32.load - (local.get $1) - ) - (i32.const -64) - ) - ) - (i32.const 0) - ) - ) - (br $label$2) - ) - (br $label$2) - ) - (i32.store - (local.get $1) - (i32.add - (i32.and - (i32.wrap_i64 - (local.get $3) - ) - (i32.const 2047) - ) - (i32.const -1022) - ) - ) - (local.set $0 - (f64.reinterpret_i64 - (i64.or - (i64.and - (local.get $2) - (i64.const -9218868437227405313) - ) - (i64.const 4602678819172646912) - ) - ) - ) - ) - (local.get $0) + (func $23 (;36;) (type $9) (param $0 i64) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i64) + block $label$1 (result i32) ;; label = @1 + local.get $0 + i32.wrap_i64 + local.set $2 + local.get $0 + i64.const 4294967295 + i64.gt_u + if ;; label = @2 + block ;; label = @3 + loop $label$3 ;; label = @4 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $0 + i64.const 10 + i64.rem_u + i64.const 48 + i64.or + i64.store8 + local.get $0 + i64.const 10 + i64.div_u + local.set $4 + local.get $0 + i64.const 42949672959 + i64.gt_u + if ;; label = @5 + block ;; label = @6 + local.get $4 + local.set $0 + br 2 (;@4;) + end + end + end + local.get $4 + i32.wrap_i64 + local.set $2 + end + end + local.get $2 + if ;; label = @2 + loop $label$6 ;; label = @3 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $2 + i32.const 10 + i32.rem_u + i32.const 48 + i32.or + i32.store8 + local.get $2 + i32.const 10 + i32.div_u + local.set $3 + local.get $2 + i32.const 10 + i32.ge_u + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.set $2 + br 2 (;@3;) + end + end + end + end + local.get $1 + end ) - ) - (func $29 (; 42 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (block $label$1 (result i32) - (if (result i32) - (local.get $0) - (block (result i32) - (if - (i32.lt_u - (local.get $1) - (i32.const 128) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (br $label$1 - (i32.const 1) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (i32.const 2048) - ) - (block - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 192) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (br $label$1 - (i32.const 2) - ) - ) - ) - (if - (i32.or - (i32.lt_u - (local.get $1) - (i32.const 55296) - ) - (i32.eq - (i32.and - (local.get $1) - (i32.const -8192) - ) - (i32.const 57344) - ) - ) - (block - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 12) - ) - (i32.const 224) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=2 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (br $label$1 - (i32.const 3) - ) - ) - ) - (if (result i32) - (i32.lt_u - (i32.add - (local.get $1) - (i32.const -65536) - ) - (i32.const 1048576) - ) - (block (result i32) - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 18) - ) - (i32.const 240) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 12) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=2 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=3 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.const 4) - ) - (block (result i32) - (i32.store - (call $12) - (i32.const 84) - ) - (i32.const -1) - ) - ) - ) - (i32.const 1) - ) + (func $24 (;37;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + i32.const 0 + local.set $1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + loop $label$5 ;; label = @5 + local.get $1 + i32.const 1689 + i32.add + i32.load8_u + local.get $0 + i32.eq + br_if 1 (;@4;) + local.get $1 + i32.const 1 + i32.add + local.tee $1 + i32.const 87 + i32.ne + br_if 0 (;@5;) + i32.const 87 + local.set $1 + i32.const 1777 + local.set $0 + br 2 (;@3;) + end + end + local.get $1 + if ;; label = @4 + block ;; label = @5 + i32.const 1777 + local.set $0 + br 2 (;@3;) + end + else + i32.const 1777 + local.set $0 + end + br 1 (;@2;) + end + loop $label$8 ;; label = @3 + local.get $0 + local.set $2 + loop $label$9 ;; label = @4 + local.get $2 + i32.const 1 + i32.add + local.set $0 + local.get $2 + i32.load8_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.set $2 + br 2 (;@4;) + end + end + end + local.get $1 + i32.const -1 + i32.add + local.tee $1 + br_if 0 (;@3;) + end + end + local.get $0 + end ) - ) - (func $30 (; 43 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.load8_s - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 74) - ) - ) - ) - ) - (i32.store8 - (local.get $2) - (i32.or - (i32.add - (local.get $1) - (i32.const 255) - ) - (local.get $1) - ) - ) - (local.tee $0 - (if (result i32) - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 8) - ) - (block (result i32) - (i32.store - (local.get $0) - (i32.or - (local.get $1) - (i32.const 32) - ) - ) - (i32.const -1) - ) - (block (result i32) - (i32.store offset=8 - (local.get $0) - (i32.const 0) - ) - (i32.store offset=4 - (local.get $0) - (i32.const 0) - ) - (i32.store offset=28 - (local.get $0) - (local.tee $1 - (i32.load offset=44 - (local.get $0) - ) - ) - ) - (i32.store offset=20 - (local.get $0) - (local.get $1) - ) - (i32.store offset=16 - (local.get $0) - (i32.add - (local.get $1) - (i32.load offset=48 - (local.get $0) - ) - ) - ) - (i32.const 0) - ) - ) - ) + (func $25 (;38;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) + (local $5 i32) (local $6 i32) (local $7 i32) + block $label$1 ;; label = @1 + global.get $global$1 + local.set $7 + global.get $global$1 + i32.const 256 + i32.add + global.set $global$1 + local.get $7 + local.set $6 + block $label$2 ;; label = @2 + local.get $2 + local.get $3 + i32.gt_s + local.get $4 + i32.const 73728 + i32.and + i32.eqz + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $6 + local.get $1 + local.get $2 + local.get $3 + i32.sub + local.tee $5 + i32.const 256 + i32.gt_u + if (result i32) ;; label = @5 + i32.const 256 + else + local.get $5 + end + call $41 + drop + local.get $0 + i32.load + local.tee $1 + i32.const 32 + i32.and + i32.eqz + local.set $4 + local.get $5 + i32.const 255 + i32.gt_u + if ;; label = @5 + block ;; label = @6 + loop $label$7 ;; label = @7 + local.get $4 + if ;; label = @8 + block ;; label = @9 + local.get $6 + i32.const 256 + local.get $0 + call $21 + drop + local.get $0 + i32.load + local.set $1 + end + end + local.get $1 + i32.const 32 + i32.and + i32.eqz + local.set $4 + local.get $5 + i32.const -256 + i32.add + local.tee $5 + i32.const 255 + i32.gt_u + br_if 0 (;@7;) + end + local.get $4 + i32.eqz + br_if 4 (;@2;) + local.get $2 + local.get $3 + i32.sub + i32.const 255 + i32.and + local.set $5 + end + else + local.get $4 + i32.eqz + br_if 3 (;@2;) + end + local.get $6 + local.get $5 + local.get $0 + call $21 + drop + end + end + end + local.get $7 + global.set $global$1 + end ) - ) - (func $31 (; 44 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (block $label$1 (result i32) - (block $label$2 - (block $label$3 - (br_if $label$3 - (i32.eqz - (i32.and - (local.tee $2 - (local.get $0) - ) - (i32.const 3) - ) - ) - ) - (local.set $1 - (local.get $2) - ) - (loop $label$4 - (if - (i32.eqz - (i32.load8_s - (local.get $0) - ) - ) - (block - (local.set $0 - (local.get $1) - ) - (br $label$2) - ) - ) - (br_if $label$4 - (i32.and - (local.tee $1 - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 3) - ) - ) - (br $label$3) - ) - ) - (loop $label$6 - (local.set $1 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - (if - (i32.eqz - (i32.and - (i32.xor - (i32.and - (local.tee $3 - (i32.load - (local.get $0) - ) - ) - (i32.const -2139062144) - ) - (i32.const -2139062144) - ) - (i32.add - (local.get $3) - (i32.const -16843009) - ) - ) - ) - (block - (local.set $0 - (local.get $1) - ) - (br $label$6) - ) - ) - ) - (if - (i32.shr_s - (i32.shl - (i32.and - (local.get $3) - (i32.const 255) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (loop $label$9 - (br_if $label$9 - (i32.load8_s - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - ) - ) - ) - ) - ) - (i32.sub - (local.get $0) - (local.get $2) - ) + (func $26 (;39;) (type $4) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + if (result i32) ;; label = @1 + local.get $0 + local.get $1 + i32.const 0 + call $29 + else + i32.const 0 + end ) - ) - (func $32 (; 45 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (block $label$1 (result i32) - (local.set $3 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store8 - (local.tee $4 - (local.get $3) - ) - (local.tee $7 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - ) - (block $label$2 - (block $label$3 - (br_if $label$3 - (local.tee $5 - (i32.load - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - ) - (if - (call $30 - (local.get $0) - ) - (local.set $1 - (i32.const -1) - ) - (block - (local.set $5 - (i32.load - (local.get $2) - ) - ) - (br $label$3) - ) - ) - (br $label$2) - ) - (if - (i32.lt_u - (local.tee $6 - (i32.load - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (local.get $5) - ) - (if - (i32.ne - (local.tee $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (i32.load8_s offset=75 - (local.get $0) - ) - ) - (block - (i32.store - (local.get $2) - (i32.add - (local.get $6) - (i32.const 1) - ) - ) - (i32.store8 - (local.get $6) - (local.get $7) - ) - (br $label$2) - ) - ) - ) - (local.set $1 - (if (result i32) - (i32.eq - (call_indirect (type $0) - (local.get $0) - (local.get $4) - (i32.const 1) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $0) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - (i32.const 1) - ) - (i32.load8_u - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (global.set $global$1 - (local.get $3) - ) - (local.get $1) + (func $27 (;40;) (type $11) (param $0 f64) (param $1 i32) (result f64) + local.get $0 + local.get $1 + call $28 ) - ) - (func $33 (; 46 ;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $4 - (i32.mul - (local.get $2) - (local.get $1) - ) - ) - (if - (i32.gt_s - (i32.load offset=76 - (local.get $3) - ) - (i32.const -1) - ) - (block - (local.set $5 - (i32.eqz - (call $20 - (local.get $3) - ) - ) - ) - (local.set $0 - (call $21 - (local.get $0) - (local.get $4) - (local.get $3) - ) - ) - (if - (i32.eqz - (local.get $5) - ) - (call $13 - (local.get $3) - ) - ) - ) - (local.set $0 - (call $21 - (local.get $0) - (local.get $4) - (local.get $3) - ) - ) - ) - (if - (i32.ne - (local.get $0) - (local.get $4) - ) - (local.set $2 - (i32.div_u - (local.get $0) - (local.get $1) - ) - ) - ) - (local.get $2) + (func $28 (;41;) (type $11) (param $0 f64) (param $1 i32) (result f64) + (local $2 i64) (local $3 i64) + block $label$1 (result f64) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + local.get $0 + i64.reinterpret_f64 + local.tee $2 + i64.const 52 + i64.shr_u + local.tee $3 + i32.wrap_i64 + i32.const 65535 + i32.and + i32.const 2047 + i32.and + i32.const 16 + i32.shl + i32.const 16 + i32.shr_s + i32.const 0 + i32.sub + br_table 0 (;@5;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 1 (;@4;) 2 (;@3;) + end + local.get $1 + local.get $0 + f64.const 0x0p+0 (;=0;) + f64.ne + if (result i32) ;; label = @5 + block (result i32) ;; label = @6 + local.get $0 + f64.const 0x1p+64 (;=18446744073709552000;) + f64.mul + local.get $1 + call $28 + local.set $0 + local.get $1 + i32.load + i32.const -64 + i32.add + end + else + i32.const 0 + end + i32.store + br 2 (;@2;) + end + br 1 (;@2;) + end + local.get $1 + local.get $3 + i32.wrap_i64 + i32.const 2047 + i32.and + i32.const -1022 + i32.add + i32.store + local.get $2 + i64.const -9218868437227405313 + i64.and + i64.const 4602678819172646912 + i64.or + f64.reinterpret_i64 + local.set $0 + end + local.get $0 + end ) - ) - (func $34 (; 47 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (block $label$1 (result i32) - (local.set $2 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store - (local.tee $3 - (local.get $2) - ) - (local.get $1) - ) - (local.set $0 - (call $18 - (i32.load - (i32.const 1024) - ) - (local.get $0) - (local.get $3) - ) - ) - (global.set $global$1 - (local.get $2) - ) - (local.get $0) + (func $29 (;42;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $1 + i32.const 128 + i32.lt_u + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.store8 + i32.const 1 + br 4 (;@1;) + end + end + local.get $1 + i32.const 2048 + i32.lt_u + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 192 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + i32.const 2 + br 4 (;@1;) + end + end + local.get $1 + i32.const 55296 + i32.lt_u + local.get $1 + i32.const -8192 + i32.and + i32.const 57344 + i32.eq + i32.or + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.const 12 + i32.shr_u + i32.const 224 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=2 + i32.const 3 + br 4 (;@1;) + end + end + local.get $1 + i32.const -65536 + i32.add + i32.const 1048576 + i32.lt_u + if (result i32) ;; label = @4 + block (result i32) ;; label = @5 + local.get $0 + local.get $1 + i32.const 18 + i32.shr_u + i32.const 240 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 12 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=2 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=3 + i32.const 4 + end + else + block (result i32) ;; label = @5 + call $12 + i32.const 84 + i32.store + i32.const -1 + end + end + end + else + i32.const 1 + end + end ) - ) - (func $35 (; 48 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (block $label$1 (result i32) - (local.set $2 - (if (result i32) - (i32.gt_s - (i32.load offset=76 - (local.tee $1 - (i32.load - (i32.const 1024) - ) - ) - ) - (i32.const -1) - ) - (call $20 - (local.get $1) - ) - (i32.const 0) - ) - ) - (local.set $0 - (block $label$4 (result i32) - (if (result i32) - (i32.lt_s - (call $36 - (local.get $0) - (local.get $1) - ) - (i32.const 0) - ) - (i32.const 1) - (block (result i32) - (if - (i32.ne - (i32.load8_s offset=75 - (local.get $1) - ) - (i32.const 10) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $3 - (i32.add - (local.get $1) - (i32.const 20) - ) - ) - ) - ) - (i32.load offset=16 - (local.get $1) - ) - ) - (block - (i32.store - (local.get $3) - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.store8 - (local.get $0) - (i32.const 10) - ) - (br $label$4 - (i32.const 0) - ) - ) - ) - ) - (i32.lt_s - (call $32 - (local.get $1) - (i32.const 10) - ) - (i32.const 0) - ) - ) - ) - ) - ) - (if - (local.get $2) - (call $13 - (local.get $1) - ) - ) - (i32.shr_s - (i32.shl - (local.get $0) - (i32.const 31) - ) - (i32.const 31) - ) + (func $30 (;43;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + i32.const 74 + i32.add + local.tee $2 + i32.load8_s + local.set $1 + local.get $2 + local.get $1 + i32.const 255 + i32.add + local.get $1 + i32.or + i32.store8 + local.get $0 + i32.load + local.tee $1 + i32.const 8 + i32.and + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $0 + local.get $1 + i32.const 32 + i32.or + i32.store + i32.const -1 + end + else + block (result i32) ;; label = @3 + local.get $0 + i32.const 0 + i32.store offset=8 + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + local.get $0 + i32.load offset=44 + local.tee $1 + i32.store offset=28 + local.get $0 + local.get $1 + i32.store offset=20 + local.get $0 + local.get $1 + local.get $0 + i32.load offset=48 + i32.add + i32.store offset=16 + i32.const 0 + end + end + local.tee $0 + end ) - ) - (func $36 (; 49 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (i32.add - (call $33 - (local.get $0) - (call $31 - (local.get $0) - ) - (i32.const 1) - (local.get $1) - ) - (i32.const -1) + (func $31 (;44;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) (local $3 i32) + block $label$1 (result i32) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $0 + local.tee $2 + i32.const 3 + i32.and + i32.eqz + br_if 0 (;@3;) + local.get $2 + local.set $1 + loop $label$4 ;; label = @4 + local.get $0 + i32.load8_s + i32.eqz + if ;; label = @5 + block ;; label = @6 + local.get $1 + local.set $0 + br 4 (;@2;) + end + end + local.get $0 + i32.const 1 + i32.add + local.tee $0 + local.tee $1 + i32.const 3 + i32.and + br_if 0 (;@4;) + br 1 (;@3;) + end + end + loop $label$6 ;; label = @3 + local.get $0 + i32.const 4 + i32.add + local.set $1 + local.get $0 + i32.load + local.tee $3 + i32.const -2139062144 + i32.and + i32.const -2139062144 + i32.xor + local.get $3 + i32.const -16843009 + i32.add + i32.and + i32.eqz + if ;; label = @4 + block ;; label = @5 + local.get $1 + local.set $0 + br 2 (;@3;) + end + end + end + local.get $3 + i32.const 255 + i32.and + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + if ;; label = @3 + loop $label$9 ;; label = @4 + local.get $0 + i32.const 1 + i32.add + local.tee $0 + i32.load8_s + br_if 0 (;@4;) + end + end + end + local.get $0 + local.get $2 + i32.sub + end ) - ) - (func $37 (; 50 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 i32) - (local $17 i32) - (local $18 i32) - (local $19 i32) - (local $20 i32) - (local $21 i32) - (block $label$1 (result i32) - (local.set $14 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (local.set $18 - (local.get $14) - ) - (block $label$2 - (if - (i32.lt_u - (local.get $0) - (i32.const 245) - ) - (block - (local.set $3 - (i32.and - (i32.add - (local.get $0) - (i32.const 11) - ) - (i32.const -8) - ) - ) - (if - (i32.and - (local.tee $0 - (i32.shr_u - (local.tee $8 - (i32.load - (i32.const 3636) - ) - ) - (local.tee $2 - (i32.shr_u - (if (result i32) - (i32.lt_u - (local.get $0) - (i32.const 11) - ) - (local.tee $3 - (i32.const 16) - ) - (local.get $3) - ) - (i32.const 3) - ) - ) - ) - ) - (i32.const 3) - ) - (block - (local.set $4 - (i32.load - (local.tee $1 - (i32.add - (local.tee $7 - (i32.load - (local.tee $3 - (i32.add - (local.tee $2 - (i32.add - (i32.shl - (i32.shl - (local.tee $5 - (i32.add - (i32.xor - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 1) - ) - (local.get $2) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $4) - ) - (i32.store - (i32.const 3636) - (i32.and - (local.get $8) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $5) - ) - (i32.const -1) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 12) - ) - ) - ) - (local.get $7) - ) - (block - (i32.store - (local.get $0) - (local.get $2) - ) - (i32.store - (local.get $3) - (local.get $4) - ) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=4 - (local.get $7) - (i32.or - (local.tee $0 - (i32.shl - (local.get $5) - (i32.const 3) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $7) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (local.get $1) - ) - ) - ) - (if - (i32.gt_u - (local.get $3) - (local.tee $16 - (i32.load - (i32.const 3644) - ) - ) - ) - (block - (if - (local.get $0) - (block - (local.set $5 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.tee $0 - (i32.and - (i32.shl - (local.get $0) - (local.get $2) - ) - (i32.or - (local.tee $0 - (i32.shl - (i32.const 2) - (local.get $2) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (local.set $12 - (i32.load - (local.tee $5 - (i32.add - (local.tee $9 - (i32.load - (local.tee $2 - (i32.add - (local.tee $4 - (i32.add - (i32.shl - (i32.shl - (local.tee $11 - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $0) - (local.get $5) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $5) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.eq - (local.get $4) - (local.get $12) - ) - (i32.store - (i32.const 3636) - (local.tee $7 - (i32.and - (local.get $8) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $11) - ) - (i32.const -1) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $12) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $12) - (i32.const 12) - ) - ) - ) - (local.get $9) - ) - (block - (i32.store - (local.get $0) - (local.get $4) - ) - (i32.store - (local.get $2) - (local.get $12) - ) - (local.set $7 - (local.get $8) - ) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=4 - (local.get $9) - (i32.or - (local.get $3) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.tee $4 - (i32.add - (local.get $9) - (local.get $3) - ) - ) - (i32.or - (local.tee $11 - (i32.sub - (i32.shl - (local.get $11) - (i32.const 3) - ) - (local.get $3) - ) - ) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $4) - (local.get $11) - ) - (local.get $11) - ) - (if - (local.get $16) - (block - (local.set $9 - (i32.load - (i32.const 3656) - ) - ) - (local.set $2 - (i32.add - (i32.shl - (i32.shl - (local.tee $0 - (i32.shr_u - (local.get $16) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - (if - (i32.and - (local.get $7) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $3 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (local.set $6 - (local.get $3) - ) - (local.set $1 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 3636) - (i32.or - (local.get $7) - (local.get $0) - ) - ) - (local.set $6 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (local.set $1 - (local.get $2) - ) - ) - ) - (i32.store - (local.get $6) - (local.get $9) - ) - (i32.store offset=12 - (local.get $1) - (local.get $9) - ) - (i32.store offset=8 - (local.get $9) - (local.get $1) - ) - (i32.store offset=12 - (local.get $9) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 3644) - (local.get $11) - ) - (i32.store - (i32.const 3656) - (local.get $4) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (local.get $5) - ) - ) - ) - (if - (local.tee $6 - (i32.load - (i32.const 3640) - ) - ) - (block - (local.set $2 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.get $6) - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (local.set $9 - (i32.sub - (i32.and - (i32.load offset=4 - (local.tee $2 - (i32.load - (i32.add - (i32.shl - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $0) - (local.get $2) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $2) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - ) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.set $1 - (local.get $2) - ) - (loop $label$25 - (block $label$26 - (if - (i32.eqz - (local.tee $0 - (i32.load offset=16 - (local.get $1) - ) - ) - ) - (br_if $label$26 - (i32.eqz - (local.tee $0 - (i32.load offset=20 - (local.get $1) - ) - ) - ) - ) - ) - (if - (local.tee $7 - (i32.lt_u - (local.tee $1 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.get $9) - ) - ) - (local.set $9 - (local.get $1) - ) - ) - (local.set $1 - (local.get $0) - ) - (if - (local.get $7) - (local.set $2 - (local.get $0) - ) - ) - (br $label$25) - ) - ) - (if - (i32.lt_u - (local.get $2) - (local.tee $12 - (i32.load - (i32.const 3652) - ) - ) - ) - (call $fimport$10) - ) - (if - (i32.ge_u - (local.get $2) - (local.tee $13 - (i32.add - (local.get $2) - (local.get $3) - ) - ) - ) - (call $fimport$10) - ) - (local.set $15 - (i32.load offset=24 - (local.get $2) - ) - ) - (block $label$32 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $2) - ) - ) - (local.get $2) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 20) - ) - ) - ) - ) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 16) - ) - ) - ) - ) - ) - (block - (local.set $4 - (i32.const 0) - ) - (br $label$32) - ) - ) - ) - (loop $label$36 - (if - (local.tee $7 - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (local.set $1 - (local.get $11) - ) - (br $label$36) - ) - ) - (if - (local.tee $7 - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (local.set $1 - (local.get $11) - ) - (br $label$36) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $12) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $4 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $11 - (i32.load offset=8 - (local.get $2) - ) - ) - (local.get $12) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 12) - ) - ) - ) - (local.get $2) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $2) - ) - (block - (i32.store - (local.get $7) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $11) - ) - (local.set $4 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (block $label$46 - (if - (local.get $15) - (block - (if - (i32.eq - (local.get $2) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $2) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $4) - ) - (if - (i32.eqz - (local.get $4) - ) - (block - (i32.store - (i32.const 3640) - (i32.and - (local.get $6) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$46) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $15) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $15) - (i32.const 16) - ) - ) - ) - (local.get $2) - ) - (i32.store - (local.get $0) - (local.get $4) - ) - (i32.store offset=20 - (local.get $15) - (local.get $4) - ) - ) - (br_if $label$46 - (i32.eqz - (local.get $4) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $4) - (local.tee $0 - (i32.load - (i32.const 3652) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $4) - (local.get $15) - ) - (if - (local.tee $1 - (i32.load offset=16 - (local.get $2) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $0) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $4) - (local.get $1) - ) - (i32.store offset=24 - (local.get $1) - (local.get $4) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=20 - (local.get $2) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $4) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $4) - ) - ) - ) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $9) - (i32.const 16) - ) - (block - (i32.store offset=4 - (local.get $2) - (i32.or - (local.tee $0 - (i32.add - (local.get $9) - (local.get $3) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $2) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - (block - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $3) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.get $13) - (i32.or - (local.get $9) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $13) - (local.get $9) - ) - (local.get $9) - ) - (if - (local.get $16) - (block - (local.set $7 - (i32.load - (i32.const 3656) - ) - ) - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.tee $0 - (i32.shr_u - (local.get $16) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - (if - (i32.and - (local.get $8) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (local.set $10 - (local.get $1) - ) - (local.set $5 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 3636) - (i32.or - (local.get $8) - (local.get $0) - ) - ) - (local.set $10 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $5 - (local.get $3) - ) - ) - ) - (i32.store - (local.get $10) - (local.get $7) - ) - (i32.store offset=12 - (local.get $5) - (local.get $7) - ) - (i32.store offset=8 - (local.get $7) - (local.get $5) - ) - (i32.store offset=12 - (local.get $7) - (local.get $3) - ) - ) - ) - (i32.store - (i32.const 3644) - (local.get $9) - ) - (i32.store - (i32.const 3656) - (local.get $13) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - (local.set $0 - (local.get $3) - ) - ) - ) - (local.set $0 - (local.get $3) - ) - ) - ) - (if - (i32.gt_u - (local.get $0) - (i32.const -65) - ) - (local.set $0 - (i32.const -1) - ) - (block - (local.set $7 - (i32.and - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 11) - ) - ) - (i32.const -8) - ) - ) - (if - (local.tee $5 - (i32.load - (i32.const 3640) - ) - ) - (block - (local.set $17 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $0) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $7) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $7) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $3 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $3) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (local.set $3 - (i32.sub - (i32.const 0) - (local.get $7) - ) - ) - (block $label$78 - (block $label$79 - (block $label$80 - (if - (local.tee $1 - (i32.load - (i32.add - (i32.shl - (local.get $17) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - ) - (block - (local.set $0 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $17) - (i32.const 1) - ) - ) - ) - (local.set $4 - (i32.const 0) - ) - (local.set $10 - (i32.shl - (local.get $7) - (if (result i32) - (i32.eq - (local.get $17) - (i32.const 31) - ) - (i32.const 0) - (local.get $0) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - (loop $label$84 - (if - (i32.lt_u - (local.tee $6 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $1) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.get $3) - ) - (if - (local.get $6) - (block - (local.set $3 - (local.get $6) - ) - (local.set $0 - (local.get $1) - ) - ) - (block - (local.set $3 - (i32.const 0) - ) - (local.set $0 - (local.get $1) - ) - (br $label$79) - ) - ) - ) - (local.set $1 - (if (result i32) - (i32.or - (i32.eqz - (local.tee $19 - (i32.load offset=20 - (local.get $1) - ) - ) - ) - (i32.eq - (local.get $19) - (local.tee $6 - (i32.load - (i32.add - (i32.add - (local.get $1) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $10) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - (local.get $4) - (local.get $19) - ) - ) - (local.set $10 - (i32.shl - (local.get $10) - (i32.xor - (i32.and - (local.tee $4 - (i32.eqz - (local.get $6) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (if - (local.get $4) - (block - (local.set $4 - (local.get $1) - ) - (local.set $1 - (local.get $0) - ) - (br $label$80) - ) - (block - (local.set $4 - (local.get $1) - ) - (local.set $1 - (local.get $6) - ) - (br $label$84) - ) - ) - ) - ) - (block - (local.set $4 - (i32.const 0) - ) - (local.set $1 - (i32.const 0) - ) - ) - ) - ) - (br_if $label$79 - (local.tee $0 - (if (result i32) - (i32.and - (i32.eqz - (local.get $4) - ) - (i32.eqz - (local.get $1) - ) - ) - (block (result i32) - (if - (i32.eqz - (local.tee $0 - (i32.and - (local.get $5) - (i32.or - (local.tee $0 - (i32.shl - (i32.const 2) - (local.get $17) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (br $label$2) - ) - ) - (local.set $10 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.get $0) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (i32.load - (i32.add - (i32.shl - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $0) - (local.get $10) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $10) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - ) - (local.get $4) - ) - ) - ) - (local.set $4 - (local.get $1) - ) - (br $label$78) - ) - (loop $label$96 - (if - (local.tee $10 - (i32.lt_u - (local.tee $4 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.get $3) - ) - ) - (local.set $3 - (local.get $4) - ) - ) - (if - (local.get $10) - (local.set $1 - (local.get $0) - ) - ) - (if - (local.tee $4 - (i32.load offset=16 - (local.get $0) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (br $label$96) - ) - ) - (br_if $label$96 - (local.tee $0 - (i32.load offset=20 - (local.get $0) - ) - ) - ) - (local.set $4 - (local.get $1) - ) - ) - ) - (if - (local.get $4) - (if - (i32.lt_u - (local.get $3) - (i32.sub - (i32.load - (i32.const 3644) - ) - (local.get $7) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (local.tee $12 - (i32.load - (i32.const 3652) - ) - ) - ) - (call $fimport$10) - ) - (if - (i32.ge_u - (local.get $4) - (local.tee $6 - (i32.add - (local.get $4) - (local.get $7) - ) - ) - ) - (call $fimport$10) - ) - (local.set $10 - (i32.load offset=24 - (local.get $4) - ) - ) - (block $label$104 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $4) - ) - ) - (local.get $4) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - ) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - ) - (block - (local.set $13 - (i32.const 0) - ) - (br $label$104) - ) - ) - ) - (loop $label$108 - (if - (local.tee $11 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $11) - ) - (local.set $1 - (local.get $9) - ) - (br $label$108) - ) - ) - (if - (local.tee $11 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $11) - ) - (local.set $1 - (local.get $9) - ) - (br $label$108) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $12) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $13 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $9 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.get $12) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $11 - (i32.add - (local.get $9) - (i32.const 12) - ) - ) - ) - (local.get $4) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (i32.store - (local.get $11) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $9) - ) - (local.set $13 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (block $label$118 - (if - (local.get $10) - (block - (if - (i32.eq - (local.get $4) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $4) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $13) - ) - (if - (i32.eqz - (local.get $13) - ) - (block - (i32.store - (i32.const 3640) - (local.tee $2 - (i32.and - (local.get $5) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - ) - (br $label$118) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $10) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $10) - (i32.const 16) - ) - ) - ) - (local.get $4) - ) - (i32.store - (local.get $0) - (local.get $13) - ) - (i32.store offset=20 - (local.get $10) - (local.get $13) - ) - ) - (if - (i32.eqz - (local.get $13) - ) - (block - (local.set $2 - (local.get $5) - ) - (br $label$118) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $13) - (local.tee $0 - (i32.load - (i32.const 3652) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $13) - (local.get $10) - ) - (if - (local.tee $1 - (i32.load offset=16 - (local.get $4) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $0) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $13) - (local.get $1) - ) - (i32.store offset=24 - (local.get $1) - (local.get $13) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=20 - (local.get $4) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $13) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $13) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (block $label$136 - (if - (i32.lt_u - (local.get $3) - (i32.const 16) - ) - (block - (i32.store offset=4 - (local.get $4) - (i32.or - (local.tee $0 - (i32.add - (local.get $3) - (local.get $7) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $4) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - (block - (i32.store offset=4 - (local.get $4) - (i32.or - (local.get $7) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $3) - ) - (local.get $3) - ) - (local.set $0 - (i32.shr_u - (local.get $3) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $3) - (i32.const 256) - ) - (block - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.get $0) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - (if - (i32.and - (local.tee $1 - (i32.load - (i32.const 3636) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (local.set $16 - (local.get $1) - ) - (local.set $8 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 3636) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (local.set $16 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $8 - (local.get $3) - ) - ) - ) - (i32.store - (local.get $16) - (local.get $6) - ) - (i32.store offset=12 - (local.get $8) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $8) - ) - (i32.store offset=12 - (local.get $6) - (local.get $3) - ) - (br $label$136) - ) - ) - (local.set $1 - (i32.add - (i32.shl - (local.tee $5 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $3) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $3) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $3) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $5 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $5) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - (i32.store offset=28 - (local.get $6) - (local.get $5) - ) - (i32.store offset=4 - (local.tee $0 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.get $2) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $5) - ) - ) - ) - ) - (block - (i32.store - (i32.const 3640) - (i32.or - (local.get $2) - (local.get $0) - ) - ) - (i32.store - (local.get $1) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $1) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$136) - ) - ) - (local.set $0 - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $5) - (i32.const 1) - ) - ) - ) - (local.set $5 - (i32.shl - (local.get $3) - (if (result i32) - (i32.eq - (local.get $5) - (i32.const 31) - ) - (i32.const 0) - (local.get $1) - ) - ) - ) - (block $label$151 - (block $label$152 - (block $label$153 - (loop $label$154 - (br_if $label$152 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.set $2 - (i32.shl - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$153 - (i32.eqz - (local.tee $1 - (i32.load - (local.tee $5 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $5) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $5 - (local.get $2) - ) - (local.set $0 - (local.get $1) - ) - (br $label$154) - ) - ) - (if - (i32.lt_u - (local.get $5) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $5) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $0) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$136) - ) - ) - (br $label$151) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $3 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $1 - (i32.load - (i32.const 3652) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $1) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $6) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $2) - ) - (i32.store offset=12 - (local.get $6) - (local.get $0) - ) - (i32.store offset=24 - (local.get $6) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $4) - (i32.const 8) - ) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - ) - ) - ) - ) - (if - (i32.ge_u - (local.tee $1 - (i32.load - (i32.const 3644) - ) - ) - (local.get $0) - ) - (block - (local.set $2 - (i32.load - (i32.const 3656) - ) - ) - (if - (i32.gt_u - (local.tee $3 - (i32.sub - (local.get $1) - (local.get $0) - ) - ) - (i32.const 15) - ) - (block - (i32.store - (i32.const 3656) - (local.tee $1 - (i32.add - (local.get $2) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 3644) - (local.get $3) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $1) - (local.get $3) - ) - (local.get $3) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - ) - (block - (i32.store - (i32.const 3644) - (i32.const 0) - ) - (i32.store - (i32.const 3656) - (i32.const 0) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $1) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $2) - (local.get $1) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.gt_u - (local.tee $10 - (i32.load - (i32.const 3648) - ) - ) - (local.get $0) - ) - (block - (i32.store - (i32.const 3648) - (local.tee $3 - (i32.sub - (local.get $10) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 3660) - (local.tee $1 - (i32.add - (local.tee $2 - (i32.load - (i32.const 3660) - ) - ) - (local.get $0) - ) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.le_u - (local.tee $6 - (i32.and - (local.tee $8 - (i32.add - (local.tee $1 - (if (result i32) - (i32.load - (i32.const 4108) - ) - (i32.load - (i32.const 4116) - ) - (block (result i32) - (i32.store - (i32.const 4116) - (i32.const 4096) - ) - (i32.store - (i32.const 4112) - (i32.const 4096) - ) - (i32.store - (i32.const 4120) - (i32.const -1) - ) - (i32.store - (i32.const 4124) - (i32.const -1) - ) - (i32.store - (i32.const 4128) - (i32.const 0) - ) - (i32.store - (i32.const 4080) - (i32.const 0) - ) - (i32.store - (local.get $18) - (local.tee $1 - (i32.xor - (i32.and - (local.get $18) - (i32.const -16) - ) - (i32.const 1431655768) - ) - ) - ) - (i32.store - (i32.const 4108) - (local.get $1) - ) - (i32.const 4096) - ) - ) - ) - (local.tee $13 - (i32.add - (local.get $0) - (i32.const 47) - ) - ) - ) - ) - (local.tee $4 - (i32.sub - (i32.const 0) - (local.get $1) - ) - ) - ) - ) - (local.get $0) - ) - (block - (global.set $global$1 - (local.get $14) - ) - (return - (i32.const 0) - ) - ) - ) - (if - (local.tee $2 - (i32.load - (i32.const 4076) - ) - ) - (if - (i32.or - (i32.le_u - (local.tee $1 - (i32.add - (local.tee $3 - (i32.load - (i32.const 4068) - ) - ) - (local.get $6) - ) - ) - (local.get $3) - ) - (i32.gt_u - (local.get $1) - (local.get $2) - ) - ) - (block - (global.set $global$1 - (local.get $14) - ) - (return - (i32.const 0) - ) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $0) - (i32.const 48) - ) - ) - (block $label$171 - (block $label$172 - (if - (i32.eqz - (i32.and - (i32.load - (i32.const 4080) - ) - (i32.const 4) - ) - ) - (block - (block $label$174 - (block $label$175 - (block $label$176 - (br_if $label$176 - (i32.eqz - (local.tee $3 - (i32.load - (i32.const 3660) - ) - ) - ) - ) - (local.set $2 - (i32.const 4084) - ) - (loop $label$177 - (block $label$178 - (if - (i32.le_u - (local.tee $1 - (i32.load - (local.get $2) - ) - ) - (local.get $3) - ) - (br_if $label$178 - (i32.gt_u - (i32.add - (local.get $1) - (i32.load - (local.tee $5 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - ) - ) - (local.get $3) - ) - ) - ) - (br_if $label$176 - (i32.eqz - (local.tee $1 - (i32.load offset=8 - (local.get $2) - ) - ) - ) - ) - (local.set $2 - (local.get $1) - ) - (br $label$177) - ) - ) - (if - (i32.lt_u - (local.tee $3 - (i32.and - (i32.sub - (local.get $8) - (local.get $10) - ) - (local.get $4) - ) - ) - (i32.const 2147483647) - ) - (if - (i32.eq - (local.tee $1 - (call $40 - (local.get $3) - ) - ) - (i32.add - (i32.load - (local.get $2) - ) - (i32.load - (local.get $5) - ) - ) - ) - (br_if $label$172 - (i32.ne - (local.get $1) - (i32.const -1) - ) - ) - (block - (local.set $2 - (local.get $1) - ) - (local.set $1 - (local.get $3) - ) - (br $label$175) - ) - ) - ) - (br $label$174) - ) - (if - (i32.ne - (local.tee $1 - (call $40 - (i32.const 0) - ) - ) - (i32.const -1) - ) - (block - (local.set $2 - (i32.sub - (i32.and - (i32.add - (local.tee $5 - (i32.add - (local.tee $2 - (i32.load - (i32.const 4112) - ) - ) - (i32.const -1) - ) - ) - (local.tee $3 - (local.get $1) - ) - ) - (i32.sub - (i32.const 0) - (local.get $2) - ) - ) - (local.get $3) - ) - ) - (local.set $4 - (i32.add - (local.tee $3 - (i32.add - (if (result i32) - (i32.and - (local.get $5) - (local.get $3) - ) - (local.get $2) - (i32.const 0) - ) - (local.get $6) - ) - ) - (local.tee $5 - (i32.load - (i32.const 4068) - ) - ) - ) - ) - (if - (i32.and - (i32.gt_u - (local.get $3) - (local.get $0) - ) - (i32.lt_u - (local.get $3) - (i32.const 2147483647) - ) - ) - (block - (if - (local.tee $2 - (i32.load - (i32.const 4076) - ) - ) - (br_if $label$174 - (i32.or - (i32.le_u - (local.get $4) - (local.get $5) - ) - (i32.gt_u - (local.get $4) - (local.get $2) - ) - ) - ) - ) - (br_if $label$172 - (i32.eq - (local.tee $2 - (call $40 - (local.get $3) - ) - ) - (local.get $1) - ) - ) - (local.set $1 - (local.get $3) - ) - (br $label$175) - ) - ) - ) - ) - (br $label$174) - ) - (local.set $5 - (i32.sub - (i32.const 0) - (local.get $1) - ) - ) - (if - (i32.and - (i32.gt_u - (local.get $7) - (local.get $1) - ) - (i32.and - (i32.lt_u - (local.get $1) - (i32.const 2147483647) - ) - (i32.ne - (local.get $2) - (i32.const -1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $3 - (i32.and - (i32.add - (i32.sub - (local.get $13) - (local.get $1) - ) - (local.tee $3 - (i32.load - (i32.const 4116) - ) - ) - ) - (i32.sub - (i32.const 0) - (local.get $3) - ) - ) - ) - (i32.const 2147483647) - ) - (if - (i32.eq - (call $40 - (local.get $3) - ) - (i32.const -1) - ) - (block - (drop - (call $40 - (local.get $5) - ) - ) - (br $label$174) - ) - (local.set $3 - (i32.add - (local.get $3) - (local.get $1) - ) - ) - ) - (local.set $3 - (local.get $1) - ) - ) - (local.set $3 - (local.get $1) - ) - ) - (if - (i32.ne - (local.get $2) - (i32.const -1) - ) - (block - (local.set $1 - (local.get $2) - ) - (br $label$172) - ) - ) - ) - (i32.store - (i32.const 4080) - (i32.or - (i32.load - (i32.const 4080) - ) - (i32.const 4) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $6) - (i32.const 2147483647) - ) - (if - (i32.and - (i32.lt_u - (local.tee $1 - (call $40 - (local.get $6) - ) - ) - (local.tee $3 - (call $40 - (i32.const 0) - ) - ) - ) - (i32.and - (i32.ne - (local.get $1) - (i32.const -1) - ) - (i32.ne - (local.get $3) - (i32.const -1) - ) - ) - ) - (br_if $label$172 - (i32.gt_u - (local.tee $3 - (i32.sub - (local.get $3) - (local.get $1) - ) - ) - (i32.add - (local.get $0) - (i32.const 40) - ) - ) - ) - ) - ) - (br $label$171) - ) - (i32.store - (i32.const 4068) - (local.tee $2 - (i32.add - (i32.load - (i32.const 4068) - ) - (local.get $3) - ) - ) - ) - (if - (i32.gt_u - (local.get $2) - (i32.load - (i32.const 4072) - ) - ) - (i32.store - (i32.const 4072) - (local.get $2) - ) - ) - (block $label$198 - (if - (local.tee $8 - (i32.load - (i32.const 3660) - ) - ) - (block - (local.set $2 - (i32.const 4084) - ) - (block $label$200 - (block $label$201 - (loop $label$202 - (br_if $label$201 - (i32.eq - (local.get $1) - (i32.add - (local.tee $4 - (i32.load - (local.get $2) - ) - ) - (local.tee $5 - (i32.load - (local.tee $7 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - ) - ) - ) - ) - ) - (br_if $label$202 - (local.tee $2 - (i32.load offset=8 - (local.get $2) - ) - ) - ) - ) - (br $label$200) - ) - (if - (i32.eqz - (i32.and - (i32.load offset=12 - (local.get $2) - ) - (i32.const 8) - ) - ) - (if - (i32.and - (i32.lt_u - (local.get $8) - (local.get $1) - ) - (i32.ge_u - (local.get $8) - (local.get $4) - ) - ) - (block - (i32.store - (local.get $7) - (i32.add - (local.get $5) - (local.get $3) - ) - ) - (local.set $5 - (i32.load - (i32.const 3648) - ) - ) - (local.set $1 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $2 - (i32.add - (local.get $8) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 3660) - (local.tee $2 - (i32.add - (local.get $8) - (if (result i32) - (i32.and - (local.get $2) - (i32.const 7) - ) - (local.get $1) - (local.tee $1 - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 3648) - (local.tee $1 - (i32.add - (i32.sub - (local.get $3) - (local.get $1) - ) - (local.get $5) - ) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $2) - (local.get $1) - ) - (i32.const 40) - ) - (i32.store - (i32.const 3664) - (i32.load - (i32.const 4124) - ) - ) - (br $label$198) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.tee $2 - (i32.load - (i32.const 3652) - ) - ) - ) - (block - (i32.store - (i32.const 3652) - (local.get $1) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - (local.set $10 - (i32.add - (local.get $1) - (local.get $3) - ) - ) - (local.set $5 - (i32.const 4084) - ) - (block $label$208 - (block $label$209 - (loop $label$210 - (br_if $label$209 - (i32.eq - (i32.load - (local.get $5) - ) - (local.get $10) - ) - ) - (br_if $label$210 - (local.tee $5 - (i32.load offset=8 - (local.get $5) - ) - ) - ) - (local.set $5 - (i32.const 4084) - ) - ) - (br $label$208) - ) - (if - (i32.and - (i32.load offset=12 - (local.get $5) - ) - (i32.const 8) - ) - (local.set $5 - (i32.const 4084) - ) - (block - (i32.store - (local.get $5) - (local.get $1) - ) - (i32.store - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - (i32.add - (i32.load - (local.get $5) - ) - (local.get $3) - ) - ) - (local.set $7 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $4 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $3 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $5 - (i32.add - (local.get $10) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $6 - (i32.add - (local.tee $13 - (i32.add - (local.get $1) - (if (result i32) - (i32.and - (local.get $4) - (i32.const 7) - ) - (local.get $7) - (i32.const 0) - ) - ) - ) - (local.get $0) - ) - ) - (local.set $7 - (i32.sub - (i32.sub - (local.tee $4 - (i32.add - (local.get $10) - (if (result i32) - (i32.and - (local.get $5) - (i32.const 7) - ) - (local.get $3) - (i32.const 0) - ) - ) - ) - (local.get $13) - ) - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $13) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (block $label$217 - (if - (i32.eq - (local.get $4) - (local.get $8) - ) - (block - (i32.store - (i32.const 3648) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3648) - ) - (local.get $7) - ) - ) - ) - (i32.store - (i32.const 3660) - (local.get $6) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - ) - (block - (if - (i32.eq - (local.get $4) - (i32.load - (i32.const 3656) - ) - ) - (block - (i32.store - (i32.const 3644) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3644) - ) - (local.get $7) - ) - ) - ) - (i32.store - (i32.const 3656) - (local.get $6) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $0) - ) - (local.get $0) - ) - (br $label$217) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (local.tee $0 - (if (result i32) - (i32.eq - (i32.and - (local.tee $0 - (i32.load offset=4 - (local.get $4) - ) - ) - (i32.const 3) - ) - (i32.const 1) - ) - (block (result i32) - (local.set $11 - (i32.and - (local.get $0) - (i32.const -8) - ) - ) - (local.set $1 - (i32.shr_u - (local.get $0) - (i32.const 3) - ) - ) - (block $label$222 - (if - (i32.lt_u - (local.get $0) - (i32.const 256) - ) - (block - (local.set $5 - (i32.load offset=12 - (local.get $4) - ) - ) - (block $label$224 - (if - (i32.ne - (local.tee $3 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.tee $0 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $3) - (local.get $2) - ) - (call $fimport$10) - ) - (br_if $label$224 - (i32.eq - (i32.load offset=12 - (local.get $3) - ) - (local.get $4) - ) - ) - (call $fimport$10) - ) - ) - ) - (if - (i32.eq - (local.get $5) - (local.get $3) - ) - (block - (i32.store - (i32.const 3636) - (i32.and - (i32.load - (i32.const 3636) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$222) - ) - ) - (block $label$228 - (if - (i32.eq - (local.get $5) - (local.get $0) - ) - (local.set $20 - (i32.add - (local.get $5) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $5) - (local.get $2) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $5) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (local.set $20 - (local.get $0) - ) - (br $label$228) - ) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=12 - (local.get $3) - (local.get $5) - ) - (i32.store - (local.get $20) - (local.get $3) - ) - ) - (block - (local.set $8 - (i32.load offset=24 - (local.get $4) - ) - ) - (block $label$234 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $4) - ) - ) - (local.get $4) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.tee $3 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load - (local.get $3) - ) - ) - (local.set $1 - (local.get $3) - ) - (block - (local.set $12 - (i32.const 0) - ) - (br $label$234) - ) - ) - ) - (loop $label$239 - (if - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $5) - ) - (br $label$239) - ) - ) - (if - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $5) - ) - (br $label$239) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $2) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $12 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.get $2) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $3 - (i32.add - (local.get $5) - (i32.const 12) - ) - ) - ) - (local.get $4) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (i32.store - (local.get $3) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $5) - ) - (local.set $12 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (br_if $label$222 - (i32.eqz - (local.get $8) - ) - ) - (block $label$249 - (if - (i32.eq - (local.get $4) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $4) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $12) - ) - (br_if $label$249 - (local.get $12) - ) - (i32.store - (i32.const 3640) - (i32.and - (i32.load - (i32.const 3640) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$222) - ) - (block - (if - (i32.lt_u - (local.get $8) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - ) - (local.get $4) - ) - (i32.store - (local.get $0) - (local.get $12) - ) - (i32.store offset=20 - (local.get $8) - (local.get $12) - ) - ) - (br_if $label$222 - (i32.eqz - (local.get $12) - ) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $12) - (local.tee $1 - (i32.load - (i32.const 3652) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $12) - (local.get $8) - ) - (if - (local.tee $3 - (i32.load - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $3) - (local.get $1) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $12) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $12) - ) - ) - ) - ) - (br_if $label$222 - (i32.eqz - (local.tee $0 - (i32.load offset=4 - (local.get $0) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $12) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $12) - ) - ) - ) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $11) - (local.get $7) - ) - ) - (i32.add - (local.get $4) - (local.get $11) - ) - ) - (local.get $4) - ) - ) - (i32.const 4) - ) - ) - (i32.and - (i32.load - (local.get $0) - ) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $7) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $7) - ) - (local.get $7) - ) - (local.set $0 - (i32.shr_u - (local.get $7) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $7) - (i32.const 256) - ) - (block - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.get $0) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - (block $label$263 - (if - (i32.and - (local.tee $1 - (i32.load - (i32.const 3636) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (block - (if - (i32.ge_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3652) - ) - ) - (block - (local.set $21 - (local.get $1) - ) - (local.set $9 - (local.get $0) - ) - (br $label$263) - ) - ) - (call $fimport$10) - ) - (block - (i32.store - (i32.const 3636) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (local.set $21 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $9 - (local.get $3) - ) - ) - ) - ) - (i32.store - (local.get $21) - (local.get $6) - ) - (i32.store offset=12 - (local.get $9) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $9) - ) - (i32.store offset=12 - (local.get $6) - (local.get $3) - ) - (br $label$217) - ) - ) - (local.set $3 - (i32.add - (i32.shl - (local.tee $2 - (block $label$267 (result i32) - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $7) - (i32.const 8) - ) - ) - (block (result i32) - (drop - (br_if $label$267 - (i32.const 31) - (i32.gt_u - (local.get $7) - (i32.const 16777215) - ) - ) - ) - (i32.or - (i32.and - (i32.shr_u - (local.get $7) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $3 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $3) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - (i32.store offset=28 - (local.get $6) - (local.get $2) - ) - (i32.store offset=4 - (local.tee $0 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (i32.const 3640) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $2) - ) - ) - ) - ) - (block - (i32.store - (i32.const 3640) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $3) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$217) - ) - ) - (local.set $0 - (i32.load - (local.get $3) - ) - ) - (local.set $1 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $2) - (i32.const 1) - ) - ) - ) - (local.set $2 - (i32.shl - (local.get $7) - (if (result i32) - (i32.eq - (local.get $2) - (i32.const 31) - ) - (i32.const 0) - (local.get $1) - ) - ) - ) - (block $label$273 - (block $label$274 - (block $label$275 - (loop $label$276 - (br_if $label$274 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.set $3 - (i32.shl - (local.get $2) - (i32.const 1) - ) - ) - (br_if $label$275 - (i32.eqz - (local.tee $1 - (i32.load - (local.tee $2 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $2) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $2 - (local.get $3) - ) - (local.set $0 - (local.get $1) - ) - (br $label$276) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $2) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $0) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$217) - ) - ) - (br $label$273) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $3 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $1 - (i32.load - (i32.const 3652) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $1) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $6) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $2) - ) - (i32.store offset=12 - (local.get $6) - (local.get $0) - ) - (i32.store offset=24 - (local.get $6) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $13) - (i32.const 8) - ) - ) - ) - ) - ) - (loop $label$281 - (block $label$282 - (if - (i32.le_u - (local.tee $2 - (i32.load - (local.get $5) - ) - ) - (local.get $8) - ) - (br_if $label$282 - (i32.gt_u - (local.tee $13 - (i32.add - (local.get $2) - (i32.load offset=4 - (local.get $5) - ) - ) - ) - (local.get $8) - ) - ) - ) - (local.set $5 - (i32.load offset=8 - (local.get $5) - ) - ) - (br $label$281) - ) - ) - (local.set $2 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $5 - (i32.add - (local.tee $7 - (i32.add - (local.get $13) - (i32.const -47) - ) - ) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $10 - (i32.add - (local.tee $7 - (if (result i32) - (i32.lt_u - (local.tee $2 - (i32.add - (local.get $7) - (if (result i32) - (i32.and - (local.get $5) - (i32.const 7) - ) - (local.get $2) - (i32.const 0) - ) - ) - ) - (local.tee $12 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - ) - (local.get $8) - (local.get $2) - ) - ) - (i32.const 8) - ) - ) - (local.set $5 - (i32.add - (local.get $7) - (i32.const 24) - ) - ) - (local.set $9 - (i32.add - (local.get $3) - (i32.const -40) - ) - ) - (local.set $2 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $4 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 3660) - (local.tee $4 - (i32.add - (local.get $1) - (if (result i32) - (i32.and - (local.get $4) - (i32.const 7) - ) - (local.get $2) - (local.tee $2 - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 3648) - (local.tee $2 - (i32.sub - (local.get $9) - (local.get $2) - ) - ) - ) - (i32.store offset=4 - (local.get $4) - (i32.or - (local.get $2) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $4) - (local.get $2) - ) - (i32.const 40) - ) - (i32.store - (i32.const 3664) - (i32.load - (i32.const 4124) - ) - ) - (i32.store - (local.tee $2 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (i32.const 27) - ) - (i64.store align=4 - (local.get $10) - (i64.load align=4 - (i32.const 4084) - ) - ) - (i64.store offset=8 align=4 - (local.get $10) - (i64.load align=4 - (i32.const 4092) - ) - ) - (i32.store - (i32.const 4084) - (local.get $1) - ) - (i32.store - (i32.const 4088) - (local.get $3) - ) - (i32.store - (i32.const 4096) - (i32.const 0) - ) - (i32.store - (i32.const 4092) - (local.get $10) - ) - (local.set $1 - (local.get $5) - ) - (loop $label$290 - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i32.const 7) - ) - (br_if $label$290 - (i32.lt_u - (i32.add - (local.get $1) - (i32.const 4) - ) - (local.get $13) - ) - ) - ) - (if - (i32.ne - (local.get $7) - (local.get $8) - ) - (block - (i32.store - (local.get $2) - (i32.and - (i32.load - (local.get $2) - ) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $8) - (i32.or - (local.tee $4 - (i32.sub - (local.get $7) - (local.get $8) - ) - ) - (i32.const 1) - ) - ) - (i32.store - (local.get $7) - (local.get $4) - ) - (local.set $1 - (i32.shr_u - (local.get $4) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $4) - (i32.const 256) - ) - (block - (local.set $2 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - (if - (i32.and - (local.tee $3 - (i32.load - (i32.const 3636) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.load - (local.tee $3 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (local.set $15 - (local.get $3) - ) - (local.set $11 - (local.get $1) - ) - ) - ) - (block - (i32.store - (i32.const 3636) - (i32.or - (local.get $3) - (local.get $1) - ) - ) - (local.set $15 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (local.set $11 - (local.get $2) - ) - ) - ) - (i32.store - (local.get $15) - (local.get $8) - ) - (i32.store offset=12 - (local.get $11) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $11) - ) - (i32.store offset=12 - (local.get $8) - (local.get $2) - ) - (br $label$198) - ) - ) - (local.set $2 - (i32.add - (i32.shl - (local.tee $5 - (if (result i32) - (local.tee $1 - (i32.shr_u - (local.get $4) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $4) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $4) - (i32.add - (local.tee $1 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $3 - (i32.shl - (local.get $1) - (local.tee $2 - (i32.and - (i32.shr_u - (i32.add - (local.get $1) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $2) - ) - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $3 - (i32.shl - (local.get $3) - (local.get $1) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $3) - (local.get $1) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $1) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - (i32.store offset=28 - (local.get $8) - (local.get $5) - ) - (i32.store offset=20 - (local.get $8) - (i32.const 0) - ) - (i32.store - (local.get $12) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.tee $3 - (i32.load - (i32.const 3640) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $5) - ) - ) - ) - ) - (block - (i32.store - (i32.const 3640) - (i32.or - (local.get $3) - (local.get $1) - ) - ) - (i32.store - (local.get $2) - (local.get $8) - ) - (i32.store offset=24 - (local.get $8) - (local.get $2) - ) - (i32.store offset=12 - (local.get $8) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $8) - ) - (br $label$198) - ) - ) - (local.set $1 - (i32.load - (local.get $2) - ) - ) - (local.set $3 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $5) - (i32.const 1) - ) - ) - ) - (local.set $5 - (i32.shl - (local.get $4) - (if (result i32) - (i32.eq - (local.get $5) - (i32.const 31) - ) - (i32.const 0) - (local.get $3) - ) - ) - ) - (block $label$304 - (block $label$305 - (block $label$306 - (loop $label$307 - (br_if $label$305 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $1) - ) - (i32.const -8) - ) - (local.get $4) - ) - ) - (local.set $2 - (i32.shl - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$306 - (i32.eqz - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (i32.add - (local.get $1) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $5) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $5 - (local.get $2) - ) - (local.set $1 - (local.get $3) - ) - (br $label$307) - ) - ) - (if - (i32.lt_u - (local.get $5) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $5) - (local.get $8) - ) - (i32.store offset=24 - (local.get $8) - (local.get $1) - ) - (i32.store offset=12 - (local.get $8) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $8) - ) - (br $label$198) - ) - ) - (br $label$304) - ) - (if - (i32.and - (i32.ge_u - (local.tee $5 - (i32.load - (local.tee $2 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - ) - (local.tee $3 - (i32.load - (i32.const 3652) - ) - ) - ) - (i32.ge_u - (local.get $1) - (local.get $3) - ) - ) - (block - (i32.store offset=12 - (local.get $5) - (local.get $8) - ) - (i32.store - (local.get $2) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $5) - ) - (i32.store offset=12 - (local.get $8) - (local.get $1) - ) - (i32.store offset=24 - (local.get $8) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - ) - (block - (if - (i32.or - (i32.eqz - (local.tee $2 - (i32.load - (i32.const 3652) - ) - ) - ) - (i32.lt_u - (local.get $1) - (local.get $2) - ) - ) - (i32.store - (i32.const 3652) - (local.get $1) - ) - ) - (i32.store - (i32.const 4084) - (local.get $1) - ) - (i32.store - (i32.const 4088) - (local.get $3) - ) - (i32.store - (i32.const 4096) - (i32.const 0) - ) - (i32.store - (i32.const 3672) - (i32.load - (i32.const 4108) - ) - ) - (i32.store - (i32.const 3668) - (i32.const -1) - ) - (local.set $2 - (i32.const 0) - ) - (loop $label$314 - (i32.store offset=12 - (local.tee $5 - (i32.add - (i32.shl - (i32.shl - (local.get $2) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - (local.get $5) - ) - (i32.store offset=8 - (local.get $5) - (local.get $5) - ) - (br_if $label$314 - (i32.ne - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 32) - ) - ) - ) - (local.set $5 - (i32.add - (local.get $3) - (i32.const -40) - ) - ) - (local.set $3 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $2 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 3660) - (local.tee $3 - (i32.add - (local.get $1) - (local.tee $1 - (if (result i32) - (i32.and - (local.get $2) - (i32.const 7) - ) - (local.get $3) - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 3648) - (local.tee $1 - (i32.sub - (local.get $5) - (local.get $1) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $3) - (local.get $1) - ) - (i32.const 40) - ) - (i32.store - (i32.const 3664) - (i32.load - (i32.const 4124) - ) - ) - ) - ) - ) - (if - (i32.gt_u - (local.tee $1 - (i32.load - (i32.const 3648) - ) - ) - (local.get $0) - ) - (block - (i32.store - (i32.const 3648) - (local.tee $3 - (i32.sub - (local.get $1) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 3660) - (local.tee $1 - (i32.add - (local.tee $2 - (i32.load - (i32.const 3660) - ) - ) - (local.get $0) - ) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - ) - (i32.store - (call $12) - (i32.const 12) - ) - (global.set $global$1 - (local.get $14) - ) - (i32.const 0) + (func $32 (;45;) (type $4) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $3 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $3 + local.tee $4 + local.get $1 + i32.const 255 + i32.and + local.tee $7 + i32.store8 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $0 + i32.const 16 + i32.add + local.tee $2 + i32.load + local.tee $5 + br_if 0 (;@3;) + local.get $0 + call $30 + if ;; label = @4 + i32.const -1 + local.set $1 + else + block ;; label = @5 + local.get $2 + i32.load + local.set $5 + br 2 (;@3;) + end + end + br 1 (;@2;) + end + local.get $0 + i32.const 20 + i32.add + local.tee $2 + i32.load + local.tee $6 + local.get $5 + i32.lt_u + if ;; label = @3 + local.get $1 + i32.const 255 + i32.and + local.tee $1 + local.get $0 + i32.load8_s offset=75 + i32.ne + if ;; label = @4 + block ;; label = @5 + local.get $2 + local.get $6 + i32.const 1 + i32.add + i32.store + local.get $6 + local.get $7 + i32.store8 + br 3 (;@2;) + end + end + end + local.get $0 + local.get $4 + i32.const 1 + local.get $0 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + i32.const 1 + i32.eq + if (result i32) ;; label = @3 + local.get $4 + i32.load8_u + else + i32.const -1 + end + local.set $1 + end + local.get $3 + global.set $global$1 + local.get $1 + end ) - ) - (func $38 (; 51 ;) (type $2) (param $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (block $label$1 - (if - (i32.eqz - (local.get $0) - ) - (return) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.add - (local.get $0) - (i32.const -8) - ) - ) - (local.tee $11 - (i32.load - (i32.const 3652) - ) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (local.tee $8 - (i32.and - (local.tee $0 - (i32.load - (i32.add - (local.get $0) - (i32.const -4) - ) - ) - ) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (call $fimport$10) - ) - (local.set $6 - (i32.add - (local.get $1) - (local.tee $4 - (i32.and - (local.get $0) - (i32.const -8) - ) - ) - ) - ) - (block $label$5 - (if - (i32.and - (local.get $0) - (i32.const 1) - ) - (block - (local.set $3 - (local.get $1) - ) - (local.set $2 - (local.get $4) - ) - ) - (block - (if - (i32.eqz - (local.get $8) - ) - (return) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.add - (local.get $1) - (i32.sub - (i32.const 0) - (local.tee $8 - (i32.load - (local.get $1) - ) - ) - ) - ) - ) - (local.get $11) - ) - (call $fimport$10) - ) - (local.set $1 - (i32.add - (local.get $8) - (local.get $4) - ) - ) - (if - (i32.eq - (local.get $0) - (i32.load - (i32.const 3656) - ) - ) - (block - (if - (i32.ne - (i32.and - (local.tee $3 - (i32.load - (local.tee $2 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - ) - ) - (i32.const 3) - ) - (i32.const 3) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (i32.store - (i32.const 3644) - (local.get $1) - ) - (i32.store - (local.get $2) - (i32.and - (local.get $3) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $0) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $0) - (local.get $1) - ) - (local.get $1) - ) - (return) - ) - ) - (local.set $10 - (i32.shr_u - (local.get $8) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $8) - (i32.const 256) - ) - (block - (local.set $3 - (i32.load offset=12 - (local.get $0) - ) - ) - (if - (i32.ne - (local.tee $4 - (i32.load offset=8 - (local.get $0) - ) - ) - (local.tee $2 - (i32.add - (i32.shl - (i32.shl - (local.get $10) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (local.get $11) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load offset=12 - (local.get $4) - ) - (local.get $0) - ) - (call $fimport$10) - ) - ) - ) - (if - (i32.eq - (local.get $3) - (local.get $4) - ) - (block - (i32.store - (i32.const 3636) - (i32.and - (i32.load - (i32.const 3636) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $10) - ) - (i32.const -1) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (if - (i32.eq - (local.get $3) - (local.get $2) - ) - (local.set $5 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $3) - (local.get $11) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $2 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - (local.get $0) - ) - (local.set $5 - (local.get $2) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=12 - (local.get $4) - (local.get $3) - ) - (i32.store - (local.get $5) - (local.get $4) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (local.set $12 - (i32.load offset=24 - (local.get $0) - ) - ) - (block $label$22 - (if - (i32.eq - (local.tee $4 - (i32.load offset=12 - (local.get $0) - ) - ) - (local.get $0) - ) - (block - (if - (local.tee $4 - (i32.load - (local.tee $8 - (i32.add - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - (local.set $5 - (local.get $8) - ) - (if - (i32.eqz - (local.tee $4 - (i32.load - (local.get $5) - ) - ) - ) - (block - (local.set $7 - (i32.const 0) - ) - (br $label$22) - ) - ) - ) - (loop $label$27 - (if - (local.tee $10 - (i32.load - (local.tee $8 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $4 - (local.get $10) - ) - (local.set $5 - (local.get $8) - ) - (br $label$27) - ) - ) - (if - (local.tee $10 - (i32.load - (local.tee $8 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $4 - (local.get $10) - ) - (local.set $5 - (local.get $8) - ) - (br $label$27) - ) - ) - ) - (if - (i32.lt_u - (local.get $5) - (local.get $11) - ) - (call $fimport$10) - (block - (i32.store - (local.get $5) - (i32.const 0) - ) - (local.set $7 - (local.get $4) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.load offset=8 - (local.get $0) - ) - ) - (local.get $11) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $8 - (i32.add - (local.get $5) - (i32.const 12) - ) - ) - ) - (local.get $0) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $10 - (i32.add - (local.get $4) - (i32.const 8) - ) - ) - ) - (local.get $0) - ) - (block - (i32.store - (local.get $8) - (local.get $4) - ) - (i32.store - (local.get $10) - (local.get $5) - ) - (local.set $7 - (local.get $4) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (if - (local.get $12) - (block - (if - (i32.eq - (local.get $0) - (i32.load - (local.tee $5 - (i32.add - (i32.shl - (local.tee $4 - (i32.load offset=28 - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - ) - ) - (block - (i32.store - (local.get $5) - (local.get $7) - ) - (if - (i32.eqz - (local.get $7) - ) - (block - (i32.store - (i32.const 3640) - (i32.and - (i32.load - (i32.const 3640) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $12) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $4 - (i32.add - (local.get $12) - (i32.const 16) - ) - ) - ) - (local.get $0) - ) - (i32.store - (local.get $4) - (local.get $7) - ) - (i32.store offset=20 - (local.get $12) - (local.get $7) - ) - ) - (if - (i32.eqz - (local.get $7) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $7) - (local.tee $5 - (i32.load - (i32.const 3652) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $7) - (local.get $12) - ) - (if - (local.tee $4 - (i32.load - (local.tee $8 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $4) - (local.get $5) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $7) - (local.get $4) - ) - (i32.store offset=24 - (local.get $4) - (local.get $7) - ) - ) - ) - ) - (if - (local.tee $4 - (i32.load offset=4 - (local.get $8) - ) - ) - (if - (i32.lt_u - (local.get $4) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $7) - (local.get $4) - ) - (i32.store offset=24 - (local.get $4) - (local.get $7) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - ) - ) - ) - (if - (i32.ge_u - (local.get $3) - (local.get $6) - ) - (call $fimport$10) - ) - (if - (i32.eqz - (i32.and - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - ) - ) - (i32.const 1) - ) - ) - (call $fimport$10) - ) - (if - (i32.and - (local.get $0) - (i32.const 2) - ) - (block - (i32.store - (local.get $1) - (i32.and - (local.get $0) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $2) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $2) - ) - (local.get $2) - ) - ) - (block - (if - (i32.eq - (local.get $6) - (i32.load - (i32.const 3660) - ) - ) - (block - (i32.store - (i32.const 3648) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3648) - ) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 3660) - (local.get $3) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (if - (i32.ne - (local.get $3) - (i32.load - (i32.const 3656) - ) - ) - (return) - ) - (i32.store - (i32.const 3656) - (i32.const 0) - ) - (i32.store - (i32.const 3644) - (i32.const 0) - ) - (return) - ) - ) - (if - (i32.eq - (local.get $6) - (i32.load - (i32.const 3656) - ) - ) - (block - (i32.store - (i32.const 3644) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3644) - ) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 3656) - (local.get $3) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $0) - ) - (local.get $0) - ) - (return) - ) - ) - (local.set $5 - (i32.add - (i32.and - (local.get $0) - (i32.const -8) - ) - (local.get $2) - ) - ) - (local.set $4 - (i32.shr_u - (local.get $0) - (i32.const 3) - ) - ) - (block $label$61 - (if - (i32.lt_u - (local.get $0) - (i32.const 256) - ) - (block - (local.set $2 - (i32.load offset=12 - (local.get $6) - ) - ) - (if - (i32.ne - (local.tee $1 - (i32.load offset=8 - (local.get $6) - ) - ) - (local.tee $0 - (i32.add - (i32.shl - (i32.shl - (local.get $4) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $1) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load offset=12 - (local.get $1) - ) - (local.get $6) - ) - (call $fimport$10) - ) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $1) - ) - (block - (i32.store - (i32.const 3636) - (i32.and - (i32.load - (i32.const 3636) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (br $label$61) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $0) - ) - (local.set $14 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - (local.get $6) - ) - (local.set $14 - (local.get $0) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=12 - (local.get $1) - (local.get $2) - ) - (i32.store - (local.get $14) - (local.get $1) - ) - ) - (block - (local.set $7 - (i32.load offset=24 - (local.get $6) - ) - ) - (block $label$73 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $6) - ) - ) - (local.get $6) - ) - (block - (if - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.tee $2 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - (local.set $2 - (local.get $1) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.get $2) - ) - ) - ) - (block - (local.set $9 - (i32.const 0) - ) - (br $label$73) - ) - ) - ) - (loop $label$78 - (if - (local.tee $4 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (local.set $2 - (local.get $1) - ) - (br $label$78) - ) - ) - (if - (local.tee $4 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (local.set $2 - (local.get $1) - ) - (br $label$78) - ) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $2) - (i32.const 0) - ) - (local.set $9 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $2 - (i32.load offset=8 - (local.get $6) - ) - ) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 12) - ) - ) - ) - (local.get $6) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $4 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $6) - ) - (block - (i32.store - (local.get $1) - (local.get $0) - ) - (i32.store - (local.get $4) - (local.get $2) - ) - (local.set $9 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (if - (local.get $7) - (block - (if - (i32.eq - (local.get $6) - (i32.load - (local.tee $2 - (i32.add - (i32.shl - (local.tee $0 - (i32.load offset=28 - (local.get $6) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - ) - ) - (block - (i32.store - (local.get $2) - (local.get $9) - ) - (if - (i32.eqz - (local.get $9) - ) - (block - (i32.store - (i32.const 3640) - (i32.and - (i32.load - (i32.const 3640) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $0) - ) - (i32.const -1) - ) - ) - ) - (br $label$61) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $7) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $7) - (i32.const 16) - ) - ) - ) - (local.get $6) - ) - (i32.store - (local.get $0) - (local.get $9) - ) - (i32.store offset=20 - (local.get $7) - (local.get $9) - ) - ) - (br_if $label$61 - (i32.eqz - (local.get $9) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $9) - (local.tee $2 - (i32.load - (i32.const 3652) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $9) - (local.get $7) - ) - (if - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $0) - (local.get $2) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $9) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $9) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=4 - (local.get $1) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $9) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $9) - ) - ) - ) - ) - ) - ) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $5) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $5) - ) - (local.get $5) - ) - (if - (i32.eq - (local.get $3) - (i32.load - (i32.const 3656) - ) - ) - (block - (i32.store - (i32.const 3644) - (local.get $5) - ) - (return) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - ) - (local.set $1 - (i32.shr_u - (local.get $2) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.const 256) - ) - (block - (local.set $0 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3676) - ) - ) - (if - (i32.and - (local.tee $2 - (i32.load - (i32.const 3636) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.load - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (local.set $15 - (local.get $2) - ) - (local.set $13 - (local.get $1) - ) - ) - ) - (block - (i32.store - (i32.const 3636) - (i32.or - (local.get $2) - (local.get $1) - ) - ) - (local.set $15 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - (local.set $13 - (local.get $0) - ) - ) - ) - (i32.store - (local.get $15) - (local.get $3) - ) - (i32.store offset=12 - (local.get $13) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $13) - ) - (i32.store offset=12 - (local.get $3) - (local.get $0) - ) - (return) - ) - ) - (local.set $0 - (i32.add - (i32.shl - (local.tee $1 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $2) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $2) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $2) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $4 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $0) - ) - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $0 - (i32.shl - (local.get $1) - (local.get $4) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $0) - (local.get $1) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 3940) - ) - ) - (i32.store offset=28 - (local.get $3) - (local.get $1) - ) - (i32.store offset=20 - (local.get $3) - (i32.const 0) - ) - (i32.store offset=16 - (local.get $3) - (i32.const 0) - ) - (block $label$113 - (if - (i32.and - (local.tee $4 - (i32.load - (i32.const 3640) - ) - ) - (local.tee $5 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (block - (local.set $0 - (i32.load - (local.get $0) - ) - ) - (local.set $4 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $1) - (i32.const 1) - ) - ) - ) - (local.set $1 - (i32.shl - (local.get $2) - (if (result i32) - (i32.eq - (local.get $1) - (i32.const 31) - ) - (i32.const 0) - (local.get $4) - ) - ) - ) - (block $label$117 - (block $label$118 - (block $label$119 - (loop $label$120 - (br_if $label$118 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $2) - ) - ) - (local.set $4 - (i32.shl - (local.get $1) - (i32.const 1) - ) - ) - (br_if $label$119 - (i32.eqz - (local.tee $5 - (i32.load - (local.tee $1 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $1) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $1 - (local.get $4) - ) - (local.set $0 - (local.get $5) - ) - (br $label$120) - ) - ) - (if - (i32.lt_u - (local.get $1) - (i32.load - (i32.const 3652) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $0) - ) - (i32.store offset=12 - (local.get $3) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $3) - ) - (br $label$113) - ) - ) - (br $label$117) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $4 - (i32.load - (i32.const 3652) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $4) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $3) - ) - (i32.store - (local.get $1) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $2) - ) - (i32.store offset=12 - (local.get $3) - (local.get $0) - ) - (i32.store offset=24 - (local.get $3) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - (block - (i32.store - (i32.const 3640) - (i32.or - (local.get $4) - (local.get $5) - ) - ) - (i32.store - (local.get $0) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $0) - ) - (i32.store offset=12 - (local.get $3) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $3) - ) - ) - ) - ) - (i32.store - (i32.const 3668) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3668) - ) - (i32.const -1) - ) - ) - ) - (if - (local.get $0) - (return) - (local.set $0 - (i32.const 4092) - ) - ) - (loop $label$128 - (local.set $0 - (i32.add - (local.tee $2 - (i32.load - (local.get $0) - ) - ) - (i32.const 8) - ) - ) - (br_if $label$128 - (local.get $2) - ) - ) - (i32.store - (i32.const 3668) - (i32.const -1) - ) + (func $33 (;46;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) + (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + local.get $2 + local.get $1 + i32.mul + local.set $4 + local.get $3 + i32.load offset=76 + i32.const -1 + i32.gt_s + if ;; label = @2 + block ;; label = @3 + local.get $3 + call $20 + i32.eqz + local.set $5 + local.get $0 + local.get $4 + local.get $3 + call $21 + local.set $0 + local.get $5 + i32.eqz + if ;; label = @4 + local.get $3 + call $13 + end + end + else + local.get $0 + local.get $4 + local.get $3 + call $21 + local.set $0 + end + local.get $0 + local.get $4 + i32.ne + if ;; label = @2 + local.get $0 + local.get $1 + i32.div_u + local.set $2 + end + local.get $2 + end ) - ) - (func $39 (; 52 ;) (type $6) - (nop) - ) - (func $40 (; 53 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.add - (local.tee $2 - (i32.load - (global.get $global$0) - ) - ) - (local.tee $0 - (i32.and - (i32.add - (local.get $0) - (i32.const 15) - ) - (i32.const -16) - ) - ) - ) - ) - (if - (i32.or - (i32.and - (i32.gt_s - (local.get $0) - (i32.const 0) - ) - (i32.lt_s - (local.get $1) - (local.get $2) - ) - ) - (i32.lt_s - (local.get $1) - (i32.const 0) - ) - ) - (block - (drop - (call $fimport$6) - ) - (call $fimport$11 - (i32.const 12) - ) - (return - (i32.const -1) - ) - ) - ) - (i32.store - (global.get $global$0) - (local.get $1) - ) - (if - (i32.gt_s - (local.get $1) - (call $fimport$5) - ) - (if - (i32.eqz - (call $fimport$4) - ) - (block - (call $fimport$11 - (i32.const 12) - ) - (i32.store - (global.get $global$0) - (local.get $2) - ) - (return - (i32.const -1) - ) - ) - ) - ) - (local.get $2) + (func $34 (;47;) (type $4) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $2 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $2 + local.tee $3 + local.get $1 + i32.store + i32.const 1024 + i32.load + local.get $0 + local.get $3 + call $18 + local.set $0 + local.get $2 + global.set $global$1 + local.get $0 + end ) - ) - (func $41 (; 54 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $4 - (i32.add - (local.get $0) - (local.get $2) - ) - ) - (if - (i32.ge_s - (local.get $2) - (i32.const 20) - ) - (block - (local.set $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (if - (local.tee $3 - (i32.and - (local.get $0) - (i32.const 3) - ) - ) - (block - (local.set $3 - (i32.sub - (i32.add - (local.get $0) - (i32.const 4) - ) - (local.get $3) - ) - ) - (loop $label$4 - (if - (i32.lt_s - (local.get $0) - (local.get $3) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (br $label$4) - ) - ) - ) - ) - ) - (local.set $3 - (i32.or - (i32.or - (i32.or - (local.get $1) - (i32.shl - (local.get $1) - (i32.const 8) - ) - ) - (i32.shl - (local.get $1) - (i32.const 16) - ) - ) - (i32.shl - (local.get $1) - (i32.const 24) - ) - ) - ) - (local.set $5 - (i32.and - (local.get $4) - (i32.const -4) - ) - ) - (loop $label$6 - (if - (i32.lt_s - (local.get $0) - (local.get $5) - ) - (block - (i32.store - (local.get $0) - (local.get $3) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - (br $label$6) - ) - ) - ) - ) - ) - (loop $label$8 - (if - (i32.lt_s - (local.get $0) - (local.get $4) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (br $label$8) - ) - ) - ) - (i32.sub - (local.get $0) - (local.get $2) - ) + (func $35 (;48;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) (local $3 i32) + block $label$1 (result i32) ;; label = @1 + i32.const 1024 + i32.load + local.tee $1 + i32.load offset=76 + i32.const -1 + i32.gt_s + if (result i32) ;; label = @2 + local.get $1 + call $20 + else + i32.const 0 + end + local.set $2 + block $label$4 (result i32) ;; label = @2 + local.get $0 + local.get $1 + call $36 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @3 + i32.const 1 + else + block (result i32) ;; label = @4 + local.get $1 + i32.load8_s offset=75 + i32.const 10 + i32.ne + if ;; label = @5 + local.get $1 + i32.const 20 + i32.add + local.tee $3 + i32.load + local.tee $0 + local.get $1 + i32.load offset=16 + i32.lt_u + if ;; label = @6 + block ;; label = @7 + local.get $3 + local.get $0 + i32.const 1 + i32.add + i32.store + local.get $0 + i32.const 10 + i32.store8 + i32.const 0 + br 5 (;@2;) + end + end + end + local.get $1 + i32.const 10 + call $32 + i32.const 0 + i32.lt_s + end + end + end + local.set $0 + local.get $2 + if ;; label = @2 + local.get $1 + call $13 + end + local.get $0 + i32.const 31 + i32.shl + i32.const 31 + i32.shr_s + end ) - ) - (func $42 (; 55 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (block $label$1 (result i32) - (if - (i32.ge_s - (local.get $2) - (i32.const 4096) - ) - (return - (call $fimport$12 - (local.get $0) - (local.get $1) - (local.get $2) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (if - (i32.eq - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.and - (local.get $1) - (i32.const 3) - ) - ) - (block - (loop $label$4 - (if - (i32.and - (local.get $0) - (i32.const 3) - ) - (block - (if - (i32.eqz - (local.get $2) - ) - (return - (local.get $3) - ) - ) - (i32.store8 - (local.get $0) - (i32.load8_s - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 1) - ) - ) - (br $label$4) - ) - ) - ) - (loop $label$7 - (if - (i32.ge_s - (local.get $2) - (i32.const 4) - ) - (block - (i32.store - (local.get $0) - (i32.load - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 4) - ) - ) - (br $label$7) - ) - ) - ) - ) - ) - (loop $label$9 - (if - (i32.gt_s - (local.get $2) - (i32.const 0) - ) - (block - (i32.store8 - (local.get $0) - (i32.load8_s - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 1) - ) - ) - (br $label$9) - ) - ) - ) - (local.get $3) + (func $36 (;49;) (type $4) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + local.get $0 + call $31 + i32.const 1 + local.get $1 + call $33 + i32.const -1 + i32.add ) - ) - (func $43 (; 56 ;) (type $3) (result i32) - (i32.const 0) - ) - (func $44 (; 57 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (call_indirect (type $1) - (local.get $1) - (i32.add - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 0) - ) + (func $37 (;50;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $14 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $14 + local.set $18 + block $label$2 ;; label = @2 + local.get $0 + i32.const 245 + i32.lt_u + if ;; label = @3 + block ;; label = @4 + local.get $0 + i32.const 11 + i32.add + i32.const -8 + i32.and + local.set $3 + i32.const 3636 + i32.load + local.tee $8 + local.get $0 + i32.const 11 + i32.lt_u + if (result i32) ;; label = @5 + i32.const 16 + local.tee $3 + else + local.get $3 + end + i32.const 3 + i32.shr_u + local.tee $2 + i32.shr_u + local.tee $0 + i32.const 3 + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $0 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $2 + i32.add + local.tee $5 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.tee $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $7 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.set $4 + local.get $2 + local.get $4 + i32.eq + if ;; label = @7 + i32.const 3636 + local.get $8 + i32.const 1 + local.get $5 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + else + block ;; label = @8 + local.get $4 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $4 + i32.const 12 + i32.add + local.tee $0 + i32.load + local.get $7 + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $0 + local.get $2 + i32.store + local.get $3 + local.get $4 + i32.store + end + else + call $fimport$10 + end + end + end + local.get $7 + local.get $5 + i32.const 3 + i32.shl + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $7 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + local.get $14 + global.set $global$1 + local.get $1 + return + end + end + local.get $3 + i32.const 3644 + i32.load + local.tee $16 + i32.gt_u + if ;; label = @5 + block ;; label = @6 + local.get $0 + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.get $2 + i32.shl + i32.const 2 + local.get $2 + i32.shl + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.or + i32.and + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $5 + local.get $0 + local.get $5 + i32.shr_u + local.tee $2 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $5 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + i32.add + local.tee $11 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.tee $4 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $9 + i32.const 8 + i32.add + local.tee $5 + i32.load + local.set $12 + local.get $4 + local.get $12 + i32.eq + if ;; label = @9 + i32.const 3636 + local.get $8 + i32.const 1 + local.get $11 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $7 + i32.store + else + block ;; label = @10 + local.get $12 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$10 + end + local.get $12 + i32.const 12 + i32.add + local.tee $0 + i32.load + local.get $9 + i32.eq + if ;; label = @11 + block ;; label = @12 + local.get $0 + local.get $4 + i32.store + local.get $2 + local.get $12 + i32.store + local.get $8 + local.set $7 + end + else + call $fimport$10 + end + end + end + local.get $9 + local.get $3 + i32.const 3 + i32.or + i32.store offset=4 + local.get $9 + local.get $3 + i32.add + local.tee $4 + local.get $11 + i32.const 3 + i32.shl + local.get $3 + i32.sub + local.tee $11 + i32.const 1 + i32.or + i32.store offset=4 + local.get $4 + local.get $11 + i32.add + local.get $11 + i32.store + local.get $16 + if ;; label = @9 + block ;; label = @10 + i32.const 3656 + i32.load + local.set $9 + local.get $16 + i32.const 3 + i32.shr_u + local.tee $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.set $2 + local.get $7 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @11 + local.get $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $0 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @12 + call $fimport$10 + else + block ;; label = @13 + local.get $3 + local.set $6 + local.get $0 + local.set $1 + end + end + else + block ;; label = @12 + i32.const 3636 + local.get $7 + local.get $0 + i32.or + i32.store + local.get $2 + i32.const 8 + i32.add + local.set $6 + local.get $2 + local.set $1 + end + end + local.get $6 + local.get $9 + i32.store + local.get $1 + local.get $9 + i32.store offset=12 + local.get $9 + local.get $1 + i32.store offset=8 + local.get $9 + local.get $2 + i32.store offset=12 + end + end + i32.const 3644 + local.get $11 + i32.store + i32.const 3656 + local.get $4 + i32.store + local.get $14 + global.set $global$1 + local.get $5 + return + end + end + i32.const 3640 + i32.load + local.tee $6 + if ;; label = @7 + block ;; label = @8 + local.get $6 + i32.const 0 + local.get $6 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $2 + local.get $0 + local.get $2 + i32.shr_u + local.tee $1 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $2 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 3940 + i32.add + i32.load + local.tee $2 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.sub + local.set $9 + local.get $2 + local.set $1 + loop $label$25 ;; label = @9 + block $label$26 ;; label = @10 + local.get $1 + i32.load offset=16 + local.tee $0 + i32.eqz + if ;; label = @11 + local.get $1 + i32.load offset=20 + local.tee $0 + i32.eqz + br_if 1 (;@10;) + end + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.sub + local.tee $1 + local.get $9 + i32.lt_u + local.tee $7 + if ;; label = @11 + local.get $1 + local.set $9 + end + local.get $0 + local.set $1 + local.get $7 + if ;; label = @11 + local.get $0 + local.set $2 + end + br 1 (;@9;) + end + end + local.get $2 + i32.const 3652 + i32.load + local.tee $12 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $2 + local.get $2 + local.get $3 + i32.add + local.tee $13 + i32.ge_u + if ;; label = @9 + call $fimport$10 + end + local.get $2 + i32.load offset=24 + local.set $15 + block $label$32 ;; label = @9 + local.get $2 + i32.load offset=12 + local.tee $0 + local.get $2 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $2 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @12 + local.get $2 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @13 + block ;; label = @14 + i32.const 0 + local.set $4 + br 5 (;@9;) + end + end + end + loop $label$36 ;; label = @12 + local.get $0 + i32.const 20 + i32.add + local.tee $11 + i32.load + local.tee $7 + if ;; label = @13 + block ;; label = @14 + local.get $7 + local.set $0 + local.get $11 + local.set $1 + br 2 (;@12;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $11 + i32.load + local.tee $7 + if ;; label = @13 + block ;; label = @14 + local.get $7 + local.set $0 + local.get $11 + local.set $1 + br 2 (;@12;) + end + end + end + local.get $1 + local.get $12 + i32.lt_u + if ;; label = @12 + call $fimport$10 + else + block ;; label = @13 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $4 + end + end + end + else + block ;; label = @11 + local.get $2 + i32.load offset=8 + local.tee $11 + local.get $12 + i32.lt_u + if ;; label = @12 + call $fimport$10 + end + local.get $11 + i32.const 12 + i32.add + local.tee $7 + i32.load + local.get $2 + i32.ne + if ;; label = @12 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $2 + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $7 + local.get $0 + i32.store + local.get $1 + local.get $11 + i32.store + local.get $0 + local.set $4 + end + else + call $fimport$10 + end + end + end + end + block $label$46 ;; label = @9 + local.get $15 + if ;; label = @10 + block ;; label = @11 + local.get $2 + local.get $2 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 3940 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $0 + local.get $4 + i32.store + local.get $4 + i32.eqz + if ;; label = @14 + block ;; label = @15 + i32.const 3640 + local.get $6 + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 6 (;@9;) + end + end + end + else + block ;; label = @13 + local.get $15 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$10 + end + local.get $15 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $2 + i32.eq + if ;; label = @14 + local.get $0 + local.get $4 + i32.store + else + local.get $15 + local.get $4 + i32.store offset=20 + end + local.get $4 + i32.eqz + br_if 4 (;@9;) + end + end + local.get $4 + i32.const 3652 + i32.load + local.tee $0 + i32.lt_u + if ;; label = @12 + call $fimport$10 + end + local.get $4 + local.get $15 + i32.store offset=24 + local.get $2 + i32.load offset=16 + local.tee $1 + if ;; label = @12 + local.get $1 + local.get $0 + i32.lt_u + if ;; label = @13 + call $fimport$10 + else + block ;; label = @14 + local.get $4 + local.get $1 + i32.store offset=16 + local.get $1 + local.get $4 + i32.store offset=24 + end + end + end + local.get $2 + i32.load offset=20 + local.tee $0 + if ;; label = @12 + local.get $0 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @13 + call $fimport$10 + else + block ;; label = @14 + local.get $4 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $4 + i32.store offset=24 + end + end + end + end + end + end + local.get $9 + i32.const 16 + i32.lt_u + if ;; label = @9 + block ;; label = @10 + local.get $2 + local.get $9 + local.get $3 + i32.add + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + else + block ;; label = @10 + local.get $2 + local.get $3 + i32.const 3 + i32.or + i32.store offset=4 + local.get $13 + local.get $9 + i32.const 1 + i32.or + i32.store offset=4 + local.get $13 + local.get $9 + i32.add + local.get $9 + i32.store + local.get $16 + if ;; label = @11 + block ;; label = @12 + i32.const 3656 + i32.load + local.set $7 + local.get $16 + i32.const 3 + i32.shr_u + local.tee $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.set $3 + local.get $8 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @13 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$10 + else + block ;; label = @15 + local.get $1 + local.set $10 + local.get $0 + local.set $5 + end + end + else + block ;; label = @14 + i32.const 3636 + local.get $8 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $10 + local.get $3 + local.set $5 + end + end + local.get $10 + local.get $7 + i32.store + local.get $5 + local.get $7 + i32.store offset=12 + local.get $7 + local.get $5 + i32.store offset=8 + local.get $7 + local.get $3 + i32.store offset=12 + end + end + i32.const 3644 + local.get $9 + i32.store + i32.const 3656 + local.get $13 + i32.store + end + end + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + else + local.get $3 + local.set $0 + end + end + else + local.get $3 + local.set $0 + end + end + else + local.get $0 + i32.const -65 + i32.gt_u + if ;; label = @4 + i32.const -1 + local.set $0 + else + block ;; label = @5 + local.get $0 + i32.const 11 + i32.add + local.tee $0 + i32.const -8 + i32.and + local.set $7 + i32.const 3640 + i32.load + local.tee $5 + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @8 + local.get $7 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @9 + i32.const 31 + else + local.get $7 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $3 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $3 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.set $17 + i32.const 0 + local.get $7 + i32.sub + local.set $3 + block $label$78 ;; label = @8 + block $label$79 ;; label = @9 + block $label$80 ;; label = @10 + local.get $17 + i32.const 2 + i32.shl + i32.const 3940 + i32.add + i32.load + local.tee $1 + if ;; label = @11 + block ;; label = @12 + i32.const 25 + local.get $17 + i32.const 1 + i32.shr_u + i32.sub + local.set $0 + i32.const 0 + local.set $4 + local.get $7 + local.get $17 + i32.const 31 + i32.eq + if (result i32) ;; label = @13 + i32.const 0 + else + local.get $0 + end + i32.shl + local.set $10 + i32.const 0 + local.set $0 + loop $label$84 ;; label = @13 + local.get $1 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.sub + local.tee $6 + local.get $3 + i32.lt_u + if ;; label = @14 + local.get $6 + if ;; label = @15 + block ;; label = @16 + local.get $6 + local.set $3 + local.get $1 + local.set $0 + end + else + block ;; label = @16 + i32.const 0 + local.set $3 + local.get $1 + local.set $0 + br 7 (;@9;) + end + end + end + local.get $1 + i32.load offset=20 + local.tee $19 + i32.eqz + local.get $19 + local.get $1 + i32.const 16 + i32.add + local.get $10 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + i32.load + local.tee $6 + i32.eq + i32.or + if (result i32) ;; label = @14 + local.get $4 + else + local.get $19 + end + local.set $1 + local.get $10 + local.get $6 + i32.eqz + local.tee $4 + i32.const 1 + i32.and + i32.const 1 + i32.xor + i32.shl + local.set $10 + local.get $4 + if ;; label = @14 + block ;; label = @15 + local.get $1 + local.set $4 + local.get $0 + local.set $1 + br 5 (;@10;) + end + else + block ;; label = @15 + local.get $1 + local.set $4 + local.get $6 + local.set $1 + br 2 (;@13;) + end + end + end + end + else + block ;; label = @12 + i32.const 0 + local.set $4 + i32.const 0 + local.set $1 + end + end + end + local.get $4 + i32.eqz + local.get $1 + i32.eqz + i32.and + if (result i32) ;; label = @10 + block (result i32) ;; label = @11 + local.get $5 + i32.const 2 + local.get $17 + i32.shl + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.or + i32.and + local.tee $0 + i32.eqz + if ;; label = @12 + block ;; label = @13 + local.get $7 + local.set $0 + br 11 (;@2;) + end + end + local.get $0 + i32.const 0 + local.get $0 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $10 + local.get $0 + local.get $10 + i32.shr_u + local.tee $4 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $10 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 3940 + i32.add + i32.load + end + else + local.get $4 + end + local.tee $0 + br_if 0 (;@9;) + local.get $1 + local.set $4 + br 1 (;@8;) + end + loop $label$96 ;; label = @9 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.sub + local.tee $4 + local.get $3 + i32.lt_u + local.tee $10 + if ;; label = @10 + local.get $4 + local.set $3 + end + local.get $10 + if ;; label = @10 + local.get $0 + local.set $1 + end + local.get $0 + i32.load offset=16 + local.tee $4 + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.set $0 + br 2 (;@9;) + end + end + local.get $0 + i32.load offset=20 + local.tee $0 + br_if 0 (;@9;) + local.get $1 + local.set $4 + end + end + local.get $4 + if ;; label = @8 + local.get $3 + i32.const 3644 + i32.load + local.get $7 + i32.sub + i32.lt_u + if ;; label = @9 + block ;; label = @10 + local.get $4 + i32.const 3652 + i32.load + local.tee $12 + i32.lt_u + if ;; label = @11 + call $fimport$10 + end + local.get $4 + local.get $4 + local.get $7 + i32.add + local.tee $6 + i32.ge_u + if ;; label = @11 + call $fimport$10 + end + local.get $4 + i32.load offset=24 + local.set $10 + block $label$104 ;; label = @11 + local.get $4 + i32.load offset=12 + local.tee $0 + local.get $4 + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $4 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @14 + local.get $4 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @15 + block ;; label = @16 + i32.const 0 + local.set $13 + br 5 (;@11;) + end + end + end + loop $label$108 ;; label = @14 + local.get $0 + i32.const 20 + i32.add + local.tee $9 + i32.load + local.tee $11 + if ;; label = @15 + block ;; label = @16 + local.get $11 + local.set $0 + local.get $9 + local.set $1 + br 2 (;@14;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $9 + i32.load + local.tee $11 + if ;; label = @15 + block ;; label = @16 + local.get $11 + local.set $0 + local.get $9 + local.set $1 + br 2 (;@14;) + end + end + end + local.get $1 + local.get $12 + i32.lt_u + if ;; label = @14 + call $fimport$10 + else + block ;; label = @15 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $13 + end + end + end + else + block ;; label = @13 + local.get $4 + i32.load offset=8 + local.tee $9 + local.get $12 + i32.lt_u + if ;; label = @14 + call $fimport$10 + end + local.get $9 + i32.const 12 + i32.add + local.tee $11 + i32.load + local.get $4 + i32.ne + if ;; label = @14 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $4 + i32.eq + if ;; label = @14 + block ;; label = @15 + local.get $11 + local.get $0 + i32.store + local.get $1 + local.get $9 + i32.store + local.get $0 + local.set $13 + end + else + call $fimport$10 + end + end + end + end + block $label$118 ;; label = @11 + local.get $10 + if ;; label = @12 + block ;; label = @13 + local.get $4 + local.get $4 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 3940 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @14 + block ;; label = @15 + local.get $0 + local.get $13 + i32.store + local.get $13 + i32.eqz + if ;; label = @16 + block ;; label = @17 + i32.const 3640 + local.get $5 + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $2 + i32.store + br 6 (;@11;) + end + end + end + else + block ;; label = @15 + local.get $10 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @16 + call $fimport$10 + end + local.get $10 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @16 + local.get $0 + local.get $13 + i32.store + else + local.get $10 + local.get $13 + i32.store offset=20 + end + local.get $13 + i32.eqz + if ;; label = @16 + block ;; label = @17 + local.get $5 + local.set $2 + br 6 (;@11;) + end + end + end + end + local.get $13 + i32.const 3652 + i32.load + local.tee $0 + i32.lt_u + if ;; label = @14 + call $fimport$10 + end + local.get $13 + local.get $10 + i32.store offset=24 + local.get $4 + i32.load offset=16 + local.tee $1 + if ;; label = @14 + local.get $1 + local.get $0 + i32.lt_u + if ;; label = @15 + call $fimport$10 + else + block ;; label = @16 + local.get $13 + local.get $1 + i32.store offset=16 + local.get $1 + local.get $13 + i32.store offset=24 + end + end + end + local.get $4 + i32.load offset=20 + local.tee $0 + if ;; label = @14 + local.get $0 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @15 + call $fimport$10 + else + block ;; label = @16 + local.get $13 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $13 + i32.store offset=24 + local.get $5 + local.set $2 + end + end + else + local.get $5 + local.set $2 + end + end + else + local.get $5 + local.set $2 + end + end + block $label$136 ;; label = @11 + local.get $3 + i32.const 16 + i32.lt_u + if ;; label = @12 + block ;; label = @13 + local.get $4 + local.get $3 + local.get $7 + i32.add + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $4 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + else + block ;; label = @13 + local.get $4 + local.get $7 + i32.const 3 + i32.or + i32.store offset=4 + local.get $6 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $3 + i32.add + local.get $3 + i32.store + local.get $3 + i32.const 3 + i32.shr_u + local.set $0 + local.get $3 + i32.const 256 + i32.lt_u + if ;; label = @14 + block ;; label = @15 + local.get $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.set $3 + i32.const 3636 + i32.load + local.tee $1 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @16 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @17 + call $fimport$10 + else + block ;; label = @18 + local.get $1 + local.set $16 + local.get $0 + local.set $8 + end + end + else + block ;; label = @17 + i32.const 3636 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $16 + local.get $3 + local.set $8 + end + end + local.get $16 + local.get $6 + i32.store + local.get $8 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $8 + i32.store offset=8 + local.get $6 + local.get $3 + i32.store offset=12 + br 4 (;@11;) + end + end + local.get $3 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @14 + local.get $3 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @15 + i32.const 31 + else + local.get $3 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $5 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $5 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $5 + i32.const 2 + i32.shl + i32.const 3940 + i32.add + local.set $1 + local.get $6 + local.get $5 + i32.store offset=28 + local.get $6 + i32.const 16 + i32.add + local.tee $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store + local.get $2 + i32.const 1 + local.get $5 + i32.shl + local.tee $0 + i32.and + i32.eqz + if ;; label = @14 + block ;; label = @15 + i32.const 3640 + local.get $2 + local.get $0 + i32.or + i32.store + local.get $1 + local.get $6 + i32.store + local.get $6 + local.get $1 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 4 (;@11;) + end + end + local.get $1 + i32.load + local.set $0 + i32.const 25 + local.get $5 + i32.const 1 + i32.shr_u + i32.sub + local.set $1 + local.get $3 + local.get $5 + i32.const 31 + i32.eq + if (result i32) ;; label = @14 + i32.const 0 + else + local.get $1 + end + i32.shl + local.set $5 + block $label$151 ;; label = @14 + block $label$152 ;; label = @15 + block $label$153 ;; label = @16 + loop $label$154 ;; label = @17 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.eq + br_if 2 (;@15;) + local.get $5 + i32.const 1 + i32.shl + local.set $2 + local.get $0 + i32.const 16 + i32.add + local.get $5 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $5 + i32.load + local.tee $1 + i32.eqz + br_if 1 (;@16;) + local.get $2 + local.set $5 + local.get $1 + local.set $0 + br 0 (;@17;) + end + end + local.get $5 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @16 + call $fimport$10 + else + block ;; label = @17 + local.get $5 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 6 (;@11;) + end + end + br 1 (;@14;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $2 + i32.const 3652 + i32.load + local.tee $1 + i32.ge_u + local.get $0 + local.get $1 + i32.ge_u + i32.and + if ;; label = @15 + block ;; label = @16 + local.get $2 + local.get $6 + i32.store offset=12 + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $2 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $6 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + end + end + local.get $14 + global.set $global$1 + local.get $4 + i32.const 8 + i32.add + return + end + else + local.get $7 + local.set $0 + end + else + local.get $7 + local.set $0 + end + end + else + local.get $7 + local.set $0 + end + end + end + end + end + i32.const 3644 + i32.load + local.tee $1 + local.get $0 + i32.ge_u + if ;; label = @2 + block ;; label = @3 + i32.const 3656 + i32.load + local.set $2 + local.get $1 + local.get $0 + i32.sub + local.tee $3 + i32.const 15 + i32.gt_u + if ;; label = @4 + block ;; label = @5 + i32.const 3656 + local.get $2 + local.get $0 + i32.add + local.tee $1 + i32.store + i32.const 3644 + local.get $3 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $1 + local.get $3 + i32.add + local.get $3 + i32.store + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + end + else + block ;; label = @5 + i32.const 3644 + i32.const 0 + i32.store + i32.const 3656 + i32.const 0 + i32.store + local.get $2 + local.get $1 + i32.const 3 + i32.or + i32.store offset=4 + local.get $2 + local.get $1 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + end + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + i32.const 3648 + i32.load + local.tee $10 + local.get $0 + i32.gt_u + if ;; label = @2 + block ;; label = @3 + i32.const 3648 + local.get $10 + local.get $0 + i32.sub + local.tee $3 + i32.store + i32.const 3660 + i32.const 3660 + i32.load + local.tee $2 + local.get $0 + i32.add + local.tee $1 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + i32.const 4108 + i32.load + if (result i32) ;; label = @2 + i32.const 4116 + i32.load + else + block (result i32) ;; label = @3 + i32.const 4116 + i32.const 4096 + i32.store + i32.const 4112 + i32.const 4096 + i32.store + i32.const 4120 + i32.const -1 + i32.store + i32.const 4124 + i32.const -1 + i32.store + i32.const 4128 + i32.const 0 + i32.store + i32.const 4080 + i32.const 0 + i32.store + local.get $18 + local.get $18 + i32.const -16 + i32.and + i32.const 1431655768 + i32.xor + local.tee $1 + i32.store + i32.const 4108 + local.get $1 + i32.store + i32.const 4096 + end + end + local.tee $1 + local.get $0 + i32.const 47 + i32.add + local.tee $13 + i32.add + local.tee $8 + i32.const 0 + local.get $1 + i32.sub + local.tee $4 + i32.and + local.tee $6 + local.get $0 + i32.le_u + if ;; label = @2 + block ;; label = @3 + local.get $14 + global.set $global$1 + i32.const 0 + return + end + end + i32.const 4076 + i32.load + local.tee $2 + if ;; label = @2 + i32.const 4068 + i32.load + local.tee $3 + local.get $6 + i32.add + local.tee $1 + local.get $3 + i32.le_u + local.get $1 + local.get $2 + i32.gt_u + i32.or + if ;; label = @3 + block ;; label = @4 + local.get $14 + global.set $global$1 + i32.const 0 + return + end + end + end + local.get $0 + i32.const 48 + i32.add + local.set $7 + block $label$171 ;; label = @2 + block $label$172 ;; label = @3 + i32.const 4080 + i32.load + i32.const 4 + i32.and + i32.eqz + if ;; label = @4 + block ;; label = @5 + block $label$174 ;; label = @6 + block $label$175 ;; label = @7 + block $label$176 ;; label = @8 + i32.const 3660 + i32.load + local.tee $3 + i32.eqz + br_if 0 (;@8;) + i32.const 4084 + local.set $2 + loop $label$177 ;; label = @9 + block $label$178 ;; label = @10 + local.get $2 + i32.load + local.tee $1 + local.get $3 + i32.le_u + if ;; label = @11 + local.get $1 + local.get $2 + i32.const 4 + i32.add + local.tee $5 + i32.load + i32.add + local.get $3 + i32.gt_u + br_if 1 (;@10;) + end + local.get $2 + i32.load offset=8 + local.tee $1 + i32.eqz + br_if 2 (;@8;) + local.get $1 + local.set $2 + br 1 (;@9;) + end + end + local.get $8 + local.get $10 + i32.sub + local.get $4 + i32.and + local.tee $3 + i32.const 2147483647 + i32.lt_u + if ;; label = @9 + local.get $3 + call $40 + local.tee $1 + local.get $2 + i32.load + local.get $5 + i32.load + i32.add + i32.eq + if ;; label = @10 + local.get $1 + i32.const -1 + i32.ne + br_if 7 (;@3;) + else + block ;; label = @11 + local.get $1 + local.set $2 + local.get $3 + local.set $1 + br 4 (;@7;) + end + end + end + br 2 (;@6;) + end + i32.const 0 + call $40 + local.tee $1 + i32.const -1 + i32.ne + if ;; label = @8 + block ;; label = @9 + i32.const 4112 + i32.load + local.tee $2 + i32.const -1 + i32.add + local.tee $5 + local.get $1 + local.tee $3 + i32.add + i32.const 0 + local.get $2 + i32.sub + i32.and + local.get $3 + i32.sub + local.set $2 + local.get $5 + local.get $3 + i32.and + if (result i32) ;; label = @10 + local.get $2 + else + i32.const 0 + end + local.get $6 + i32.add + local.tee $3 + i32.const 4068 + i32.load + local.tee $5 + i32.add + local.set $4 + local.get $3 + local.get $0 + i32.gt_u + local.get $3 + i32.const 2147483647 + i32.lt_u + i32.and + if ;; label = @10 + block ;; label = @11 + i32.const 4076 + i32.load + local.tee $2 + if ;; label = @12 + local.get $4 + local.get $5 + i32.le_u + local.get $4 + local.get $2 + i32.gt_u + i32.or + br_if 6 (;@6;) + end + local.get $3 + call $40 + local.tee $2 + local.get $1 + i32.eq + br_if 8 (;@3;) + local.get $3 + local.set $1 + br 4 (;@7;) + end + end + end + end + br 1 (;@6;) + end + i32.const 0 + local.get $1 + i32.sub + local.set $5 + local.get $7 + local.get $1 + i32.gt_u + local.get $1 + i32.const 2147483647 + i32.lt_u + local.get $2 + i32.const -1 + i32.ne + i32.and + i32.and + if ;; label = @7 + local.get $13 + local.get $1 + i32.sub + i32.const 4116 + i32.load + local.tee $3 + i32.add + i32.const 0 + local.get $3 + i32.sub + i32.and + local.tee $3 + i32.const 2147483647 + i32.lt_u + if ;; label = @8 + local.get $3 + call $40 + i32.const -1 + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $5 + call $40 + drop + br 4 (;@6;) + end + else + local.get $3 + local.get $1 + i32.add + local.set $3 + end + else + local.get $1 + local.set $3 + end + else + local.get $1 + local.set $3 + end + local.get $2 + i32.const -1 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $2 + local.set $1 + br 5 (;@3;) + end + end + end + i32.const 4080 + i32.const 4080 + i32.load + i32.const 4 + i32.or + i32.store + end + end + local.get $6 + i32.const 2147483647 + i32.lt_u + if ;; label = @4 + local.get $6 + call $40 + local.tee $1 + i32.const 0 + call $40 + local.tee $3 + i32.lt_u + local.get $1 + i32.const -1 + i32.ne + local.get $3 + i32.const -1 + i32.ne + i32.and + i32.and + if ;; label = @5 + local.get $3 + local.get $1 + i32.sub + local.tee $3 + local.get $0 + i32.const 40 + i32.add + i32.gt_u + br_if 2 (;@3;) + end + end + br 1 (;@2;) + end + i32.const 4068 + i32.const 4068 + i32.load + local.get $3 + i32.add + local.tee $2 + i32.store + local.get $2 + i32.const 4072 + i32.load + i32.gt_u + if ;; label = @3 + i32.const 4072 + local.get $2 + i32.store + end + block $label$198 ;; label = @3 + i32.const 3660 + i32.load + local.tee $8 + if ;; label = @4 + block ;; label = @5 + i32.const 4084 + local.set $2 + block $label$200 ;; label = @6 + block $label$201 ;; label = @7 + loop $label$202 ;; label = @8 + local.get $1 + local.get $2 + i32.load + local.tee $4 + local.get $2 + i32.const 4 + i32.add + local.tee $7 + i32.load + local.tee $5 + i32.add + i32.eq + br_if 1 (;@7;) + local.get $2 + i32.load offset=8 + local.tee $2 + br_if 0 (;@8;) + end + br 1 (;@6;) + end + local.get $2 + i32.load offset=12 + i32.const 8 + i32.and + i32.eqz + if ;; label = @7 + local.get $8 + local.get $1 + i32.lt_u + local.get $8 + local.get $4 + i32.ge_u + i32.and + if ;; label = @8 + block ;; label = @9 + local.get $7 + local.get $5 + local.get $3 + i32.add + i32.store + i32.const 3648 + i32.load + local.set $5 + i32.const 0 + local.get $8 + i32.const 8 + i32.add + local.tee $2 + i32.sub + i32.const 7 + i32.and + local.set $1 + i32.const 3660 + local.get $8 + local.get $2 + i32.const 7 + i32.and + if (result i32) ;; label = @10 + local.get $1 + else + i32.const 0 + local.tee $1 + end + i32.add + local.tee $2 + i32.store + i32.const 3648 + local.get $3 + local.get $1 + i32.sub + local.get $5 + i32.add + local.tee $1 + i32.store + local.get $2 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $1 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 3664 + i32.const 4124 + i32.load + i32.store + br 6 (;@3;) + end + end + end + end + local.get $1 + i32.const 3652 + i32.load + local.tee $2 + i32.lt_u + if ;; label = @6 + block ;; label = @7 + i32.const 3652 + local.get $1 + i32.store + local.get $1 + local.set $2 + end + end + local.get $1 + local.get $3 + i32.add + local.set $10 + i32.const 4084 + local.set $5 + block $label$208 ;; label = @6 + block $label$209 ;; label = @7 + loop $label$210 ;; label = @8 + local.get $5 + i32.load + local.get $10 + i32.eq + br_if 1 (;@7;) + local.get $5 + i32.load offset=8 + local.tee $5 + br_if 0 (;@8;) + i32.const 4084 + local.set $5 + end + br 1 (;@6;) + end + local.get $5 + i32.load offset=12 + i32.const 8 + i32.and + if ;; label = @7 + i32.const 4084 + local.set $5 + else + block ;; label = @8 + local.get $5 + local.get $1 + i32.store + local.get $5 + i32.const 4 + i32.add + local.tee $5 + local.get $5 + i32.load + local.get $3 + i32.add + i32.store + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $4 + i32.sub + i32.const 7 + i32.and + local.set $7 + i32.const 0 + local.get $10 + i32.const 8 + i32.add + local.tee $5 + i32.sub + i32.const 7 + i32.and + local.set $3 + local.get $1 + local.get $4 + i32.const 7 + i32.and + if (result i32) ;; label = @9 + local.get $7 + else + i32.const 0 + end + i32.add + local.tee $13 + local.get $0 + i32.add + local.set $6 + local.get $10 + local.get $5 + i32.const 7 + i32.and + if (result i32) ;; label = @9 + local.get $3 + else + i32.const 0 + end + i32.add + local.tee $4 + local.get $13 + i32.sub + local.get $0 + i32.sub + local.set $7 + local.get $13 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + block $label$217 ;; label = @9 + local.get $4 + local.get $8 + i32.eq + if ;; label = @10 + block ;; label = @11 + i32.const 3648 + i32.const 3648 + i32.load + local.get $7 + i32.add + local.tee $0 + i32.store + i32.const 3660 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + end + else + block ;; label = @11 + local.get $4 + i32.const 3656 + i32.load + i32.eq + if ;; label = @12 + block ;; label = @13 + i32.const 3644 + i32.const 3644 + i32.load + local.get $7 + i32.add + local.tee $0 + i32.store + i32.const 3656 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $0 + i32.add + local.get $0 + i32.store + br 4 (;@9;) + end + end + local.get $4 + i32.load offset=4 + local.tee $0 + i32.const 3 + i32.and + i32.const 1 + i32.eq + if (result i32) ;; label = @12 + block (result i32) ;; label = @13 + local.get $0 + i32.const -8 + i32.and + local.set $11 + local.get $0 + i32.const 3 + i32.shr_u + local.set $1 + block $label$222 ;; label = @14 + local.get $0 + i32.const 256 + i32.lt_u + if ;; label = @15 + block ;; label = @16 + local.get $4 + i32.load offset=12 + local.set $5 + block $label$224 ;; label = @17 + local.get $4 + i32.load offset=8 + local.tee $3 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.tee $0 + i32.ne + if ;; label = @18 + block ;; label = @19 + local.get $3 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $3 + i32.load offset=12 + local.get $4 + i32.eq + br_if 2 (;@17;) + call $fimport$10 + end + end + end + local.get $5 + local.get $3 + i32.eq + if ;; label = @17 + block ;; label = @18 + i32.const 3636 + i32.const 3636 + i32.load + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 4 (;@14;) + end + end + block $label$228 ;; label = @17 + local.get $5 + local.get $0 + i32.eq + if ;; label = @18 + local.get $5 + i32.const 8 + i32.add + local.set $20 + else + block ;; label = @19 + local.get $5 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $5 + i32.const 8 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + block ;; label = @21 + local.get $0 + local.set $20 + br 4 (;@17;) + end + end + call $fimport$10 + end + end + end + local.get $3 + local.get $5 + i32.store offset=12 + local.get $20 + local.get $3 + i32.store + end + else + block ;; label = @16 + local.get $4 + i32.load offset=24 + local.set $8 + block $label$234 ;; label = @17 + local.get $4 + i32.load offset=12 + local.tee $0 + local.get $4 + i32.eq + if ;; label = @18 + block ;; label = @19 + local.get $4 + i32.const 16 + i32.add + local.tee $3 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @20 + local.get $3 + i32.load + local.tee $0 + if ;; label = @21 + local.get $3 + local.set $1 + else + block ;; label = @22 + i32.const 0 + local.set $12 + br 5 (;@17;) + end + end + end + loop $label$239 ;; label = @20 + local.get $0 + i32.const 20 + i32.add + local.tee $5 + i32.load + local.tee $3 + if ;; label = @21 + block ;; label = @22 + local.get $3 + local.set $0 + local.get $5 + local.set $1 + br 2 (;@20;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $5 + i32.load + local.tee $3 + if ;; label = @21 + block ;; label = @22 + local.get $3 + local.set $0 + local.get $5 + local.set $1 + br 2 (;@20;) + end + end + end + local.get $1 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + else + block ;; label = @21 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $12 + end + end + end + else + block ;; label = @19 + local.get $4 + i32.load offset=8 + local.tee $5 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $5 + i32.const 12 + i32.add + local.tee $3 + i32.load + local.get $4 + i32.ne + if ;; label = @20 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + block ;; label = @21 + local.get $3 + local.get $0 + i32.store + local.get $1 + local.get $5 + i32.store + local.get $0 + local.set $12 + end + else + call $fimport$10 + end + end + end + end + local.get $8 + i32.eqz + br_if 2 (;@14;) + block $label$249 ;; label = @17 + local.get $4 + local.get $4 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 3940 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @18 + block ;; label = @19 + local.get $0 + local.get $12 + i32.store + local.get $12 + br_if 2 (;@17;) + i32.const 3640 + i32.const 3640 + i32.load + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 5 (;@14;) + end + else + block ;; label = @19 + local.get $8 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $8 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + local.get $0 + local.get $12 + i32.store + else + local.get $8 + local.get $12 + i32.store offset=20 + end + local.get $12 + i32.eqz + br_if 5 (;@14;) + end + end + end + local.get $12 + i32.const 3652 + i32.load + local.tee $1 + i32.lt_u + if ;; label = @17 + call $fimport$10 + end + local.get $12 + local.get $8 + i32.store offset=24 + local.get $4 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.tee $3 + if ;; label = @17 + local.get $3 + local.get $1 + i32.lt_u + if ;; label = @18 + call $fimport$10 + else + block ;; label = @19 + local.get $12 + local.get $3 + i32.store offset=16 + local.get $3 + local.get $12 + i32.store offset=24 + end + end + end + local.get $0 + i32.load offset=4 + local.tee $0 + i32.eqz + br_if 2 (;@14;) + local.get $0 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @17 + call $fimport$10 + else + block ;; label = @18 + local.get $12 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $12 + i32.store offset=24 + end + end + end + end + end + local.get $11 + local.get $7 + i32.add + local.set $7 + local.get $4 + local.get $11 + i32.add + end + else + local.get $4 + end + local.tee $0 + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const -2 + i32.and + i32.store + local.get $6 + local.get $7 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $7 + i32.add + local.get $7 + i32.store + local.get $7 + i32.const 3 + i32.shr_u + local.set $0 + local.get $7 + i32.const 256 + i32.lt_u + if ;; label = @12 + block ;; label = @13 + local.get $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.set $3 + block $label$263 ;; label = @14 + i32.const 3636 + i32.load + local.tee $1 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @15 + block ;; label = @16 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 3652 + i32.load + i32.ge_u + if ;; label = @17 + block ;; label = @18 + local.get $1 + local.set $21 + local.get $0 + local.set $9 + br 4 (;@14;) + end + end + call $fimport$10 + end + else + block ;; label = @16 + i32.const 3636 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $21 + local.get $3 + local.set $9 + end + end + end + local.get $21 + local.get $6 + i32.store + local.get $9 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $9 + i32.store offset=8 + local.get $6 + local.get $3 + i32.store offset=12 + br 4 (;@9;) + end + end + block $label$267 (result i32) ;; label = @12 + local.get $7 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @13 + block (result i32) ;; label = @14 + i32.const 31 + local.get $7 + i32.const 16777215 + i32.gt_u + br_if 2 (;@12;) + drop + local.get $7 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $3 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $3 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + end + local.tee $2 + i32.const 2 + i32.shl + i32.const 3940 + i32.add + local.set $3 + local.get $6 + local.get $2 + i32.store offset=28 + local.get $6 + i32.const 16 + i32.add + local.tee $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store + i32.const 3640 + i32.load + local.tee $1 + i32.const 1 + local.get $2 + i32.shl + local.tee $0 + i32.and + i32.eqz + if ;; label = @12 + block ;; label = @13 + i32.const 3640 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $3 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 4 (;@9;) + end + end + local.get $3 + i32.load + local.set $0 + i32.const 25 + local.get $2 + i32.const 1 + i32.shr_u + i32.sub + local.set $1 + local.get $7 + local.get $2 + i32.const 31 + i32.eq + if (result i32) ;; label = @12 + i32.const 0 + else + local.get $1 + end + i32.shl + local.set $2 + block $label$273 ;; label = @12 + block $label$274 ;; label = @13 + block $label$275 ;; label = @14 + loop $label$276 ;; label = @15 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.eq + br_if 2 (;@13;) + local.get $2 + i32.const 1 + i32.shl + local.set $3 + local.get $0 + i32.const 16 + i32.add + local.get $2 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $2 + i32.load + local.tee $1 + i32.eqz + br_if 1 (;@14;) + local.get $3 + local.set $2 + local.get $1 + local.set $0 + br 0 (;@15;) + end + end + local.get $2 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$10 + else + block ;; label = @15 + local.get $2 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 6 (;@9;) + end + end + br 1 (;@12;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $2 + i32.const 3652 + i32.load + local.tee $1 + i32.ge_u + local.get $0 + local.get $1 + i32.ge_u + i32.and + if ;; label = @13 + block ;; label = @14 + local.get $2 + local.get $6 + i32.store offset=12 + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $2 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $6 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + end + end + local.get $14 + global.set $global$1 + local.get $13 + i32.const 8 + i32.add + return + end + end + end + loop $label$281 ;; label = @6 + block $label$282 ;; label = @7 + local.get $5 + i32.load + local.tee $2 + local.get $8 + i32.le_u + if ;; label = @8 + local.get $2 + local.get $5 + i32.load offset=4 + i32.add + local.tee $13 + local.get $8 + i32.gt_u + br_if 1 (;@7;) + end + local.get $5 + i32.load offset=8 + local.set $5 + br 1 (;@6;) + end + end + i32.const 0 + local.get $13 + i32.const -47 + i32.add + local.tee $7 + i32.const 8 + i32.add + local.tee $5 + i32.sub + i32.const 7 + i32.and + local.set $2 + local.get $7 + local.get $5 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $2 + else + i32.const 0 + end + i32.add + local.tee $2 + local.get $8 + i32.const 16 + i32.add + local.tee $12 + i32.lt_u + if (result i32) ;; label = @6 + local.get $8 + else + local.get $2 + end + local.tee $7 + i32.const 8 + i32.add + local.set $10 + local.get $7 + i32.const 24 + i32.add + local.set $5 + local.get $3 + i32.const -40 + i32.add + local.set $9 + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $4 + i32.sub + i32.const 7 + i32.and + local.set $2 + i32.const 3660 + local.get $1 + local.get $4 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $2 + else + i32.const 0 + local.tee $2 + end + i32.add + local.tee $4 + i32.store + i32.const 3648 + local.get $9 + local.get $2 + i32.sub + local.tee $2 + i32.store + local.get $4 + local.get $2 + i32.const 1 + i32.or + i32.store offset=4 + local.get $4 + local.get $2 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 3664 + i32.const 4124 + i32.load + i32.store + local.get $7 + i32.const 4 + i32.add + local.tee $2 + i32.const 27 + i32.store + local.get $10 + i32.const 4084 + i64.load align=4 + i64.store align=4 + local.get $10 + i32.const 4092 + i64.load align=4 + i64.store offset=8 align=4 + i32.const 4084 + local.get $1 + i32.store + i32.const 4088 + local.get $3 + i32.store + i32.const 4096 + i32.const 0 + i32.store + i32.const 4092 + local.get $10 + i32.store + local.get $5 + local.set $1 + loop $label$290 ;; label = @6 + local.get $1 + i32.const 4 + i32.add + local.tee $1 + i32.const 7 + i32.store + local.get $1 + i32.const 4 + i32.add + local.get $13 + i32.lt_u + br_if 0 (;@6;) + end + local.get $7 + local.get $8 + i32.ne + if ;; label = @6 + block ;; label = @7 + local.get $2 + local.get $2 + i32.load + i32.const -2 + i32.and + i32.store + local.get $8 + local.get $7 + local.get $8 + i32.sub + local.tee $4 + i32.const 1 + i32.or + i32.store offset=4 + local.get $7 + local.get $4 + i32.store + local.get $4 + i32.const 3 + i32.shr_u + local.set $1 + local.get $4 + i32.const 256 + i32.lt_u + if ;; label = @8 + block ;; label = @9 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.set $2 + i32.const 3636 + i32.load + local.tee $3 + i32.const 1 + local.get $1 + i32.shl + local.tee $1 + i32.and + if ;; label = @10 + local.get $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $1 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$10 + else + block ;; label = @12 + local.get $3 + local.set $15 + local.get $1 + local.set $11 + end + end + else + block ;; label = @11 + i32.const 3636 + local.get $3 + local.get $1 + i32.or + i32.store + local.get $2 + i32.const 8 + i32.add + local.set $15 + local.get $2 + local.set $11 + end + end + local.get $15 + local.get $8 + i32.store + local.get $11 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $11 + i32.store offset=8 + local.get $8 + local.get $2 + i32.store offset=12 + br 6 (;@3;) + end + end + local.get $4 + i32.const 8 + i32.shr_u + local.tee $1 + if (result i32) ;; label = @8 + local.get $4 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @9 + i32.const 31 + else + local.get $4 + i32.const 14 + local.get $1 + local.get $1 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $2 + i32.shl + local.tee $3 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $1 + local.get $2 + i32.or + local.get $3 + local.get $1 + i32.shl + local.tee $3 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $1 + i32.or + i32.sub + local.get $3 + local.get $1 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $1 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $1 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $5 + i32.const 2 + i32.shl + i32.const 3940 + i32.add + local.set $2 + local.get $8 + local.get $5 + i32.store offset=28 + local.get $8 + i32.const 0 + i32.store offset=20 + local.get $12 + i32.const 0 + i32.store + i32.const 3640 + i32.load + local.tee $3 + i32.const 1 + local.get $5 + i32.shl + local.tee $1 + i32.and + i32.eqz + if ;; label = @8 + block ;; label = @9 + i32.const 3640 + local.get $3 + local.get $1 + i32.or + i32.store + local.get $2 + local.get $8 + i32.store + local.get $8 + local.get $2 + i32.store offset=24 + local.get $8 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $8 + i32.store offset=8 + br 6 (;@3;) + end + end + local.get $2 + i32.load + local.set $1 + i32.const 25 + local.get $5 + i32.const 1 + i32.shr_u + i32.sub + local.set $3 + local.get $4 + local.get $5 + i32.const 31 + i32.eq + if (result i32) ;; label = @8 + i32.const 0 + else + local.get $3 + end + i32.shl + local.set $5 + block $label$304 ;; label = @8 + block $label$305 ;; label = @9 + block $label$306 ;; label = @10 + loop $label$307 ;; label = @11 + local.get $1 + i32.load offset=4 + i32.const -8 + i32.and + local.get $4 + i32.eq + br_if 2 (;@9;) + local.get $5 + i32.const 1 + i32.shl + local.set $2 + local.get $1 + i32.const 16 + i32.add + local.get $5 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $5 + i32.load + local.tee $3 + i32.eqz + br_if 1 (;@10;) + local.get $2 + local.set $5 + local.get $3 + local.set $1 + br 0 (;@11;) + end + end + local.get $5 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $5 + local.get $8 + i32.store + local.get $8 + local.get $1 + i32.store offset=24 + local.get $8 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $8 + i32.store offset=8 + br 8 (;@3;) + end + end + br 1 (;@8;) + end + local.get $1 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $5 + i32.const 3652 + i32.load + local.tee $3 + i32.ge_u + local.get $1 + local.get $3 + i32.ge_u + i32.and + if ;; label = @9 + block ;; label = @10 + local.get $5 + local.get $8 + i32.store offset=12 + local.get $2 + local.get $8 + i32.store + local.get $8 + local.get $5 + i32.store offset=8 + local.get $8 + local.get $1 + i32.store offset=12 + local.get $8 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + end + end + else + block ;; label = @5 + i32.const 3652 + i32.load + local.tee $2 + i32.eqz + local.get $1 + local.get $2 + i32.lt_u + i32.or + if ;; label = @6 + i32.const 3652 + local.get $1 + i32.store + end + i32.const 4084 + local.get $1 + i32.store + i32.const 4088 + local.get $3 + i32.store + i32.const 4096 + i32.const 0 + i32.store + i32.const 3672 + i32.const 4108 + i32.load + i32.store + i32.const 3668 + i32.const -1 + i32.store + i32.const 0 + local.set $2 + loop $label$314 ;; label = @6 + local.get $2 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.tee $5 + local.get $5 + i32.store offset=12 + local.get $5 + local.get $5 + i32.store offset=8 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 32 + i32.ne + br_if 0 (;@6;) + end + local.get $3 + i32.const -40 + i32.add + local.set $5 + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $2 + i32.sub + i32.const 7 + i32.and + local.set $3 + i32.const 3660 + local.get $1 + local.get $2 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $3 + else + i32.const 0 + end + local.tee $1 + i32.add + local.tee $3 + i32.store + i32.const 3648 + local.get $5 + local.get $1 + i32.sub + local.tee $1 + i32.store + local.get $3 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $1 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 3664 + i32.const 4124 + i32.load + i32.store + end + end + end + i32.const 3648 + i32.load + local.tee $1 + local.get $0 + i32.gt_u + if ;; label = @3 + block ;; label = @4 + i32.const 3648 + local.get $1 + local.get $0 + i32.sub + local.tee $3 + i32.store + i32.const 3660 + i32.const 3660 + i32.load + local.tee $2 + local.get $0 + i32.add + local.tee $1 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + end + call $12 + i32.const 12 + i32.store + local.get $14 + global.set $global$1 + i32.const 0 + end ) - ) - (func $45 (; 58 ;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - (call_indirect (type $0) - (local.get $1) - (local.get $2) - (local.get $3) - (i32.add - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.const 2) - ) + (func $38 (;51;) (type $2) (param $0 i32) + (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) + block $label$1 ;; label = @1 + local.get $0 + i32.eqz + if ;; label = @2 + return + end + local.get $0 + i32.const -8 + i32.add + local.tee $1 + i32.const 3652 + i32.load + local.tee $11 + i32.lt_u + if ;; label = @2 + call $fimport$10 + end + local.get $0 + i32.const -4 + i32.add + i32.load + local.tee $0 + i32.const 3 + i32.and + local.tee $8 + i32.const 1 + i32.eq + if ;; label = @2 + call $fimport$10 + end + local.get $1 + local.get $0 + i32.const -8 + i32.and + local.tee $4 + i32.add + local.set $6 + block $label$5 ;; label = @2 + local.get $0 + i32.const 1 + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $1 + local.set $3 + local.get $4 + local.set $2 + end + else + block ;; label = @4 + local.get $8 + i32.eqz + if ;; label = @5 + return + end + local.get $1 + i32.const 0 + local.get $1 + i32.load + local.tee $8 + i32.sub + i32.add + local.tee $0 + local.get $11 + i32.lt_u + if ;; label = @5 + call $fimport$10 + end + local.get $8 + local.get $4 + i32.add + local.set $1 + local.get $0 + i32.const 3656 + i32.load + i32.eq + if ;; label = @5 + block ;; label = @6 + local.get $6 + i32.const 4 + i32.add + local.tee $2 + i32.load + local.tee $3 + i32.const 3 + i32.and + i32.const 3 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 6 (;@2;) + end + end + i32.const 3644 + local.get $1 + i32.store + local.get $2 + local.get $3 + i32.const -2 + i32.and + i32.store + local.get $0 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $0 + local.get $1 + i32.add + local.get $1 + i32.store + return + end + end + local.get $8 + i32.const 3 + i32.shr_u + local.set $10 + local.get $8 + i32.const 256 + i32.lt_u + if ;; label = @5 + block ;; label = @6 + local.get $0 + i32.load offset=12 + local.set $3 + local.get $0 + i32.load offset=8 + local.tee $4 + local.get $10 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.tee $2 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $4 + local.get $11 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $4 + i32.load offset=12 + local.get $0 + i32.ne + if ;; label = @9 + call $fimport$10 + end + end + end + local.get $3 + local.get $4 + i32.eq + if ;; label = @7 + block ;; label = @8 + i32.const 3636 + i32.const 3636 + i32.load + i32.const 1 + local.get $10 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 6 (;@2;) + end + end + local.get $3 + local.get $2 + i32.eq + if ;; label = @7 + local.get $3 + i32.const 8 + i32.add + local.set $5 + else + block ;; label = @8 + local.get $3 + local.get $11 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $3 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.get $0 + i32.eq + if ;; label = @9 + local.get $2 + local.set $5 + else + call $fimport$10 + end + end + end + local.get $4 + local.get $3 + i32.store offset=12 + local.get $5 + local.get $4 + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 4 (;@2;) + end + end + local.get $0 + i32.load offset=24 + local.set $12 + block $label$22 ;; label = @5 + local.get $0 + i32.load offset=12 + local.tee $4 + local.get $0 + i32.eq + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 16 + i32.add + local.tee $5 + i32.const 4 + i32.add + local.tee $8 + i32.load + local.tee $4 + if ;; label = @8 + local.get $8 + local.set $5 + else + local.get $5 + i32.load + local.tee $4 + i32.eqz + if ;; label = @9 + block ;; label = @10 + i32.const 0 + local.set $7 + br 5 (;@5;) + end + end + end + loop $label$27 ;; label = @8 + local.get $4 + i32.const 20 + i32.add + local.tee $8 + i32.load + local.tee $10 + if ;; label = @9 + block ;; label = @10 + local.get $10 + local.set $4 + local.get $8 + local.set $5 + br 2 (;@8;) + end + end + local.get $4 + i32.const 16 + i32.add + local.tee $8 + i32.load + local.tee $10 + if ;; label = @9 + block ;; label = @10 + local.get $10 + local.set $4 + local.get $8 + local.set $5 + br 2 (;@8;) + end + end + end + local.get $5 + local.get $11 + i32.lt_u + if ;; label = @8 + call $fimport$10 + else + block ;; label = @9 + local.get $5 + i32.const 0 + i32.store + local.get $4 + local.set $7 + end + end + end + else + block ;; label = @7 + local.get $0 + i32.load offset=8 + local.tee $5 + local.get $11 + i32.lt_u + if ;; label = @8 + call $fimport$10 + end + local.get $5 + i32.const 12 + i32.add + local.tee $8 + i32.load + local.get $0 + i32.ne + if ;; label = @8 + call $fimport$10 + end + local.get $4 + i32.const 8 + i32.add + local.tee $10 + i32.load + local.get $0 + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $8 + local.get $4 + i32.store + local.get $10 + local.get $5 + i32.store + local.get $4 + local.set $7 + end + else + call $fimport$10 + end + end + end + end + local.get $12 + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $0 + i32.load offset=28 + local.tee $4 + i32.const 2 + i32.shl + i32.const 3940 + i32.add + local.tee $5 + i32.load + i32.eq + if ;; label = @7 + block ;; label = @8 + local.get $5 + local.get $7 + i32.store + local.get $7 + i32.eqz + if ;; label = @9 + block ;; label = @10 + i32.const 3640 + i32.const 3640 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 8 (;@2;) + end + end + end + else + block ;; label = @8 + local.get $12 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $12 + i32.const 16 + i32.add + local.tee $4 + i32.load + local.get $0 + i32.eq + if ;; label = @9 + local.get $4 + local.get $7 + i32.store + else + local.get $12 + local.get $7 + i32.store offset=20 + end + local.get $7 + i32.eqz + if ;; label = @9 + block ;; label = @10 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 8 (;@2;) + end + end + end + end + local.get $7 + i32.const 3652 + i32.load + local.tee $5 + i32.lt_u + if ;; label = @7 + call $fimport$10 + end + local.get $7 + local.get $12 + i32.store offset=24 + local.get $0 + i32.const 16 + i32.add + local.tee $8 + i32.load + local.tee $4 + if ;; label = @7 + local.get $4 + local.get $5 + i32.lt_u + if ;; label = @8 + call $fimport$10 + else + block ;; label = @9 + local.get $7 + local.get $4 + i32.store offset=16 + local.get $4 + local.get $7 + i32.store offset=24 + end + end + end + local.get $8 + i32.load offset=4 + local.tee $4 + if ;; label = @7 + local.get $4 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @8 + call $fimport$10 + else + block ;; label = @9 + local.get $7 + local.get $4 + i32.store offset=20 + local.get $4 + local.get $7 + i32.store offset=24 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + else + block ;; label = @8 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + end + else + block ;; label = @6 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + end + end + end + local.get $3 + local.get $6 + i32.ge_u + if ;; label = @2 + call $fimport$10 + end + local.get $6 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 1 + i32.and + i32.eqz + if ;; label = @2 + call $fimport$10 + end + local.get $0 + i32.const 2 + i32.and + if ;; label = @2 + block ;; label = @3 + local.get $1 + local.get $0 + i32.const -2 + i32.and + i32.store + local.get $3 + local.get $2 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $2 + i32.add + local.get $2 + i32.store + end + else + block ;; label = @3 + local.get $6 + i32.const 3660 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 3648 + i32.const 3648 + i32.load + local.get $2 + i32.add + local.tee $0 + i32.store + i32.const 3660 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + i32.const 3656 + i32.load + i32.ne + if ;; label = @6 + return + end + i32.const 3656 + i32.const 0 + i32.store + i32.const 3644 + i32.const 0 + i32.store + return + end + end + local.get $6 + i32.const 3656 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 3644 + i32.const 3644 + i32.load + local.get $2 + i32.add + local.tee $0 + i32.store + i32.const 3656 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $0 + i32.add + local.get $0 + i32.store + return + end + end + local.get $0 + i32.const -8 + i32.and + local.get $2 + i32.add + local.set $5 + local.get $0 + i32.const 3 + i32.shr_u + local.set $4 + block $label$61 ;; label = @4 + local.get $0 + i32.const 256 + i32.lt_u + if ;; label = @5 + block ;; label = @6 + local.get $6 + i32.load offset=12 + local.set $2 + local.get $6 + i32.load offset=8 + local.tee $1 + local.get $4 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.tee $0 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $1 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $1 + i32.load offset=12 + local.get $6 + i32.ne + if ;; label = @9 + call $fimport$10 + end + end + end + local.get $2 + local.get $1 + i32.eq + if ;; label = @7 + block ;; label = @8 + i32.const 3636 + i32.const 3636 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 4 (;@4;) + end + end + local.get $2 + local.get $0 + i32.eq + if ;; label = @7 + local.get $2 + i32.const 8 + i32.add + local.set $14 + else + block ;; label = @8 + local.get $2 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $2 + i32.const 8 + i32.add + local.tee $0 + i32.load + local.get $6 + i32.eq + if ;; label = @9 + local.get $0 + local.set $14 + else + call $fimport$10 + end + end + end + local.get $1 + local.get $2 + i32.store offset=12 + local.get $14 + local.get $1 + i32.store + end + else + block ;; label = @6 + local.get $6 + i32.load offset=24 + local.set $7 + block $label$73 ;; label = @7 + local.get $6 + i32.load offset=12 + local.tee $0 + local.get $6 + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $6 + i32.const 16 + i32.add + local.tee $2 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + if ;; label = @10 + local.get $1 + local.set $2 + else + local.get $2 + i32.load + local.tee $0 + i32.eqz + if ;; label = @11 + block ;; label = @12 + i32.const 0 + local.set $9 + br 5 (;@7;) + end + end + end + loop $label$78 ;; label = @10 + local.get $0 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $4 + if ;; label = @11 + block ;; label = @12 + local.get $4 + local.set $0 + local.get $1 + local.set $2 + br 2 (;@10;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $4 + if ;; label = @11 + block ;; label = @12 + local.get $4 + local.set $0 + local.get $1 + local.set $2 + br 2 (;@10;) + end + end + end + local.get $2 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $2 + i32.const 0 + i32.store + local.get $0 + local.set $9 + end + end + end + else + block ;; label = @9 + local.get $6 + i32.load offset=8 + local.tee $2 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + end + local.get $2 + i32.const 12 + i32.add + local.tee $1 + i32.load + local.get $6 + i32.ne + if ;; label = @10 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $4 + i32.load + local.get $6 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $1 + local.get $0 + i32.store + local.get $4 + local.get $2 + i32.store + local.get $0 + local.set $9 + end + else + call $fimport$10 + end + end + end + end + local.get $7 + if ;; label = @7 + block ;; label = @8 + local.get $6 + local.get $6 + i32.load offset=28 + local.tee $0 + i32.const 2 + i32.shl + i32.const 3940 + i32.add + local.tee $2 + i32.load + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $2 + local.get $9 + i32.store + local.get $9 + i32.eqz + if ;; label = @11 + block ;; label = @12 + i32.const 3640 + i32.const 3640 + i32.load + i32.const 1 + local.get $0 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 8 (;@4;) + end + end + end + else + block ;; label = @10 + local.get $7 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$10 + end + local.get $7 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $6 + i32.eq + if ;; label = @11 + local.get $0 + local.get $9 + i32.store + else + local.get $7 + local.get $9 + i32.store offset=20 + end + local.get $9 + i32.eqz + br_if 6 (;@4;) + end + end + local.get $9 + i32.const 3652 + i32.load + local.tee $2 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $9 + local.get $7 + i32.store offset=24 + local.get $6 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + if ;; label = @9 + local.get $0 + local.get $2 + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $9 + local.get $0 + i32.store offset=16 + local.get $0 + local.get $9 + i32.store offset=24 + end + end + end + local.get $1 + i32.load offset=4 + local.tee $0 + if ;; label = @9 + local.get $0 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $9 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $9 + i32.store offset=24 + end + end + end + end + end + end + end + end + local.get $3 + local.get $5 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $5 + i32.add + local.get $5 + i32.store + local.get $3 + i32.const 3656 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 3644 + local.get $5 + i32.store + return + end + else + local.get $5 + local.set $2 + end + end + end + local.get $2 + i32.const 3 + i32.shr_u + local.set $1 + local.get $2 + i32.const 256 + i32.lt_u + if ;; label = @2 + block ;; label = @3 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3676 + i32.add + local.set $0 + i32.const 3636 + i32.load + local.tee $2 + i32.const 1 + local.get $1 + i32.shl + local.tee $1 + i32.and + if ;; label = @4 + local.get $0 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $1 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @5 + call $fimport$10 + else + block ;; label = @6 + local.get $2 + local.set $15 + local.get $1 + local.set $13 + end + end + else + block ;; label = @5 + i32.const 3636 + local.get $2 + local.get $1 + i32.or + i32.store + local.get $0 + i32.const 8 + i32.add + local.set $15 + local.get $0 + local.set $13 + end + end + local.get $15 + local.get $3 + i32.store + local.get $13 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $13 + i32.store offset=8 + local.get $3 + local.get $0 + i32.store offset=12 + return + end + end + local.get $2 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @2 + local.get $2 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @3 + i32.const 31 + else + local.get $2 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $4 + local.get $0 + i32.or + local.get $1 + local.get $4 + i32.shl + local.tee $0 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $1 + i32.or + i32.sub + local.get $0 + local.get $1 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $1 + i32.const 2 + i32.shl + i32.const 3940 + i32.add + local.set $0 + local.get $3 + local.get $1 + i32.store offset=28 + local.get $3 + i32.const 0 + i32.store offset=20 + local.get $3 + i32.const 0 + i32.store offset=16 + block $label$113 ;; label = @2 + i32.const 3640 + i32.load + local.tee $4 + i32.const 1 + local.get $1 + i32.shl + local.tee $5 + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $0 + i32.load + local.set $0 + i32.const 25 + local.get $1 + i32.const 1 + i32.shr_u + i32.sub + local.set $4 + local.get $2 + local.get $1 + i32.const 31 + i32.eq + if (result i32) ;; label = @5 + i32.const 0 + else + local.get $4 + end + i32.shl + local.set $1 + block $label$117 ;; label = @5 + block $label$118 ;; label = @6 + block $label$119 ;; label = @7 + loop $label$120 ;; label = @8 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $2 + i32.eq + br_if 2 (;@6;) + local.get $1 + i32.const 1 + i32.shl + local.set $4 + local.get $0 + i32.const 16 + i32.add + local.get $1 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $1 + i32.load + local.tee $5 + i32.eqz + br_if 1 (;@7;) + local.get $4 + local.set $1 + local.get $5 + local.set $0 + br 0 (;@8;) + end + end + local.get $1 + i32.const 3652 + i32.load + i32.lt_u + if ;; label = @7 + call $fimport$10 + else + block ;; label = @8 + local.get $1 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.store offset=24 + local.get $3 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $3 + i32.store offset=8 + br 6 (;@2;) + end + end + br 1 (;@5;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $2 + i32.const 3652 + i32.load + local.tee $4 + i32.ge_u + local.get $0 + local.get $4 + i32.ge_u + i32.and + if ;; label = @6 + block ;; label = @7 + local.get $2 + local.get $3 + i32.store offset=12 + local.get $1 + local.get $3 + i32.store + local.get $3 + local.get $2 + i32.store offset=8 + local.get $3 + local.get $0 + i32.store offset=12 + local.get $3 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + else + block ;; label = @4 + i32.const 3640 + local.get $4 + local.get $5 + i32.or + i32.store + local.get $0 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.store offset=24 + local.get $3 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $3 + i32.store offset=8 + end + end + end + i32.const 3668 + i32.const 3668 + i32.load + i32.const -1 + i32.add + local.tee $0 + i32.store + local.get $0 + if ;; label = @2 + return + else + i32.const 4092 + local.set $0 + end + loop $label$128 ;; label = @2 + local.get $0 + i32.load + local.tee $2 + i32.const 8 + i32.add + local.set $0 + local.get $2 + br_if 0 (;@2;) + end + i32.const 3668 + i32.const -1 + i32.store + end ) - ) - (func $46 (; 59 ;) (type $5) (param $0 i32) (param $1 i32) - (call_indirect (type $2) - (local.get $1) - (i32.add - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 6) - ) + (func $39 (;52;) (type $6) + nop ) - ) - (func $47 (; 60 ;) (type $1) (param $0 i32) (result i32) - (block $label$1 (result i32) - (call $fimport$3 - (i32.const 0) - ) - (i32.const 0) + (func $40 (;53;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$0 + i32.load + local.tee $2 + local.get $0 + i32.const 15 + i32.add + i32.const -16 + i32.and + local.tee $0 + i32.add + local.set $1 + local.get $0 + i32.const 0 + i32.gt_s + local.get $1 + local.get $2 + i32.lt_s + i32.and + local.get $1 + i32.const 0 + i32.lt_s + i32.or + if ;; label = @2 + block ;; label = @3 + call $fimport$6 + drop + i32.const 12 + call $fimport$11 + i32.const -1 + return + end + end + global.get $global$0 + local.get $1 + i32.store + local.get $1 + call $fimport$5 + i32.gt_s + if ;; label = @2 + call $fimport$4 + i32.eqz + if ;; label = @3 + block ;; label = @4 + i32.const 12 + call $fimport$11 + global.get $global$0 + local.get $2 + i32.store + i32.const -1 + return + end + end + end + local.get $2 + end ) - ) - (func $48 (; 61 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (block $label$1 (result i32) - (call $fimport$3 - (i32.const 1) - ) - (i32.const 0) + (func $41 (;54;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + local.get $2 + i32.add + local.set $4 + local.get $2 + i32.const 20 + i32.ge_s + if ;; label = @2 + block ;; label = @3 + local.get $1 + i32.const 255 + i32.and + local.set $1 + local.get $0 + i32.const 3 + i32.and + local.tee $3 + if ;; label = @4 + block ;; label = @5 + local.get $0 + i32.const 4 + i32.add + local.get $3 + i32.sub + local.set $3 + loop $label$4 ;; label = @6 + local.get $0 + local.get $3 + i32.lt_s + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.get $1 + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br 2 (;@6;) + end + end + end + end + end + local.get $1 + local.get $1 + i32.const 8 + i32.shl + i32.or + local.get $1 + i32.const 16 + i32.shl + i32.or + local.get $1 + i32.const 24 + i32.shl + i32.or + local.set $3 + local.get $4 + i32.const -4 + i32.and + local.set $5 + loop $label$6 ;; label = @4 + local.get $0 + local.get $5 + i32.lt_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $3 + i32.store + local.get $0 + i32.const 4 + i32.add + local.set $0 + br 2 (;@4;) + end + end + end + end + end + loop $label$8 ;; label = @2 + local.get $0 + local.get $4 + i32.lt_s + if ;; label = @3 + block ;; label = @4 + local.get $0 + local.get $1 + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br 2 (;@2;) + end + end + end + local.get $0 + local.get $2 + i32.sub + end ) - ) - (func $49 (; 62 ;) (type $2) (param $0 i32) - (call $fimport$3 - (i32.const 2) + (func $42 (;55;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + block $label$1 (result i32) ;; label = @1 + local.get $2 + i32.const 4096 + i32.ge_s + if ;; label = @2 + local.get $0 + local.get $1 + local.get $2 + call $fimport$12 + return + end + local.get $0 + local.set $3 + local.get $0 + i32.const 3 + i32.and + local.get $1 + i32.const 3 + i32.and + i32.eq + if ;; label = @2 + block ;; label = @3 + loop $label$4 ;; label = @4 + local.get $0 + i32.const 3 + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $2 + i32.eqz + if ;; label = @7 + local.get $3 + return + end + local.get $0 + local.get $1 + i32.load8_s + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + br 2 (;@4;) + end + end + end + loop $label$7 ;; label = @4 + local.get $2 + i32.const 4 + i32.ge_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $1 + i32.load + i32.store + local.get $0 + i32.const 4 + i32.add + local.set $0 + local.get $1 + i32.const 4 + i32.add + local.set $1 + local.get $2 + i32.const 4 + i32.sub + local.set $2 + br 2 (;@4;) + end + end + end + end + end + loop $label$9 ;; label = @2 + local.get $2 + i32.const 0 + i32.gt_s + if ;; label = @3 + block ;; label = @4 + local.get $0 + local.get $1 + i32.load8_s + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + br 2 (;@2;) + end + end + end + local.get $3 + end ) - ) -) - + (func $43 (;56;) (type $3) (result i32) + i32.const 0 + ) + (func $44 (;57;) (type $4) (param $0 i32) (param $1 i32) (result i32) + local.get $1 + local.get $0 + i32.const 1 + i32.and + i32.const 0 + i32.add + call_indirect (type $1) + ) + (func $45 (;58;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) + local.get $1 + local.get $2 + local.get $3 + local.get $0 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + ) + (func $46 (;59;) (type $5) (param $0 i32) (param $1 i32) + local.get $1 + local.get $0 + i32.const 1 + i32.and + i32.const 6 + i32.add + call_indirect (type $2) + ) + (func $47 (;60;) (type $1) (param $0 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + i32.const 0 + call $fimport$3 + i32.const 0 + end + ) + (func $48 (;61;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + i32.const 1 + call $fimport$3 + i32.const 0 + end + ) + (func $49 (;62;) (type $2) (param $0 i32) + i32.const 2 + call $fimport$3 + ) + (global $global$0 (;5;) (mut i32) global.get $gimport$0) + (global $global$1 (;6;) (mut i32) global.get $gimport$1) + (global $global$2 (;7;) (mut i32) global.get $gimport$2) + (global $global$3 (;8;) (mut i32) i32.const 0) + (global $global$4 (;9;) (mut i32) i32.const 0) + (global $global$5 (;10;) (mut i32) i32.const 0) + (export "_sbrk" (func $40)) + (export "_free" (func $38)) + (export "_main" (func $8)) + (export "_pthread_self" (func $43)) + (export "_memset" (func $41)) + (export "_malloc" (func $37)) + (export "_memcpy" (func $42)) + (export "___errno_location" (func $12)) + (export "runPostSets" (func $39)) + (export "stackAlloc" (func $0)) + (export "stackSave" (func $1)) + (export "stackRestore" (func $2)) + (export "establishStackSpace" (func $3)) + (export "setThrew" (func $4)) + (export "setTempRet0" (func $5)) + (export "getTempRet0" (func $6)) + (export "dynCall_ii" (func $44)) + (export "dynCall_iiii" (func $45)) + (export "dynCall_vi" (func $46)) + (elem (;0;) (global.get $gimport$19) func $47 $9 $48 $14 $10 $15 $49 $16) + (data (;0;) (i32.const 1024) "\04\04\00\00\05") + (data (;1;) (i32.const 1040) "\01") + (data (;2;) (i32.const 1064) "\01\00\00\00\02\00\00\00,\10\00\00\00\04") + (data (;3;) (i32.const 1088) "\01") + (data (;4;) (i32.const 1103) "\0a\ff\ff\ff\ff") + (data (;5;) (i32.const 1140) "error: %d\5cn\00ok\00\11\00\0a\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\09\00\00\00\00\0b") + (data (;6;) (i32.const 1187) "\11\00\0f\0a\11\11\11\03\0a\07\00\01\13\09\0b\0b\00\00\09\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") + (data (;7;) (i32.const 1236) "\0b") + (data (;8;) (i32.const 1245) "\11\00\0a\0a\11\11\11\00\0a\00\00\02\00\09\0b\00\00\00\09\00\0b\00\00\0b") + (data (;9;) (i32.const 1294) "\0c") + (data (;10;) (i32.const 1306) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c") + (data (;11;) (i32.const 1352) "\0e") + (data (;12;) (i32.const 1364) "\0d\00\00\00\04\0d\00\00\00\00\09\0e\00\00\00\00\00\0e\00\00\0e") + (data (;13;) (i32.const 1410) "\10") + (data (;14;) (i32.const 1422) "\0f\00\00\00\00\0f\00\00\00\00\09\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") + (data (;15;) (i32.const 1477) "\12\00\00\00\12\12\12\00\00\00\00\00\00\09") + (data (;16;) (i32.const 1526) "\0b") + (data (;17;) (i32.const 1538) "\0a\00\00\00\00\0a\00\00\00\00\09\0b\00\00\00\00\00\0b\00\00\0b") + (data (;18;) (i32.const 1584) "\0c") + (data (;19;) (i32.const 1596) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\22\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\09\0a\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\5c]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") +) \ No newline at end of file diff --git a/cranelift/wasm/wasmtests/embenchen_primes.wat b/cranelift/wasm/wasmtests/embenchen_primes.wat index 4f4603198c9d..1bdf443ae581 100644 --- a/cranelift/wasm/wasmtests/embenchen_primes.wat +++ b/cranelift/wasm/wasmtests/embenchen_primes.wat @@ -1,15334 +1,11185 @@ (module - (type $0 (func (param i32 i32 i32) (result i32))) - (type $1 (func (param i32) (result i32))) - (type $2 (func (param i32))) - (type $3 (func (result i32))) - (type $4 (func (param i32 i32) (result i32))) - (type $5 (func (param i32 i32))) - (type $6 (func)) - (type $7 (func (param i32 i32 i32 i32 i32) (result i32))) - (type $8 (func (param i32 i32 i32))) - (type $9 (func (param i64 i32) (result i32))) - (type $10 (func (param i32 i32 i32 i32 i32))) - (type $11 (func (param f64 i32) (result f64))) - (type $12 (func (param i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory $16 2048 2048)) - (data (i32.const 1024) "\04\04\00\00\05") - (data (i32.const 1040) "\01") - (data (i32.const 1064) "\01\00\00\00\02\00\00\004\10\00\00\00\04") - (data (i32.const 1088) "\01") - (data (i32.const 1103) "\n\ff\ff\ff\ff") - (data (i32.const 1140) "error: %d\\n\00lastprime: %d.\n\00\11\00\n\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\t\00\00\00\00\0b") - (data (i32.const 1200) "\11\00\0f\n\11\11\11\03\n\07\00\01\13\t\0b\0b\00\00\t\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") - (data (i32.const 1249) "\0b") - (data (i32.const 1258) "\11\00\n\n\11\11\11\00\n\00\00\02\00\t\0b\00\00\00\t\00\0b\00\00\0b") - (data (i32.const 1307) "\0c") - (data (i32.const 1319) "\0c\00\00\00\00\0c\00\00\00\00\t\0c\00\00\00\00\00\0c\00\00\0c") - (data (i32.const 1365) "\0e") - (data (i32.const 1377) "\0d\00\00\00\04\0d\00\00\00\00\t\0e\00\00\00\00\00\0e\00\00\0e") - (data (i32.const 1423) "\10") - (data (i32.const 1435) "\0f\00\00\00\00\0f\00\00\00\00\t\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") - (data (i32.const 1490) "\12\00\00\00\12\12\12\00\00\00\00\00\00\t") - (data (i32.const 1539) "\0b") - (data (i32.const 1551) "\n\00\00\00\00\n\00\00\00\00\t\0b\00\00\00\00\00\0b\00\00\0b") - (data (i32.const 1597) "\0c") - (data (i32.const 1609) "\0c\00\00\00\00\0c\00\00\00\00\t\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\"\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e\'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\t\n\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\\]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") - (import "env" "table" (table $timport$17 8 8 funcref)) - (elem (global.get $gimport$19) $41 $8 $42 $13 $9 $14 $43 $15) - (import "env" "DYNAMICTOP_PTR" (global $gimport$0 i32)) - (import "env" "STACKTOP" (global $gimport$1 i32)) - (import "env" "STACK_MAX" (global $gimport$2 i32)) - (import "env" "memoryBase" (global $gimport$18 i32)) - (import "env" "tableBase" (global $gimport$19 i32)) - (import "env" "abort" (func $fimport$3 (param i32))) - (import "env" "enlargeMemory" (func $fimport$4 (result i32))) - (import "env" "getTotalMemory" (func $fimport$5 (result i32))) - (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (result i32))) - (import "env" "_pthread_cleanup_pop" (func $fimport$7 (param i32))) - (import "env" "___syscall6" (func $fimport$8 (param i32 i32) (result i32))) - (import "env" "_pthread_cleanup_push" (func $fimport$9 (param i32 i32))) - (import "env" "_abort" (func $fimport$10)) - (import "env" "___setErrNo" (func $fimport$11 (param i32))) - (import "env" "_emscripten_memcpy_big" (func $fimport$12 (param i32 i32 i32) (result i32))) - (import "env" "___syscall54" (func $fimport$13 (param i32 i32) (result i32))) - (import "env" "___syscall140" (func $fimport$14 (param i32 i32) (result i32))) - (import "env" "___syscall146" (func $fimport$15 (param i32 i32) (result i32))) - (global $global$0 (mut i32) (global.get $gimport$0)) - (global $global$1 (mut i32) (global.get $gimport$1)) - (global $global$2 (mut i32) (global.get $gimport$2)) - (global $global$3 (mut i32) (i32.const 0)) - (global $global$4 (mut i32) (i32.const 0)) - (global $global$5 (mut i32) (i32.const 0)) - (export "_sbrk" (func $34)) - (export "_free" (func $32)) - (export "_main" (func $7)) - (export "_pthread_self" (func $37)) - (export "_memset" (func $35)) - (export "_malloc" (func $31)) - (export "_memcpy" (func $36)) - (export "___errno_location" (func $11)) - (export "runPostSets" (func $33)) - (export "stackAlloc" (func $0)) - (export "stackSave" (func $1)) - (export "stackRestore" (func $2)) - (export "establishStackSpace" (func $3)) - (export "setThrew" (func $4)) - (export "setTempRet0" (func $5)) - (export "getTempRet0" (func $6)) - (export "dynCall_ii" (func $38)) - (export "dynCall_iiii" (func $39)) - (export "dynCall_vi" (func $40)) - (func $0 (; 13 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (block $label$1 (result i32) - (local.set $1 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (local.get $0) - ) - ) - (global.set $global$1 - (i32.and - (i32.add - (global.get $global$1) - (i32.const 15) - ) - (i32.const -16) - ) - ) - (local.get $1) + (type $0 (;0;) (func (param i32 i32 i32) (result i32))) + (type $1 (;1;) (func (param i32) (result i32))) + (type $2 (;2;) (func (param i32))) + (type $3 (;3;) (func (result i32))) + (type $4 (;4;) (func (param i32 i32) (result i32))) + (type $5 (;5;) (func (param i32 i32))) + (type $6 (;6;) (func)) + (type $7 (;7;) (func (param i32 i32 i32 i32 i32) (result i32))) + (type $8 (;8;) (func (param i32 i32 i32))) + (type $9 (;9;) (func (param i64 i32) (result i32))) + (type $10 (;10;) (func (param i32 i32 i32 i32 i32))) + (type $11 (;11;) (func (param f64 i32) (result f64))) + (type $12 (;12;) (func (param i32 i32 i32 i32) (result i32))) + (import "env" "memory" (memory $16 (;0;) 2048 2048)) + (import "env" "table" (table $timport$17 (;0;) 8 8 funcref)) + (import "env" "DYNAMICTOP_PTR" (global $gimport$0 (;0;) i32)) + (import "env" "STACKTOP" (global $gimport$1 (;1;) i32)) + (import "env" "STACK_MAX" (global $gimport$2 (;2;) i32)) + (import "env" "memoryBase" (global $gimport$18 (;3;) i32)) + (import "env" "tableBase" (global $gimport$19 (;4;) i32)) + (import "env" "abort" (func $fimport$3 (;0;) (type $2))) + (import "env" "enlargeMemory" (func $fimport$4 (;1;) (type $3))) + (import "env" "getTotalMemory" (func $fimport$5 (;2;) (type $3))) + (import "env" "abortOnCannotGrowMemory" (func $fimport$6 (;3;) (type $3))) + (import "env" "_pthread_cleanup_pop" (func $fimport$7 (;4;) (type $2))) + (import "env" "___syscall6" (func $fimport$8 (;5;) (type $4))) + (import "env" "_pthread_cleanup_push" (func $fimport$9 (;6;) (type $5))) + (import "env" "_abort" (func $fimport$10 (;7;) (type $6))) + (import "env" "___setErrNo" (func $fimport$11 (;8;) (type $2))) + (import "env" "_emscripten_memcpy_big" (func $fimport$12 (;9;) (type $0))) + (import "env" "___syscall54" (func $fimport$13 (;10;) (type $4))) + (import "env" "___syscall140" (func $fimport$14 (;11;) (type $4))) + (import "env" "___syscall146" (func $fimport$15 (;12;) (type $4))) + (func $0 (;13;) (type $1) (param $0 i32) (result i32) + (local $1 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $1 + global.get $global$1 + local.get $0 + i32.add + global.set $global$1 + global.get $global$1 + i32.const 15 + i32.add + i32.const -16 + i32.and + global.set $global$1 + local.get $1 + end ) - ) - (func $1 (; 14 ;) (type $3) (result i32) - (global.get $global$1) - ) - (func $2 (; 15 ;) (type $2) (param $0 i32) - (global.set $global$1 - (local.get $0) + (func $1 (;14;) (type $3) (result i32) + global.get $global$1 ) - ) - (func $3 (; 16 ;) (type $5) (param $0 i32) (param $1 i32) - (block $label$1 - (global.set $global$1 - (local.get $0) - ) - (global.set $global$2 - (local.get $1) - ) + (func $2 (;15;) (type $2) (param $0 i32) + local.get $0 + global.set $global$1 ) - ) - (func $4 (; 17 ;) (type $5) (param $0 i32) (param $1 i32) - (if - (i32.eqz - (global.get $global$3) - ) - (block - (global.set $global$3 - (local.get $0) - ) - (global.set $global$4 - (local.get $1) - ) - ) + (func $3 (;16;) (type $5) (param $0 i32) (param $1 i32) + block $label$1 ;; label = @1 + local.get $0 + global.set $global$1 + local.get $1 + global.set $global$2 + end ) - ) - (func $5 (; 18 ;) (type $2) (param $0 i32) - (global.set $global$5 - (local.get $0) + (func $4 (;17;) (type $5) (param $0 i32) (param $1 i32) + global.get $global$3 + i32.eqz + if ;; label = @1 + block ;; label = @2 + local.get $0 + global.set $global$3 + local.get $1 + global.set $global$4 + end + end ) - ) - (func $6 (; 19 ;) (type $3) (result i32) - (global.get $global$5) - ) - (func $7 (; 20 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 f32) - (block $label$1 (result i32) - (local.set $3 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (local.set $5 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $2 - (local.get $3) - ) - (block $label$2 - (block $label$3 - (br_if $label$3 - (i32.le_s - (local.get $0) - (i32.const 1) - ) - ) - (block $label$4 - (block $label$5 - (block $label$6 - (block $label$7 - (block $label$8 - (block $label$9 - (block $label$10 - (br_table $label$5 $label$10 $label$8 $label$9 $label$7 $label$6 $label$4 - (i32.sub - (local.tee $0 - (i32.load8_s - (i32.load offset=4 - (local.get $1) - ) - ) - ) - (i32.const 48) - ) - ) - ) - (local.set $4 - (i32.const 33000) - ) - (br $label$2) - ) - (br $label$3) - ) - (local.set $4 - (i32.const 130000) - ) - (br $label$2) - ) - (local.set $4 - (i32.const 610000) - ) - (br $label$2) - ) - (local.set $4 - (i32.const 1010000) - ) - (br $label$2) - ) - (global.set $global$1 - (local.get $3) - ) - (return - (i32.const 0) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $0) - (i32.const -48) - ) - ) - (drop - (call $30 - (i32.const 1140) - (local.get $2) - ) - ) - (global.set $global$1 - (local.get $3) - ) - (return - (i32.const -1) - ) - ) - (local.set $4 - (i32.const 220000) - ) - ) - (local.set $1 - (i32.const 2) - ) - (local.set $0 - (i32.const 0) - ) - (loop $label$11 - (block $label$12 - (block $label$13 - (br_if $label$13 - (i32.eqz - (f32.gt - (local.tee $6 - (f32.sqrt - (f32.convert_i32_s - (local.get $1) - ) - ) - ) - (f32.const 2) - ) - ) - ) - (local.set $2 - (i32.const 2) - ) - (loop $label$14 - (br_if $label$12 - (i32.eqz - (i32.rem_s - (local.get $1) - (local.get $2) - ) - ) - ) - (br_if $label$14 - (f32.lt - (f32.convert_i32_s - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - ) - (local.get $6) - ) - ) - (br $label$13) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - ) - (local.set $2 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (if - (i32.lt_s - (local.get $0) - (local.get $4) - ) - (block - (local.set $1 - (local.get $2) - ) - (br $label$11) - ) - ) - ) - (i32.store - (local.get $5) - (local.get $1) - ) - (drop - (call $30 - (i32.const 1152) - (local.get $5) - ) - ) - (global.set $global$1 - (local.get $3) - ) - (i32.const 0) + (func $5 (;18;) (type $2) (param $0 i32) + local.get $0 + global.set $global$5 ) - ) - (func $8 (; 21 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store - (local.tee $2 - (local.get $1) - ) - (i32.load offset=60 - (local.get $0) - ) - ) - (local.set $0 - (call $10 - (call $fimport$8 - (i32.const 6) - (local.get $2) - ) - ) - ) - (global.set $global$1 - (local.get $1) - ) - (local.get $0) + (func $6 (;19;) (type $3) (result i32) + global.get $global$5 ) - ) - (func $9 (; 22 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 32) - ) - ) - (i32.store - (local.tee $3 - (local.get $4) - ) - (i32.load offset=60 - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.const 0) - ) - (i32.store offset=8 - (local.get $3) - (local.get $1) - ) - (i32.store offset=12 - (local.get $3) - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - (i32.store offset=16 - (local.get $3) - (local.get $2) - ) - (local.set $0 - (if (result i32) - (i32.lt_s - (call $10 - (call $fimport$14 - (i32.const 140) - (local.get $3) - ) - ) - (i32.const 0) - ) - (block (result i32) - (i32.store - (local.get $0) - (i32.const -1) - ) - (i32.const -1) - ) - (i32.load - (local.get $0) - ) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $0) + (func $7 (;20;) (type $4) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 f32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $3 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $3 + i32.const 8 + i32.add + local.set $5 + local.get $3 + local.set $2 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $0 + i32.const 1 + i32.le_s + br_if 0 (;@3;) + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + block $label$6 ;; label = @6 + block $label$7 ;; label = @7 + block $label$8 ;; label = @8 + block $label$9 ;; label = @9 + block $label$10 ;; label = @10 + local.get $1 + i32.load offset=4 + i32.load8_s + local.tee $0 + i32.const 48 + i32.sub + br_table 5 (;@5;) 0 (;@10;) 2 (;@8;) 1 (;@9;) 3 (;@7;) 4 (;@6;) 6 (;@4;) + end + i32.const 33000 + local.set $4 + br 7 (;@2;) + end + br 5 (;@3;) + end + i32.const 130000 + local.set $4 + br 5 (;@2;) + end + i32.const 610000 + local.set $4 + br 4 (;@2;) + end + i32.const 1010000 + local.set $4 + br 3 (;@2;) + end + local.get $3 + global.set $global$1 + i32.const 0 + return + end + local.get $2 + local.get $0 + i32.const -48 + i32.add + i32.store + i32.const 1140 + local.get $2 + call $30 + drop + local.get $3 + global.set $global$1 + i32.const -1 + return + end + i32.const 220000 + local.set $4 + end + i32.const 2 + local.set $1 + i32.const 0 + local.set $0 + loop $label$11 ;; label = @2 + block $label$12 ;; label = @3 + block $label$13 ;; label = @4 + local.get $1 + f32.convert_i32_s + f32.sqrt + local.tee $6 + f32.const 0x1p+1 (;=2;) + f32.gt + i32.eqz + br_if 0 (;@4;) + i32.const 2 + local.set $2 + loop $label$14 ;; label = @5 + local.get $1 + local.get $2 + i32.rem_s + i32.eqz + br_if 2 (;@3;) + local.get $2 + i32.const 1 + i32.add + local.tee $2 + f32.convert_i32_s + local.get $6 + f32.lt + br_if 0 (;@5;) + br 1 (;@4;) + end + end + local.get $0 + i32.const 1 + i32.add + local.set $0 + end + local.get $1 + i32.const 1 + i32.add + local.set $2 + local.get $0 + local.get $4 + i32.lt_s + if ;; label = @3 + block ;; label = @4 + local.get $2 + local.set $1 + br 2 (;@2;) + end + end + end + local.get $5 + local.get $1 + i32.store + i32.const 1152 + local.get $5 + call $30 + drop + local.get $3 + global.set $global$1 + i32.const 0 + end ) - ) - (func $10 (; 23 ;) (type $1) (param $0 i32) (result i32) - (if (result i32) - (i32.gt_u - (local.get $0) - (i32.const -4096) - ) - (block (result i32) - (i32.store - (call $11) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - (local.get $0) + (func $8 (;21;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $1 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $1 + local.tee $2 + local.get $0 + i32.load offset=60 + i32.store + i32.const 6 + local.get $2 + call $fimport$8 + call $10 + local.set $0 + local.get $1 + global.set $global$1 + local.get $0 + end ) - ) - (func $11 (; 24 ;) (type $3) (result i32) - (i32.const 3640) - ) - (func $12 (; 25 ;) (type $2) (param $0 i32) - (nop) - ) - (func $13 (; 26 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 80) - ) - ) - (local.set $3 - (local.get $4) - ) - (local.set $5 - (i32.add - (local.get $4) - (i32.const 12) - ) - ) - (i32.store offset=36 - (local.get $0) - (i32.const 3) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 64) - ) - ) - (block - (i32.store - (local.get $3) - (i32.load offset=60 - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.const 21505) - ) - (i32.store offset=8 - (local.get $3) - (local.get $5) - ) - (if - (call $fimport$13 - (i32.const 54) - (local.get $3) - ) - (i32.store8 offset=75 - (local.get $0) - (i32.const -1) - ) - ) - ) - ) - (local.set $0 - (call $14 - (local.get $0) - (local.get $1) - (local.get $2) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $0) + (func $9 (;22;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 32 + i32.add + global.set $global$1 + local.get $4 + local.tee $3 + local.get $0 + i32.load offset=60 + i32.store + local.get $3 + i32.const 0 + i32.store offset=4 + local.get $3 + local.get $1 + i32.store offset=8 + local.get $3 + local.get $4 + i32.const 20 + i32.add + local.tee $0 + i32.store offset=12 + local.get $3 + local.get $2 + i32.store offset=16 + i32.const 140 + local.get $3 + call $fimport$14 + call $10 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $0 + i32.const -1 + i32.store + i32.const -1 + end + else + local.get $0 + i32.load + end + local.set $0 + local.get $4 + global.set $global$1 + local.get $0 + end ) - ) - (func $14 (; 27 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (block $label$1 (result i32) - (local.set $8 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 48) - ) - ) - (local.set $9 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - (local.set $10 - (local.get $8) - ) - (i32.store - (local.tee $3 - (i32.add - (local.get $8) - (i32.const 32) - ) - ) - (local.tee $4 - (i32.load - (local.tee $6 - (i32.add - (local.get $0) - (i32.const 28) - ) - ) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (local.tee $5 - (i32.sub - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - (local.get $4) - ) - ) - ) - (i32.store offset=8 - (local.get $3) - (local.get $1) - ) - (i32.store offset=12 - (local.get $3) - (local.get $2) - ) - (local.set $13 - (i32.add - (local.get $0) - (i32.const 60) - ) - ) - (local.set $14 - (i32.add - (local.get $0) - (i32.const 44) - ) - ) - (local.set $1 - (local.get $3) - ) - (local.set $4 - (i32.const 2) - ) - (local.set $12 - (i32.add - (local.get $5) - (local.get $2) - ) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (loop $label$5 - (if - (i32.load - (i32.const 3596) - ) - (block - (call $fimport$9 - (i32.const 1) - (local.get $0) - ) - (i32.store - (local.get $10) - (i32.load - (local.get $13) - ) - ) - (i32.store offset=4 - (local.get $10) - (local.get $1) - ) - (i32.store offset=8 - (local.get $10) - (local.get $4) - ) - (local.set $3 - (call $10 - (call $fimport$15 - (i32.const 146) - (local.get $10) - ) - ) - ) - (call $fimport$7 - (i32.const 0) - ) - ) - (block - (i32.store - (local.get $9) - (i32.load - (local.get $13) - ) - ) - (i32.store offset=4 - (local.get $9) - (local.get $1) - ) - (i32.store offset=8 - (local.get $9) - (local.get $4) - ) - (local.set $3 - (call $10 - (call $fimport$15 - (i32.const 146) - (local.get $9) - ) - ) - ) - ) - ) - (br_if $label$4 - (i32.eq - (local.get $12) - (local.get $3) - ) - ) - (br_if $label$3 - (i32.lt_s - (local.get $3) - (i32.const 0) - ) - ) - (local.set $5 - (if (result i32) - (i32.gt_u - (local.get $3) - (local.tee $5 - (i32.load offset=4 - (local.get $1) - ) - ) - ) - (block (result i32) - (i32.store - (local.get $6) - (local.tee $7 - (i32.load - (local.get $14) - ) - ) - ) - (i32.store - (local.get $11) - (local.get $7) - ) - (local.set $7 - (i32.load offset=12 - (local.get $1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (local.set $4 - (i32.add - (local.get $4) - (i32.const -1) - ) - ) - (i32.sub - (local.get $3) - (local.get $5) - ) - ) - (if (result i32) - (i32.eq - (local.get $4) - (i32.const 2) - ) - (block (result i32) - (i32.store - (local.get $6) - (i32.add - (i32.load - (local.get $6) - ) - (local.get $3) - ) - ) - (local.set $7 - (local.get $5) - ) - (local.set $4 - (i32.const 2) - ) - (local.get $3) - ) - (block (result i32) - (local.set $7 - (local.get $5) - ) - (local.get $3) - ) - ) - ) - ) - (i32.store - (local.get $1) - (i32.add - (i32.load - (local.get $1) - ) - (local.get $5) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.sub - (local.get $7) - (local.get $5) - ) - ) - (local.set $12 - (i32.sub - (local.get $12) - (local.get $3) - ) - ) - (br $label$5) - ) - ) - (i32.store offset=16 - (local.get $0) - (i32.add - (local.tee $1 - (i32.load - (local.get $14) - ) - ) - (i32.load offset=48 - (local.get $0) - ) - ) - ) - (i32.store - (local.get $6) - (local.get $1) - ) - (i32.store - (local.get $11) - (local.get $1) - ) - (br $label$2) - ) - (i32.store offset=16 - (local.get $0) - (i32.const 0) - ) - (i32.store - (local.get $6) - (i32.const 0) - ) - (i32.store - (local.get $11) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (local.set $2 - (if (result i32) - (i32.eq - (local.get $4) - (i32.const 2) - ) - (i32.const 0) - (i32.sub - (local.get $2) - (i32.load offset=4 - (local.get $1) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $8) - ) - (local.get $2) + (func $10 (;23;) (type $1) (param $0 i32) (result i32) + local.get $0 + i32.const -4096 + i32.gt_u + if (result i32) ;; label = @1 + block (result i32) ;; label = @2 + call $11 + i32.const 0 + local.get $0 + i32.sub + i32.store + i32.const -1 + end + else + local.get $0 + end ) - ) - (func $15 (; 28 ;) (type $2) (param $0 i32) - (if - (i32.eqz - (i32.load offset=68 - (local.get $0) - ) - ) - (call $12 - (local.get $0) - ) + (func $11 (;24;) (type $3) (result i32) + i32.const 3640 ) - ) - (func $16 (; 29 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $5 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (if - (i32.and - (local.tee $4 - (i32.ne - (local.get $2) - (i32.const 0) - ) - ) - (i32.ne - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.const 0) - ) - ) - (block - (local.set $4 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (local.set $3 - (local.get $2) - ) - (local.set $2 - (local.get $0) - ) - (loop $label$6 - (if - (i32.eq - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.get $4) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (br $label$3) - ) - ) - (br_if $label$6 - (i32.and - (local.tee $0 - (i32.ne - (local.tee $3 - (i32.add - (local.get $3) - (i32.const -1) - ) - ) - (i32.const 0) - ) - ) - (i32.ne - (i32.and - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 3) - ) - (i32.const 0) - ) - ) - ) - (br $label$4) - ) - ) - (block - (local.set $3 - (local.get $2) - ) - (local.set $2 - (local.get $0) - ) - (local.set $0 - (local.get $4) - ) - ) - ) - ) - (if - (local.get $0) - (block - (local.set $0 - (local.get $3) - ) - (br $label$3) - ) - (local.set $0 - (i32.const 0) - ) - ) - (br $label$2) - ) - (if - (i32.ne - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.tee $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $3 - (i32.mul - (local.get $5) - (i32.const 16843009) - ) - ) - (block $label$12 - (block $label$13 - (br_if $label$13 - (i32.le_u - (local.get $0) - (i32.const 3) - ) - ) - (loop $label$14 - (if - (i32.eqz - (i32.and - (i32.xor - (i32.and - (local.tee $4 - (i32.xor - (i32.load - (local.get $2) - ) - (local.get $3) - ) - ) - (i32.const -2139062144) - ) - (i32.const -2139062144) - ) - (i32.add - (local.get $4) - (i32.const -16843009) - ) - ) - ) - (block - (local.set $2 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - (br_if $label$14 - (i32.gt_u - (local.tee $0 - (i32.add - (local.get $0) - (i32.const -4) - ) - ) - (i32.const 3) - ) - ) - (br $label$13) - ) - ) - ) - (br $label$12) - ) - (if - (i32.eqz - (local.get $0) - ) - (block - (local.set $0 - (i32.const 0) - ) - (br $label$2) - ) - ) - ) - (loop $label$17 - (br_if $label$2 - (i32.eq - (i32.load8_s - (local.get $2) - ) - (i32.shr_s - (i32.shl - (local.get $1) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (local.set $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (br_if $label$17 - (local.tee $0 - (i32.add - (local.get $0) - (i32.const -1) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - ) - ) - ) - ) - (if (result i32) - (local.get $0) - (local.get $2) - (i32.const 0) - ) + (func $12 (;25;) (type $2) (param $0 i32) + nop ) - ) - (func $17 (; 30 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (block $label$1 (result i32) - (local.set $4 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 224) - ) - ) - (local.set $5 - (i32.add - (local.get $4) - (i32.const 136) - ) - ) - (i64.store align=4 - (local.tee $3 - (i32.add - (local.get $4) - (i32.const 80) - ) - ) - (i64.const 0) - ) - (i64.store offset=8 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=16 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=24 align=4 - (local.get $3) - (i64.const 0) - ) - (i64.store offset=32 align=4 - (local.get $3) - (i64.const 0) - ) - (i32.store - (local.tee $6 - (i32.add - (local.get $4) - (i32.const 120) - ) - ) - (i32.load - (local.get $2) - ) - ) - (if - (i32.lt_s - (call $18 - (i32.const 0) - (local.get $1) - (local.get $6) - (local.tee $2 - (local.get $4) - ) - (local.get $3) - ) - (i32.const 0) - ) - (local.set $1 - (i32.const -1) - ) - (block - (local.set $12 - (if (result i32) - (i32.gt_s - (i32.load offset=76 - (local.get $0) - ) - (i32.const -1) - ) - (call $19 - (local.get $0) - ) - (i32.const 0) - ) - ) - (local.set $7 - (i32.load - (local.get $0) - ) - ) - (if - (i32.lt_s - (i32.load8_s offset=74 - (local.get $0) - ) - (i32.const 1) - ) - (i32.store - (local.get $0) - (i32.and - (local.get $7) - (i32.const -33) - ) - ) - ) - (if - (i32.load - (local.tee $8 - (i32.add - (local.get $0) - (i32.const 48) - ) - ) - ) - (local.set $1 - (call $18 - (local.get $0) - (local.get $1) - (local.get $6) - (local.get $2) - (local.get $3) - ) - ) - (block - (local.set $10 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 44) - ) - ) - ) - ) - (i32.store - (local.get $9) - (local.get $5) - ) - (i32.store - (local.tee $13 - (i32.add - (local.get $0) - (i32.const 28) - ) - ) - (local.get $5) - ) - (i32.store - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - (local.get $5) - ) - (i32.store - (local.get $8) - (i32.const 80) - ) - (i32.store - (local.tee $14 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - (i32.add - (local.get $5) - (i32.const 80) - ) - ) - (local.set $1 - (call $18 - (local.get $0) - (local.get $1) - (local.get $6) - (local.get $2) - (local.get $3) - ) - ) - (if - (local.get $10) - (block - (drop - (call_indirect (type $0) - (local.get $0) - (i32.const 0) - (i32.const 0) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $0) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $11) - ) - ) - (local.set $1 - (i32.const -1) - ) - ) - (i32.store - (local.get $9) - (local.get $10) - ) - (i32.store - (local.get $8) - (i32.const 0) - ) - (i32.store - (local.get $14) - (i32.const 0) - ) - (i32.store - (local.get $13) - (i32.const 0) - ) - (i32.store - (local.get $11) - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (local.get $0) - (i32.or - (local.tee $2 - (i32.load - (local.get $0) - ) - ) - (i32.and - (local.get $7) - (i32.const 32) - ) - ) - ) - (if - (local.get $12) - (call $12 - (local.get $0) - ) - ) - (if - (i32.and - (local.get $2) - (i32.const 32) - ) - (local.set $1 - (i32.const -1) - ) - ) - ) - ) - (global.set $global$1 - (local.get $4) - ) - (local.get $1) + (func $13 (;26;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 80 + i32.add + global.set $global$1 + local.get $4 + local.set $3 + local.get $4 + i32.const 12 + i32.add + local.set $5 + local.get $0 + i32.const 3 + i32.store offset=36 + local.get $0 + i32.load + i32.const 64 + i32.and + i32.eqz + if ;; label = @2 + block ;; label = @3 + local.get $3 + local.get $0 + i32.load offset=60 + i32.store + local.get $3 + i32.const 21505 + i32.store offset=4 + local.get $3 + local.get $5 + i32.store offset=8 + i32.const 54 + local.get $3 + call $fimport$13 + if ;; label = @4 + local.get $0 + i32.const -1 + i32.store8 offset=75 + end + end + end + local.get $0 + local.get $1 + local.get $2 + call $14 + local.set $0 + local.get $4 + global.set $global$1 + local.get $0 + end ) - ) - (func $18 (; 31 ;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 i32) - (local $17 i32) - (local $18 i32) - (local $19 i32) - (local $20 i32) - (local $21 i32) - (local $22 i32) - (local $23 i32) - (local $24 i32) - (local $25 i32) - (local $26 i32) - (local $27 i32) - (local $28 i32) - (local $29 i32) - (local $30 i32) - (local $31 i32) - (local $32 i32) - (local $33 i32) - (local $34 i32) - (local $35 i32) - (local $36 i32) - (local $37 i32) - (local $38 i32) - (local $39 i32) - (local $40 i32) - (local $41 i32) - (local $42 i32) - (local $43 i32) - (local $44 i32) - (local $45 i32) - (local $46 i32) - (local $47 i32) - (local $48 i32) - (local $49 i32) - (local $50 i64) - (local $51 i64) - (local $52 f64) - (local $53 f64) - (block $label$1 (result i32) - (local.set $23 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 624) - ) - ) - (local.set $20 - (i32.add - (local.get $23) - (i32.const 16) - ) - ) - (local.set $16 - (local.get $23) - ) - (local.set $36 - (i32.add - (local.get $23) - (i32.const 528) - ) - ) - (local.set $30 - (i32.ne - (local.get $0) - (i32.const 0) - ) - ) - (local.set $38 - (local.tee $21 - (i32.add - (local.tee $17 - (i32.add - (local.get $23) - (i32.const 536) - ) - ) - (i32.const 40) - ) - ) - ) - (local.set $39 - (i32.add - (local.get $17) - (i32.const 39) - ) - ) - (local.set $42 - (i32.add - (local.tee $37 - (i32.add - (local.get $23) - (i32.const 8) - ) - ) - (i32.const 4) - ) - ) - (local.set $43 - (i32.sub - (i32.const 0) - (local.tee $27 - (local.tee $19 - (i32.add - (local.get $23) - (i32.const 588) - ) - ) - ) - ) - ) - (local.set $33 - (i32.add - (local.tee $17 - (i32.add - (local.get $23) - (i32.const 576) - ) - ) - (i32.const 12) - ) - ) - (local.set $40 - (i32.add - (local.get $17) - (i32.const 11) - ) - ) - (local.set $44 - (i32.sub - (local.tee $28 - (local.get $33) - ) - (local.get $27) - ) - ) - (local.set $45 - (i32.sub - (i32.const -2) - (local.get $27) - ) - ) - (local.set $46 - (i32.add - (local.get $28) - (i32.const 2) - ) - ) - (local.set $48 - (i32.add - (local.tee $47 - (i32.add - (local.get $23) - (i32.const 24) - ) - ) - (i32.const 288) - ) - ) - (local.set $41 - (local.tee $31 - (i32.add - (local.get $19) - (i32.const 9) - ) - ) - ) - (local.set $34 - (i32.add - (local.get $19) - (i32.const 8) - ) - ) - (local.set $15 - (i32.const 0) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $17 - (i32.const 0) - ) - (block $label$2 - (block $label$3 - (loop $label$4 - (block $label$5 - (if - (i32.gt_s - (local.get $15) - (i32.const -1) - ) - (local.set $15 - (if (result i32) - (i32.gt_s - (local.get $10) - (i32.sub - (i32.const 2147483647) - (local.get $15) - ) - ) - (block (result i32) - (i32.store - (call $11) - (i32.const 75) - ) - (i32.const -1) - ) - (i32.add - (local.get $10) - (local.get $15) - ) - ) - ) - ) - (br_if $label$3 - (i32.eqz - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.get $1) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (local.set $11 - (local.get $1) - ) - (block $label$9 - (block $label$10 - (loop $label$11 - (block $label$12 - (block $label$13 - (block $label$14 - (block $label$15 - (br_table $label$14 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$13 $label$15 $label$13 - (i32.sub - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 0) - ) - ) - ) - (local.set $5 - (local.get $11) - ) - (br $label$10) - ) - (local.set $5 - (local.get $11) - ) - (br $label$12) - ) - (local.set $5 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (br $label$11) - ) - ) - (br $label$9) - ) - (loop $label$16 - (br_if $label$9 - (i32.ne - (i32.load8_s offset=1 - (local.get $5) - ) - (i32.const 37) - ) - ) - (local.set $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - (br_if $label$16 - (i32.eq - (i32.load8_s - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 2) - ) - ) - ) - (i32.const 37) - ) - ) - ) - ) - (local.set $10 - (i32.sub - (local.get $11) - (local.get $1) - ) - ) - (if - (local.get $30) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $1) - (local.get $10) - (local.get $0) - ) - ) - ) - ) - (if - (local.get $10) - (block - (local.set $1 - (local.get $5) - ) - (br $label$4) - ) - ) - (local.set $10 - (if (result i32) - (i32.lt_u - (local.tee $9 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $10 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block (result i32) - (local.set $10 - (i32.add - (local.get $5) - (i32.const 3) - ) - ) - (if - (local.tee $12 - (i32.eq - (i32.load8_s offset=2 - (local.get $5) - ) - (i32.const 36) - ) - ) - (local.set $11 - (local.get $10) - ) - ) - (if - (local.get $12) - (local.set $17 - (i32.const 1) - ) - ) - (local.set $5 - (i32.load8_s - (local.get $11) - ) - ) - (if - (i32.eqz - (local.get $12) - ) - (local.set $9 - (i32.const -1) - ) - ) - (local.get $17) - ) - (block (result i32) - (local.set $5 - (local.get $10) - ) - (local.set $9 - (i32.const -1) - ) - (local.get $17) - ) - ) - ) - (block $label$25 - (if - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (i32.const 32) - ) - (block - (local.set $17 - (i32.const 0) - ) - (loop $label$27 - (br_if $label$25 - (i32.eqz - (i32.and - (i32.shl - (i32.const 1) - (local.get $12) - ) - (i32.const 75913) - ) - ) - ) - (local.set $17 - (i32.or - (i32.shl - (i32.const 1) - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (local.get $17) - ) - ) - (br_if $label$27 - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -32) - ) - ) - (i32.const 32) - ) - ) - ) - ) - (local.set $17 - (i32.const 0) - ) - ) - ) - (block $label$29 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 42) - ) - (block - (local.set $11 - (block $label$31 (result i32) - (block $label$32 - (br_if $label$32 - (i32.ge_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - (br_if $label$32 - (i32.ne - (i32.load8_s offset=2 - (local.get $11) - ) - (i32.const 36) - ) - ) - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $12) - (i32.const 2) - ) - ) - (i32.const 10) - ) - (local.set $8 - (i32.const 1) - ) - (local.set $10 - (i32.wrap_i64 - (i64.load - (i32.add - (local.get $3) - (i32.shl - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -48) - ) - (i32.const 3) - ) - ) - ) - ) - ) - (br $label$31 - (i32.add - (local.get $11) - (i32.const 3) - ) - ) - ) - (if - (local.get $10) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $12 - (local.get $17) - ) - (local.set $17 - (i32.const 0) - ) - (local.set $11 - (local.get $7) - ) - (local.set $10 - (i32.const 0) - ) - (br $label$29) - ) - ) - (local.set $10 - (i32.load - (local.tee $11 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (local.set $8 - (i32.const 0) - ) - (local.get $7) - ) - ) - (local.set $12 - (i32.or - (local.get $17) - (i32.const 8192) - ) - ) - (local.set $7 - (i32.sub - (i32.const 0) - (local.get $10) - ) - ) - (local.set $5 - (i32.load8_s - (local.get $11) - ) - ) - (if - (i32.eqz - (local.tee $6 - (i32.lt_s - (local.get $10) - (i32.const 0) - ) - ) - ) - (local.set $12 - (local.get $17) - ) - ) - (local.set $17 - (local.get $8) - ) - (if - (local.get $6) - (local.set $10 - (local.get $7) - ) - ) - ) - (if - (i32.lt_u - (local.tee $12 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block - (local.set $7 - (i32.const 0) - ) - (local.set $5 - (local.get $12) - ) - (loop $label$39 - (local.set $7 - (i32.add - (i32.mul - (local.get $7) - (i32.const 10) - ) - (local.get $5) - ) - ) - (br_if $label$39 - (i32.lt_u - (local.tee $5 - (i32.add - (i32.shr_s - (i32.shl - (local.tee $12 - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - ) - (if - (i32.lt_s - (local.get $7) - (i32.const 0) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - (block - (local.set $5 - (local.get $12) - ) - (local.set $12 - (local.get $17) - ) - (local.set $17 - (local.get $10) - ) - (local.set $10 - (local.get $7) - ) - ) - ) - ) - (block - (local.set $12 - (local.get $17) - ) - (local.set $17 - (local.get $10) - ) - (local.set $10 - (i32.const 0) - ) - ) - ) - ) - ) - (block $label$43 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 46) - ) - (block - (if - (i32.ne - (i32.shr_s - (i32.shl - (local.tee $5 - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 42) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.add - (i32.shr_s - (i32.shl - (local.get $5) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (block - (local.set $11 - (local.get $7) - ) - (local.set $7 - (i32.const 0) - ) - ) - (block - (local.set $5 - (i32.const 0) - ) - (local.set $11 - (local.get $7) - ) - (br $label$43) - ) - ) - (loop $label$48 - (local.set $5 - (i32.add - (i32.mul - (local.get $7) - (i32.const 10) - ) - (local.get $5) - ) - ) - (br_if $label$43 - (i32.ge_u - (local.tee $8 - (i32.add - (i32.load8_s - (local.tee $11 - (i32.add - (local.get $11) - (i32.const 1) - ) - ) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - ) - (local.set $7 - (local.get $5) - ) - (local.set $5 - (local.get $8) - ) - (br $label$48) - ) - ) - ) - (if - (i32.lt_u - (local.tee $5 - (i32.add - (i32.load8_s - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 2) - ) - ) - ) - (i32.const -48) - ) - ) - (i32.const 10) - ) - (if - (i32.eq - (i32.load8_s offset=3 - (local.get $11) - ) - (i32.const 36) - ) - (block - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $5) - (i32.const 2) - ) - ) - (i32.const 10) - ) - (local.set $5 - (i32.wrap_i64 - (i64.load - (i32.add - (local.get $3) - (i32.shl - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -48) - ) - (i32.const 3) - ) - ) - ) - ) - ) - (local.set $11 - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (br $label$43) - ) - ) - ) - (if - (local.get $17) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $11 - (if (result i32) - (local.get $30) - (block (result i32) - (local.set $5 - (i32.load - (local.tee $11 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $11) - (i32.const 4) - ) - ) - (local.get $7) - ) - (block (result i32) - (local.set $5 - (i32.const 0) - ) - (local.get $7) - ) - ) - ) - ) - (local.set $5 - (i32.const -1) - ) - ) - ) - (local.set $7 - (local.get $11) - ) - (local.set $8 - (i32.const 0) - ) - (loop $label$55 - (if - (i32.gt_u - (local.tee $6 - (i32.add - (i32.load8_s - (local.get $7) - ) - (i32.const -65) - ) - ) - (i32.const 57) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $11 - (i32.add - (local.get $7) - (i32.const 1) - ) - ) - (if - (i32.lt_u - (i32.add - (local.tee $6 - (i32.and - (local.tee $13 - (i32.load8_s - (i32.add - (i32.add - (i32.mul - (local.get $8) - (i32.const 58) - ) - (i32.const 1168) - ) - (local.get $6) - ) - ) - ) - (i32.const 255) - ) - ) - (i32.const -1) - ) - (i32.const 8) - ) - (block - (local.set $7 - (local.get $11) - ) - (local.set $8 - (local.get $6) - ) - (br $label$55) - ) - ) - ) - (if - (i32.eqz - (i32.shr_s - (i32.shl - (local.get $13) - (i32.const 24) - ) - (i32.const 24) - ) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (local.set $14 - (i32.gt_s - (local.get $9) - (i32.const -1) - ) - ) - (block $label$59 - (block $label$60 - (if - (i32.eq - (i32.shr_s - (i32.shl - (local.get $13) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 19) - ) - (if - (local.get $14) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - (br $label$60) - ) - (block - (if - (local.get $14) - (block - (i32.store - (i32.add - (local.get $4) - (i32.shl - (local.get $9) - (i32.const 2) - ) - ) - (local.get $6) - ) - (i64.store - (local.get $16) - (i64.load - (i32.add - (local.get $3) - (i32.shl - (local.get $9) - (i32.const 3) - ) - ) - ) - ) - (br $label$60) - ) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $15 - (i32.const 0) - ) - (br $label$5) - ) - ) - (call $21 - (local.get $16) - (local.get $6) - (local.get $2) - ) - ) - ) - (br $label$59) - ) - (if - (i32.eqz - (local.get $30) - ) - (block - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - ) - ) - (local.set $9 - (i32.and - (local.tee $7 - (i32.load8_s - (local.get $7) - ) - ) - (i32.const -33) - ) - ) - (if - (i32.eqz - (i32.and - (i32.ne - (local.get $8) - (i32.const 0) - ) - (i32.eq - (i32.and - (local.get $7) - (i32.const 15) - ) - (i32.const 3) - ) - ) - ) - (local.set $9 - (local.get $7) - ) - ) - (local.set $7 - (i32.and - (local.get $12) - (i32.const -65537) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 8192) - ) - (local.set $12 - (local.get $7) - ) - ) - (block $label$70 - (block $label$71 - (block $label$72 - (block $label$73 - (block $label$74 - (block $label$75 - (block $label$76 - (block $label$77 - (block $label$78 - (block $label$79 - (block $label$80 - (block $label$81 - (block $label$82 - (block $label$83 - (block $label$84 - (block $label$85 - (block $label$86 - (block $label$87 - (block $label$88 - (block $label$89 - (br_table $label$78 $label$77 $label$80 $label$77 $label$78 $label$78 $label$78 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$79 $label$77 $label$77 $label$77 $label$77 $label$87 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$77 $label$78 $label$77 $label$83 $label$85 $label$78 $label$78 $label$78 $label$77 $label$85 $label$77 $label$77 $label$77 $label$82 $label$89 $label$86 $label$88 $label$77 $label$77 $label$81 $label$77 $label$84 $label$77 $label$77 $label$87 $label$77 - (i32.sub - (local.get $9) - (i32.const 65) - ) - ) - ) - (block $label$90 - (block $label$91 - (block $label$92 - (block $label$93 - (block $label$94 - (block $label$95 - (block $label$96 - (block $label$97 - (br_table $label$97 $label$96 $label$95 $label$94 $label$93 $label$90 $label$92 $label$91 $label$90 - (i32.sub - (i32.shr_s - (i32.shl - (i32.and - (local.get $8) - (i32.const 255) - ) - (i32.const 24) - ) - (i32.const 24) - ) - (i32.const 0) - ) - ) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i64.store - (i32.load - (local.get $16) - ) - (i64.extend_i32_s - (local.get $15) - ) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store16 - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store8 - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i32.store - (i32.load - (local.get $16) - ) - (local.get $15) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (i64.store - (i32.load - (local.get $16) - ) - (i64.extend_i32_s - (local.get $15) - ) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $10 - (i32.const 0) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $12 - (i32.or - (local.get $12) - (i32.const 8) - ) - ) - (if - (i32.le_u - (local.get $5) - (i32.const 8) - ) - (local.set $5 - (i32.const 8) - ) - ) - (local.set $9 - (i32.const 120) - ) - (br $label$76) - ) - (br $label$76) - ) - (if - (i64.eq - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (local.set $7 - (local.get $21) - ) - (block - (local.set $1 - (local.get $21) - ) - (loop $label$101 - (i64.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i64.or - (i64.and - (local.get $50) - (i64.const 7) - ) - (i64.const 48) - ) - ) - (br_if $label$101 - (i64.ne - (local.tee $50 - (i64.shr_u - (local.get $50) - (i64.const 3) - ) - ) - (i64.const 0) - ) - ) - (local.set $7 - (local.get $1) - ) - ) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 8) - ) - (block - (local.set $8 - (i32.add - (local.tee $1 - (i32.sub - (local.get $38) - (local.get $7) - ) - ) - (i32.const 1) - ) - ) - (if - (i32.le_s - (local.get $5) - (local.get $1) - ) - (local.set $5 - (local.get $8) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1648) - ) - (br $label$71) - ) - (block - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1648) - ) - (br $label$71) - ) - ) - ) - (if - (i64.lt_s - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (block - (i64.store - (local.get $16) - (local.tee $50 - (i64.sub - (i64.const 0) - (local.get $50) - ) - ) - ) - (local.set $6 - (i32.const 1) - ) - (local.set $8 - (i32.const 1648) - ) - (br $label$75) - ) - ) - (if - (i32.and - (local.get $12) - (i32.const 2048) - ) - (block - (local.set $6 - (i32.const 1) - ) - (local.set $8 - (i32.const 1649) - ) - (br $label$75) - ) - (block - (local.set $6 - (local.tee $1 - (i32.and - (local.get $12) - (i32.const 1) - ) - ) - ) - (local.set $8 - (if (result i32) - (local.get $1) - (i32.const 1650) - (i32.const 1648) - ) - ) - (br $label$75) - ) - ) - ) - (local.set $50 - (i64.load - (local.get $16) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1648) - ) - (br $label$75) - ) - (i64.store8 - (local.get $39) - (i64.load - (local.get $16) - ) - ) - (local.set $1 - (local.get $39) - ) - (local.set $12 - (local.get $7) - ) - (local.set $7 - (i32.const 1) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1648) - ) - (local.set $5 - (local.get $21) - ) - (br $label$70) - ) - (local.set $1 - (call $23 - (i32.load - (call $11) - ) - ) - ) - (br $label$74) - ) - (if - (i32.eqz - (local.tee $1 - (i32.load - (local.get $16) - ) - ) - ) - (local.set $1 - (i32.const 1658) - ) - ) - (br $label$74) - ) - (i64.store32 - (local.get $37) - (i64.load - (local.get $16) - ) - ) - (i32.store - (local.get $42) - (i32.const 0) - ) - (i32.store - (local.get $16) - (local.get $37) - ) - (local.set $7 - (local.get $37) - ) - (local.set $6 - (i32.const -1) - ) - (br $label$73) - ) - (local.set $7 - (i32.load - (local.get $16) - ) - ) - (if - (local.get $5) - (block - (local.set $6 - (local.get $5) - ) - (br $label$73) - ) - (block - (call $24 - (local.get $0) - (i32.const 32) - (local.get $10) - (i32.const 0) - (local.get $12) - ) - (local.set $1 - (i32.const 0) - ) - (br $label$72) - ) - ) - ) - (local.set $52 - (f64.load - (local.get $16) - ) - ) - (i32.store - (local.get $20) - (i32.const 0) - ) - (local.set $26 - (if (result i32) - (i64.lt_s - (i64.reinterpret_f64 - (local.get $52) - ) - (i64.const 0) - ) - (block (result i32) - (local.set $24 - (i32.const 1) - ) - (local.set $52 - (f64.neg - (local.get $52) - ) - ) - (i32.const 1665) - ) - (block (result i32) - (local.set $1 - (i32.and - (local.get $12) - (i32.const 1) - ) - ) - (if (result i32) - (i32.and - (local.get $12) - (i32.const 2048) - ) - (block (result i32) - (local.set $24 - (i32.const 1) - ) - (i32.const 1668) - ) - (block (result i32) - (local.set $24 - (local.get $1) - ) - (if (result i32) - (local.get $1) - (i32.const 1671) - (i32.const 1666) - ) - ) - ) - ) - ) - ) - (block $label$119 - (if - (i64.lt_u - (i64.and - (i64.reinterpret_f64 - (local.get $52) - ) - (i64.const 9218868437227405312) - ) - (i64.const 9218868437227405312) - ) - (block - (if - (local.tee $1 - (f64.ne - (local.tee $52 - (f64.mul - (call $26 - (local.get $52) - (local.get $20) - ) - (f64.const 2) - ) - ) - (f64.const 0) - ) - ) - (i32.store - (local.get $20) - (i32.add - (i32.load - (local.get $20) - ) - (i32.const -1) - ) - ) - ) - (if - (i32.eq - (local.tee $22 - (i32.or - (local.get $9) - (i32.const 32) - ) - ) - (i32.const 97) - ) - (block - (local.set $1 - (i32.add - (local.get $26) - (i32.const 9) - ) - ) - (if - (local.tee $6 - (i32.and - (local.get $9) - (i32.const 32) - ) - ) - (local.set $26 - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.or - (i32.gt_u - (local.get $5) - (i32.const 11) - ) - (i32.eqz - (local.tee $1 - (i32.sub - (i32.const 12) - (local.get $5) - ) - ) - ) - ) - ) - (block - (local.set $53 - (f64.const 8) - ) - (loop $label$125 - (local.set $53 - (f64.mul - (local.get $53) - (f64.const 16) - ) - ) - (br_if $label$125 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - (local.set $52 - (if (result f64) - (i32.eq - (i32.load8_s - (local.get $26) - ) - (i32.const 45) - ) - (f64.neg - (f64.add - (local.get $53) - (f64.sub - (f64.neg - (local.get $52) - ) - (local.get $53) - ) - ) - ) - (f64.sub - (f64.add - (local.get $52) - (local.get $53) - ) - (local.get $53) - ) - ) - ) - ) - ) - (local.set $1 - (i32.sub - (i32.const 0) - (local.tee $7 - (i32.load - (local.get $20) - ) - ) - ) - ) - (if - (i32.eq - (local.tee $1 - (call $22 - (i64.extend_i32_s - (if (result i32) - (i32.lt_s - (local.get $7) - (i32.const 0) - ) - (local.get $1) - (local.get $7) - ) - ) - (local.get $33) - ) - ) - (local.get $33) - ) - (block - (i32.store8 - (local.get $40) - (i32.const 48) - ) - (local.set $1 - (local.get $40) - ) - ) - ) - (local.set $13 - (i32.or - (local.get $24) - (i32.const 2) - ) - ) - (i32.store8 - (i32.add - (local.get $1) - (i32.const -1) - ) - (i32.add - (i32.and - (i32.shr_s - (local.get $7) - (i32.const 31) - ) - (i32.const 2) - ) - (i32.const 43) - ) - ) - (i32.store8 - (local.tee $8 - (i32.add - (local.get $1) - (i32.const -2) - ) - ) - (i32.add - (local.get $9) - (i32.const 15) - ) - ) - (local.set $9 - (i32.lt_s - (local.get $5) - (i32.const 1) - ) - ) - (local.set $14 - (i32.eqz - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - ) - (local.set $1 - (local.get $19) - ) - (loop $label$131 - (i32.store8 - (local.get $1) - (i32.or - (i32.load8_u - (i32.add - (local.tee $7 - (i32.trunc_f64_s - (local.get $52) - ) - ) - (i32.const 1632) - ) - ) - (local.get $6) - ) - ) - (local.set $52 - (f64.mul - (f64.sub - (local.get $52) - (f64.convert_i32_s - (local.get $7) - ) - ) - (f64.const 16) - ) - ) - (local.set $1 - (block $label$132 (result i32) - (if (result i32) - (i32.eq - (i32.sub - (local.tee $7 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.get $27) - ) - (i32.const 1) - ) - (block (result i32) - (drop - (br_if $label$132 - (local.get $7) - (i32.and - (local.get $14) - (i32.and - (local.get $9) - (f64.eq - (local.get $52) - (f64.const 0) - ) - ) - ) - ) - ) - (i32.store8 - (local.get $7) - (i32.const 46) - ) - (i32.add - (local.get $1) - (i32.const 2) - ) - ) - (local.get $7) - ) - ) - ) - (br_if $label$131 - (f64.ne - (local.get $52) - (f64.const 0) - ) - ) - ) - (local.set $6 - (i32.sub - (i32.add - (local.get $46) - (local.get $5) - ) - (local.tee $7 - (local.get $8) - ) - ) - ) - (local.set $9 - (i32.add - (i32.sub - (local.get $44) - (local.get $7) - ) - (local.get $1) - ) - ) - (call $24 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $5 - (i32.add - (if (result i32) - (i32.and - (i32.ne - (local.get $5) - (i32.const 0) - ) - (i32.lt_s - (i32.add - (local.get $45) - (local.get $1) - ) - (local.get $5) - ) - ) - (local.get $6) - (local.tee $6 - (local.get $9) - ) - ) - (local.get $13) - ) - ) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $26) - (local.get $13) - (local.get $0) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (local.set $1 - (i32.sub - (local.get $1) - (local.get $27) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $19) - (local.get $1) - (local.get $0) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 48) - (i32.sub - (local.get $6) - (i32.add - (local.get $1) - (local.tee $1 - (i32.sub - (local.get $28) - (local.get $7) - ) - ) - ) - ) - (i32.const 0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $8) - (local.get $1) - (local.get $0) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $5) - (local.get $10) - ) - (local.set $10 - (local.get $5) - ) - ) - (br $label$119) - ) - ) - (if - (local.get $1) - (block - (i32.store - (local.get $20) - (local.tee $6 - (i32.add - (i32.load - (local.get $20) - ) - (i32.const -28) - ) - ) - ) - (local.set $52 - (f64.mul - (local.get $52) - (f64.const 268435456) - ) - ) - ) - (local.set $6 - (i32.load - (local.get $20) - ) - ) - ) - (local.set $8 - (local.tee $7 - (if (result i32) - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (local.get $47) - (local.get $48) - ) - ) - ) - (loop $label$145 - (i32.store - (local.get $8) - (local.tee $1 - (i32.trunc_f64_s - (local.get $52) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (br_if $label$145 - (f64.ne - (local.tee $52 - (f64.mul - (f64.sub - (local.get $52) - (f64.convert_i32_u - (local.get $1) - ) - ) - (f64.const 1e9) - ) - ) - (f64.const 0) - ) - ) - ) - (if - (i32.gt_s - (local.get $6) - (i32.const 0) - ) - (block - (local.set $1 - (local.get $7) - ) - (loop $label$147 - (local.set $14 - (if (result i32) - (i32.gt_s - (local.get $6) - (i32.const 29) - ) - (i32.const 29) - (local.get $6) - ) - ) - (block $label$150 - (if - (i32.ge_u - (local.tee $6 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - (local.get $1) - ) - (block - (local.set $50 - (i64.extend_i32_u - (local.get $14) - ) - ) - (local.set $13 - (i32.const 0) - ) - (loop $label$152 - (i64.store32 - (local.get $6) - (i64.rem_u - (local.tee $51 - (i64.add - (i64.shl - (i64.extend_i32_u - (i32.load - (local.get $6) - ) - ) - (local.get $50) - ) - (i64.extend_i32_u - (local.get $13) - ) - ) - ) - (i64.const 1000000000) - ) - ) - (local.set $13 - (i32.wrap_i64 - (i64.div_u - (local.get $51) - (i64.const 1000000000) - ) - ) - ) - (br_if $label$152 - (i32.ge_u - (local.tee $6 - (i32.add - (local.get $6) - (i32.const -4) - ) - ) - (local.get $1) - ) - ) - ) - (br_if $label$150 - (i32.eqz - (local.get $13) - ) - ) - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -4) - ) - ) - (local.get $13) - ) - ) - ) - ) - (loop $label$153 - (if - (i32.gt_u - (local.get $8) - (local.get $1) - ) - (if - (i32.eqz - (i32.load - (local.tee $6 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - ) - (block - (local.set $8 - (local.get $6) - ) - (br $label$153) - ) - ) - ) - ) - (i32.store - (local.get $20) - (local.tee $6 - (i32.sub - (i32.load - (local.get $20) - ) - (local.get $14) - ) - ) - ) - (br_if $label$147 - (i32.gt_s - (local.get $6) - (i32.const 0) - ) - ) - ) - ) - (local.set $1 - (local.get $7) - ) - ) - (local.set $18 - (if (result i32) - (i32.lt_s - (local.get $5) - (i32.const 0) - ) - (i32.const 6) - (local.get $5) - ) - ) - (if - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (block - (local.set $14 - (i32.add - (i32.div_s - (i32.add - (local.get $18) - (i32.const 25) - ) - (i32.const 9) - ) - (i32.const 1) - ) - ) - (local.set $25 - (i32.eq - (local.get $22) - (i32.const 102) - ) - ) - (local.set $5 - (local.get $8) - ) - (loop $label$160 - (if - (i32.gt_s - (local.tee $13 - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (i32.const 9) - ) - (local.set $13 - (i32.const 9) - ) - ) - (block $label$162 - (if - (i32.lt_u - (local.get $1) - (local.get $5) - ) - (block - (local.set $29 - (i32.add - (i32.shl - (i32.const 1) - (local.get $13) - ) - (i32.const -1) - ) - ) - (local.set $35 - (i32.shr_u - (i32.const 1000000000) - (local.get $13) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (local.get $1) - ) - (loop $label$164 - (i32.store - (local.get $8) - (i32.add - (i32.shr_u - (local.tee $32 - (i32.load - (local.get $8) - ) - ) - (local.get $13) - ) - (local.get $6) - ) - ) - (local.set $6 - (i32.mul - (i32.and - (local.get $32) - (local.get $29) - ) - (local.get $35) - ) - ) - (br_if $label$164 - (i32.lt_u - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (local.get $5) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (local.get $8) - ) - ) - (br_if $label$162 - (i32.eqz - (local.get $6) - ) - ) - (i32.store - (local.get $5) - (local.get $6) - ) - (local.set $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - ) - (block - (local.set $8 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (if - (i32.eqz - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (local.get $8) - ) - ) - ) - ) - ) - (local.set $6 - (i32.add - (local.tee $8 - (if (result i32) - (local.get $25) - (local.get $7) - (local.get $1) - ) - ) - (i32.shl - (local.get $14) - (i32.const 2) - ) - ) - ) - (if - (i32.gt_s - (i32.shr_s - (i32.sub - (local.get $5) - (local.get $8) - ) - (i32.const 2) - ) - (local.get $14) - ) - (local.set $5 - (local.get $6) - ) - ) - (i32.store - (local.get $20) - (local.tee $6 - (i32.add - (i32.load - (local.get $20) - ) - (local.get $13) - ) - ) - ) - (br_if $label$160 - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - ) - (local.set $13 - (local.get $5) - ) - ) - ) - (local.set $13 - (local.get $8) - ) - ) - (local.set $25 - (local.get $7) - ) - (block $label$172 - (if - (i32.lt_u - (local.get $1) - (local.get $13) - ) - (block - (local.set $5 - (i32.mul - (i32.shr_s - (i32.sub - (local.get $25) - (local.get $1) - ) - (i32.const 2) - ) - (i32.const 9) - ) - ) - (br_if $label$172 - (i32.lt_u - (local.tee $6 - (i32.load - (local.get $1) - ) - ) - (i32.const 10) - ) - ) - (local.set $8 - (i32.const 10) - ) - (loop $label$174 - (local.set $5 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$174 - (i32.ge_u - (local.get $6) - (local.tee $8 - (i32.mul - (local.get $8) - (i32.const 10) - ) - ) - ) - ) - ) - ) - (local.set $5 - (i32.const 0) - ) - ) - ) - (local.set $29 - (i32.eq - (local.get $22) - (i32.const 103) - ) - ) - (local.set $35 - (i32.ne - (local.get $18) - (i32.const 0) - ) - ) - (if - (i32.lt_s - (local.tee $8 - (i32.add - (i32.sub - (local.get $18) - (if (result i32) - (i32.ne - (local.get $22) - (i32.const 102) - ) - (local.get $5) - (i32.const 0) - ) - ) - (i32.shr_s - (i32.shl - (i32.and - (local.get $35) - (local.get $29) - ) - (i32.const 31) - ) - (i32.const 31) - ) - ) - ) - (i32.add - (i32.mul - (i32.shr_s - (i32.sub - (local.get $13) - (local.get $25) - ) - (i32.const 2) - ) - (i32.const 9) - ) - (i32.const -9) - ) - ) - (block - (if - (i32.lt_s - (local.tee $8 - (i32.add - (i32.rem_s - (local.tee $14 - (i32.add - (local.get $8) - (i32.const 9216) - ) - ) - (i32.const 9) - ) - (i32.const 1) - ) - ) - (i32.const 9) - ) - (block - (local.set $6 - (i32.const 10) - ) - (loop $label$180 - (local.set $6 - (i32.mul - (local.get $6) - (i32.const 10) - ) - ) - (br_if $label$180 - (i32.ne - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 1) - ) - ) - (i32.const 9) - ) - ) - ) - ) - (local.set $6 - (i32.const 10) - ) - ) - (local.set $14 - (i32.rem_u - (local.tee $22 - (i32.load - (local.tee $8 - (i32.add - (i32.add - (local.get $7) - (i32.const 4) - ) - (i32.shl - (i32.add - (i32.div_s - (local.get $14) - (i32.const 9) - ) - (i32.const -1024) - ) - (i32.const 2) - ) - ) - ) - ) - ) - (local.get $6) - ) - ) - (block $label$182 - (if - (i32.eqz - (i32.and - (local.tee $32 - (i32.eq - (i32.add - (local.get $8) - (i32.const 4) - ) - (local.get $13) - ) - ) - (i32.eqz - (local.get $14) - ) - ) - ) - (block - (local.set $52 - (if (result f64) - (i32.lt_u - (local.get $14) - (local.tee $49 - (i32.div_s - (local.get $6) - (i32.const 2) - ) - ) - ) - (f64.const 0.5) - (if (result f64) - (i32.and - (local.get $32) - (i32.eq - (local.get $14) - (local.get $49) - ) - ) - (f64.const 1) - (f64.const 1.5) - ) - ) - ) - (local.set $53 - (if (result f64) - (i32.and - (i32.div_u - (local.get $22) - (local.get $6) - ) - (i32.const 1) - ) - (f64.const 9007199254740994) - (f64.const 9007199254740992) - ) - ) - (block $label$190 - (if - (local.get $24) - (block - (br_if $label$190 - (i32.ne - (i32.load8_s - (local.get $26) - ) - (i32.const 45) - ) - ) - (local.set $53 - (f64.neg - (local.get $53) - ) - ) - (local.set $52 - (f64.neg - (local.get $52) - ) - ) - ) - ) - ) - (i32.store - (local.get $8) - (local.tee $14 - (i32.sub - (local.get $22) - (local.get $14) - ) - ) - ) - (br_if $label$182 - (f64.eq - (f64.add - (local.get $53) - (local.get $52) - ) - (local.get $53) - ) - ) - (i32.store - (local.get $8) - (local.tee $5 - (i32.add - (local.get $14) - (local.get $6) - ) - ) - ) - (if - (i32.gt_u - (local.get $5) - (i32.const 999999999) - ) - (loop $label$193 - (i32.store - (local.get $8) - (i32.const 0) - ) - (if - (i32.lt_u - (local.tee $8 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - (local.get $1) - ) - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -4) - ) - ) - (i32.const 0) - ) - ) - (i32.store - (local.get $8) - (local.tee $5 - (i32.add - (i32.load - (local.get $8) - ) - (i32.const 1) - ) - ) - ) - (br_if $label$193 - (i32.gt_u - (local.get $5) - (i32.const 999999999) - ) - ) - ) - ) - (local.set $5 - (i32.mul - (i32.shr_s - (i32.sub - (local.get $25) - (local.get $1) - ) - (i32.const 2) - ) - (i32.const 9) - ) - ) - (br_if $label$182 - (i32.lt_u - (local.tee $14 - (i32.load - (local.get $1) - ) - ) - (i32.const 10) - ) - ) - (local.set $6 - (i32.const 10) - ) - (loop $label$195 - (local.set $5 - (i32.add - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$195 - (i32.ge_u - (local.get $14) - (local.tee $6 - (i32.mul - (local.get $6) - (i32.const 10) - ) - ) - ) - ) - ) - ) - ) - ) - (local.set $14 - (local.get $1) - ) - (local.set $6 - (local.get $5) - ) - (if - (i32.le_u - (local.get $13) - (local.tee $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - ) - (local.set $8 - (local.get $13) - ) - ) - ) - (block - (local.set $14 - (local.get $1) - ) - (local.set $6 - (local.get $5) - ) - (local.set $8 - (local.get $13) - ) - ) - ) - (local.set $32 - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (loop $label$198 - (block $label$199 - (if - (i32.le_u - (local.get $8) - (local.get $14) - ) - (block - (local.set $22 - (i32.const 0) - ) - (br $label$199) - ) - ) - (if - (i32.load - (local.tee $1 - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - (local.set $22 - (i32.const 1) - ) - (block - (local.set $8 - (local.get $1) - ) - (br $label$198) - ) - ) - ) - ) - (block $label$203 - (if - (local.get $29) - (block - (local.set $1 - (if (result i32) - (i32.and - (i32.gt_s - (local.tee $1 - (i32.add - (i32.xor - (i32.and - (local.get $35) - (i32.const 1) - ) - (i32.const 1) - ) - (local.get $18) - ) - ) - (local.get $6) - ) - (i32.gt_s - (local.get $6) - (i32.const -5) - ) - ) - (block (result i32) - (local.set $5 - (i32.add - (local.get $9) - (i32.const -1) - ) - ) - (i32.sub - (i32.add - (local.get $1) - (i32.const -1) - ) - (local.get $6) - ) - ) - (block (result i32) - (local.set $5 - (i32.add - (local.get $9) - (i32.const -2) - ) - ) - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - (br_if $label$203 - (local.tee $13 - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - ) - (block $label$207 - (if - (local.get $22) - (block - (if - (i32.eqz - (local.tee $18 - (i32.load - (i32.add - (local.get $8) - (i32.const -4) - ) - ) - ) - ) - (block - (local.set $9 - (i32.const 9) - ) - (br $label$207) - ) - ) - (if - (i32.rem_u - (local.get $18) - (i32.const 10) - ) - (block - (local.set $9 - (i32.const 0) - ) - (br $label$207) - ) - (block - (local.set $13 - (i32.const 10) - ) - (local.set $9 - (i32.const 0) - ) - ) - ) - (loop $label$212 - (local.set $9 - (i32.add - (local.get $9) - (i32.const 1) - ) - ) - (br_if $label$212 - (i32.eqz - (i32.rem_u - (local.get $18) - (local.tee $13 - (i32.mul - (local.get $13) - (i32.const 10) - ) - ) - ) - ) - ) - ) - ) - (local.set $9 - (i32.const 9) - ) - ) - ) - (local.set $18 - (i32.add - (i32.mul - (i32.shr_s - (i32.sub - (local.get $8) - (local.get $25) - ) - (i32.const 2) - ) - (i32.const 9) - ) - (i32.const -9) - ) - ) - (if - (i32.eq - (i32.or - (local.get $5) - (i32.const 32) - ) - (i32.const 102) - ) - (block - (local.set $13 - (i32.const 0) - ) - (if - (i32.ge_s - (local.get $1) - (if (result i32) - (i32.lt_s - (local.tee $9 - (i32.sub - (local.get $18) - (local.get $9) - ) - ) - (i32.const 0) - ) - (local.tee $9 - (i32.const 0) - ) - (local.get $9) - ) - ) - (local.set $1 - (local.get $9) - ) - ) - ) - (block - (local.set $13 - (i32.const 0) - ) - (if - (i32.ge_s - (local.get $1) - (if (result i32) - (i32.lt_s - (local.tee $9 - (i32.sub - (i32.add - (local.get $18) - (local.get $6) - ) - (local.get $9) - ) - ) - (i32.const 0) - ) - (local.tee $9 - (i32.const 0) - ) - (local.get $9) - ) - ) - (local.set $1 - (local.get $9) - ) - ) - ) - ) - ) - (block - (local.set $13 - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - (local.set $1 - (local.get $18) - ) - (local.set $5 - (local.get $9) - ) - ) - ) - ) - (if - (local.tee $25 - (i32.eq - (i32.or - (local.get $5) - (i32.const 32) - ) - (i32.const 102) - ) - ) - (block - (local.set $9 - (i32.const 0) - ) - (if - (i32.le_s - (local.get $6) - (i32.const 0) - ) - (local.set $6 - (i32.const 0) - ) - ) - ) - (block - (if - (i32.lt_s - (i32.sub - (local.get $28) - (local.tee $9 - (call $22 - (i64.extend_i32_s - (if (result i32) - (i32.lt_s - (local.get $6) - (i32.const 0) - ) - (local.get $32) - (local.get $6) - ) - ) - (local.get $33) - ) - ) - ) - (i32.const 2) - ) - (loop $label$229 - (i32.store8 - (local.tee $9 - (i32.add - (local.get $9) - (i32.const -1) - ) - ) - (i32.const 48) - ) - (br_if $label$229 - (i32.lt_s - (i32.sub - (local.get $28) - (local.get $9) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.store8 - (i32.add - (local.get $9) - (i32.const -1) - ) - (i32.add - (i32.and - (i32.shr_s - (local.get $6) - (i32.const 31) - ) - (i32.const 2) - ) - (i32.const 43) - ) - ) - (i32.store8 - (local.tee $6 - (i32.add - (local.get $9) - (i32.const -2) - ) - ) - (local.get $5) - ) - (local.set $9 - (local.get $6) - ) - (local.set $6 - (i32.sub - (local.get $28) - (local.get $6) - ) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $18 - (i32.add - (i32.add - (i32.add - (i32.add - (local.get $24) - (i32.const 1) - ) - (local.get $1) - ) - (i32.ne - (local.tee $29 - (i32.or - (local.get $1) - (local.get $13) - ) - ) - (i32.const 0) - ) - ) - (local.get $6) - ) - ) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $26) - (local.get $24) - (local.get $0) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $18) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (block $label$231 - (if - (local.get $25) - (block - (local.set $6 - (local.tee $9 - (if (result i32) - (i32.gt_u - (local.get $14) - (local.get $7) - ) - (local.get $7) - (local.get $14) - ) - ) - ) - (loop $label$235 - (local.set $5 - (call $22 - (i64.extend_i32_u - (i32.load - (local.get $6) - ) - ) - (local.get $31) - ) - ) - (block $label$236 - (if - (i32.eq - (local.get $6) - (local.get $9) - ) - (block - (br_if $label$236 - (i32.ne - (local.get $5) - (local.get $31) - ) - ) - (i32.store8 - (local.get $34) - (i32.const 48) - ) - (local.set $5 - (local.get $34) - ) - ) - (block - (br_if $label$236 - (i32.le_u - (local.get $5) - (local.get $19) - ) - ) - (drop - (call $35 - (local.get $19) - (i32.const 48) - (i32.sub - (local.get $5) - (local.get $27) - ) - ) - ) - (loop $label$239 - (br_if $label$239 - (i32.gt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $5) - (i32.sub - (local.get $41) - (local.get $5) - ) - (local.get $0) - ) - ) - ) - (if - (i32.le_u - (local.tee $5 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - (local.get $7) - ) - (block - (local.set $6 - (local.get $5) - ) - (br $label$235) - ) - ) - ) - (block $label$242 - (if - (local.get $29) - (block - (br_if $label$242 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (i32.const 1700) - (i32.const 1) - (local.get $0) - ) - ) - ) - ) - ) - (if - (i32.and - (i32.gt_s - (local.get $1) - (i32.const 0) - ) - (i32.lt_u - (local.get $5) - (local.get $8) - ) - ) - (loop $label$245 - (if - (i32.gt_u - (local.tee $7 - (call $22 - (i64.extend_i32_u - (i32.load - (local.get $5) - ) - ) - (local.get $31) - ) - ) - (local.get $19) - ) - (block - (drop - (call $35 - (local.get $19) - (i32.const 48) - (i32.sub - (local.get $7) - (local.get $27) - ) - ) - ) - (loop $label$247 - (br_if $label$247 - (i32.gt_u - (local.tee $7 - (i32.add - (local.get $7) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $7) - (if (result i32) - (i32.gt_s - (local.get $1) - (i32.const 9) - ) - (i32.const 9) - (local.get $1) - ) - (local.get $0) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $1) - (i32.const -9) - ) - ) - (if - (i32.and - (i32.gt_s - (local.get $1) - (i32.const 9) - ) - (i32.lt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - (local.get $8) - ) - ) - (block - (local.set $1 - (local.get $7) - ) - (br $label$245) - ) - (local.set $1 - (local.get $7) - ) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 48) - (i32.add - (local.get $1) - (i32.const 9) - ) - (i32.const 9) - (i32.const 0) - ) - ) - (block - (local.set $5 - (i32.add - (local.get $14) - (i32.const 4) - ) - ) - (if - (i32.eqz - (local.get $22) - ) - (local.set $8 - (local.get $5) - ) - ) - (if - (i32.gt_s - (local.get $1) - (i32.const -1) - ) - (block - (local.set $13 - (i32.eqz - (local.get $13) - ) - ) - (local.set $7 - (local.get $14) - ) - (local.set $5 - (local.get $1) - ) - (loop $label$256 - (if - (i32.eq - (local.tee $1 - (call $22 - (i64.extend_i32_u - (i32.load - (local.get $7) - ) - ) - (local.get $31) - ) - ) - (local.get $31) - ) - (block - (i32.store8 - (local.get $34) - (i32.const 48) - ) - (local.set $1 - (local.get $34) - ) - ) - ) - (block $label$258 - (if - (i32.eq - (local.get $7) - (local.get $14) - ) - (block - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $1) - (i32.const 1) - (local.get $0) - ) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (br_if $label$258 - (i32.and - (local.get $13) - (i32.lt_s - (local.get $5) - (i32.const 1) - ) - ) - ) - (br_if $label$258 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (i32.const 1700) - (i32.const 1) - (local.get $0) - ) - ) - ) - (block - (br_if $label$258 - (i32.le_u - (local.get $1) - (local.get $19) - ) - ) - (drop - (call $35 - (local.get $19) - (i32.const 48) - (i32.add - (local.get $1) - (local.get $43) - ) - ) - ) - (loop $label$262 - (br_if $label$262 - (i32.gt_u - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (local.get $19) - ) - ) - ) - ) - ) - ) - (local.set $6 - (i32.sub - (local.get $41) - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $1) - (if (result i32) - (i32.gt_s - (local.get $5) - (local.get $6) - ) - (local.get $6) - (local.get $5) - ) - (local.get $0) - ) - ) - ) - (br_if $label$256 - (i32.and - (i32.lt_u - (local.tee $7 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (local.get $8) - ) - (i32.gt_s - (local.tee $5 - (i32.sub - (local.get $5) - (local.get $6) - ) - ) - (i32.const -1) - ) - ) - ) - (local.set $1 - (local.get $5) - ) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 48) - (i32.add - (local.get $1) - (i32.const 18) - ) - (i32.const 18) - (i32.const 0) - ) - (br_if $label$231 - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $9) - (i32.sub - (local.get $28) - (local.get $9) - ) - (local.get $0) - ) - ) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $18) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $18) - (local.get $10) - ) - (local.set $10 - (local.get $18) - ) - ) - ) - (block - (call $24 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.tee $8 - (i32.add - (if (result i32) - (local.tee $6 - (i32.or - (f64.ne - (local.get $52) - (local.get $52) - ) - (i32.const 0) - ) - ) - (local.tee $24 - (i32.const 0) - ) - (local.get $24) - ) - (i32.const 3) - ) - ) - (local.get $7) - ) - (if - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 32) - ) - ) - (block - (drop - (call $20 - (local.get $26) - (local.get $24) - (local.get $0) - ) - ) - (local.set $1 - (i32.load - (local.get $0) - ) - ) - ) - ) - (local.set $7 - (if (result i32) - (local.tee $5 - (i32.ne - (i32.and - (local.get $9) - (i32.const 32) - ) - (i32.const 0) - ) - ) - (i32.const 1684) - (i32.const 1688) - ) - ) - (local.set $5 - (if (result i32) - (local.get $5) - (i32.const 1692) - (i32.const 1696) - ) - ) - (if - (i32.eqz - (local.get $6) - ) - (local.set $5 - (local.get $7) - ) - ) - (if - (i32.eqz - (i32.and - (local.get $1) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $5) - (i32.const 3) - (local.get $0) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $8) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.ge_s - (local.get $8) - (local.get $10) - ) - (local.set $10 - (local.get $8) - ) - ) - ) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $7 - (local.get $5) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1648) - ) - (local.set $5 - (local.get $21) - ) - (br $label$70) - ) - (local.set $7 - (i32.and - (local.get $9) - (i32.const 32) - ) - ) - (local.set $7 - (if (result i32) - (i64.eq - (local.tee $50 - (i64.load - (local.get $16) - ) - ) - (i64.const 0) - ) - (block (result i32) - (local.set $50 - (i64.const 0) - ) - (local.get $21) - ) - (block (result i32) - (local.set $1 - (local.get $21) - ) - (loop $label$280 - (i32.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i32.or - (i32.load8_u - (i32.add - (i32.and - (i32.wrap_i64 - (local.get $50) - ) - (i32.const 15) - ) - (i32.const 1632) - ) - ) - (local.get $7) - ) - ) - (br_if $label$280 - (i64.ne - (local.tee $50 - (i64.shr_u - (local.get $50) - (i64.const 4) - ) - ) - (i64.const 0) - ) - ) - ) - (local.set $50 - (i64.load - (local.get $16) - ) - ) - (local.get $1) - ) - ) - ) - (local.set $8 - (i32.add - (i32.shr_s - (local.get $9) - (i32.const 4) - ) - (i32.const 1648) - ) - ) - (if - (local.tee $1 - (i32.or - (i32.eqz - (i32.and - (local.get $12) - (i32.const 8) - ) - ) - (i64.eq - (local.get $50) - (i64.const 0) - ) - ) - ) - (local.set $8 - (i32.const 1648) - ) - ) - (local.set $6 - (if (result i32) - (local.get $1) - (i32.const 0) - (i32.const 2) - ) - ) - (br $label$71) - ) - (local.set $7 - (call $22 - (local.get $50) - (local.get $21) - ) - ) - (br $label$71) - ) - (local.set $14 - (i32.eqz - (local.tee $13 - (call $16 - (local.get $1) - (i32.const 0) - (local.get $5) - ) - ) - ) - ) - (local.set $8 - (i32.sub - (local.get $13) - (local.get $1) - ) - ) - (local.set $9 - (i32.add - (local.get $1) - (local.get $5) - ) - ) - (local.set $12 - (local.get $7) - ) - (local.set $7 - (if (result i32) - (local.get $14) - (local.get $5) - (local.get $8) - ) - ) - (local.set $6 - (i32.const 0) - ) - (local.set $8 - (i32.const 1648) - ) - (local.set $5 - (if (result i32) - (local.get $14) - (local.get $9) - (local.get $13) - ) - ) - (br $label$70) - ) - (local.set $1 - (i32.const 0) - ) - (local.set $5 - (i32.const 0) - ) - (local.set $8 - (local.get $7) - ) - (loop $label$288 - (block $label$289 - (br_if $label$289 - (i32.eqz - (local.tee $9 - (i32.load - (local.get $8) - ) - ) - ) - ) - (br_if $label$289 - (i32.or - (i32.lt_s - (local.tee $5 - (call $25 - (local.get $36) - (local.get $9) - ) - ) - (i32.const 0) - ) - (i32.gt_u - (local.get $5) - (i32.sub - (local.get $6) - (local.get $1) - ) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (br_if $label$288 - (i32.gt_u - (local.get $6) - (local.tee $1 - (i32.add - (local.get $5) - (local.get $1) - ) - ) - ) - ) - ) - ) - (if - (i32.lt_s - (local.get $5) - (i32.const 0) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$5) - ) - ) - (call $24 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $1) - (local.get $12) - ) - (if - (local.get $1) - (block - (local.set $5 - (i32.const 0) - ) - (loop $label$292 - (br_if $label$72 - (i32.eqz - (local.tee $8 - (i32.load - (local.get $7) - ) - ) - ) - ) - (br_if $label$72 - (i32.gt_s - (local.tee $5 - (i32.add - (local.tee $8 - (call $25 - (local.get $36) - (local.get $8) - ) - ) - (local.get $5) - ) - ) - (local.get $1) - ) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $36) - (local.get $8) - (local.get $0) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (br_if $label$292 - (i32.lt_u - (local.get $5) - (local.get $1) - ) - ) - (br $label$72) - ) - ) - (block - (local.set $1 - (i32.const 0) - ) - (br $label$72) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $1) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (if - (i32.le_s - (local.get $10) - (local.get $1) - ) - (local.set $10 - (local.get $1) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - (local.set $1 - (i32.and - (local.get $12) - (i32.const -65537) - ) - ) - (if - (i32.gt_s - (local.get $5) - (i32.const -1) - ) - (local.set $12 - (local.get $1) - ) - ) - (local.set $5 - (if (result i32) - (i32.or - (local.get $5) - (local.tee $9 - (i64.ne - (i64.load - (local.get $16) - ) - (i64.const 0) - ) - ) - ) - (block (result i32) - (local.set $1 - (local.get $7) - ) - (if - (i32.gt_s - (local.get $5) - (local.tee $7 - (i32.add - (i32.xor - (i32.and - (local.get $9) - (i32.const 1) - ) - (i32.const 1) - ) - (i32.sub - (local.get $38) - (local.get $7) - ) - ) - ) - ) - (local.set $7 - (local.get $5) - ) - ) - (local.get $21) - ) - (block (result i32) - (local.set $1 - (local.get $21) - ) - (local.set $7 - (i32.const 0) - ) - (local.get $21) - ) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 32) - (if (result i32) - (i32.lt_s - (local.get $10) - (local.tee $5 - (i32.add - (if (result i32) - (i32.lt_s - (local.get $7) - (local.tee $9 - (i32.sub - (local.get $5) - (local.get $1) - ) - ) - ) - (local.tee $7 - (local.get $9) - ) - (local.get $7) - ) - (local.get $6) - ) - ) - ) - (local.tee $10 - (local.get $5) - ) - (local.get $10) - ) - (local.get $5) - (local.get $12) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $8) - (local.get $6) - (local.get $0) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 48) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 65536) - ) - ) - (call $24 - (local.get $0) - (i32.const 48) - (local.get $7) - (local.get $9) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (i32.load - (local.get $0) - ) - (i32.const 32) - ) - ) - (drop - (call $20 - (local.get $1) - (local.get $9) - (local.get $0) - ) - ) - ) - (call $24 - (local.get $0) - (i32.const 32) - (local.get $10) - (local.get $5) - (i32.xor - (local.get $12) - (i32.const 8192) - ) - ) - (local.set $1 - (local.get $11) - ) - (br $label$4) - ) - ) - (br $label$2) - ) - (if - (i32.eqz - (local.get $0) - ) - (if - (local.get $17) - (block - (local.set $0 - (i32.const 1) - ) - (loop $label$308 - (if - (local.tee $1 - (i32.load - (i32.add - (local.get $4) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - ) - ) - (block - (call $21 - (i32.add - (local.get $3) - (i32.shl - (local.get $0) - (i32.const 3) - ) - ) - (local.get $1) - (local.get $2) - ) - (br_if $label$308 - (i32.lt_s - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.const 10) - ) - ) - (local.set $15 - (i32.const 1) - ) - (br $label$2) - ) - ) - ) - (loop $label$310 - (if - (i32.load - (i32.add - (local.get $4) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - ) - (block - (local.set $15 - (i32.const -1) - ) - (br $label$2) - ) - ) - (br_if $label$310 - (i32.lt_s - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.const 10) - ) - ) - (local.set $15 - (i32.const 1) - ) - ) - ) - (local.set $15 - (i32.const 0) - ) - ) - ) - ) - (global.set $global$1 - (local.get $23) - ) - (local.get $15) + (func $14 (;27;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $8 + global.get $global$1 + i32.const 48 + i32.add + global.set $global$1 + local.get $8 + i32.const 16 + i32.add + local.set $9 + local.get $8 + local.set $10 + local.get $8 + i32.const 32 + i32.add + local.tee $3 + local.get $0 + i32.const 28 + i32.add + local.tee $6 + i32.load + local.tee $4 + i32.store + local.get $3 + local.get $0 + i32.const 20 + i32.add + local.tee $11 + i32.load + local.get $4 + i32.sub + local.tee $5 + i32.store offset=4 + local.get $3 + local.get $1 + i32.store offset=8 + local.get $3 + local.get $2 + i32.store offset=12 + local.get $0 + i32.const 60 + i32.add + local.set $13 + local.get $0 + i32.const 44 + i32.add + local.set $14 + local.get $3 + local.set $1 + i32.const 2 + local.set $4 + local.get $5 + local.get $2 + i32.add + local.set $12 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + loop $label$5 ;; label = @5 + i32.const 3596 + i32.load + if ;; label = @6 + block ;; label = @7 + i32.const 1 + local.get $0 + call $fimport$9 + local.get $10 + local.get $13 + i32.load + i32.store + local.get $10 + local.get $1 + i32.store offset=4 + local.get $10 + local.get $4 + i32.store offset=8 + i32.const 146 + local.get $10 + call $fimport$15 + call $10 + local.set $3 + i32.const 0 + call $fimport$7 + end + else + block ;; label = @7 + local.get $9 + local.get $13 + i32.load + i32.store + local.get $9 + local.get $1 + i32.store offset=4 + local.get $9 + local.get $4 + i32.store offset=8 + i32.const 146 + local.get $9 + call $fimport$15 + call $10 + local.set $3 + end + end + local.get $12 + local.get $3 + i32.eq + br_if 1 (;@4;) + local.get $3 + i32.const 0 + i32.lt_s + br_if 2 (;@3;) + local.get $3 + local.get $1 + i32.load offset=4 + local.tee $5 + i32.gt_u + if (result i32) ;; label = @6 + block (result i32) ;; label = @7 + local.get $6 + local.get $14 + i32.load + local.tee $7 + i32.store + local.get $11 + local.get $7 + i32.store + local.get $1 + i32.load offset=12 + local.set $7 + local.get $1 + i32.const 8 + i32.add + local.set $1 + local.get $4 + i32.const -1 + i32.add + local.set $4 + local.get $3 + local.get $5 + i32.sub + end + else + local.get $4 + i32.const 2 + i32.eq + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + local.get $6 + local.get $6 + i32.load + local.get $3 + i32.add + i32.store + local.get $5 + local.set $7 + i32.const 2 + local.set $4 + local.get $3 + end + else + block (result i32) ;; label = @8 + local.get $5 + local.set $7 + local.get $3 + end + end + end + local.set $5 + local.get $1 + local.get $1 + i32.load + local.get $5 + i32.add + i32.store + local.get $1 + local.get $7 + local.get $5 + i32.sub + i32.store offset=4 + local.get $12 + local.get $3 + i32.sub + local.set $12 + br 0 (;@5;) + end + end + local.get $0 + local.get $14 + i32.load + local.tee $1 + local.get $0 + i32.load offset=48 + i32.add + i32.store offset=16 + local.get $6 + local.get $1 + i32.store + local.get $11 + local.get $1 + i32.store + br 1 (;@2;) + end + local.get $0 + i32.const 0 + i32.store offset=16 + local.get $6 + i32.const 0 + i32.store + local.get $11 + i32.const 0 + i32.store + local.get $0 + local.get $0 + i32.load + i32.const 32 + i32.or + i32.store + local.get $4 + i32.const 2 + i32.eq + if (result i32) ;; label = @3 + i32.const 0 + else + local.get $2 + local.get $1 + i32.load offset=4 + i32.sub + end + local.set $2 + end + local.get $8 + global.set $global$1 + local.get $2 + end ) - ) - (func $19 (; 32 ;) (type $1) (param $0 i32) (result i32) - (i32.const 0) - ) - (func $20 (; 33 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (block $label$1 (result i32) - (block $label$2 - (block $label$3 - (br_if $label$3 - (local.tee $3 - (i32.load - (local.tee $4 - (i32.add - (local.get $2) - (i32.const 16) - ) - ) - ) - ) - ) - (if - (call $29 - (local.get $2) - ) - (local.set $3 - (i32.const 0) - ) - (block - (local.set $3 - (i32.load - (local.get $4) - ) - ) - (br $label$3) - ) - ) - (br $label$2) - ) - (if - (i32.lt_u - (i32.sub - (local.get $3) - (local.tee $4 - (i32.load - (local.tee $5 - (i32.add - (local.get $2) - (i32.const 20) - ) - ) - ) - ) - ) - (local.get $1) - ) - (block - (local.set $3 - (call_indirect (type $0) - (local.get $2) - (local.get $0) - (local.get $1) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $2) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - ) - (br $label$2) - ) - ) - (local.set $2 - (block $label$7 (result i32) - (if (result i32) - (i32.gt_s - (i32.load8_s offset=75 - (local.get $2) - ) - (i32.const -1) - ) - (block (result i32) - (local.set $3 - (local.get $1) - ) - (loop $label$9 - (drop - (br_if $label$7 - (i32.const 0) - (i32.eqz - (local.get $3) - ) - ) - ) - (if - (i32.ne - (i32.load8_s - (i32.add - (local.get $0) - (local.tee $6 - (i32.add - (local.get $3) - (i32.const -1) - ) - ) - ) - ) - (i32.const 10) - ) - (block - (local.set $3 - (local.get $6) - ) - (br $label$9) - ) - ) - ) - (br_if $label$2 - (i32.lt_u - (call_indirect (type $0) - (local.get $2) - (local.get $0) - (local.get $3) - (i32.add - (i32.and - (i32.load offset=36 - (local.get $2) - ) - (i32.const 3) - ) - (i32.const 2) - ) - ) - (local.get $3) - ) - ) - (local.set $4 - (i32.load - (local.get $5) - ) - ) - (local.set $1 - (i32.sub - (local.get $1) - (local.get $3) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (local.get $3) - ) - ) - (local.get $3) - ) - (i32.const 0) - ) - ) - ) - (drop - (call $36 - (local.get $4) - (local.get $0) - (local.get $1) - ) - ) - (i32.store - (local.get $5) - (i32.add - (i32.load - (local.get $5) - ) - (local.get $1) - ) - ) - (local.set $3 - (i32.add - (local.get $2) - (local.get $1) - ) - ) - ) - (local.get $3) + (func $15 (;28;) (type $2) (param $0 i32) + local.get $0 + i32.load offset=68 + i32.eqz + if ;; label = @1 + local.get $0 + call $12 + end ) - ) - (func $21 (; 34 ;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i64) - (local $5 f64) - (block $label$1 - (if - (i32.le_u - (local.get $1) - (i32.const 20) - ) - (block $label$3 - (block $label$4 - (block $label$5 - (block $label$6 - (block $label$7 - (block $label$8 - (block $label$9 - (block $label$10 - (block $label$11 - (block $label$12 - (block $label$13 - (br_table $label$13 $label$12 $label$11 $label$10 $label$9 $label$8 $label$7 $label$6 $label$5 $label$4 $label$3 - (i32.sub - (local.get $1) - (i32.const 9) - ) - ) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i32.store - (local.get $0) - (local.get $3) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (local.get $3) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (local.get $3) - ) - ) - (br $label$1) - ) - (local.set $4 - (i64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (i64.store - (local.get $0) - (local.get $4) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (i32.shr_s - (i32.shl - (i32.and - (local.get $3) - (i32.const 65535) - ) - (i32.const 16) - ) - (i32.const 16) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (i32.and - (local.get $3) - (i32.const 65535) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_s - (i32.shr_s - (i32.shl - (i32.and - (local.get $3) - (i32.const 255) - ) - (i32.const 24) - ) - (i32.const 24) - ) - ) - ) - (br $label$1) - ) - (local.set $3 - (i32.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 3) - ) - (i32.const -4) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i64.store - (local.get $0) - (i64.extend_i32_u - (i32.and - (local.get $3) - (i32.const 255) - ) - ) - ) - (br $label$1) - ) - (local.set $5 - (f64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (f64.store - (local.get $0) - (local.get $5) - ) - (br $label$1) - ) - (local.set $5 - (f64.load - (local.tee $1 - (i32.and - (i32.add - (i32.load - (local.get $2) - ) - (i32.const 7) - ) - (i32.const -8) - ) - ) - ) - ) - (i32.store - (local.get $2) - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - (f64.store - (local.get $0) - (local.get $5) - ) - ) - ) + (func $16 (;29;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + local.get $1 + i32.const 255 + i32.and + local.set $5 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + local.get $2 + i32.const 0 + i32.ne + local.tee $4 + local.get $0 + i32.const 3 + i32.and + i32.const 0 + i32.ne + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $1 + i32.const 255 + i32.and + local.set $4 + local.get $2 + local.set $3 + local.get $0 + local.set $2 + loop $label$6 ;; label = @7 + local.get $2 + i32.load8_s + local.get $4 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $3 + local.set $0 + br 6 (;@3;) + end + end + local.get $3 + i32.const -1 + i32.add + local.tee $3 + i32.const 0 + i32.ne + local.tee $0 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 3 + i32.and + i32.const 0 + i32.ne + i32.and + br_if 0 (;@7;) + br 3 (;@4;) + end + end + else + block ;; label = @6 + local.get $2 + local.set $3 + local.get $0 + local.set $2 + local.get $4 + local.set $0 + end + end + end + local.get $0 + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.set $0 + br 2 (;@3;) + end + else + i32.const 0 + local.set $0 + end + br 1 (;@2;) + end + local.get $2 + i32.load8_s + local.get $1 + i32.const 255 + i32.and + local.tee $1 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.ne + if ;; label = @3 + block ;; label = @4 + local.get $5 + i32.const 16843009 + i32.mul + local.set $3 + block $label$12 ;; label = @5 + block $label$13 ;; label = @6 + local.get $0 + i32.const 3 + i32.le_u + br_if 0 (;@6;) + loop $label$14 ;; label = @7 + local.get $2 + i32.load + local.get $3 + i32.xor + local.tee $4 + i32.const -2139062144 + i32.and + i32.const -2139062144 + i32.xor + local.get $4 + i32.const -16843009 + i32.add + i32.and + i32.eqz + if ;; label = @8 + block ;; label = @9 + local.get $2 + i32.const 4 + i32.add + local.set $2 + local.get $0 + i32.const -4 + i32.add + local.tee $0 + i32.const 3 + i32.gt_u + br_if 2 (;@7;) + br 3 (;@6;) + end + end + end + br 1 (;@5;) + end + local.get $0 + i32.eqz + if ;; label = @6 + block ;; label = @7 + i32.const 0 + local.set $0 + br 5 (;@2;) + end + end + end + loop $label$17 ;; label = @5 + local.get $2 + i32.load8_s + local.get $1 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eq + br_if 3 (;@2;) + local.get $2 + i32.const 1 + i32.add + local.set $2 + local.get $0 + i32.const -1 + i32.add + local.tee $0 + br_if 0 (;@5;) + i32.const 0 + local.set $0 + end + end + end + end + local.get $0 + if (result i32) ;; label = @2 + local.get $2 + else + i32.const 0 + end + end ) - ) - (func $22 (; 35 ;) (type $9) (param $0 i64) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i64) - (block $label$1 (result i32) - (local.set $2 - (i32.wrap_i64 - (local.get $0) - ) - ) - (if - (i64.gt_u - (local.get $0) - (i64.const 4294967295) - ) - (block - (loop $label$3 - (i64.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i64.or - (i64.rem_u - (local.get $0) - (i64.const 10) - ) - (i64.const 48) - ) - ) - (local.set $4 - (i64.div_u - (local.get $0) - (i64.const 10) - ) - ) - (if - (i64.gt_u - (local.get $0) - (i64.const 42949672959) - ) - (block - (local.set $0 - (local.get $4) - ) - (br $label$3) - ) - ) - ) - (local.set $2 - (i32.wrap_i64 - (local.get $4) - ) - ) - ) - ) - (if - (local.get $2) - (loop $label$6 - (i32.store8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - (i32.or - (i32.rem_u - (local.get $2) - (i32.const 10) - ) - (i32.const 48) - ) - ) - (local.set $3 - (i32.div_u - (local.get $2) - (i32.const 10) - ) - ) - (if - (i32.ge_u - (local.get $2) - (i32.const 10) - ) - (block - (local.set $2 - (local.get $3) - ) - (br $label$6) - ) - ) - ) - ) - (local.get $1) + (func $17 (;30;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $4 + global.get $global$1 + i32.const 224 + i32.add + global.set $global$1 + local.get $4 + i32.const 136 + i32.add + local.set $5 + local.get $4 + i32.const 80 + i32.add + local.tee $3 + i64.const 0 + i64.store align=4 + local.get $3 + i64.const 0 + i64.store offset=8 align=4 + local.get $3 + i64.const 0 + i64.store offset=16 align=4 + local.get $3 + i64.const 0 + i64.store offset=24 align=4 + local.get $3 + i64.const 0 + i64.store offset=32 align=4 + local.get $4 + i32.const 120 + i32.add + local.tee $6 + local.get $2 + i32.load + i32.store + i32.const 0 + local.get $1 + local.get $6 + local.get $4 + local.tee $2 + local.get $3 + call $18 + i32.const 0 + i32.lt_s + if ;; label = @2 + i32.const -1 + local.set $1 + else + block ;; label = @3 + local.get $0 + i32.load offset=76 + i32.const -1 + i32.gt_s + if (result i32) ;; label = @4 + local.get $0 + call $19 + else + i32.const 0 + end + local.set $12 + local.get $0 + i32.load + local.set $7 + local.get $0 + i32.load8_s offset=74 + i32.const 1 + i32.lt_s + if ;; label = @4 + local.get $0 + local.get $7 + i32.const -33 + i32.and + i32.store + end + local.get $0 + i32.const 48 + i32.add + local.tee $8 + i32.load + if ;; label = @4 + local.get $0 + local.get $1 + local.get $6 + local.get $2 + local.get $3 + call $18 + local.set $1 + else + block ;; label = @5 + local.get $0 + i32.const 44 + i32.add + local.tee $9 + i32.load + local.set $10 + local.get $9 + local.get $5 + i32.store + local.get $0 + i32.const 28 + i32.add + local.tee $13 + local.get $5 + i32.store + local.get $0 + i32.const 20 + i32.add + local.tee $11 + local.get $5 + i32.store + local.get $8 + i32.const 80 + i32.store + local.get $0 + i32.const 16 + i32.add + local.tee $14 + local.get $5 + i32.const 80 + i32.add + i32.store + local.get $0 + local.get $1 + local.get $6 + local.get $2 + local.get $3 + call $18 + local.set $1 + local.get $10 + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 0 + i32.const 0 + local.get $0 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + drop + local.get $11 + i32.load + i32.eqz + if ;; label = @8 + i32.const -1 + local.set $1 + end + local.get $9 + local.get $10 + i32.store + local.get $8 + i32.const 0 + i32.store + local.get $14 + i32.const 0 + i32.store + local.get $13 + i32.const 0 + i32.store + local.get $11 + i32.const 0 + i32.store + end + end + end + end + local.get $0 + local.get $0 + i32.load + local.tee $2 + local.get $7 + i32.const 32 + i32.and + i32.or + i32.store + local.get $12 + if ;; label = @4 + local.get $0 + call $12 + end + local.get $2 + i32.const 32 + i32.and + if ;; label = @4 + i32.const -1 + local.set $1 + end + end + end + local.get $4 + global.set $global$1 + local.get $1 + end ) - ) - (func $23 (; 36 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.const 0) - ) - (block $label$2 - (block $label$3 - (block $label$4 - (loop $label$5 - (br_if $label$4 - (i32.eq - (i32.load8_u - (i32.add - (local.get $1) - (i32.const 1702) - ) - ) - (local.get $0) - ) - ) - (br_if $label$5 - (i32.ne - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (i32.const 87) - ) - ) - (local.set $1 - (i32.const 87) - ) - (local.set $0 - (i32.const 1790) - ) - (br $label$3) - ) - ) - (if - (local.get $1) - (block - (local.set $0 - (i32.const 1790) - ) - (br $label$3) - ) - (local.set $0 - (i32.const 1790) - ) - ) - (br $label$2) - ) - (loop $label$8 - (local.set $2 - (local.get $0) - ) - (loop $label$9 - (local.set $0 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (if - (i32.load8_s - (local.get $2) - ) - (block - (local.set $2 - (local.get $0) - ) - (br $label$9) - ) - ) - ) - (br_if $label$8 - (local.tee $1 - (i32.add - (local.get $1) - (i32.const -1) - ) - ) - ) - ) - ) - (local.get $0) + (func $18 (;31;) (type $7) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32) + (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) (local $22 i32) (local $23 i32) (local $24 i32) (local $25 i32) (local $26 i32) (local $27 i32) (local $28 i32) (local $29 i32) (local $30 i32) (local $31 i32) (local $32 i32) (local $33 i32) (local $34 i32) (local $35 i32) (local $36 i32) (local $37 i32) (local $38 i32) (local $39 i32) (local $40 i32) (local $41 i32) (local $42 i32) (local $43 i32) (local $44 i32) (local $45 i32) (local $46 i32) (local $47 i32) (local $48 i32) (local $49 i32) (local $50 i64) (local $51 i64) (local $52 f64) (local $53 f64) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $23 + global.get $global$1 + i32.const 624 + i32.add + global.set $global$1 + local.get $23 + i32.const 16 + i32.add + local.set $20 + local.get $23 + local.set $16 + local.get $23 + i32.const 528 + i32.add + local.set $36 + local.get $0 + i32.const 0 + i32.ne + local.set $30 + local.get $23 + i32.const 536 + i32.add + local.tee $17 + i32.const 40 + i32.add + local.tee $21 + local.set $38 + local.get $17 + i32.const 39 + i32.add + local.set $39 + local.get $23 + i32.const 8 + i32.add + local.tee $37 + i32.const 4 + i32.add + local.set $42 + i32.const 0 + local.get $23 + i32.const 588 + i32.add + local.tee $19 + local.tee $27 + i32.sub + local.set $43 + local.get $23 + i32.const 576 + i32.add + local.tee $17 + i32.const 12 + i32.add + local.set $33 + local.get $17 + i32.const 11 + i32.add + local.set $40 + local.get $33 + local.tee $28 + local.get $27 + i32.sub + local.set $44 + i32.const -2 + local.get $27 + i32.sub + local.set $45 + local.get $28 + i32.const 2 + i32.add + local.set $46 + local.get $23 + i32.const 24 + i32.add + local.tee $47 + i32.const 288 + i32.add + local.set $48 + local.get $19 + i32.const 9 + i32.add + local.tee $31 + local.set $41 + local.get $19 + i32.const 8 + i32.add + local.set $34 + i32.const 0 + local.set $15 + i32.const 0 + local.set $10 + i32.const 0 + local.set $17 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + loop $label$4 ;; label = @4 + block $label$5 ;; label = @5 + local.get $15 + i32.const -1 + i32.gt_s + if ;; label = @6 + local.get $10 + i32.const 2147483647 + local.get $15 + i32.sub + i32.gt_s + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + call $11 + i32.const 75 + i32.store + i32.const -1 + end + else + local.get $10 + local.get $15 + i32.add + end + local.set $15 + end + local.get $1 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eqz + br_if 2 (;@3;) + local.get $1 + local.set $11 + block $label$9 ;; label = @6 + block $label$10 ;; label = @7 + loop $label$11 ;; label = @8 + block $label$12 ;; label = @9 + block $label$13 ;; label = @10 + block $label$14 ;; label = @11 + block $label$15 ;; label = @12 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 0 + i32.sub + br_table 1 (;@11;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 2 (;@10;) 0 (;@12;) 2 (;@10;) + end + local.get $11 + local.set $5 + br 4 (;@7;) + end + local.get $11 + local.set $5 + br 1 (;@9;) + end + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.set $5 + br 1 (;@8;) + end + end + br 1 (;@6;) + end + loop $label$16 ;; label = @7 + local.get $5 + i32.load8_s offset=1 + i32.const 37 + i32.ne + br_if 1 (;@6;) + local.get $11 + i32.const 1 + i32.add + local.set $11 + local.get $5 + i32.const 2 + i32.add + local.tee $5 + i32.load8_s + i32.const 37 + i32.eq + br_if 0 (;@7;) + end + end + local.get $11 + local.get $1 + i32.sub + local.set $10 + local.get $30 + if ;; label = @6 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @7 + local.get $1 + local.get $10 + local.get $0 + call $20 + drop + end + end + local.get $10 + if ;; label = @6 + block ;; label = @7 + local.get $5 + local.set $1 + br 3 (;@4;) + end + end + local.get $5 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $10 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $9 + i32.const 10 + i32.lt_u + if (result i32) ;; label = @6 + block (result i32) ;; label = @7 + local.get $5 + i32.const 3 + i32.add + local.set $10 + local.get $5 + i32.load8_s offset=2 + i32.const 36 + i32.eq + local.tee $12 + if ;; label = @8 + local.get $10 + local.set $11 + end + local.get $12 + if ;; label = @8 + i32.const 1 + local.set $17 + end + local.get $11 + i32.load8_s + local.set $5 + local.get $12 + i32.eqz + if ;; label = @8 + i32.const -1 + local.set $9 + end + local.get $17 + end + else + block (result i32) ;; label = @7 + local.get $10 + local.set $5 + i32.const -1 + local.set $9 + local.get $17 + end + end + local.set $10 + block $label$25 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + local.tee $12 + i32.const 32 + i32.lt_u + if ;; label = @7 + block ;; label = @8 + i32.const 0 + local.set $17 + loop $label$27 ;; label = @9 + i32.const 1 + local.get $12 + i32.shl + i32.const 75913 + i32.and + i32.eqz + br_if 3 (;@6;) + i32.const 1 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + i32.shl + local.get $17 + i32.or + local.set $17 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -32 + i32.add + local.tee $12 + i32.const 32 + i32.lt_u + br_if 0 (;@9;) + end + end + else + i32.const 0 + local.set $17 + end + end + block $label$29 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 42 + i32.eq + if ;; label = @7 + block ;; label = @8 + block $label$31 (result i32) ;; label = @9 + block $label$32 ;; label = @10 + local.get $11 + i32.const 1 + i32.add + local.tee $7 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $12 + i32.const 10 + i32.ge_u + br_if 0 (;@10;) + local.get $11 + i32.load8_s offset=2 + i32.const 36 + i32.ne + br_if 0 (;@10;) + local.get $4 + local.get $12 + i32.const 2 + i32.shl + i32.add + i32.const 10 + i32.store + i32.const 1 + local.set $8 + local.get $3 + local.get $7 + i32.load8_s + i32.const -48 + i32.add + i32.const 3 + i32.shl + i32.add + i64.load + i32.wrap_i64 + local.set $10 + local.get $11 + i32.const 3 + i32.add + br 1 (;@9;) + end + local.get $10 + if ;; label = @10 + block ;; label = @11 + i32.const -1 + local.set $15 + br 6 (;@5;) + end + end + local.get $30 + i32.eqz + if ;; label = @10 + block ;; label = @11 + local.get $17 + local.set $12 + i32.const 0 + local.set $17 + local.get $7 + local.set $11 + i32.const 0 + local.set $10 + br 5 (;@6;) + end + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $11 + i32.load + local.set $10 + local.get $2 + local.get $11 + i32.const 4 + i32.add + i32.store + i32.const 0 + local.set $8 + local.get $7 + end + local.set $11 + local.get $17 + i32.const 8192 + i32.or + local.set $12 + i32.const 0 + local.get $10 + i32.sub + local.set $7 + local.get $11 + i32.load8_s + local.set $5 + local.get $10 + i32.const 0 + i32.lt_s + local.tee $6 + i32.eqz + if ;; label = @9 + local.get $17 + local.set $12 + end + local.get $8 + local.set $17 + local.get $6 + if ;; label = @9 + local.get $7 + local.set $10 + end + end + else + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $12 + i32.const 10 + i32.lt_u + if ;; label = @8 + block ;; label = @9 + i32.const 0 + local.set $7 + local.get $12 + local.set $5 + loop $label$39 ;; label = @10 + local.get $7 + i32.const 10 + i32.mul + local.get $5 + i32.add + local.set $7 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + local.tee $12 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + br_if 0 (;@10;) + end + local.get $7 + i32.const 0 + i32.lt_s + if ;; label = @10 + block ;; label = @11 + i32.const -1 + local.set $15 + br 6 (;@5;) + end + else + block ;; label = @11 + local.get $12 + local.set $5 + local.get $17 + local.set $12 + local.get $10 + local.set $17 + local.get $7 + local.set $10 + end + end + end + else + block ;; label = @9 + local.get $17 + local.set $12 + local.get $10 + local.set $17 + i32.const 0 + local.set $10 + end + end + end + end + block $label$43 ;; label = @6 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 46 + i32.eq + if ;; label = @7 + block ;; label = @8 + local.get $11 + i32.const 1 + i32.add + local.tee $7 + i32.load8_s + local.tee $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 42 + i32.ne + if ;; label = @9 + block ;; label = @10 + local.get $5 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + if ;; label = @11 + block ;; label = @12 + local.get $7 + local.set $11 + i32.const 0 + local.set $7 + end + else + block ;; label = @12 + i32.const 0 + local.set $5 + local.get $7 + local.set $11 + br 6 (;@6;) + end + end + loop $label$48 ;; label = @11 + local.get $7 + i32.const 10 + i32.mul + local.get $5 + i32.add + local.set $5 + local.get $11 + i32.const 1 + i32.add + local.tee $11 + i32.load8_s + i32.const -48 + i32.add + local.tee $8 + i32.const 10 + i32.ge_u + br_if 5 (;@6;) + local.get $5 + local.set $7 + local.get $8 + local.set $5 + br 0 (;@11;) + end + end + end + local.get $11 + i32.const 2 + i32.add + local.tee $7 + i32.load8_s + i32.const -48 + i32.add + local.tee $5 + i32.const 10 + i32.lt_u + if ;; label = @9 + local.get $11 + i32.load8_s offset=3 + i32.const 36 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.get $5 + i32.const 2 + i32.shl + i32.add + i32.const 10 + i32.store + local.get $3 + local.get $7 + i32.load8_s + i32.const -48 + i32.add + i32.const 3 + i32.shl + i32.add + i64.load + i32.wrap_i64 + local.set $5 + local.get $11 + i32.const 4 + i32.add + local.set $11 + br 5 (;@6;) + end + end + end + local.get $17 + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + end + local.get $30 + if (result i32) ;; label = @9 + block (result i32) ;; label = @10 + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $11 + i32.load + local.set $5 + local.get $2 + local.get $11 + i32.const 4 + i32.add + i32.store + local.get $7 + end + else + block (result i32) ;; label = @10 + i32.const 0 + local.set $5 + local.get $7 + end + end + local.set $11 + end + else + i32.const -1 + local.set $5 + end + end + local.get $11 + local.set $7 + i32.const 0 + local.set $8 + loop $label$55 ;; label = @6 + local.get $7 + i32.load8_s + i32.const -65 + i32.add + local.tee $6 + i32.const 57 + i32.gt_u + if ;; label = @7 + block ;; label = @8 + i32.const -1 + local.set $15 + br 3 (;@5;) + end + end + local.get $7 + i32.const 1 + i32.add + local.set $11 + local.get $8 + i32.const 58 + i32.mul + i32.const 1168 + i32.add + local.get $6 + i32.add + i32.load8_s + local.tee $13 + i32.const 255 + i32.and + local.tee $6 + i32.const -1 + i32.add + i32.const 8 + i32.lt_u + if ;; label = @7 + block ;; label = @8 + local.get $11 + local.set $7 + local.get $6 + local.set $8 + br 2 (;@6;) + end + end + end + local.get $13 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.eqz + if ;; label = @6 + block ;; label = @7 + i32.const -1 + local.set $15 + br 2 (;@5;) + end + end + local.get $9 + i32.const -1 + i32.gt_s + local.set $14 + block $label$59 ;; label = @6 + block $label$60 ;; label = @7 + local.get $13 + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 19 + i32.eq + if ;; label = @8 + local.get $14 + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + else + br 2 (;@7;) + end + else + block ;; label = @9 + local.get $14 + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.get $9 + i32.const 2 + i32.shl + i32.add + local.get $6 + i32.store + local.get $16 + local.get $3 + local.get $9 + i32.const 3 + i32.shl + i32.add + i64.load + i64.store + br 4 (;@7;) + end + end + local.get $30 + i32.eqz + if ;; label = @10 + block ;; label = @11 + i32.const 0 + local.set $15 + br 6 (;@5;) + end + end + local.get $16 + local.get $6 + local.get $2 + call $21 + end + end + br 1 (;@6;) + end + local.get $30 + i32.eqz + if ;; label = @7 + block ;; label = @8 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 4 (;@4;) + end + end + end + local.get $7 + i32.load8_s + local.tee $7 + i32.const -33 + i32.and + local.set $9 + local.get $8 + i32.const 0 + i32.ne + local.get $7 + i32.const 15 + i32.and + i32.const 3 + i32.eq + i32.and + i32.eqz + if ;; label = @6 + local.get $7 + local.set $9 + end + local.get $12 + i32.const -65537 + i32.and + local.set $7 + local.get $12 + i32.const 8192 + i32.and + if ;; label = @6 + local.get $7 + local.set $12 + end + block $label$70 ;; label = @6 + block $label$71 ;; label = @7 + block $label$72 ;; label = @8 + block $label$73 ;; label = @9 + block $label$74 ;; label = @10 + block $label$75 ;; label = @11 + block $label$76 ;; label = @12 + block $label$77 ;; label = @13 + block $label$78 ;; label = @14 + block $label$79 ;; label = @15 + block $label$80 ;; label = @16 + block $label$81 ;; label = @17 + block $label$82 ;; label = @18 + block $label$83 ;; label = @19 + block $label$84 ;; label = @20 + block $label$85 ;; label = @21 + block $label$86 ;; label = @22 + block $label$87 ;; label = @23 + block $label$88 ;; label = @24 + block $label$89 ;; label = @25 + local.get $9 + i32.const 65 + i32.sub + br_table 11 (;@14;) 12 (;@13;) 9 (;@16;) 12 (;@13;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 10 (;@15;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 11 (;@14;) 12 (;@13;) 6 (;@19;) 4 (;@21;) 11 (;@14;) 11 (;@14;) 11 (;@14;) 12 (;@13;) 4 (;@21;) 12 (;@13;) 12 (;@13;) 12 (;@13;) 7 (;@18;) 0 (;@25;) 3 (;@22;) 1 (;@24;) 12 (;@13;) 12 (;@13;) 8 (;@17;) 12 (;@13;) 5 (;@20;) 12 (;@13;) 12 (;@13;) 2 (;@23;) 12 (;@13;) + end + block $label$90 ;; label = @25 + block $label$91 ;; label = @26 + block $label$92 ;; label = @27 + block $label$93 ;; label = @28 + block $label$94 ;; label = @29 + block $label$95 ;; label = @30 + block $label$96 ;; label = @31 + block $label$97 ;; label = @32 + local.get $8 + i32.const 255 + i32.and + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i32.const 0 + i32.sub + br_table 0 (;@32;) 1 (;@31;) 2 (;@30;) 3 (;@29;) 4 (;@28;) 7 (;@25;) 5 (;@27;) 6 (;@26;) 7 (;@25;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 27 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 26 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i64.extend_i32_s + i64.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 25 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store16 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 24 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store8 + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 23 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i32.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 22 (;@4;) + end + local.get $16 + i32.load + local.get $15 + i64.extend_i32_s + i64.store + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 21 (;@4;) + end + i32.const 0 + local.set $10 + local.get $11 + local.set $1 + br 20 (;@4;) + end + local.get $12 + i32.const 8 + i32.or + local.set $12 + local.get $5 + i32.const 8 + i32.le_u + if ;; label = @24 + i32.const 8 + local.set $5 + end + i32.const 120 + local.set $9 + br 11 (;@12;) + end + br 10 (;@12;) + end + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.eq + if ;; label = @22 + local.get $21 + local.set $7 + else + block ;; label = @23 + local.get $21 + local.set $1 + loop $label$101 ;; label = @24 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $50 + i64.const 7 + i64.and + i64.const 48 + i64.or + i64.store8 + local.get $50 + i64.const 3 + i64.shr_u + local.tee $50 + i64.const 0 + i64.ne + br_if 0 (;@24;) + local.get $1 + local.set $7 + end + end + end + local.get $12 + i32.const 8 + i32.and + if ;; label = @22 + block ;; label = @23 + local.get $38 + local.get $7 + i32.sub + local.tee $1 + i32.const 1 + i32.add + local.set $8 + local.get $5 + local.get $1 + i32.le_s + if ;; label = @24 + local.get $8 + local.set $5 + end + i32.const 0 + local.set $6 + i32.const 1648 + local.set $8 + br 16 (;@7;) + end + else + block ;; label = @23 + i32.const 0 + local.set $6 + i32.const 1648 + local.set $8 + br 16 (;@7;) + end + end + end + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.lt_s + if ;; label = @21 + block ;; label = @22 + local.get $16 + i64.const 0 + local.get $50 + i64.sub + local.tee $50 + i64.store + i32.const 1 + local.set $6 + i32.const 1648 + local.set $8 + br 11 (;@11;) + end + end + local.get $12 + i32.const 2048 + i32.and + if ;; label = @21 + block ;; label = @22 + i32.const 1 + local.set $6 + i32.const 1649 + local.set $8 + br 11 (;@11;) + end + else + block ;; label = @22 + local.get $12 + i32.const 1 + i32.and + local.tee $1 + local.set $6 + local.get $1 + if (result i32) ;; label = @23 + i32.const 1650 + else + i32.const 1648 + end + local.set $8 + br 11 (;@11;) + end + end + end + local.get $16 + i64.load + local.set $50 + i32.const 0 + local.set $6 + i32.const 1648 + local.set $8 + br 8 (;@11;) + end + local.get $39 + local.get $16 + i64.load + i64.store8 + local.get $39 + local.set $1 + local.get $7 + local.set $12 + i32.const 1 + local.set $7 + i32.const 0 + local.set $6 + i32.const 1648 + local.set $8 + local.get $21 + local.set $5 + br 12 (;@6;) + end + call $11 + i32.load + call $23 + local.set $1 + br 7 (;@10;) + end + local.get $16 + i32.load + local.tee $1 + i32.eqz + if ;; label = @17 + i32.const 1658 + local.set $1 + end + br 6 (;@10;) + end + local.get $37 + local.get $16 + i64.load + i64.store32 + local.get $42 + i32.const 0 + i32.store + local.get $16 + local.get $37 + i32.store + local.get $37 + local.set $7 + i32.const -1 + local.set $6 + br 6 (;@9;) + end + local.get $16 + i32.load + local.set $7 + local.get $5 + if ;; label = @15 + block ;; label = @16 + local.get $5 + local.set $6 + br 7 (;@9;) + end + else + block ;; label = @16 + local.get $0 + i32.const 32 + local.get $10 + i32.const 0 + local.get $12 + call $24 + i32.const 0 + local.set $1 + br 8 (;@8;) + end + end + end + local.get $16 + f64.load + local.set $52 + local.get $20 + i32.const 0 + i32.store + local.get $52 + i64.reinterpret_f64 + i64.const 0 + i64.lt_s + if (result i32) ;; label = @14 + block (result i32) ;; label = @15 + i32.const 1 + local.set $24 + local.get $52 + f64.neg + local.set $52 + i32.const 1665 + end + else + block (result i32) ;; label = @15 + local.get $12 + i32.const 1 + i32.and + local.set $1 + local.get $12 + i32.const 2048 + i32.and + if (result i32) ;; label = @16 + block (result i32) ;; label = @17 + i32.const 1 + local.set $24 + i32.const 1668 + end + else + block (result i32) ;; label = @17 + local.get $1 + local.set $24 + local.get $1 + if (result i32) ;; label = @18 + i32.const 1671 + else + i32.const 1666 + end + end + end + end + end + local.set $26 + block $label$119 ;; label = @14 + local.get $52 + i64.reinterpret_f64 + i64.const 9218868437227405312 + i64.and + i64.const 9218868437227405312 + i64.lt_u + if ;; label = @15 + block ;; label = @16 + local.get $52 + local.get $20 + call $26 + f64.const 0x1p+1 (;=2;) + f64.mul + local.tee $52 + f64.const 0x0p+0 (;=0;) + f64.ne + local.tee $1 + if ;; label = @17 + local.get $20 + local.get $20 + i32.load + i32.const -1 + i32.add + i32.store + end + local.get $9 + i32.const 32 + i32.or + local.tee $22 + i32.const 97 + i32.eq + if ;; label = @17 + block ;; label = @18 + local.get $26 + i32.const 9 + i32.add + local.set $1 + local.get $9 + i32.const 32 + i32.and + local.tee $6 + if ;; label = @19 + local.get $1 + local.set $26 + end + local.get $5 + i32.const 11 + i32.gt_u + i32.const 12 + local.get $5 + i32.sub + local.tee $1 + i32.eqz + i32.or + i32.eqz + if ;; label = @19 + block ;; label = @20 + f64.const 0x1p+3 (;=8;) + local.set $53 + loop $label$125 ;; label = @21 + local.get $53 + f64.const 0x1p+4 (;=16;) + f64.mul + local.set $53 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + br_if 0 (;@21;) + end + local.get $26 + i32.load8_s + i32.const 45 + i32.eq + if (result f64) ;; label = @21 + local.get $53 + local.get $52 + f64.neg + local.get $53 + f64.sub + f64.add + f64.neg + else + local.get $52 + local.get $53 + f64.add + local.get $53 + f64.sub + end + local.set $52 + end + end + i32.const 0 + local.get $20 + i32.load + local.tee $7 + i32.sub + local.set $1 + local.get $7 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @19 + local.get $1 + else + local.get $7 + end + i64.extend_i32_s + local.get $33 + call $22 + local.tee $1 + local.get $33 + i32.eq + if ;; label = @19 + block ;; label = @20 + local.get $40 + i32.const 48 + i32.store8 + local.get $40 + local.set $1 + end + end + local.get $24 + i32.const 2 + i32.or + local.set $13 + local.get $1 + i32.const -1 + i32.add + local.get $7 + i32.const 31 + i32.shr_s + i32.const 2 + i32.and + i32.const 43 + i32.add + i32.store8 + local.get $1 + i32.const -2 + i32.add + local.tee $8 + local.get $9 + i32.const 15 + i32.add + i32.store8 + local.get $5 + i32.const 1 + i32.lt_s + local.set $9 + local.get $12 + i32.const 8 + i32.and + i32.eqz + local.set $14 + local.get $19 + local.set $1 + loop $label$131 ;; label = @19 + local.get $1 + local.get $52 + i32.trunc_f64_s + local.tee $7 + i32.const 1632 + i32.add + i32.load8_u + local.get $6 + i32.or + i32.store8 + local.get $52 + local.get $7 + f64.convert_i32_s + f64.sub + f64.const 0x1p+4 (;=16;) + f64.mul + local.set $52 + block $label$132 (result i32) ;; label = @20 + local.get $1 + i32.const 1 + i32.add + local.tee $7 + local.get $27 + i32.sub + i32.const 1 + i32.eq + if (result i32) ;; label = @21 + block (result i32) ;; label = @22 + local.get $7 + local.get $14 + local.get $9 + local.get $52 + f64.const 0x0p+0 (;=0;) + f64.eq + i32.and + i32.and + br_if 2 (;@20;) + drop + local.get $7 + i32.const 46 + i32.store8 + local.get $1 + i32.const 2 + i32.add + end + else + local.get $7 + end + end + local.set $1 + local.get $52 + f64.const 0x0p+0 (;=0;) + f64.ne + br_if 0 (;@19;) + end + local.get $46 + local.get $5 + i32.add + local.get $8 + local.tee $7 + i32.sub + local.set $6 + local.get $44 + local.get $7 + i32.sub + local.get $1 + i32.add + local.set $9 + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + i32.const 0 + i32.ne + local.get $45 + local.get $1 + i32.add + local.get $5 + i32.lt_s + i32.and + if (result i32) ;; label = @19 + local.get $6 + else + local.get $9 + local.tee $6 + end + local.get $13 + i32.add + local.tee $5 + local.get $12 + call $24 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $26 + local.get $13 + local.get $0 + call $20 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $5 + local.get $12 + i32.const 65536 + i32.xor + call $24 + local.get $1 + local.get $27 + i32.sub + local.set $1 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $19 + local.get $1 + local.get $0 + call $20 + drop + end + local.get $0 + i32.const 48 + local.get $6 + local.get $1 + local.get $28 + local.get $7 + i32.sub + local.tee $1 + i32.add + i32.sub + i32.const 0 + i32.const 0 + call $24 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @19 + local.get $8 + local.get $1 + local.get $0 + call $20 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + local.get $12 + i32.const 8192 + i32.xor + call $24 + local.get $5 + local.get $10 + i32.ge_s + if ;; label = @19 + local.get $5 + local.set $10 + end + br 4 (;@14;) + end + end + local.get $1 + if ;; label = @17 + block ;; label = @18 + local.get $20 + local.get $20 + i32.load + i32.const -28 + i32.add + local.tee $6 + i32.store + local.get $52 + f64.const 0x1p+28 (;=268435456;) + f64.mul + local.set $52 + end + else + local.get $20 + i32.load + local.set $6 + end + local.get $6 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @17 + local.get $47 + else + local.get $48 + end + local.tee $7 + local.set $8 + loop $label$145 ;; label = @17 + local.get $8 + local.get $52 + i32.trunc_f64_s + local.tee $1 + i32.store + local.get $8 + i32.const 4 + i32.add + local.set $8 + local.get $52 + local.get $1 + f64.convert_i32_u + f64.sub + f64.const 0x1.dcd65p+29 (;=1000000000;) + f64.mul + local.tee $52 + f64.const 0x0p+0 (;=0;) + f64.ne + br_if 0 (;@17;) + end + local.get $6 + i32.const 0 + i32.gt_s + if ;; label = @17 + block ;; label = @18 + local.get $7 + local.set $1 + loop $label$147 ;; label = @19 + local.get $6 + i32.const 29 + i32.gt_s + if (result i32) ;; label = @20 + i32.const 29 + else + local.get $6 + end + local.set $14 + block $label$150 ;; label = @20 + local.get $8 + i32.const -4 + i32.add + local.tee $6 + local.get $1 + i32.ge_u + if ;; label = @21 + block ;; label = @22 + local.get $14 + i64.extend_i32_u + local.set $50 + i32.const 0 + local.set $13 + loop $label$152 ;; label = @23 + local.get $6 + local.get $6 + i32.load + i64.extend_i32_u + local.get $50 + i64.shl + local.get $13 + i64.extend_i32_u + i64.add + local.tee $51 + i64.const 1000000000 + i64.rem_u + i64.store32 + local.get $51 + i64.const 1000000000 + i64.div_u + i32.wrap_i64 + local.set $13 + local.get $6 + i32.const -4 + i32.add + local.tee $6 + local.get $1 + i32.ge_u + br_if 0 (;@23;) + end + local.get $13 + i32.eqz + br_if 2 (;@20;) + local.get $1 + i32.const -4 + i32.add + local.tee $1 + local.get $13 + i32.store + end + end + end + loop $label$153 ;; label = @20 + local.get $8 + local.get $1 + i32.gt_u + if ;; label = @21 + local.get $8 + i32.const -4 + i32.add + local.tee $6 + i32.load + i32.eqz + if ;; label = @22 + block ;; label = @23 + local.get $6 + local.set $8 + br 3 (;@20;) + end + end + end + end + local.get $20 + local.get $20 + i32.load + local.get $14 + i32.sub + local.tee $6 + i32.store + local.get $6 + i32.const 0 + i32.gt_s + br_if 0 (;@19;) + end + end + else + local.get $7 + local.set $1 + end + local.get $5 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @17 + i32.const 6 + else + local.get $5 + end + local.set $18 + local.get $6 + i32.const 0 + i32.lt_s + if ;; label = @17 + block ;; label = @18 + local.get $18 + i32.const 25 + i32.add + i32.const 9 + i32.div_s + i32.const 1 + i32.add + local.set $14 + local.get $22 + i32.const 102 + i32.eq + local.set $25 + local.get $8 + local.set $5 + loop $label$160 ;; label = @19 + i32.const 0 + local.get $6 + i32.sub + local.tee $13 + i32.const 9 + i32.gt_s + if ;; label = @20 + i32.const 9 + local.set $13 + end + block $label$162 ;; label = @20 + local.get $1 + local.get $5 + i32.lt_u + if ;; label = @21 + block ;; label = @22 + i32.const 1 + local.get $13 + i32.shl + i32.const -1 + i32.add + local.set $29 + i32.const 1000000000 + local.get $13 + i32.shr_u + local.set $35 + i32.const 0 + local.set $6 + local.get $1 + local.set $8 + loop $label$164 ;; label = @23 + local.get $8 + local.get $8 + i32.load + local.tee $32 + local.get $13 + i32.shr_u + local.get $6 + i32.add + i32.store + local.get $32 + local.get $29 + i32.and + local.get $35 + i32.mul + local.set $6 + local.get $8 + i32.const 4 + i32.add + local.tee $8 + local.get $5 + i32.lt_u + br_if 0 (;@23;) + end + local.get $1 + i32.const 4 + i32.add + local.set $8 + local.get $1 + i32.load + i32.eqz + if ;; label = @23 + local.get $8 + local.set $1 + end + local.get $6 + i32.eqz + br_if 2 (;@20;) + local.get $5 + local.get $6 + i32.store + local.get $5 + i32.const 4 + i32.add + local.set $5 + end + else + block ;; label = @22 + local.get $1 + i32.const 4 + i32.add + local.set $8 + local.get $1 + i32.load + i32.eqz + if ;; label = @23 + local.get $8 + local.set $1 + end + end + end + end + local.get $25 + if (result i32) ;; label = @20 + local.get $7 + else + local.get $1 + end + local.tee $8 + local.get $14 + i32.const 2 + i32.shl + i32.add + local.set $6 + local.get $5 + local.get $8 + i32.sub + i32.const 2 + i32.shr_s + local.get $14 + i32.gt_s + if ;; label = @20 + local.get $6 + local.set $5 + end + local.get $20 + local.get $20 + i32.load + local.get $13 + i32.add + local.tee $6 + i32.store + local.get $6 + i32.const 0 + i32.lt_s + br_if 0 (;@19;) + local.get $5 + local.set $13 + end + end + else + local.get $8 + local.set $13 + end + local.get $7 + local.set $25 + block $label$172 ;; label = @17 + local.get $1 + local.get $13 + i32.lt_u + if ;; label = @18 + block ;; label = @19 + local.get $25 + local.get $1 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + local.set $5 + local.get $1 + i32.load + local.tee $6 + i32.const 10 + i32.lt_u + br_if 2 (;@17;) + i32.const 10 + local.set $8 + loop $label$174 ;; label = @20 + local.get $5 + i32.const 1 + i32.add + local.set $5 + local.get $6 + local.get $8 + i32.const 10 + i32.mul + local.tee $8 + i32.ge_u + br_if 0 (;@20;) + end + end + else + i32.const 0 + local.set $5 + end + end + local.get $22 + i32.const 103 + i32.eq + local.set $29 + local.get $18 + i32.const 0 + i32.ne + local.set $35 + local.get $18 + local.get $22 + i32.const 102 + i32.ne + if (result i32) ;; label = @17 + local.get $5 + else + i32.const 0 + end + i32.sub + local.get $35 + local.get $29 + i32.and + i32.const 31 + i32.shl + i32.const 31 + i32.shr_s + i32.add + local.tee $8 + local.get $13 + local.get $25 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + i32.const -9 + i32.add + i32.lt_s + if ;; label = @17 + block ;; label = @18 + local.get $8 + i32.const 9216 + i32.add + local.tee $14 + i32.const 9 + i32.rem_s + i32.const 1 + i32.add + local.tee $8 + i32.const 9 + i32.lt_s + if ;; label = @19 + block ;; label = @20 + i32.const 10 + local.set $6 + loop $label$180 ;; label = @21 + local.get $6 + i32.const 10 + i32.mul + local.set $6 + local.get $8 + i32.const 1 + i32.add + local.tee $8 + i32.const 9 + i32.ne + br_if 0 (;@21;) + end + end + else + i32.const 10 + local.set $6 + end + local.get $7 + i32.const 4 + i32.add + local.get $14 + i32.const 9 + i32.div_s + i32.const -1024 + i32.add + i32.const 2 + i32.shl + i32.add + local.tee $8 + i32.load + local.tee $22 + local.get $6 + i32.rem_u + local.set $14 + block $label$182 ;; label = @19 + local.get $8 + i32.const 4 + i32.add + local.get $13 + i32.eq + local.tee $32 + local.get $14 + i32.eqz + i32.and + i32.eqz + if ;; label = @20 + block ;; label = @21 + local.get $14 + local.get $6 + i32.const 2 + i32.div_s + local.tee $49 + i32.lt_u + if (result f64) ;; label = @22 + f64.const 0x1p-1 (;=0.5;) + else + local.get $32 + local.get $14 + local.get $49 + i32.eq + i32.and + if (result f64) ;; label = @23 + f64.const 0x1p+0 (;=1;) + else + f64.const 0x1.8p+0 (;=1.5;) + end + end + local.set $52 + local.get $22 + local.get $6 + i32.div_u + i32.const 1 + i32.and + if (result f64) ;; label = @22 + f64.const 0x1.0000000000001p+53 (;=9007199254740994;) + else + f64.const 0x1p+53 (;=9007199254740992;) + end + local.set $53 + block $label$190 ;; label = @22 + local.get $24 + if ;; label = @23 + block ;; label = @24 + local.get $26 + i32.load8_s + i32.const 45 + i32.ne + br_if 2 (;@22;) + local.get $53 + f64.neg + local.set $53 + local.get $52 + f64.neg + local.set $52 + end + end + end + local.get $8 + local.get $22 + local.get $14 + i32.sub + local.tee $14 + i32.store + local.get $53 + local.get $52 + f64.add + local.get $53 + f64.eq + br_if 2 (;@19;) + local.get $8 + local.get $14 + local.get $6 + i32.add + local.tee $5 + i32.store + local.get $5 + i32.const 999999999 + i32.gt_u + if ;; label = @22 + loop $label$193 ;; label = @23 + local.get $8 + i32.const 0 + i32.store + local.get $8 + i32.const -4 + i32.add + local.tee $8 + local.get $1 + i32.lt_u + if ;; label = @24 + local.get $1 + i32.const -4 + i32.add + local.tee $1 + i32.const 0 + i32.store + end + local.get $8 + local.get $8 + i32.load + i32.const 1 + i32.add + local.tee $5 + i32.store + local.get $5 + i32.const 999999999 + i32.gt_u + br_if 0 (;@23;) + end + end + local.get $25 + local.get $1 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + local.set $5 + local.get $1 + i32.load + local.tee $14 + i32.const 10 + i32.lt_u + br_if 2 (;@19;) + i32.const 10 + local.set $6 + loop $label$195 ;; label = @22 + local.get $5 + i32.const 1 + i32.add + local.set $5 + local.get $14 + local.get $6 + i32.const 10 + i32.mul + local.tee $6 + i32.ge_u + br_if 0 (;@22;) + end + end + end + end + local.get $1 + local.set $14 + local.get $5 + local.set $6 + local.get $13 + local.get $8 + i32.const 4 + i32.add + local.tee $8 + i32.le_u + if ;; label = @19 + local.get $13 + local.set $8 + end + end + else + block ;; label = @18 + local.get $1 + local.set $14 + local.get $5 + local.set $6 + local.get $13 + local.set $8 + end + end + i32.const 0 + local.get $6 + i32.sub + local.set $32 + loop $label$198 ;; label = @17 + block $label$199 ;; label = @18 + local.get $8 + local.get $14 + i32.le_u + if ;; label = @19 + block ;; label = @20 + i32.const 0 + local.set $22 + br 2 (;@18;) + end + end + local.get $8 + i32.const -4 + i32.add + local.tee $1 + i32.load + if ;; label = @19 + i32.const 1 + local.set $22 + else + block ;; label = @20 + local.get $1 + local.set $8 + br 3 (;@17;) + end + end + end + end + block $label$203 ;; label = @17 + local.get $29 + if ;; label = @18 + block ;; label = @19 + local.get $35 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $18 + i32.add + local.tee $1 + local.get $6 + i32.gt_s + local.get $6 + i32.const -5 + i32.gt_s + i32.and + if (result i32) ;; label = @20 + block (result i32) ;; label = @21 + local.get $9 + i32.const -1 + i32.add + local.set $5 + local.get $1 + i32.const -1 + i32.add + local.get $6 + i32.sub + end + else + block (result i32) ;; label = @21 + local.get $9 + i32.const -2 + i32.add + local.set $5 + local.get $1 + i32.const -1 + i32.add + end + end + local.set $1 + local.get $12 + i32.const 8 + i32.and + local.tee $13 + br_if 2 (;@17;) + block $label$207 ;; label = @20 + local.get $22 + if ;; label = @21 + block ;; label = @22 + local.get $8 + i32.const -4 + i32.add + i32.load + local.tee $18 + i32.eqz + if ;; label = @23 + block ;; label = @24 + i32.const 9 + local.set $9 + br 4 (;@20;) + end + end + local.get $18 + i32.const 10 + i32.rem_u + if ;; label = @23 + block ;; label = @24 + i32.const 0 + local.set $9 + br 4 (;@20;) + end + else + block ;; label = @24 + i32.const 10 + local.set $13 + i32.const 0 + local.set $9 + end + end + loop $label$212 ;; label = @23 + local.get $9 + i32.const 1 + i32.add + local.set $9 + local.get $18 + local.get $13 + i32.const 10 + i32.mul + local.tee $13 + i32.rem_u + i32.eqz + br_if 0 (;@23;) + end + end + else + i32.const 9 + local.set $9 + end + end + local.get $8 + local.get $25 + i32.sub + i32.const 2 + i32.shr_s + i32.const 9 + i32.mul + i32.const -9 + i32.add + local.set $18 + local.get $5 + i32.const 32 + i32.or + i32.const 102 + i32.eq + if ;; label = @20 + block ;; label = @21 + i32.const 0 + local.set $13 + local.get $1 + local.get $18 + local.get $9 + i32.sub + local.tee $9 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @22 + i32.const 0 + local.tee $9 + else + local.get $9 + end + i32.ge_s + if ;; label = @22 + local.get $9 + local.set $1 + end + end + else + block ;; label = @21 + i32.const 0 + local.set $13 + local.get $1 + local.get $18 + local.get $6 + i32.add + local.get $9 + i32.sub + local.tee $9 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @22 + i32.const 0 + local.tee $9 + else + local.get $9 + end + i32.ge_s + if ;; label = @22 + local.get $9 + local.set $1 + end + end + end + end + else + block ;; label = @19 + local.get $12 + i32.const 8 + i32.and + local.set $13 + local.get $18 + local.set $1 + local.get $9 + local.set $5 + end + end + end + local.get $5 + i32.const 32 + i32.or + i32.const 102 + i32.eq + local.tee $25 + if ;; label = @17 + block ;; label = @18 + i32.const 0 + local.set $9 + local.get $6 + i32.const 0 + i32.le_s + if ;; label = @19 + i32.const 0 + local.set $6 + end + end + else + block ;; label = @18 + local.get $28 + local.get $6 + i32.const 0 + i32.lt_s + if (result i32) ;; label = @19 + local.get $32 + else + local.get $6 + end + i64.extend_i32_s + local.get $33 + call $22 + local.tee $9 + i32.sub + i32.const 2 + i32.lt_s + if ;; label = @19 + loop $label$229 ;; label = @20 + local.get $9 + i32.const -1 + i32.add + local.tee $9 + i32.const 48 + i32.store8 + local.get $28 + local.get $9 + i32.sub + i32.const 2 + i32.lt_s + br_if 0 (;@20;) + end + end + local.get $9 + i32.const -1 + i32.add + local.get $6 + i32.const 31 + i32.shr_s + i32.const 2 + i32.and + i32.const 43 + i32.add + i32.store8 + local.get $9 + i32.const -2 + i32.add + local.tee $6 + local.get $5 + i32.store8 + local.get $6 + local.set $9 + local.get $28 + local.get $6 + i32.sub + local.set $6 + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $24 + i32.const 1 + i32.add + local.get $1 + i32.add + local.get $1 + local.get $13 + i32.or + local.tee $29 + i32.const 0 + i32.ne + i32.add + local.get $6 + i32.add + local.tee $18 + local.get $12 + call $24 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + local.get $26 + local.get $24 + local.get $0 + call $20 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $18 + local.get $12 + i32.const 65536 + i32.xor + call $24 + block $label$231 ;; label = @17 + local.get $25 + if ;; label = @18 + block ;; label = @19 + local.get $14 + local.get $7 + i32.gt_u + if (result i32) ;; label = @20 + local.get $7 + else + local.get $14 + end + local.tee $9 + local.set $6 + loop $label$235 ;; label = @20 + local.get $6 + i32.load + i64.extend_i32_u + local.get $31 + call $22 + local.set $5 + block $label$236 ;; label = @21 + local.get $6 + local.get $9 + i32.eq + if ;; label = @22 + block ;; label = @23 + local.get $5 + local.get $31 + i32.ne + br_if 2 (;@21;) + local.get $34 + i32.const 48 + i32.store8 + local.get $34 + local.set $5 + end + else + block ;; label = @23 + local.get $5 + local.get $19 + i32.le_u + br_if 2 (;@21;) + local.get $19 + i32.const 48 + local.get $5 + local.get $27 + i32.sub + call $35 + drop + loop $label$239 ;; label = @24 + local.get $5 + i32.const -1 + i32.add + local.tee $5 + local.get $19 + i32.gt_u + br_if 0 (;@24;) + end + end + end + end + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @21 + local.get $5 + local.get $41 + local.get $5 + i32.sub + local.get $0 + call $20 + drop + end + local.get $6 + i32.const 4 + i32.add + local.tee $5 + local.get $7 + i32.le_u + if ;; label = @21 + block ;; label = @22 + local.get $5 + local.set $6 + br 2 (;@20;) + end + end + end + block $label$242 ;; label = @20 + local.get $29 + if ;; label = @21 + block ;; label = @22 + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@20;) + i32.const 1700 + i32.const 1 + local.get $0 + call $20 + drop + end + end + end + local.get $1 + i32.const 0 + i32.gt_s + local.get $5 + local.get $8 + i32.lt_u + i32.and + if ;; label = @20 + loop $label$245 ;; label = @21 + local.get $5 + i32.load + i64.extend_i32_u + local.get $31 + call $22 + local.tee $7 + local.get $19 + i32.gt_u + if ;; label = @22 + block ;; label = @23 + local.get $19 + i32.const 48 + local.get $7 + local.get $27 + i32.sub + call $35 + drop + loop $label$247 ;; label = @24 + local.get $7 + i32.const -1 + i32.add + local.tee $7 + local.get $19 + i32.gt_u + br_if 0 (;@24;) + end + end + end + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @22 + local.get $7 + local.get $1 + i32.const 9 + i32.gt_s + if (result i32) ;; label = @23 + i32.const 9 + else + local.get $1 + end + local.get $0 + call $20 + drop + end + local.get $1 + i32.const -9 + i32.add + local.set $7 + local.get $1 + i32.const 9 + i32.gt_s + local.get $5 + i32.const 4 + i32.add + local.tee $5 + local.get $8 + i32.lt_u + i32.and + if ;; label = @22 + block ;; label = @23 + local.get $7 + local.set $1 + br 2 (;@21;) + end + else + local.get $7 + local.set $1 + end + end + end + local.get $0 + i32.const 48 + local.get $1 + i32.const 9 + i32.add + i32.const 9 + i32.const 0 + call $24 + end + else + block ;; label = @19 + local.get $14 + i32.const 4 + i32.add + local.set $5 + local.get $22 + i32.eqz + if ;; label = @20 + local.get $5 + local.set $8 + end + local.get $1 + i32.const -1 + i32.gt_s + if ;; label = @20 + block ;; label = @21 + local.get $13 + i32.eqz + local.set $13 + local.get $14 + local.set $7 + local.get $1 + local.set $5 + loop $label$256 ;; label = @22 + local.get $7 + i32.load + i64.extend_i32_u + local.get $31 + call $22 + local.tee $1 + local.get $31 + i32.eq + if ;; label = @23 + block ;; label = @24 + local.get $34 + i32.const 48 + i32.store8 + local.get $34 + local.set $1 + end + end + block $label$258 ;; label = @23 + local.get $7 + local.get $14 + i32.eq + if ;; label = @24 + block ;; label = @25 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @26 + local.get $1 + i32.const 1 + local.get $0 + call $20 + drop + end + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $13 + local.get $5 + i32.const 1 + i32.lt_s + i32.and + br_if 2 (;@23;) + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@23;) + i32.const 1700 + i32.const 1 + local.get $0 + call $20 + drop + end + else + block ;; label = @25 + local.get $1 + local.get $19 + i32.le_u + br_if 2 (;@23;) + local.get $19 + i32.const 48 + local.get $1 + local.get $43 + i32.add + call $35 + drop + loop $label$262 ;; label = @26 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $19 + i32.gt_u + br_if 0 (;@26;) + end + end + end + end + local.get $41 + local.get $1 + i32.sub + local.set $6 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @23 + local.get $1 + local.get $5 + local.get $6 + i32.gt_s + if (result i32) ;; label = @24 + local.get $6 + else + local.get $5 + end + local.get $0 + call $20 + drop + end + local.get $7 + i32.const 4 + i32.add + local.tee $7 + local.get $8 + i32.lt_u + local.get $5 + local.get $6 + i32.sub + local.tee $5 + i32.const -1 + i32.gt_s + i32.and + br_if 0 (;@22;) + local.get $5 + local.set $1 + end + end + end + local.get $0 + i32.const 48 + local.get $1 + i32.const 18 + i32.add + i32.const 18 + i32.const 0 + call $24 + local.get $0 + i32.load + i32.const 32 + i32.and + br_if 2 (;@17;) + local.get $9 + local.get $28 + local.get $9 + i32.sub + local.get $0 + call $20 + drop + end + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $18 + local.get $12 + i32.const 8192 + i32.xor + call $24 + local.get $18 + local.get $10 + i32.ge_s + if ;; label = @17 + local.get $18 + local.set $10 + end + end + else + block ;; label = @16 + local.get $0 + i32.const 32 + local.get $10 + local.get $52 + local.get $52 + f64.ne + i32.const 0 + i32.or + local.tee $6 + if (result i32) ;; label = @17 + i32.const 0 + local.tee $24 + else + local.get $24 + end + i32.const 3 + i32.add + local.tee $8 + local.get $7 + call $24 + local.get $0 + i32.load + local.tee $1 + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + block ;; label = @18 + local.get $26 + local.get $24 + local.get $0 + call $20 + drop + local.get $0 + i32.load + local.set $1 + end + end + local.get $9 + i32.const 32 + i32.and + i32.const 0 + i32.ne + local.tee $5 + if (result i32) ;; label = @17 + i32.const 1684 + else + i32.const 1688 + end + local.set $7 + local.get $5 + if (result i32) ;; label = @17 + i32.const 1692 + else + i32.const 1696 + end + local.set $5 + local.get $6 + i32.eqz + if ;; label = @17 + local.get $7 + local.set $5 + end + local.get $1 + i32.const 32 + i32.and + i32.eqz + if ;; label = @17 + local.get $5 + i32.const 3 + local.get $0 + call $20 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $8 + local.get $12 + i32.const 8192 + i32.xor + call $24 + local.get $8 + local.get $10 + i32.ge_s + if ;; label = @17 + local.get $8 + local.set $10 + end + end + end + end + local.get $11 + local.set $1 + br 9 (;@4;) + end + local.get $5 + local.set $7 + i32.const 0 + local.set $6 + i32.const 1648 + local.set $8 + local.get $21 + local.set $5 + br 6 (;@6;) + end + local.get $9 + i32.const 32 + i32.and + local.set $7 + local.get $16 + i64.load + local.tee $50 + i64.const 0 + i64.eq + if (result i32) ;; label = @12 + block (result i32) ;; label = @13 + i64.const 0 + local.set $50 + local.get $21 + end + else + block (result i32) ;; label = @13 + local.get $21 + local.set $1 + loop $label$280 ;; label = @14 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $50 + i32.wrap_i64 + i32.const 15 + i32.and + i32.const 1632 + i32.add + i32.load8_u + local.get $7 + i32.or + i32.store8 + local.get $50 + i64.const 4 + i64.shr_u + local.tee $50 + i64.const 0 + i64.ne + br_if 0 (;@14;) + end + local.get $16 + i64.load + local.set $50 + local.get $1 + end + end + local.set $7 + local.get $9 + i32.const 4 + i32.shr_s + i32.const 1648 + i32.add + local.set $8 + local.get $12 + i32.const 8 + i32.and + i32.eqz + local.get $50 + i64.const 0 + i64.eq + i32.or + local.tee $1 + if ;; label = @12 + i32.const 1648 + local.set $8 + end + local.get $1 + if (result i32) ;; label = @12 + i32.const 0 + else + i32.const 2 + end + local.set $6 + br 4 (;@7;) + end + local.get $50 + local.get $21 + call $22 + local.set $7 + br 3 (;@7;) + end + local.get $1 + i32.const 0 + local.get $5 + call $16 + local.tee $13 + i32.eqz + local.set $14 + local.get $13 + local.get $1 + i32.sub + local.set $8 + local.get $1 + local.get $5 + i32.add + local.set $9 + local.get $7 + local.set $12 + local.get $14 + if (result i32) ;; label = @10 + local.get $5 + else + local.get $8 + end + local.set $7 + i32.const 0 + local.set $6 + i32.const 1648 + local.set $8 + local.get $14 + if (result i32) ;; label = @10 + local.get $9 + else + local.get $13 + end + local.set $5 + br 3 (;@6;) + end + i32.const 0 + local.set $1 + i32.const 0 + local.set $5 + local.get $7 + local.set $8 + loop $label$288 ;; label = @9 + block $label$289 ;; label = @10 + local.get $8 + i32.load + local.tee $9 + i32.eqz + br_if 0 (;@10;) + local.get $36 + local.get $9 + call $25 + local.tee $5 + i32.const 0 + i32.lt_s + local.get $5 + local.get $6 + local.get $1 + i32.sub + i32.gt_u + i32.or + br_if 0 (;@10;) + local.get $8 + i32.const 4 + i32.add + local.set $8 + local.get $6 + local.get $5 + local.get $1 + i32.add + local.tee $1 + i32.gt_u + br_if 1 (;@9;) + end + end + local.get $5 + i32.const 0 + i32.lt_s + if ;; label = @9 + block ;; label = @10 + i32.const -1 + local.set $15 + br 5 (;@5;) + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $1 + local.get $12 + call $24 + local.get $1 + if ;; label = @9 + block ;; label = @10 + i32.const 0 + local.set $5 + loop $label$292 ;; label = @11 + local.get $7 + i32.load + local.tee $8 + i32.eqz + br_if 3 (;@8;) + local.get $36 + local.get $8 + call $25 + local.tee $8 + local.get $5 + i32.add + local.tee $5 + local.get $1 + i32.gt_s + br_if 3 (;@8;) + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @12 + local.get $36 + local.get $8 + local.get $0 + call $20 + drop + end + local.get $7 + i32.const 4 + i32.add + local.set $7 + local.get $5 + local.get $1 + i32.lt_u + br_if 0 (;@11;) + br 3 (;@8;) + end + end + else + block ;; label = @10 + i32.const 0 + local.set $1 + br 2 (;@8;) + end + end + end + local.get $0 + i32.const 32 + local.get $10 + local.get $1 + local.get $12 + i32.const 8192 + i32.xor + call $24 + local.get $10 + local.get $1 + i32.le_s + if ;; label = @8 + local.get $1 + local.set $10 + end + local.get $11 + local.set $1 + br 3 (;@4;) + end + local.get $12 + i32.const -65537 + i32.and + local.set $1 + local.get $5 + i32.const -1 + i32.gt_s + if ;; label = @7 + local.get $1 + local.set $12 + end + local.get $5 + local.get $16 + i64.load + i64.const 0 + i64.ne + local.tee $9 + i32.or + if (result i32) ;; label = @7 + block (result i32) ;; label = @8 + local.get $7 + local.set $1 + local.get $5 + local.get $9 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $38 + local.get $7 + i32.sub + i32.add + local.tee $7 + i32.gt_s + if ;; label = @9 + local.get $5 + local.set $7 + end + local.get $21 + end + else + block (result i32) ;; label = @8 + local.get $21 + local.set $1 + i32.const 0 + local.set $7 + local.get $21 + end + end + local.set $5 + end + local.get $0 + i32.const 32 + local.get $10 + local.get $7 + local.get $5 + local.get $1 + i32.sub + local.tee $9 + i32.lt_s + if (result i32) ;; label = @6 + local.get $9 + local.tee $7 + else + local.get $7 + end + local.get $6 + i32.add + local.tee $5 + i32.lt_s + if (result i32) ;; label = @6 + local.get $5 + local.tee $10 + else + local.get $10 + end + local.get $5 + local.get $12 + call $24 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @6 + local.get $8 + local.get $6 + local.get $0 + call $20 + drop + end + local.get $0 + i32.const 48 + local.get $10 + local.get $5 + local.get $12 + i32.const 65536 + i32.xor + call $24 + local.get $0 + i32.const 48 + local.get $7 + local.get $9 + i32.const 0 + call $24 + local.get $0 + i32.load + i32.const 32 + i32.and + i32.eqz + if ;; label = @6 + local.get $1 + local.get $9 + local.get $0 + call $20 + drop + end + local.get $0 + i32.const 32 + local.get $10 + local.get $5 + local.get $12 + i32.const 8192 + i32.xor + call $24 + local.get $11 + local.set $1 + br 1 (;@4;) + end + end + br 1 (;@2;) + end + local.get $0 + i32.eqz + if ;; label = @3 + local.get $17 + if ;; label = @4 + block ;; label = @5 + i32.const 1 + local.set $0 + loop $label$308 ;; label = @6 + local.get $4 + local.get $0 + i32.const 2 + i32.shl + i32.add + i32.load + local.tee $1 + if ;; label = @7 + block ;; label = @8 + local.get $3 + local.get $0 + i32.const 3 + i32.shl + i32.add + local.get $1 + local.get $2 + call $21 + local.get $0 + i32.const 1 + i32.add + local.tee $0 + i32.const 10 + i32.lt_s + br_if 2 (;@6;) + i32.const 1 + local.set $15 + br 6 (;@2;) + end + end + end + loop $label$310 ;; label = @6 + local.get $4 + local.get $0 + i32.const 2 + i32.shl + i32.add + i32.load + if ;; label = @7 + block ;; label = @8 + i32.const -1 + local.set $15 + br 6 (;@2;) + end + end + local.get $0 + i32.const 1 + i32.add + local.tee $0 + i32.const 10 + i32.lt_s + br_if 0 (;@6;) + i32.const 1 + local.set $15 + end + end + else + i32.const 0 + local.set $15 + end + end + end + local.get $23 + global.set $global$1 + local.get $15 + end ) - ) - (func $24 (; 37 ;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (block $label$1 - (local.set $7 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 256) - ) - ) - (local.set $6 - (local.get $7) - ) - (block $label$2 - (if - (i32.and - (i32.gt_s - (local.get $2) - (local.get $3) - ) - (i32.eqz - (i32.and - (local.get $4) - (i32.const 73728) - ) - ) - ) - (block - (drop - (call $35 - (local.get $6) - (local.get $1) - (if (result i32) - (i32.gt_u - (local.tee $5 - (i32.sub - (local.get $2) - (local.get $3) - ) - ) - (i32.const 256) - ) - (i32.const 256) - (local.get $5) - ) - ) - ) - (local.set $4 - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 32) - ) - ) - ) - (if - (i32.gt_u - (local.get $5) - (i32.const 255) - ) - (block - (loop $label$7 - (if - (local.get $4) - (block - (drop - (call $20 - (local.get $6) - (i32.const 256) - (local.get $0) - ) - ) - (local.set $1 - (i32.load - (local.get $0) - ) - ) - ) - ) - (local.set $4 - (i32.eqz - (i32.and - (local.get $1) - (i32.const 32) - ) - ) - ) - (br_if $label$7 - (i32.gt_u - (local.tee $5 - (i32.add - (local.get $5) - (i32.const -256) - ) - ) - (i32.const 255) - ) - ) - ) - (br_if $label$2 - (i32.eqz - (local.get $4) - ) - ) - (local.set $5 - (i32.and - (i32.sub - (local.get $2) - (local.get $3) - ) - (i32.const 255) - ) - ) - ) - (br_if $label$2 - (i32.eqz - (local.get $4) - ) - ) - ) - (drop - (call $20 - (local.get $6) - (local.get $5) - (local.get $0) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $7) - ) + (func $19 (;32;) (type $1) (param $0 i32) (result i32) + i32.const 0 ) - ) - (func $25 (; 38 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (if (result i32) - (local.get $0) - (call $28 - (local.get $0) - (local.get $1) - (i32.const 0) - ) - (i32.const 0) + (func $20 (;33;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) + block $label$1 (result i32) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + local.get $2 + i32.const 16 + i32.add + local.tee $4 + i32.load + local.tee $3 + br_if 0 (;@3;) + local.get $2 + call $29 + if ;; label = @4 + i32.const 0 + local.set $3 + else + block ;; label = @5 + local.get $4 + i32.load + local.set $3 + br 2 (;@3;) + end + end + br 1 (;@2;) + end + local.get $3 + local.get $2 + i32.const 20 + i32.add + local.tee $5 + i32.load + local.tee $4 + i32.sub + local.get $1 + i32.lt_u + if ;; label = @3 + block ;; label = @4 + local.get $2 + local.get $0 + local.get $1 + local.get $2 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + local.set $3 + br 2 (;@2;) + end + end + block $label$7 (result i32) ;; label = @3 + local.get $2 + i32.load8_s offset=75 + i32.const -1 + i32.gt_s + if (result i32) ;; label = @4 + block (result i32) ;; label = @5 + local.get $1 + local.set $3 + loop $label$9 ;; label = @6 + i32.const 0 + local.get $3 + i32.eqz + br_if 3 (;@3;) + drop + local.get $0 + local.get $3 + i32.const -1 + i32.add + local.tee $6 + i32.add + i32.load8_s + i32.const 10 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $6 + local.set $3 + br 2 (;@6;) + end + end + end + local.get $2 + local.get $0 + local.get $3 + local.get $2 + i32.load offset=36 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + local.get $3 + i32.lt_u + br_if 3 (;@2;) + local.get $5 + i32.load + local.set $4 + local.get $1 + local.get $3 + i32.sub + local.set $1 + local.get $0 + local.get $3 + i32.add + local.set $0 + local.get $3 + end + else + i32.const 0 + end + end + local.set $2 + local.get $4 + local.get $0 + local.get $1 + call $36 + drop + local.get $5 + local.get $5 + i32.load + local.get $1 + i32.add + i32.store + local.get $2 + local.get $1 + i32.add + local.set $3 + end + local.get $3 + end ) - ) - (func $26 (; 39 ;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (call $27 - (local.get $0) - (local.get $1) + (func $21 (;34;) (type $8) (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) (local $4 i64) (local $5 f64) + block $label$1 ;; label = @1 + local.get $1 + i32.const 20 + i32.le_u + if ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + block $label$6 ;; label = @6 + block $label$7 ;; label = @7 + block $label$8 ;; label = @8 + block $label$9 ;; label = @9 + block $label$10 ;; label = @10 + block $label$11 ;; label = @11 + block $label$12 ;; label = @12 + block $label$13 ;; label = @13 + local.get $1 + i32.const 9 + i32.sub + br_table 0 (;@13;) 1 (;@12;) 2 (;@11;) 3 (;@10;) 4 (;@9;) 5 (;@8;) 6 (;@7;) 7 (;@6;) 8 (;@5;) 9 (;@4;) 10 (;@3;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.store + br 11 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i64.extend_i32_s + i64.store + br 10 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i64.extend_i32_u + i64.store + br 9 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + i64.load + local.set $4 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $4 + i64.store + br 8 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 65535 + i32.and + i32.const 16 + i32.shl + i32.const 16 + i32.shr_s + i64.extend_i32_s + i64.store + br 7 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 65535 + i32.and + i64.extend_i32_u + i64.store + br 6 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 255 + i32.and + i32.const 24 + i32.shl + i32.const 24 + i32.shr_s + i64.extend_i32_s + i64.store + br 5 (;@1;) + end + local.get $2 + i32.load + i32.const 3 + i32.add + i32.const -4 + i32.and + local.tee $1 + i32.load + local.set $3 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.store + local.get $0 + local.get $3 + i32.const 255 + i32.and + i64.extend_i32_u + i64.store + br 4 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + f64.load + local.set $5 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $5 + f64.store + br 3 (;@1;) + end + local.get $2 + i32.load + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $1 + f64.load + local.set $5 + local.get $2 + local.get $1 + i32.const 8 + i32.add + i32.store + local.get $0 + local.get $5 + f64.store + end + end + end ) - ) - (func $27 (; 40 ;) (type $11) (param $0 f64) (param $1 i32) (result f64) - (local $2 i64) - (local $3 i64) - (block $label$1 (result f64) - (block $label$2 - (block $label$3 - (block $label$4 - (block $label$5 - (br_table $label$5 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$3 $label$4 $label$3 - (i32.sub - (i32.shr_s - (i32.shl - (i32.and - (i32.and - (i32.wrap_i64 - (local.tee $3 - (i64.shr_u - (local.tee $2 - (i64.reinterpret_f64 - (local.get $0) - ) - ) - (i64.const 52) - ) - ) - ) - (i32.const 65535) - ) - (i32.const 2047) - ) - (i32.const 16) - ) - (i32.const 16) - ) - (i32.const 0) - ) - ) - ) - (i32.store - (local.get $1) - (if (result i32) - (f64.ne - (local.get $0) - (f64.const 0) - ) - (block (result i32) - (local.set $0 - (call $27 - (f64.mul - (local.get $0) - (f64.const 18446744073709551615) - ) - (local.get $1) - ) - ) - (i32.add - (i32.load - (local.get $1) - ) - (i32.const -64) - ) - ) - (i32.const 0) - ) - ) - (br $label$2) - ) - (br $label$2) - ) - (i32.store - (local.get $1) - (i32.add - (i32.and - (i32.wrap_i64 - (local.get $3) - ) - (i32.const 2047) - ) - (i32.const -1022) - ) - ) - (local.set $0 - (f64.reinterpret_i64 - (i64.or - (i64.and - (local.get $2) - (i64.const -9218868437227405313) - ) - (i64.const 4602678819172646912) - ) - ) - ) - ) - (local.get $0) + (func $22 (;35;) (type $9) (param $0 i64) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i64) + block $label$1 (result i32) ;; label = @1 + local.get $0 + i32.wrap_i64 + local.set $2 + local.get $0 + i64.const 4294967295 + i64.gt_u + if ;; label = @2 + block ;; label = @3 + loop $label$3 ;; label = @4 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $0 + i64.const 10 + i64.rem_u + i64.const 48 + i64.or + i64.store8 + local.get $0 + i64.const 10 + i64.div_u + local.set $4 + local.get $0 + i64.const 42949672959 + i64.gt_u + if ;; label = @5 + block ;; label = @6 + local.get $4 + local.set $0 + br 2 (;@4;) + end + end + end + local.get $4 + i32.wrap_i64 + local.set $2 + end + end + local.get $2 + if ;; label = @2 + loop $label$6 ;; label = @3 + local.get $1 + i32.const -1 + i32.add + local.tee $1 + local.get $2 + i32.const 10 + i32.rem_u + i32.const 48 + i32.or + i32.store8 + local.get $2 + i32.const 10 + i32.div_u + local.set $3 + local.get $2 + i32.const 10 + i32.ge_u + if ;; label = @4 + block ;; label = @5 + local.get $3 + local.set $2 + br 2 (;@3;) + end + end + end + end + local.get $1 + end ) - ) - (func $28 (; 41 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (block $label$1 (result i32) - (if (result i32) - (local.get $0) - (block (result i32) - (if - (i32.lt_u - (local.get $1) - (i32.const 128) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (br $label$1 - (i32.const 1) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (i32.const 2048) - ) - (block - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 192) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (br $label$1 - (i32.const 2) - ) - ) - ) - (if - (i32.or - (i32.lt_u - (local.get $1) - (i32.const 55296) - ) - (i32.eq - (i32.and - (local.get $1) - (i32.const -8192) - ) - (i32.const 57344) - ) - ) - (block - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 12) - ) - (i32.const 224) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=2 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (br $label$1 - (i32.const 3) - ) - ) - ) - (if (result i32) - (i32.lt_u - (i32.add - (local.get $1) - (i32.const -65536) - ) - (i32.const 1048576) - ) - (block (result i32) - (i32.store8 - (local.get $0) - (i32.or - (i32.shr_u - (local.get $1) - (i32.const 18) - ) - (i32.const 240) - ) - ) - (i32.store8 offset=1 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 12) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=2 - (local.get $0) - (i32.or - (i32.and - (i32.shr_u - (local.get $1) - (i32.const 6) - ) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.store8 offset=3 - (local.get $0) - (i32.or - (i32.and - (local.get $1) - (i32.const 63) - ) - (i32.const 128) - ) - ) - (i32.const 4) - ) - (block (result i32) - (i32.store - (call $11) - (i32.const 84) - ) - (i32.const -1) - ) - ) - ) - (i32.const 1) - ) + (func $23 (;36;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + i32.const 0 + local.set $1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + loop $label$5 ;; label = @5 + local.get $1 + i32.const 1702 + i32.add + i32.load8_u + local.get $0 + i32.eq + br_if 1 (;@4;) + local.get $1 + i32.const 1 + i32.add + local.tee $1 + i32.const 87 + i32.ne + br_if 0 (;@5;) + i32.const 87 + local.set $1 + i32.const 1790 + local.set $0 + br 2 (;@3;) + end + end + local.get $1 + if ;; label = @4 + block ;; label = @5 + i32.const 1790 + local.set $0 + br 2 (;@3;) + end + else + i32.const 1790 + local.set $0 + end + br 1 (;@2;) + end + loop $label$8 ;; label = @3 + local.get $0 + local.set $2 + loop $label$9 ;; label = @4 + local.get $2 + i32.const 1 + i32.add + local.set $0 + local.get $2 + i32.load8_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.set $2 + br 2 (;@4;) + end + end + end + local.get $1 + i32.const -1 + i32.add + local.tee $1 + br_if 0 (;@3;) + end + end + local.get $0 + end ) - ) - (func $29 (; 42 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.load8_s - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 74) - ) - ) - ) - ) - (i32.store8 - (local.get $2) - (i32.or - (i32.add - (local.get $1) - (i32.const 255) - ) - (local.get $1) - ) - ) - (local.tee $0 - (if (result i32) - (i32.and - (local.tee $1 - (i32.load - (local.get $0) - ) - ) - (i32.const 8) - ) - (block (result i32) - (i32.store - (local.get $0) - (i32.or - (local.get $1) - (i32.const 32) - ) - ) - (i32.const -1) - ) - (block (result i32) - (i32.store offset=8 - (local.get $0) - (i32.const 0) - ) - (i32.store offset=4 - (local.get $0) - (i32.const 0) - ) - (i32.store offset=28 - (local.get $0) - (local.tee $1 - (i32.load offset=44 - (local.get $0) - ) - ) - ) - (i32.store offset=20 - (local.get $0) - (local.get $1) - ) - (i32.store offset=16 - (local.get $0) - (i32.add - (local.get $1) - (i32.load offset=48 - (local.get $0) - ) - ) - ) - (i32.const 0) - ) - ) - ) + (func $24 (;37;) (type $10) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) + (local $5 i32) (local $6 i32) (local $7 i32) + block $label$1 ;; label = @1 + global.get $global$1 + local.set $7 + global.get $global$1 + i32.const 256 + i32.add + global.set $global$1 + local.get $7 + local.set $6 + block $label$2 ;; label = @2 + local.get $2 + local.get $3 + i32.gt_s + local.get $4 + i32.const 73728 + i32.and + i32.eqz + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $6 + local.get $1 + local.get $2 + local.get $3 + i32.sub + local.tee $5 + i32.const 256 + i32.gt_u + if (result i32) ;; label = @5 + i32.const 256 + else + local.get $5 + end + call $35 + drop + local.get $0 + i32.load + local.tee $1 + i32.const 32 + i32.and + i32.eqz + local.set $4 + local.get $5 + i32.const 255 + i32.gt_u + if ;; label = @5 + block ;; label = @6 + loop $label$7 ;; label = @7 + local.get $4 + if ;; label = @8 + block ;; label = @9 + local.get $6 + i32.const 256 + local.get $0 + call $20 + drop + local.get $0 + i32.load + local.set $1 + end + end + local.get $1 + i32.const 32 + i32.and + i32.eqz + local.set $4 + local.get $5 + i32.const -256 + i32.add + local.tee $5 + i32.const 255 + i32.gt_u + br_if 0 (;@7;) + end + local.get $4 + i32.eqz + br_if 4 (;@2;) + local.get $2 + local.get $3 + i32.sub + i32.const 255 + i32.and + local.set $5 + end + else + local.get $4 + i32.eqz + br_if 3 (;@2;) + end + local.get $6 + local.get $5 + local.get $0 + call $20 + drop + end + end + end + local.get $7 + global.set $global$1 + end ) - ) - (func $30 (; 43 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (block $label$1 (result i32) - (local.set $2 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (i32.store - (local.tee $3 - (local.get $2) - ) - (local.get $1) - ) - (local.set $0 - (call $17 - (i32.load - (i32.const 1024) - ) - (local.get $0) - (local.get $3) - ) - ) - (global.set $global$1 - (local.get $2) - ) - (local.get $0) + (func $25 (;38;) (type $4) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + if (result i32) ;; label = @1 + local.get $0 + local.get $1 + i32.const 0 + call $28 + else + i32.const 0 + end ) - ) - (func $31 (; 44 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 i32) - (local $17 i32) - (local $18 i32) - (local $19 i32) - (local $20 i32) - (local $21 i32) - (block $label$1 (result i32) - (local.set $14 - (global.get $global$1) - ) - (global.set $global$1 - (i32.add - (global.get $global$1) - (i32.const 16) - ) - ) - (local.set $18 - (local.get $14) - ) - (block $label$2 - (if - (i32.lt_u - (local.get $0) - (i32.const 245) - ) - (block - (local.set $3 - (i32.and - (i32.add - (local.get $0) - (i32.const 11) - ) - (i32.const -8) - ) - ) - (if - (i32.and - (local.tee $0 - (i32.shr_u - (local.tee $8 - (i32.load - (i32.const 3644) - ) - ) - (local.tee $2 - (i32.shr_u - (if (result i32) - (i32.lt_u - (local.get $0) - (i32.const 11) - ) - (local.tee $3 - (i32.const 16) - ) - (local.get $3) - ) - (i32.const 3) - ) - ) - ) - ) - (i32.const 3) - ) - (block - (local.set $4 - (i32.load - (local.tee $1 - (i32.add - (local.tee $7 - (i32.load - (local.tee $3 - (i32.add - (local.tee $2 - (i32.add - (i32.shl - (i32.shl - (local.tee $5 - (i32.add - (i32.xor - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 1) - ) - (local.get $2) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $4) - ) - (i32.store - (i32.const 3644) - (i32.and - (local.get $8) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $5) - ) - (i32.const -1) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 12) - ) - ) - ) - (local.get $7) - ) - (block - (i32.store - (local.get $0) - (local.get $2) - ) - (i32.store - (local.get $3) - (local.get $4) - ) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=4 - (local.get $7) - (i32.or - (local.tee $0 - (i32.shl - (local.get $5) - (i32.const 3) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $7) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (local.get $1) - ) - ) - ) - (if - (i32.gt_u - (local.get $3) - (local.tee $16 - (i32.load - (i32.const 3652) - ) - ) - ) - (block - (if - (local.get $0) - (block - (local.set $5 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.tee $0 - (i32.and - (i32.shl - (local.get $0) - (local.get $2) - ) - (i32.or - (local.tee $0 - (i32.shl - (i32.const 2) - (local.get $2) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (local.set $12 - (i32.load - (local.tee $5 - (i32.add - (local.tee $9 - (i32.load - (local.tee $2 - (i32.add - (local.tee $4 - (i32.add - (i32.shl - (i32.shl - (local.tee $11 - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $0) - (local.get $5) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $5) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $2 - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $2) - (local.get $0) - ) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.eq - (local.get $4) - (local.get $12) - ) - (i32.store - (i32.const 3644) - (local.tee $7 - (i32.and - (local.get $8) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $11) - ) - (i32.const -1) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $12) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $12) - (i32.const 12) - ) - ) - ) - (local.get $9) - ) - (block - (i32.store - (local.get $0) - (local.get $4) - ) - (i32.store - (local.get $2) - (local.get $12) - ) - (local.set $7 - (local.get $8) - ) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=4 - (local.get $9) - (i32.or - (local.get $3) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.tee $4 - (i32.add - (local.get $9) - (local.get $3) - ) - ) - (i32.or - (local.tee $11 - (i32.sub - (i32.shl - (local.get $11) - (i32.const 3) - ) - (local.get $3) - ) - ) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $4) - (local.get $11) - ) - (local.get $11) - ) - (if - (local.get $16) - (block - (local.set $9 - (i32.load - (i32.const 3664) - ) - ) - (local.set $2 - (i32.add - (i32.shl - (i32.shl - (local.tee $0 - (i32.shr_u - (local.get $16) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - (if - (i32.and - (local.get $7) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $3 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (local.set $6 - (local.get $3) - ) - (local.set $1 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 3644) - (i32.or - (local.get $7) - (local.get $0) - ) - ) - (local.set $6 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (local.set $1 - (local.get $2) - ) - ) - ) - (i32.store - (local.get $6) - (local.get $9) - ) - (i32.store offset=12 - (local.get $1) - (local.get $9) - ) - (i32.store offset=8 - (local.get $9) - (local.get $1) - ) - (i32.store offset=12 - (local.get $9) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 3652) - (local.get $11) - ) - (i32.store - (i32.const 3664) - (local.get $4) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (local.get $5) - ) - ) - ) - (if - (local.tee $6 - (i32.load - (i32.const 3648) - ) - ) - (block - (local.set $2 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.get $6) - (i32.sub - (i32.const 0) - (local.get $6) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (local.set $9 - (i32.sub - (i32.and - (i32.load offset=4 - (local.tee $2 - (i32.load - (i32.add - (i32.shl - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $0) - (local.get $2) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $2) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $1 - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $1) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - ) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.set $1 - (local.get $2) - ) - (loop $label$25 - (block $label$26 - (if - (i32.eqz - (local.tee $0 - (i32.load offset=16 - (local.get $1) - ) - ) - ) - (br_if $label$26 - (i32.eqz - (local.tee $0 - (i32.load offset=20 - (local.get $1) - ) - ) - ) - ) - ) - (if - (local.tee $7 - (i32.lt_u - (local.tee $1 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.get $9) - ) - ) - (local.set $9 - (local.get $1) - ) - ) - (local.set $1 - (local.get $0) - ) - (if - (local.get $7) - (local.set $2 - (local.get $0) - ) - ) - (br $label$25) - ) - ) - (if - (i32.lt_u - (local.get $2) - (local.tee $12 - (i32.load - (i32.const 3660) - ) - ) - ) - (call $fimport$10) - ) - (if - (i32.ge_u - (local.get $2) - (local.tee $13 - (i32.add - (local.get $2) - (local.get $3) - ) - ) - ) - (call $fimport$10) - ) - (local.set $15 - (i32.load offset=24 - (local.get $2) - ) - ) - (block $label$32 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $2) - ) - ) - (local.get $2) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 20) - ) - ) - ) - ) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 16) - ) - ) - ) - ) - ) - (block - (local.set $4 - (i32.const 0) - ) - (br $label$32) - ) - ) - ) - (loop $label$36 - (if - (local.tee $7 - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (local.set $1 - (local.get $11) - ) - (br $label$36) - ) - ) - (if - (local.tee $7 - (i32.load - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (local.set $1 - (local.get $11) - ) - (br $label$36) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $12) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $4 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $11 - (i32.load offset=8 - (local.get $2) - ) - ) - (local.get $12) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $7 - (i32.add - (local.get $11) - (i32.const 12) - ) - ) - ) - (local.get $2) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $2) - ) - (block - (i32.store - (local.get $7) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $11) - ) - (local.set $4 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (block $label$46 - (if - (local.get $15) - (block - (if - (i32.eq - (local.get $2) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $2) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $4) - ) - (if - (i32.eqz - (local.get $4) - ) - (block - (i32.store - (i32.const 3648) - (i32.and - (local.get $6) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$46) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $15) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $15) - (i32.const 16) - ) - ) - ) - (local.get $2) - ) - (i32.store - (local.get $0) - (local.get $4) - ) - (i32.store offset=20 - (local.get $15) - (local.get $4) - ) - ) - (br_if $label$46 - (i32.eqz - (local.get $4) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $4) - (local.tee $0 - (i32.load - (i32.const 3660) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $4) - (local.get $15) - ) - (if - (local.tee $1 - (i32.load offset=16 - (local.get $2) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $0) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $4) - (local.get $1) - ) - (i32.store offset=24 - (local.get $1) - (local.get $4) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=20 - (local.get $2) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $4) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $4) - ) - ) - ) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $9) - (i32.const 16) - ) - (block - (i32.store offset=4 - (local.get $2) - (i32.or - (local.tee $0 - (i32.add - (local.get $9) - (local.get $3) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $2) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - (block - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $3) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.get $13) - (i32.or - (local.get $9) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $13) - (local.get $9) - ) - (local.get $9) - ) - (if - (local.get $16) - (block - (local.set $7 - (i32.load - (i32.const 3664) - ) - ) - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.tee $0 - (i32.shr_u - (local.get $16) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - (if - (i32.and - (local.get $8) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (local.set $10 - (local.get $1) - ) - (local.set $5 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 3644) - (i32.or - (local.get $8) - (local.get $0) - ) - ) - (local.set $10 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $5 - (local.get $3) - ) - ) - ) - (i32.store - (local.get $10) - (local.get $7) - ) - (i32.store offset=12 - (local.get $5) - (local.get $7) - ) - (i32.store offset=8 - (local.get $7) - (local.get $5) - ) - (i32.store offset=12 - (local.get $7) - (local.get $3) - ) - ) - ) - (i32.store - (i32.const 3652) - (local.get $9) - ) - (i32.store - (i32.const 3664) - (local.get $13) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - (local.set $0 - (local.get $3) - ) - ) - ) - (local.set $0 - (local.get $3) - ) - ) - ) - (if - (i32.gt_u - (local.get $0) - (i32.const -65) - ) - (local.set $0 - (i32.const -1) - ) - (block - (local.set $7 - (i32.and - (local.tee $0 - (i32.add - (local.get $0) - (i32.const 11) - ) - ) - (i32.const -8) - ) - ) - (if - (local.tee $5 - (i32.load - (i32.const 3648) - ) - ) - (block - (local.set $17 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $0) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $7) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $7) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $3 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $3) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (local.set $3 - (i32.sub - (i32.const 0) - (local.get $7) - ) - ) - (block $label$78 - (block $label$79 - (block $label$80 - (if - (local.tee $1 - (i32.load - (i32.add - (i32.shl - (local.get $17) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - ) - (block - (local.set $0 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $17) - (i32.const 1) - ) - ) - ) - (local.set $4 - (i32.const 0) - ) - (local.set $10 - (i32.shl - (local.get $7) - (if (result i32) - (i32.eq - (local.get $17) - (i32.const 31) - ) - (i32.const 0) - (local.get $0) - ) - ) - ) - (local.set $0 - (i32.const 0) - ) - (loop $label$84 - (if - (i32.lt_u - (local.tee $6 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $1) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.get $3) - ) - (if - (local.get $6) - (block - (local.set $3 - (local.get $6) - ) - (local.set $0 - (local.get $1) - ) - ) - (block - (local.set $3 - (i32.const 0) - ) - (local.set $0 - (local.get $1) - ) - (br $label$79) - ) - ) - ) - (local.set $1 - (if (result i32) - (i32.or - (i32.eqz - (local.tee $19 - (i32.load offset=20 - (local.get $1) - ) - ) - ) - (i32.eq - (local.get $19) - (local.tee $6 - (i32.load - (i32.add - (i32.add - (local.get $1) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $10) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - (local.get $4) - (local.get $19) - ) - ) - (local.set $10 - (i32.shl - (local.get $10) - (i32.xor - (i32.and - (local.tee $4 - (i32.eqz - (local.get $6) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (if - (local.get $4) - (block - (local.set $4 - (local.get $1) - ) - (local.set $1 - (local.get $0) - ) - (br $label$80) - ) - (block - (local.set $4 - (local.get $1) - ) - (local.set $1 - (local.get $6) - ) - (br $label$84) - ) - ) - ) - ) - (block - (local.set $4 - (i32.const 0) - ) - (local.set $1 - (i32.const 0) - ) - ) - ) - ) - (br_if $label$79 - (local.tee $0 - (if (result i32) - (i32.and - (i32.eqz - (local.get $4) - ) - (i32.eqz - (local.get $1) - ) - ) - (block (result i32) - (if - (i32.eqz - (local.tee $0 - (i32.and - (local.get $5) - (i32.or - (local.tee $0 - (i32.shl - (i32.const 2) - (local.get $17) - ) - ) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $7) - ) - (br $label$2) - ) - ) - (local.set $10 - (i32.and - (i32.shr_u - (local.tee $0 - (i32.add - (i32.and - (local.get $0) - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (i32.const -1) - ) - ) - (i32.const 12) - ) - (i32.const 16) - ) - ) - (i32.load - (i32.add - (i32.shl - (i32.add - (i32.or - (i32.or - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $0) - (local.get $10) - ) - ) - (i32.const 5) - ) - (i32.const 8) - ) - ) - (local.get $10) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 4) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 2) - ) - ) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (local.tee $4 - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 1) - ) - (i32.const 1) - ) - ) - ) - (i32.shr_u - (local.get $4) - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - ) - (local.get $4) - ) - ) - ) - (local.set $4 - (local.get $1) - ) - (br $label$78) - ) - (loop $label$96 - (if - (local.tee $10 - (i32.lt_u - (local.tee $4 - (i32.sub - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.get $3) - ) - ) - (local.set $3 - (local.get $4) - ) - ) - (if - (local.get $10) - (local.set $1 - (local.get $0) - ) - ) - (if - (local.tee $4 - (i32.load offset=16 - (local.get $0) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (br $label$96) - ) - ) - (br_if $label$96 - (local.tee $0 - (i32.load offset=20 - (local.get $0) - ) - ) - ) - (local.set $4 - (local.get $1) - ) - ) - ) - (if - (local.get $4) - (if - (i32.lt_u - (local.get $3) - (i32.sub - (i32.load - (i32.const 3652) - ) - (local.get $7) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (local.tee $12 - (i32.load - (i32.const 3660) - ) - ) - ) - (call $fimport$10) - ) - (if - (i32.ge_u - (local.get $4) - (local.tee $6 - (i32.add - (local.get $4) - (local.get $7) - ) - ) - ) - (call $fimport$10) - ) - (local.set $10 - (i32.load offset=24 - (local.get $4) - ) - ) - (block $label$104 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $4) - ) - ) - (local.get $4) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - ) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - ) - (block - (local.set $13 - (i32.const 0) - ) - (br $label$104) - ) - ) - ) - (loop $label$108 - (if - (local.tee $11 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $11) - ) - (local.set $1 - (local.get $9) - ) - (br $label$108) - ) - ) - (if - (local.tee $11 - (i32.load - (local.tee $9 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $11) - ) - (local.set $1 - (local.get $9) - ) - (br $label$108) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $12) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $13 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $9 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.get $12) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $11 - (i32.add - (local.get $9) - (i32.const 12) - ) - ) - ) - (local.get $4) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (i32.store - (local.get $11) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $9) - ) - (local.set $13 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (block $label$118 - (if - (local.get $10) - (block - (if - (i32.eq - (local.get $4) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $4) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $13) - ) - (if - (i32.eqz - (local.get $13) - ) - (block - (i32.store - (i32.const 3648) - (local.tee $2 - (i32.and - (local.get $5) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - ) - (br $label$118) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $10) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $10) - (i32.const 16) - ) - ) - ) - (local.get $4) - ) - (i32.store - (local.get $0) - (local.get $13) - ) - (i32.store offset=20 - (local.get $10) - (local.get $13) - ) - ) - (if - (i32.eqz - (local.get $13) - ) - (block - (local.set $2 - (local.get $5) - ) - (br $label$118) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $13) - (local.tee $0 - (i32.load - (i32.const 3660) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $13) - (local.get $10) - ) - (if - (local.tee $1 - (i32.load offset=16 - (local.get $4) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $0) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $13) - (local.get $1) - ) - (i32.store offset=24 - (local.get $1) - (local.get $13) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=20 - (local.get $4) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $13) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $13) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - (block $label$136 - (if - (i32.lt_u - (local.get $3) - (i32.const 16) - ) - (block - (i32.store offset=4 - (local.get $4) - (i32.or - (local.tee $0 - (i32.add - (local.get $3) - (local.get $7) - ) - ) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $4) - (local.get $0) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - (block - (i32.store offset=4 - (local.get $4) - (i32.or - (local.get $7) - (i32.const 3) - ) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $3) - ) - (local.get $3) - ) - (local.set $0 - (i32.shr_u - (local.get $3) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $3) - (i32.const 256) - ) - (block - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.get $0) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - (if - (i32.and - (local.tee $1 - (i32.load - (i32.const 3644) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (local.set $16 - (local.get $1) - ) - (local.set $8 - (local.get $0) - ) - ) - ) - (block - (i32.store - (i32.const 3644) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (local.set $16 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $8 - (local.get $3) - ) - ) - ) - (i32.store - (local.get $16) - (local.get $6) - ) - (i32.store offset=12 - (local.get $8) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $8) - ) - (i32.store offset=12 - (local.get $6) - (local.get $3) - ) - (br $label$136) - ) - ) - (local.set $1 - (i32.add - (i32.shl - (local.tee $5 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $3) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $3) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $3) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $5 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $5) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - (i32.store offset=28 - (local.get $6) - (local.get $5) - ) - (i32.store offset=4 - (local.tee $0 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.get $2) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $5) - ) - ) - ) - ) - (block - (i32.store - (i32.const 3648) - (i32.or - (local.get $2) - (local.get $0) - ) - ) - (i32.store - (local.get $1) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $1) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$136) - ) - ) - (local.set $0 - (i32.load - (local.get $1) - ) - ) - (local.set $1 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $5) - (i32.const 1) - ) - ) - ) - (local.set $5 - (i32.shl - (local.get $3) - (if (result i32) - (i32.eq - (local.get $5) - (i32.const 31) - ) - (i32.const 0) - (local.get $1) - ) - ) - ) - (block $label$151 - (block $label$152 - (block $label$153 - (loop $label$154 - (br_if $label$152 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $3) - ) - ) - (local.set $2 - (i32.shl - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$153 - (i32.eqz - (local.tee $1 - (i32.load - (local.tee $5 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $5) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $5 - (local.get $2) - ) - (local.set $0 - (local.get $1) - ) - (br $label$154) - ) - ) - (if - (i32.lt_u - (local.get $5) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $5) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $0) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$136) - ) - ) - (br $label$151) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $3 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $1 - (i32.load - (i32.const 3660) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $1) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $6) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $2) - ) - (i32.store offset=12 - (local.get $6) - (local.get $0) - ) - (i32.store offset=24 - (local.get $6) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $4) - (i32.const 8) - ) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - ) - (local.set $0 - (local.get $7) - ) - ) - ) - ) - ) - ) - (if - (i32.ge_u - (local.tee $1 - (i32.load - (i32.const 3652) - ) - ) - (local.get $0) - ) - (block - (local.set $2 - (i32.load - (i32.const 3664) - ) - ) - (if - (i32.gt_u - (local.tee $3 - (i32.sub - (local.get $1) - (local.get $0) - ) - ) - (i32.const 15) - ) - (block - (i32.store - (i32.const 3664) - (local.tee $1 - (i32.add - (local.get $2) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 3652) - (local.get $3) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $1) - (local.get $3) - ) - (local.get $3) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - ) - (block - (i32.store - (i32.const 3652) - (i32.const 0) - ) - (i32.store - (i32.const 3664) - (i32.const 0) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $1) - (i32.const 3) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (i32.add - (local.get $2) - (local.get $1) - ) - (i32.const 4) - ) - ) - (i32.or - (i32.load - (local.get $0) - ) - (i32.const 1) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.gt_u - (local.tee $10 - (i32.load - (i32.const 3656) - ) - ) - (local.get $0) - ) - (block - (i32.store - (i32.const 3656) - (local.tee $3 - (i32.sub - (local.get $10) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 3668) - (local.tee $1 - (i32.add - (local.tee $2 - (i32.load - (i32.const 3668) - ) - ) - (local.get $0) - ) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (if - (i32.le_u - (local.tee $6 - (i32.and - (local.tee $8 - (i32.add - (local.tee $1 - (if (result i32) - (i32.load - (i32.const 4116) - ) - (i32.load - (i32.const 4124) - ) - (block (result i32) - (i32.store - (i32.const 4124) - (i32.const 4096) - ) - (i32.store - (i32.const 4120) - (i32.const 4096) - ) - (i32.store - (i32.const 4128) - (i32.const -1) - ) - (i32.store - (i32.const 4132) - (i32.const -1) - ) - (i32.store - (i32.const 4136) - (i32.const 0) - ) - (i32.store - (i32.const 4088) - (i32.const 0) - ) - (i32.store - (local.get $18) - (local.tee $1 - (i32.xor - (i32.and - (local.get $18) - (i32.const -16) - ) - (i32.const 1431655768) - ) - ) - ) - (i32.store - (i32.const 4116) - (local.get $1) - ) - (i32.const 4096) - ) - ) - ) - (local.tee $13 - (i32.add - (local.get $0) - (i32.const 47) - ) - ) - ) - ) - (local.tee $4 - (i32.sub - (i32.const 0) - (local.get $1) - ) - ) - ) - ) - (local.get $0) - ) - (block - (global.set $global$1 - (local.get $14) - ) - (return - (i32.const 0) - ) - ) - ) - (if - (local.tee $2 - (i32.load - (i32.const 4084) - ) - ) - (if - (i32.or - (i32.le_u - (local.tee $1 - (i32.add - (local.tee $3 - (i32.load - (i32.const 4076) - ) - ) - (local.get $6) - ) - ) - (local.get $3) - ) - (i32.gt_u - (local.get $1) - (local.get $2) - ) - ) - (block - (global.set $global$1 - (local.get $14) - ) - (return - (i32.const 0) - ) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $0) - (i32.const 48) - ) - ) - (block $label$171 - (block $label$172 - (if - (i32.eqz - (i32.and - (i32.load - (i32.const 4088) - ) - (i32.const 4) - ) - ) - (block - (block $label$174 - (block $label$175 - (block $label$176 - (br_if $label$176 - (i32.eqz - (local.tee $3 - (i32.load - (i32.const 3668) - ) - ) - ) - ) - (local.set $2 - (i32.const 4092) - ) - (loop $label$177 - (block $label$178 - (if - (i32.le_u - (local.tee $1 - (i32.load - (local.get $2) - ) - ) - (local.get $3) - ) - (br_if $label$178 - (i32.gt_u - (i32.add - (local.get $1) - (i32.load - (local.tee $5 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - ) - ) - (local.get $3) - ) - ) - ) - (br_if $label$176 - (i32.eqz - (local.tee $1 - (i32.load offset=8 - (local.get $2) - ) - ) - ) - ) - (local.set $2 - (local.get $1) - ) - (br $label$177) - ) - ) - (if - (i32.lt_u - (local.tee $3 - (i32.and - (i32.sub - (local.get $8) - (local.get $10) - ) - (local.get $4) - ) - ) - (i32.const 2147483647) - ) - (if - (i32.eq - (local.tee $1 - (call $34 - (local.get $3) - ) - ) - (i32.add - (i32.load - (local.get $2) - ) - (i32.load - (local.get $5) - ) - ) - ) - (br_if $label$172 - (i32.ne - (local.get $1) - (i32.const -1) - ) - ) - (block - (local.set $2 - (local.get $1) - ) - (local.set $1 - (local.get $3) - ) - (br $label$175) - ) - ) - ) - (br $label$174) - ) - (if - (i32.ne - (local.tee $1 - (call $34 - (i32.const 0) - ) - ) - (i32.const -1) - ) - (block - (local.set $2 - (i32.sub - (i32.and - (i32.add - (local.tee $5 - (i32.add - (local.tee $2 - (i32.load - (i32.const 4120) - ) - ) - (i32.const -1) - ) - ) - (local.tee $3 - (local.get $1) - ) - ) - (i32.sub - (i32.const 0) - (local.get $2) - ) - ) - (local.get $3) - ) - ) - (local.set $4 - (i32.add - (local.tee $3 - (i32.add - (if (result i32) - (i32.and - (local.get $5) - (local.get $3) - ) - (local.get $2) - (i32.const 0) - ) - (local.get $6) - ) - ) - (local.tee $5 - (i32.load - (i32.const 4076) - ) - ) - ) - ) - (if - (i32.and - (i32.gt_u - (local.get $3) - (local.get $0) - ) - (i32.lt_u - (local.get $3) - (i32.const 2147483647) - ) - ) - (block - (if - (local.tee $2 - (i32.load - (i32.const 4084) - ) - ) - (br_if $label$174 - (i32.or - (i32.le_u - (local.get $4) - (local.get $5) - ) - (i32.gt_u - (local.get $4) - (local.get $2) - ) - ) - ) - ) - (br_if $label$172 - (i32.eq - (local.tee $2 - (call $34 - (local.get $3) - ) - ) - (local.get $1) - ) - ) - (local.set $1 - (local.get $3) - ) - (br $label$175) - ) - ) - ) - ) - (br $label$174) - ) - (local.set $5 - (i32.sub - (i32.const 0) - (local.get $1) - ) - ) - (if - (i32.and - (i32.gt_u - (local.get $7) - (local.get $1) - ) - (i32.and - (i32.lt_u - (local.get $1) - (i32.const 2147483647) - ) - (i32.ne - (local.get $2) - (i32.const -1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $3 - (i32.and - (i32.add - (i32.sub - (local.get $13) - (local.get $1) - ) - (local.tee $3 - (i32.load - (i32.const 4124) - ) - ) - ) - (i32.sub - (i32.const 0) - (local.get $3) - ) - ) - ) - (i32.const 2147483647) - ) - (if - (i32.eq - (call $34 - (local.get $3) - ) - (i32.const -1) - ) - (block - (drop - (call $34 - (local.get $5) - ) - ) - (br $label$174) - ) - (local.set $3 - (i32.add - (local.get $3) - (local.get $1) - ) - ) - ) - (local.set $3 - (local.get $1) - ) - ) - (local.set $3 - (local.get $1) - ) - ) - (if - (i32.ne - (local.get $2) - (i32.const -1) - ) - (block - (local.set $1 - (local.get $2) - ) - (br $label$172) - ) - ) - ) - (i32.store - (i32.const 4088) - (i32.or - (i32.load - (i32.const 4088) - ) - (i32.const 4) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $6) - (i32.const 2147483647) - ) - (if - (i32.and - (i32.lt_u - (local.tee $1 - (call $34 - (local.get $6) - ) - ) - (local.tee $3 - (call $34 - (i32.const 0) - ) - ) - ) - (i32.and - (i32.ne - (local.get $1) - (i32.const -1) - ) - (i32.ne - (local.get $3) - (i32.const -1) - ) - ) - ) - (br_if $label$172 - (i32.gt_u - (local.tee $3 - (i32.sub - (local.get $3) - (local.get $1) - ) - ) - (i32.add - (local.get $0) - (i32.const 40) - ) - ) - ) - ) - ) - (br $label$171) - ) - (i32.store - (i32.const 4076) - (local.tee $2 - (i32.add - (i32.load - (i32.const 4076) - ) - (local.get $3) - ) - ) - ) - (if - (i32.gt_u - (local.get $2) - (i32.load - (i32.const 4080) - ) - ) - (i32.store - (i32.const 4080) - (local.get $2) - ) - ) - (block $label$198 - (if - (local.tee $8 - (i32.load - (i32.const 3668) - ) - ) - (block - (local.set $2 - (i32.const 4092) - ) - (block $label$200 - (block $label$201 - (loop $label$202 - (br_if $label$201 - (i32.eq - (local.get $1) - (i32.add - (local.tee $4 - (i32.load - (local.get $2) - ) - ) - (local.tee $5 - (i32.load - (local.tee $7 - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - ) - ) - ) - ) - ) - (br_if $label$202 - (local.tee $2 - (i32.load offset=8 - (local.get $2) - ) - ) - ) - ) - (br $label$200) - ) - (if - (i32.eqz - (i32.and - (i32.load offset=12 - (local.get $2) - ) - (i32.const 8) - ) - ) - (if - (i32.and - (i32.lt_u - (local.get $8) - (local.get $1) - ) - (i32.ge_u - (local.get $8) - (local.get $4) - ) - ) - (block - (i32.store - (local.get $7) - (i32.add - (local.get $5) - (local.get $3) - ) - ) - (local.set $5 - (i32.load - (i32.const 3656) - ) - ) - (local.set $1 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $2 - (i32.add - (local.get $8) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 3668) - (local.tee $2 - (i32.add - (local.get $8) - (if (result i32) - (i32.and - (local.get $2) - (i32.const 7) - ) - (local.get $1) - (local.tee $1 - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 3656) - (local.tee $1 - (i32.add - (i32.sub - (local.get $3) - (local.get $1) - ) - (local.get $5) - ) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $2) - (local.get $1) - ) - (i32.const 40) - ) - (i32.store - (i32.const 3672) - (i32.load - (i32.const 4132) - ) - ) - (br $label$198) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.tee $2 - (i32.load - (i32.const 3660) - ) - ) - ) - (block - (i32.store - (i32.const 3660) - (local.get $1) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - (local.set $10 - (i32.add - (local.get $1) - (local.get $3) - ) - ) - (local.set $5 - (i32.const 4092) - ) - (block $label$208 - (block $label$209 - (loop $label$210 - (br_if $label$209 - (i32.eq - (i32.load - (local.get $5) - ) - (local.get $10) - ) - ) - (br_if $label$210 - (local.tee $5 - (i32.load offset=8 - (local.get $5) - ) - ) - ) - (local.set $5 - (i32.const 4092) - ) - ) - (br $label$208) - ) - (if - (i32.and - (i32.load offset=12 - (local.get $5) - ) - (i32.const 8) - ) - (local.set $5 - (i32.const 4092) - ) - (block - (i32.store - (local.get $5) - (local.get $1) - ) - (i32.store - (local.tee $5 - (i32.add - (local.get $5) - (i32.const 4) - ) - ) - (i32.add - (i32.load - (local.get $5) - ) - (local.get $3) - ) - ) - (local.set $7 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $4 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $3 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $5 - (i32.add - (local.get $10) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $6 - (i32.add - (local.tee $13 - (i32.add - (local.get $1) - (if (result i32) - (i32.and - (local.get $4) - (i32.const 7) - ) - (local.get $7) - (i32.const 0) - ) - ) - ) - (local.get $0) - ) - ) - (local.set $7 - (i32.sub - (i32.sub - (local.tee $4 - (i32.add - (local.get $10) - (if (result i32) - (i32.and - (local.get $5) - (i32.const 7) - ) - (local.get $3) - (i32.const 0) - ) - ) - ) - (local.get $13) - ) - (local.get $0) - ) - ) - (i32.store offset=4 - (local.get $13) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (block $label$217 - (if - (i32.eq - (local.get $4) - (local.get $8) - ) - (block - (i32.store - (i32.const 3656) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3656) - ) - (local.get $7) - ) - ) - ) - (i32.store - (i32.const 3668) - (local.get $6) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - ) - (block - (if - (i32.eq - (local.get $4) - (i32.load - (i32.const 3664) - ) - ) - (block - (i32.store - (i32.const 3652) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3652) - ) - (local.get $7) - ) - ) - ) - (i32.store - (i32.const 3664) - (local.get $6) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $0) - ) - (local.get $0) - ) - (br $label$217) - ) - ) - (i32.store - (local.tee $0 - (i32.add - (local.tee $0 - (if (result i32) - (i32.eq - (i32.and - (local.tee $0 - (i32.load offset=4 - (local.get $4) - ) - ) - (i32.const 3) - ) - (i32.const 1) - ) - (block (result i32) - (local.set $11 - (i32.and - (local.get $0) - (i32.const -8) - ) - ) - (local.set $1 - (i32.shr_u - (local.get $0) - (i32.const 3) - ) - ) - (block $label$222 - (if - (i32.lt_u - (local.get $0) - (i32.const 256) - ) - (block - (local.set $5 - (i32.load offset=12 - (local.get $4) - ) - ) - (block $label$224 - (if - (i32.ne - (local.tee $3 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.tee $0 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $3) - (local.get $2) - ) - (call $fimport$10) - ) - (br_if $label$224 - (i32.eq - (i32.load offset=12 - (local.get $3) - ) - (local.get $4) - ) - ) - (call $fimport$10) - ) - ) - ) - (if - (i32.eq - (local.get $5) - (local.get $3) - ) - (block - (i32.store - (i32.const 3644) - (i32.and - (i32.load - (i32.const 3644) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$222) - ) - ) - (block $label$228 - (if - (i32.eq - (local.get $5) - (local.get $0) - ) - (local.set $20 - (i32.add - (local.get $5) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $5) - (local.get $2) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $5) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (local.set $20 - (local.get $0) - ) - (br $label$228) - ) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=12 - (local.get $3) - (local.get $5) - ) - (i32.store - (local.get $20) - (local.get $3) - ) - ) - (block - (local.set $8 - (i32.load offset=24 - (local.get $4) - ) - ) - (block $label$234 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $4) - ) - ) - (local.get $4) - ) - (block - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.tee $3 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load - (local.get $3) - ) - ) - (local.set $1 - (local.get $3) - ) - (block - (local.set $12 - (i32.const 0) - ) - (br $label$234) - ) - ) - ) - (loop $label$239 - (if - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $5) - ) - (br $label$239) - ) - ) - (if - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $5) - ) - (br $label$239) - ) - ) - ) - (if - (i32.lt_u - (local.get $1) - (local.get $2) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (i32.const 0) - ) - (local.set $12 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.load offset=8 - (local.get $4) - ) - ) - (local.get $2) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $3 - (i32.add - (local.get $5) - (i32.const 12) - ) - ) - ) - (local.get $4) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $4) - ) - (block - (i32.store - (local.get $3) - (local.get $0) - ) - (i32.store - (local.get $1) - (local.get $5) - ) - (local.set $12 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (br_if $label$222 - (i32.eqz - (local.get $8) - ) - ) - (block $label$249 - (if - (i32.eq - (local.get $4) - (i32.load - (local.tee $0 - (i32.add - (i32.shl - (local.tee $1 - (i32.load offset=28 - (local.get $4) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - ) - ) - (block - (i32.store - (local.get $0) - (local.get $12) - ) - (br_if $label$249 - (local.get $12) - ) - (i32.store - (i32.const 3648) - (i32.and - (i32.load - (i32.const 3648) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $1) - ) - (i32.const -1) - ) - ) - ) - (br $label$222) - ) - (block - (if - (i32.lt_u - (local.get $8) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - ) - (local.get $4) - ) - (i32.store - (local.get $0) - (local.get $12) - ) - (i32.store offset=20 - (local.get $8) - (local.get $12) - ) - ) - (br_if $label$222 - (i32.eqz - (local.get $12) - ) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $12) - (local.tee $1 - (i32.load - (i32.const 3660) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $12) - (local.get $8) - ) - (if - (local.tee $3 - (i32.load - (local.tee $0 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $3) - (local.get $1) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $12) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $12) - ) - ) - ) - ) - (br_if $label$222 - (i32.eqz - (local.tee $0 - (i32.load offset=4 - (local.get $0) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $12) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $12) - ) - ) - ) - ) - ) - ) - (local.set $7 - (i32.add - (local.get $11) - (local.get $7) - ) - ) - (i32.add - (local.get $4) - (local.get $11) - ) - ) - (local.get $4) - ) - ) - (i32.const 4) - ) - ) - (i32.and - (i32.load - (local.get $0) - ) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $6) - (i32.or - (local.get $7) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $6) - (local.get $7) - ) - (local.get $7) - ) - (local.set $0 - (i32.shr_u - (local.get $7) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $7) - (i32.const 256) - ) - (block - (local.set $3 - (i32.add - (i32.shl - (i32.shl - (local.get $0) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - (block $label$263 - (if - (i32.and - (local.tee $1 - (i32.load - (i32.const 3644) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $0) - ) - ) - ) - (block - (if - (i32.ge_u - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3660) - ) - ) - (block - (local.set $21 - (local.get $1) - ) - (local.set $9 - (local.get $0) - ) - (br $label$263) - ) - ) - (call $fimport$10) - ) - (block - (i32.store - (i32.const 3644) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (local.set $21 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (local.set $9 - (local.get $3) - ) - ) - ) - ) - (i32.store - (local.get $21) - (local.get $6) - ) - (i32.store offset=12 - (local.get $9) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $9) - ) - (i32.store offset=12 - (local.get $6) - (local.get $3) - ) - (br $label$217) - ) - ) - (local.set $3 - (i32.add - (i32.shl - (local.tee $2 - (block $label$267 (result i32) - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $7) - (i32.const 8) - ) - ) - (block (result i32) - (drop - (br_if $label$267 - (i32.const 31) - (i32.gt_u - (local.get $7) - (i32.const 16777215) - ) - ) - ) - (i32.or - (i32.and - (i32.shr_u - (local.get $7) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $3 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $3) - ) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $1) - (local.get $0) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $1) - (local.get $0) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - (i32.store offset=28 - (local.get $6) - (local.get $2) - ) - (i32.store offset=4 - (local.tee $0 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 0) - ) - (i32.store - (local.get $0) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.tee $1 - (i32.load - (i32.const 3648) - ) - ) - (local.tee $0 - (i32.shl - (i32.const 1) - (local.get $2) - ) - ) - ) - ) - (block - (i32.store - (i32.const 3648) - (i32.or - (local.get $1) - (local.get $0) - ) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $3) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$217) - ) - ) - (local.set $0 - (i32.load - (local.get $3) - ) - ) - (local.set $1 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $2) - (i32.const 1) - ) - ) - ) - (local.set $2 - (i32.shl - (local.get $7) - (if (result i32) - (i32.eq - (local.get $2) - (i32.const 31) - ) - (i32.const 0) - (local.get $1) - ) - ) - ) - (block $label$273 - (block $label$274 - (block $label$275 - (loop $label$276 - (br_if $label$274 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $7) - ) - ) - (local.set $3 - (i32.shl - (local.get $2) - (i32.const 1) - ) - ) - (br_if $label$275 - (i32.eqz - (local.tee $1 - (i32.load - (local.tee $2 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $2) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $2 - (local.get $3) - ) - (local.set $0 - (local.get $1) - ) - (br $label$276) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $2) - (local.get $6) - ) - (i32.store offset=24 - (local.get $6) - (local.get $0) - ) - (i32.store offset=12 - (local.get $6) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $6) - ) - (br $label$217) - ) - ) - (br $label$273) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $3 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $1 - (i32.load - (i32.const 3660) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $1) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $6) - ) - (i32.store - (local.get $3) - (local.get $6) - ) - (i32.store offset=8 - (local.get $6) - (local.get $2) - ) - (i32.store offset=12 - (local.get $6) - (local.get $0) - ) - (i32.store offset=24 - (local.get $6) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $13) - (i32.const 8) - ) - ) - ) - ) - ) - (loop $label$281 - (block $label$282 - (if - (i32.le_u - (local.tee $2 - (i32.load - (local.get $5) - ) - ) - (local.get $8) - ) - (br_if $label$282 - (i32.gt_u - (local.tee $13 - (i32.add - (local.get $2) - (i32.load offset=4 - (local.get $5) - ) - ) - ) - (local.get $8) - ) - ) - ) - (local.set $5 - (i32.load offset=8 - (local.get $5) - ) - ) - (br $label$281) - ) - ) - (local.set $2 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $5 - (i32.add - (local.tee $7 - (i32.add - (local.get $13) - (i32.const -47) - ) - ) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (local.set $10 - (i32.add - (local.tee $7 - (if (result i32) - (i32.lt_u - (local.tee $2 - (i32.add - (local.get $7) - (if (result i32) - (i32.and - (local.get $5) - (i32.const 7) - ) - (local.get $2) - (i32.const 0) - ) - ) - ) - (local.tee $12 - (i32.add - (local.get $8) - (i32.const 16) - ) - ) - ) - (local.get $8) - (local.get $2) - ) - ) - (i32.const 8) - ) - ) - (local.set $5 - (i32.add - (local.get $7) - (i32.const 24) - ) - ) - (local.set $9 - (i32.add - (local.get $3) - (i32.const -40) - ) - ) - (local.set $2 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $4 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 3668) - (local.tee $4 - (i32.add - (local.get $1) - (if (result i32) - (i32.and - (local.get $4) - (i32.const 7) - ) - (local.get $2) - (local.tee $2 - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 3656) - (local.tee $2 - (i32.sub - (local.get $9) - (local.get $2) - ) - ) - ) - (i32.store offset=4 - (local.get $4) - (i32.or - (local.get $2) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $4) - (local.get $2) - ) - (i32.const 40) - ) - (i32.store - (i32.const 3672) - (i32.load - (i32.const 4132) - ) - ) - (i32.store - (local.tee $2 - (i32.add - (local.get $7) - (i32.const 4) - ) - ) - (i32.const 27) - ) - (i64.store align=4 - (local.get $10) - (i64.load align=4 - (i32.const 4092) - ) - ) - (i64.store offset=8 align=4 - (local.get $10) - (i64.load align=4 - (i32.const 4100) - ) - ) - (i32.store - (i32.const 4092) - (local.get $1) - ) - (i32.store - (i32.const 4096) - (local.get $3) - ) - (i32.store - (i32.const 4104) - (i32.const 0) - ) - (i32.store - (i32.const 4100) - (local.get $10) - ) - (local.set $1 - (local.get $5) - ) - (loop $label$290 - (i32.store - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (i32.const 7) - ) - (br_if $label$290 - (i32.lt_u - (i32.add - (local.get $1) - (i32.const 4) - ) - (local.get $13) - ) - ) - ) - (if - (i32.ne - (local.get $7) - (local.get $8) - ) - (block - (i32.store - (local.get $2) - (i32.and - (i32.load - (local.get $2) - ) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $8) - (i32.or - (local.tee $4 - (i32.sub - (local.get $7) - (local.get $8) - ) - ) - (i32.const 1) - ) - ) - (i32.store - (local.get $7) - (local.get $4) - ) - (local.set $1 - (i32.shr_u - (local.get $4) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $4) - (i32.const 256) - ) - (block - (local.set $2 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - (if - (i32.and - (local.tee $3 - (i32.load - (i32.const 3644) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.load - (local.tee $3 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (local.set $15 - (local.get $3) - ) - (local.set $11 - (local.get $1) - ) - ) - ) - (block - (i32.store - (i32.const 3644) - (i32.or - (local.get $3) - (local.get $1) - ) - ) - (local.set $15 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (local.set $11 - (local.get $2) - ) - ) - ) - (i32.store - (local.get $15) - (local.get $8) - ) - (i32.store offset=12 - (local.get $11) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $11) - ) - (i32.store offset=12 - (local.get $8) - (local.get $2) - ) - (br $label$198) - ) - ) - (local.set $2 - (i32.add - (i32.shl - (local.tee $5 - (if (result i32) - (local.tee $1 - (i32.shr_u - (local.get $4) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $4) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $4) - (i32.add - (local.tee $1 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $3 - (i32.shl - (local.get $1) - (local.tee $2 - (i32.and - (i32.shr_u - (i32.add - (local.get $1) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $2) - ) - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $3 - (i32.shl - (local.get $3) - (local.get $1) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $3) - (local.get $1) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $1) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - (i32.store offset=28 - (local.get $8) - (local.get $5) - ) - (i32.store offset=20 - (local.get $8) - (i32.const 0) - ) - (i32.store - (local.get $12) - (i32.const 0) - ) - (if - (i32.eqz - (i32.and - (local.tee $3 - (i32.load - (i32.const 3648) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $5) - ) - ) - ) - ) - (block - (i32.store - (i32.const 3648) - (i32.or - (local.get $3) - (local.get $1) - ) - ) - (i32.store - (local.get $2) - (local.get $8) - ) - (i32.store offset=24 - (local.get $8) - (local.get $2) - ) - (i32.store offset=12 - (local.get $8) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $8) - ) - (br $label$198) - ) - ) - (local.set $1 - (i32.load - (local.get $2) - ) - ) - (local.set $3 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $5) - (i32.const 1) - ) - ) - ) - (local.set $5 - (i32.shl - (local.get $4) - (if (result i32) - (i32.eq - (local.get $5) - (i32.const 31) - ) - (i32.const 0) - (local.get $3) - ) - ) - ) - (block $label$304 - (block $label$305 - (block $label$306 - (loop $label$307 - (br_if $label$305 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $1) - ) - (i32.const -8) - ) - (local.get $4) - ) - ) - (local.set $2 - (i32.shl - (local.get $5) - (i32.const 1) - ) - ) - (br_if $label$306 - (i32.eqz - (local.tee $3 - (i32.load - (local.tee $5 - (i32.add - (i32.add - (local.get $1) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $5) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $5 - (local.get $2) - ) - (local.set $1 - (local.get $3) - ) - (br $label$307) - ) - ) - (if - (i32.lt_u - (local.get $5) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $5) - (local.get $8) - ) - (i32.store offset=24 - (local.get $8) - (local.get $1) - ) - (i32.store offset=12 - (local.get $8) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $8) - ) - (br $label$198) - ) - ) - (br $label$304) - ) - (if - (i32.and - (i32.ge_u - (local.tee $5 - (i32.load - (local.tee $2 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - ) - (local.tee $3 - (i32.load - (i32.const 3660) - ) - ) - ) - (i32.ge_u - (local.get $1) - (local.get $3) - ) - ) - (block - (i32.store offset=12 - (local.get $5) - (local.get $8) - ) - (i32.store - (local.get $2) - (local.get $8) - ) - (i32.store offset=8 - (local.get $8) - (local.get $5) - ) - (i32.store offset=12 - (local.get $8) - (local.get $1) - ) - (i32.store offset=24 - (local.get $8) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - ) - (block - (if - (i32.or - (i32.eqz - (local.tee $2 - (i32.load - (i32.const 3660) - ) - ) - ) - (i32.lt_u - (local.get $1) - (local.get $2) - ) - ) - (i32.store - (i32.const 3660) - (local.get $1) - ) - ) - (i32.store - (i32.const 4092) - (local.get $1) - ) - (i32.store - (i32.const 4096) - (local.get $3) - ) - (i32.store - (i32.const 4104) - (i32.const 0) - ) - (i32.store - (i32.const 3680) - (i32.load - (i32.const 4116) - ) - ) - (i32.store - (i32.const 3676) - (i32.const -1) - ) - (local.set $2 - (i32.const 0) - ) - (loop $label$314 - (i32.store offset=12 - (local.tee $5 - (i32.add - (i32.shl - (i32.shl - (local.get $2) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - (local.get $5) - ) - (i32.store offset=8 - (local.get $5) - (local.get $5) - ) - (br_if $label$314 - (i32.ne - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (i32.const 32) - ) - ) - ) - (local.set $5 - (i32.add - (local.get $3) - (i32.const -40) - ) - ) - (local.set $3 - (i32.and - (i32.sub - (i32.const 0) - (local.tee $2 - (i32.add - (local.get $1) - (i32.const 8) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.store - (i32.const 3668) - (local.tee $3 - (i32.add - (local.get $1) - (local.tee $1 - (if (result i32) - (i32.and - (local.get $2) - (i32.const 7) - ) - (local.get $3) - (i32.const 0) - ) - ) - ) - ) - ) - (i32.store - (i32.const 3656) - (local.tee $1 - (i32.sub - (local.get $5) - (local.get $1) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store offset=4 - (i32.add - (local.get $3) - (local.get $1) - ) - (i32.const 40) - ) - (i32.store - (i32.const 3672) - (i32.load - (i32.const 4132) - ) - ) - ) - ) - ) - (if - (i32.gt_u - (local.tee $1 - (i32.load - (i32.const 3656) - ) - ) - (local.get $0) - ) - (block - (i32.store - (i32.const 3656) - (local.tee $3 - (i32.sub - (local.get $1) - (local.get $0) - ) - ) - ) - (i32.store - (i32.const 3668) - (local.tee $1 - (i32.add - (local.tee $2 - (i32.load - (i32.const 3668) - ) - ) - (local.get $0) - ) - ) - ) - (i32.store offset=4 - (local.get $1) - (i32.or - (local.get $3) - (i32.const 1) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.or - (local.get $0) - (i32.const 3) - ) - ) - (global.set $global$1 - (local.get $14) - ) - (return - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - ) - ) - (i32.store - (call $11) - (i32.const 12) - ) - (global.set $global$1 - (local.get $14) - ) - (i32.const 0) + (func $26 (;39;) (type $11) (param $0 f64) (param $1 i32) (result f64) + local.get $0 + local.get $1 + call $27 ) - ) - (func $32 (; 45 ;) (type $2) (param $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (block $label$1 - (if - (i32.eqz - (local.get $0) - ) - (return) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.add - (local.get $0) - (i32.const -8) - ) - ) - (local.tee $11 - (i32.load - (i32.const 3660) - ) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (local.tee $8 - (i32.and - (local.tee $0 - (i32.load - (i32.add - (local.get $0) - (i32.const -4) - ) - ) - ) - (i32.const 3) - ) - ) - (i32.const 1) - ) - (call $fimport$10) - ) - (local.set $6 - (i32.add - (local.get $1) - (local.tee $4 - (i32.and - (local.get $0) - (i32.const -8) - ) - ) - ) - ) - (block $label$5 - (if - (i32.and - (local.get $0) - (i32.const 1) - ) - (block - (local.set $3 - (local.get $1) - ) - (local.set $2 - (local.get $4) - ) - ) - (block - (if - (i32.eqz - (local.get $8) - ) - (return) - ) - (if - (i32.lt_u - (local.tee $0 - (i32.add - (local.get $1) - (i32.sub - (i32.const 0) - (local.tee $8 - (i32.load - (local.get $1) - ) - ) - ) - ) - ) - (local.get $11) - ) - (call $fimport$10) - ) - (local.set $1 - (i32.add - (local.get $8) - (local.get $4) - ) - ) - (if - (i32.eq - (local.get $0) - (i32.load - (i32.const 3664) - ) - ) - (block - (if - (i32.ne - (i32.and - (local.tee $3 - (i32.load - (local.tee $2 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - ) - ) - (i32.const 3) - ) - (i32.const 3) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (i32.store - (i32.const 3652) - (local.get $1) - ) - (i32.store - (local.get $2) - (i32.and - (local.get $3) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $0) - (i32.or - (local.get $1) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $0) - (local.get $1) - ) - (local.get $1) - ) - (return) - ) - ) - (local.set $10 - (i32.shr_u - (local.get $8) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $8) - (i32.const 256) - ) - (block - (local.set $3 - (i32.load offset=12 - (local.get $0) - ) - ) - (if - (i32.ne - (local.tee $4 - (i32.load offset=8 - (local.get $0) - ) - ) - (local.tee $2 - (i32.add - (i32.shl - (i32.shl - (local.get $10) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $4) - (local.get $11) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load offset=12 - (local.get $4) - ) - (local.get $0) - ) - (call $fimport$10) - ) - ) - ) - (if - (i32.eq - (local.get $3) - (local.get $4) - ) - (block - (i32.store - (i32.const 3644) - (i32.and - (i32.load - (i32.const 3644) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $10) - ) - (i32.const -1) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (if - (i32.eq - (local.get $3) - (local.get $2) - ) - (local.set $5 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $3) - (local.get $11) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $2 - (i32.add - (local.get $3) - (i32.const 8) - ) - ) - ) - (local.get $0) - ) - (local.set $5 - (local.get $2) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=12 - (local.get $4) - (local.get $3) - ) - (i32.store - (local.get $5) - (local.get $4) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - (local.set $12 - (i32.load offset=24 - (local.get $0) - ) - ) - (block $label$22 - (if - (i32.eq - (local.tee $4 - (i32.load offset=12 - (local.get $0) - ) - ) - (local.get $0) - ) - (block - (if - (local.tee $4 - (i32.load - (local.tee $8 - (i32.add - (local.tee $5 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - (local.set $5 - (local.get $8) - ) - (if - (i32.eqz - (local.tee $4 - (i32.load - (local.get $5) - ) - ) - ) - (block - (local.set $7 - (i32.const 0) - ) - (br $label$22) - ) - ) - ) - (loop $label$27 - (if - (local.tee $10 - (i32.load - (local.tee $8 - (i32.add - (local.get $4) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $4 - (local.get $10) - ) - (local.set $5 - (local.get $8) - ) - (br $label$27) - ) - ) - (if - (local.tee $10 - (i32.load - (local.tee $8 - (i32.add - (local.get $4) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $4 - (local.get $10) - ) - (local.set $5 - (local.get $8) - ) - (br $label$27) - ) - ) - ) - (if - (i32.lt_u - (local.get $5) - (local.get $11) - ) - (call $fimport$10) - (block - (i32.store - (local.get $5) - (i32.const 0) - ) - (local.set $7 - (local.get $4) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $5 - (i32.load offset=8 - (local.get $0) - ) - ) - (local.get $11) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $8 - (i32.add - (local.get $5) - (i32.const 12) - ) - ) - ) - (local.get $0) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $10 - (i32.add - (local.get $4) - (i32.const 8) - ) - ) - ) - (local.get $0) - ) - (block - (i32.store - (local.get $8) - (local.get $4) - ) - (i32.store - (local.get $10) - (local.get $5) - ) - (local.set $7 - (local.get $4) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (if - (local.get $12) - (block - (if - (i32.eq - (local.get $0) - (i32.load - (local.tee $5 - (i32.add - (i32.shl - (local.tee $4 - (i32.load offset=28 - (local.get $0) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - ) - ) - (block - (i32.store - (local.get $5) - (local.get $7) - ) - (if - (i32.eqz - (local.get $7) - ) - (block - (i32.store - (i32.const 3648) - (i32.and - (i32.load - (i32.const 3648) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $12) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $4 - (i32.add - (local.get $12) - (i32.const 16) - ) - ) - ) - (local.get $0) - ) - (i32.store - (local.get $4) - (local.get $7) - ) - (i32.store offset=20 - (local.get $12) - (local.get $7) - ) - ) - (if - (i32.eqz - (local.get $7) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - (br $label$5) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $7) - (local.tee $5 - (i32.load - (i32.const 3660) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $7) - (local.get $12) - ) - (if - (local.tee $4 - (i32.load - (local.tee $8 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $4) - (local.get $5) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $7) - (local.get $4) - ) - (i32.store offset=24 - (local.get $4) - (local.get $7) - ) - ) - ) - ) - (if - (local.tee $4 - (i32.load offset=4 - (local.get $8) - ) - ) - (if - (i32.lt_u - (local.get $4) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $7) - (local.get $4) - ) - (i32.store offset=24 - (local.get $4) - (local.get $7) - ) - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - ) - (block - (local.set $3 - (local.get $0) - ) - (local.set $2 - (local.get $1) - ) - ) - ) - ) - ) - ) - (if - (i32.ge_u - (local.get $3) - (local.get $6) - ) - (call $fimport$10) - ) - (if - (i32.eqz - (i32.and - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $6) - (i32.const 4) - ) - ) - ) - ) - (i32.const 1) - ) - ) - (call $fimport$10) - ) - (if - (i32.and - (local.get $0) - (i32.const 2) - ) - (block - (i32.store - (local.get $1) - (i32.and - (local.get $0) - (i32.const -2) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $2) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $2) - ) - (local.get $2) - ) - ) - (block - (if - (i32.eq - (local.get $6) - (i32.load - (i32.const 3668) - ) - ) - (block - (i32.store - (i32.const 3656) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3656) - ) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 3668) - (local.get $3) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (if - (i32.ne - (local.get $3) - (i32.load - (i32.const 3664) - ) - ) - (return) - ) - (i32.store - (i32.const 3664) - (i32.const 0) - ) - (i32.store - (i32.const 3652) - (i32.const 0) - ) - (return) - ) - ) - (if - (i32.eq - (local.get $6) - (i32.load - (i32.const 3664) - ) - ) - (block - (i32.store - (i32.const 3652) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3652) - ) - (local.get $2) - ) - ) - ) - (i32.store - (i32.const 3664) - (local.get $3) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $0) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $0) - ) - (local.get $0) - ) - (return) - ) - ) - (local.set $5 - (i32.add - (i32.and - (local.get $0) - (i32.const -8) - ) - (local.get $2) - ) - ) - (local.set $4 - (i32.shr_u - (local.get $0) - (i32.const 3) - ) - ) - (block $label$61 - (if - (i32.lt_u - (local.get $0) - (i32.const 256) - ) - (block - (local.set $2 - (i32.load offset=12 - (local.get $6) - ) - ) - (if - (i32.ne - (local.tee $1 - (i32.load offset=8 - (local.get $6) - ) - ) - (local.tee $0 - (i32.add - (i32.shl - (i32.shl - (local.get $4) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $1) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load offset=12 - (local.get $1) - ) - (local.get $6) - ) - (call $fimport$10) - ) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $1) - ) - (block - (i32.store - (i32.const 3644) - (i32.and - (i32.load - (i32.const 3644) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $4) - ) - (i32.const -1) - ) - ) - ) - (br $label$61) - ) - ) - (if - (i32.eq - (local.get $2) - (local.get $0) - ) - (local.set $14 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - (block - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $2) - (i32.const 8) - ) - ) - ) - (local.get $6) - ) - (local.set $14 - (local.get $0) - ) - (call $fimport$10) - ) - ) - ) - (i32.store offset=12 - (local.get $1) - (local.get $2) - ) - (i32.store - (local.get $14) - (local.get $1) - ) - ) - (block - (local.set $7 - (i32.load offset=24 - (local.get $6) - ) - ) - (block $label$73 - (if - (i32.eq - (local.tee $0 - (i32.load offset=12 - (local.get $6) - ) - ) - (local.get $6) - ) - (block - (if - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.tee $2 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - (i32.const 4) - ) - ) - ) - ) - (local.set $2 - (local.get $1) - ) - (if - (i32.eqz - (local.tee $0 - (i32.load - (local.get $2) - ) - ) - ) - (block - (local.set $9 - (i32.const 0) - ) - (br $label$73) - ) - ) - ) - (loop $label$78 - (if - (local.tee $4 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (local.set $2 - (local.get $1) - ) - (br $label$78) - ) - ) - (if - (local.tee $4 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 16) - ) - ) - ) - ) - (block - (local.set $0 - (local.get $4) - ) - (local.set $2 - (local.get $1) - ) - (br $label$78) - ) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $2) - (i32.const 0) - ) - (local.set $9 - (local.get $0) - ) - ) - ) - ) - (block - (if - (i32.lt_u - (local.tee $2 - (i32.load offset=8 - (local.get $6) - ) - ) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - ) - (if - (i32.ne - (i32.load - (local.tee $1 - (i32.add - (local.get $2) - (i32.const 12) - ) - ) - ) - (local.get $6) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $4 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - (local.get $6) - ) - (block - (i32.store - (local.get $1) - (local.get $0) - ) - (i32.store - (local.get $4) - (local.get $2) - ) - (local.set $9 - (local.get $0) - ) - ) - (call $fimport$10) - ) - ) - ) - ) - (if - (local.get $7) - (block - (if - (i32.eq - (local.get $6) - (i32.load - (local.tee $2 - (i32.add - (i32.shl - (local.tee $0 - (i32.load offset=28 - (local.get $6) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - ) - ) - (block - (i32.store - (local.get $2) - (local.get $9) - ) - (if - (i32.eqz - (local.get $9) - ) - (block - (i32.store - (i32.const 3648) - (i32.and - (i32.load - (i32.const 3648) - ) - (i32.xor - (i32.shl - (i32.const 1) - (local.get $0) - ) - (i32.const -1) - ) - ) - ) - (br $label$61) - ) - ) - ) - (block - (if - (i32.lt_u - (local.get $7) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - ) - (if - (i32.eq - (i32.load - (local.tee $0 - (i32.add - (local.get $7) - (i32.const 16) - ) - ) - ) - (local.get $6) - ) - (i32.store - (local.get $0) - (local.get $9) - ) - (i32.store offset=20 - (local.get $7) - (local.get $9) - ) - ) - (br_if $label$61 - (i32.eqz - (local.get $9) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $9) - (local.tee $2 - (i32.load - (i32.const 3660) - ) - ) - ) - (call $fimport$10) - ) - (i32.store offset=24 - (local.get $9) - (local.get $7) - ) - (if - (local.tee $0 - (i32.load - (local.tee $1 - (i32.add - (local.get $6) - (i32.const 16) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $0) - (local.get $2) - ) - (call $fimport$10) - (block - (i32.store offset=16 - (local.get $9) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $9) - ) - ) - ) - ) - (if - (local.tee $0 - (i32.load offset=4 - (local.get $1) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (i32.store offset=20 - (local.get $9) - (local.get $0) - ) - (i32.store offset=24 - (local.get $0) - (local.get $9) - ) - ) - ) - ) - ) - ) - ) - ) - ) - (i32.store offset=4 - (local.get $3) - (i32.or - (local.get $5) - (i32.const 1) - ) - ) - (i32.store - (i32.add - (local.get $3) - (local.get $5) - ) - (local.get $5) - ) - (if - (i32.eq - (local.get $3) - (i32.load - (i32.const 3664) - ) - ) - (block - (i32.store - (i32.const 3652) - (local.get $5) - ) - (return) - ) - (local.set $2 - (local.get $5) - ) - ) - ) - ) - (local.set $1 - (i32.shr_u - (local.get $2) - (i32.const 3) - ) - ) - (if - (i32.lt_u - (local.get $2) - (i32.const 256) - ) - (block - (local.set $0 - (i32.add - (i32.shl - (i32.shl - (local.get $1) - (i32.const 1) - ) - (i32.const 2) - ) - (i32.const 3684) - ) - ) - (if - (i32.and - (local.tee $2 - (i32.load - (i32.const 3644) - ) - ) - (local.tee $1 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (if - (i32.lt_u - (local.tee $1 - (i32.load - (local.tee $2 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (local.set $15 - (local.get $2) - ) - (local.set $13 - (local.get $1) - ) - ) - ) - (block - (i32.store - (i32.const 3644) - (i32.or - (local.get $2) - (local.get $1) - ) - ) - (local.set $15 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - (local.set $13 - (local.get $0) - ) - ) - ) - (i32.store - (local.get $15) - (local.get $3) - ) - (i32.store offset=12 - (local.get $13) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $13) - ) - (i32.store offset=12 - (local.get $3) - (local.get $0) - ) - (return) - ) - ) - (local.set $0 - (i32.add - (i32.shl - (local.tee $1 - (if (result i32) - (local.tee $0 - (i32.shr_u - (local.get $2) - (i32.const 8) - ) - ) - (if (result i32) - (i32.gt_u - (local.get $2) - (i32.const 16777215) - ) - (i32.const 31) - (i32.or - (i32.and - (i32.shr_u - (local.get $2) - (i32.add - (local.tee $0 - (i32.add - (i32.sub - (i32.const 14) - (i32.or - (i32.or - (local.tee $4 - (i32.and - (i32.shr_u - (i32.add - (local.tee $1 - (i32.shl - (local.get $0) - (local.tee $0 - (i32.and - (i32.shr_u - (i32.add - (local.get $0) - (i32.const 1048320) - ) - (i32.const 16) - ) - (i32.const 8) - ) - ) - ) - ) - (i32.const 520192) - ) - (i32.const 16) - ) - (i32.const 4) - ) - ) - (local.get $0) - ) - (local.tee $1 - (i32.and - (i32.shr_u - (i32.add - (local.tee $0 - (i32.shl - (local.get $1) - (local.get $4) - ) - ) - (i32.const 245760) - ) - (i32.const 16) - ) - (i32.const 2) - ) - ) - ) - ) - (i32.shr_u - (i32.shl - (local.get $0) - (local.get $1) - ) - (i32.const 15) - ) - ) - ) - (i32.const 7) - ) - ) - (i32.const 1) - ) - (i32.shl - (local.get $0) - (i32.const 1) - ) - ) - ) - (i32.const 0) - ) - ) - (i32.const 2) - ) - (i32.const 3948) - ) - ) - (i32.store offset=28 - (local.get $3) - (local.get $1) - ) - (i32.store offset=20 - (local.get $3) - (i32.const 0) - ) - (i32.store offset=16 - (local.get $3) - (i32.const 0) - ) - (block $label$113 - (if - (i32.and - (local.tee $4 - (i32.load - (i32.const 3648) - ) - ) - (local.tee $5 - (i32.shl - (i32.const 1) - (local.get $1) - ) - ) - ) - (block - (local.set $0 - (i32.load - (local.get $0) - ) - ) - (local.set $4 - (i32.sub - (i32.const 25) - (i32.shr_u - (local.get $1) - (i32.const 1) - ) - ) - ) - (local.set $1 - (i32.shl - (local.get $2) - (if (result i32) - (i32.eq - (local.get $1) - (i32.const 31) - ) - (i32.const 0) - (local.get $4) - ) - ) - ) - (block $label$117 - (block $label$118 - (block $label$119 - (loop $label$120 - (br_if $label$118 - (i32.eq - (i32.and - (i32.load offset=4 - (local.get $0) - ) - (i32.const -8) - ) - (local.get $2) - ) - ) - (local.set $4 - (i32.shl - (local.get $1) - (i32.const 1) - ) - ) - (br_if $label$119 - (i32.eqz - (local.tee $5 - (i32.load - (local.tee $1 - (i32.add - (i32.add - (local.get $0) - (i32.const 16) - ) - (i32.shl - (i32.shr_u - (local.get $1) - (i32.const 31) - ) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (local.set $1 - (local.get $4) - ) - (local.set $0 - (local.get $5) - ) - (br $label$120) - ) - ) - (if - (i32.lt_u - (local.get $1) - (i32.load - (i32.const 3660) - ) - ) - (call $fimport$10) - (block - (i32.store - (local.get $1) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $0) - ) - (i32.store offset=12 - (local.get $3) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $3) - ) - (br $label$113) - ) - ) - (br $label$117) - ) - (if - (i32.and - (i32.ge_u - (local.tee $2 - (i32.load - (local.tee $1 - (i32.add - (local.get $0) - (i32.const 8) - ) - ) - ) - ) - (local.tee $4 - (i32.load - (i32.const 3660) - ) - ) - ) - (i32.ge_u - (local.get $0) - (local.get $4) - ) - ) - (block - (i32.store offset=12 - (local.get $2) - (local.get $3) - ) - (i32.store - (local.get $1) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $2) - ) - (i32.store offset=12 - (local.get $3) - (local.get $0) - ) - (i32.store offset=24 - (local.get $3) - (i32.const 0) - ) - ) - (call $fimport$10) - ) - ) - ) - (block - (i32.store - (i32.const 3648) - (i32.or - (local.get $4) - (local.get $5) - ) - ) - (i32.store - (local.get $0) - (local.get $3) - ) - (i32.store offset=24 - (local.get $3) - (local.get $0) - ) - (i32.store offset=12 - (local.get $3) - (local.get $3) - ) - (i32.store offset=8 - (local.get $3) - (local.get $3) - ) - ) - ) - ) - (i32.store - (i32.const 3676) - (local.tee $0 - (i32.add - (i32.load - (i32.const 3676) - ) - (i32.const -1) - ) - ) - ) - (if - (local.get $0) - (return) - (local.set $0 - (i32.const 4100) - ) - ) - (loop $label$128 - (local.set $0 - (i32.add - (local.tee $2 - (i32.load - (local.get $0) - ) - ) - (i32.const 8) - ) - ) - (br_if $label$128 - (local.get $2) - ) - ) - (i32.store - (i32.const 3676) - (i32.const -1) - ) + (func $27 (;40;) (type $11) (param $0 f64) (param $1 i32) (result f64) + (local $2 i64) (local $3 i64) + block $label$1 (result f64) ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + block $label$4 ;; label = @4 + block $label$5 ;; label = @5 + local.get $0 + i64.reinterpret_f64 + local.tee $2 + i64.const 52 + i64.shr_u + local.tee $3 + i32.wrap_i64 + i32.const 65535 + i32.and + i32.const 2047 + i32.and + i32.const 16 + i32.shl + i32.const 16 + i32.shr_s + i32.const 0 + i32.sub + br_table 0 (;@5;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 2 (;@3;) 1 (;@4;) 2 (;@3;) + end + local.get $1 + local.get $0 + f64.const 0x0p+0 (;=0;) + f64.ne + if (result i32) ;; label = @5 + block (result i32) ;; label = @6 + local.get $0 + f64.const 0x1p+64 (;=18446744073709552000;) + f64.mul + local.get $1 + call $27 + local.set $0 + local.get $1 + i32.load + i32.const -64 + i32.add + end + else + i32.const 0 + end + i32.store + br 2 (;@2;) + end + br 1 (;@2;) + end + local.get $1 + local.get $3 + i32.wrap_i64 + i32.const 2047 + i32.and + i32.const -1022 + i32.add + i32.store + local.get $2 + i64.const -9218868437227405313 + i64.and + i64.const 4602678819172646912 + i64.or + f64.reinterpret_i64 + local.set $0 + end + local.get $0 + end ) - ) - (func $33 (; 46 ;) (type $6) - (nop) - ) - (func $34 (; 47 ;) (type $1) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (block $label$1 (result i32) - (local.set $1 - (i32.add - (local.tee $2 - (i32.load - (global.get $global$0) - ) - ) - (local.tee $0 - (i32.and - (i32.add - (local.get $0) - (i32.const 15) - ) - (i32.const -16) - ) - ) - ) - ) - (if - (i32.or - (i32.and - (i32.gt_s - (local.get $0) - (i32.const 0) - ) - (i32.lt_s - (local.get $1) - (local.get $2) - ) - ) - (i32.lt_s - (local.get $1) - (i32.const 0) - ) - ) - (block - (drop - (call $fimport$6) - ) - (call $fimport$11 - (i32.const 12) - ) - (return - (i32.const -1) - ) - ) - ) - (i32.store - (global.get $global$0) - (local.get $1) - ) - (if - (i32.gt_s - (local.get $1) - (call $fimport$5) - ) - (if - (i32.eqz - (call $fimport$4) - ) - (block - (call $fimport$11 - (i32.const 12) - ) - (i32.store - (global.get $global$0) - (local.get $2) - ) - (return - (i32.const -1) - ) - ) - ) - ) - (local.get $2) + (func $28 (;41;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $1 + i32.const 128 + i32.lt_u + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.store8 + i32.const 1 + br 4 (;@1;) + end + end + local.get $1 + i32.const 2048 + i32.lt_u + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 192 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + i32.const 2 + br 4 (;@1;) + end + end + local.get $1 + i32.const 55296 + i32.lt_u + local.get $1 + i32.const -8192 + i32.and + i32.const 57344 + i32.eq + i32.or + if ;; label = @4 + block ;; label = @5 + local.get $0 + local.get $1 + i32.const 12 + i32.shr_u + i32.const 224 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=2 + i32.const 3 + br 4 (;@1;) + end + end + local.get $1 + i32.const -65536 + i32.add + i32.const 1048576 + i32.lt_u + if (result i32) ;; label = @4 + block (result i32) ;; label = @5 + local.get $0 + local.get $1 + i32.const 18 + i32.shr_u + i32.const 240 + i32.or + i32.store8 + local.get $0 + local.get $1 + i32.const 12 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=1 + local.get $0 + local.get $1 + i32.const 6 + i32.shr_u + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=2 + local.get $0 + local.get $1 + i32.const 63 + i32.and + i32.const 128 + i32.or + i32.store8 offset=3 + i32.const 4 + end + else + block (result i32) ;; label = @5 + call $11 + i32.const 84 + i32.store + i32.const -1 + end + end + end + else + i32.const 1 + end + end ) - ) - (func $35 (; 48 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (block $label$1 (result i32) - (local.set $4 - (i32.add - (local.get $0) - (local.get $2) - ) - ) - (if - (i32.ge_s - (local.get $2) - (i32.const 20) - ) - (block - (local.set $1 - (i32.and - (local.get $1) - (i32.const 255) - ) - ) - (if - (local.tee $3 - (i32.and - (local.get $0) - (i32.const 3) - ) - ) - (block - (local.set $3 - (i32.sub - (i32.add - (local.get $0) - (i32.const 4) - ) - (local.get $3) - ) - ) - (loop $label$4 - (if - (i32.lt_s - (local.get $0) - (local.get $3) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (br $label$4) - ) - ) - ) - ) - ) - (local.set $3 - (i32.or - (i32.or - (i32.or - (local.get $1) - (i32.shl - (local.get $1) - (i32.const 8) - ) - ) - (i32.shl - (local.get $1) - (i32.const 16) - ) - ) - (i32.shl - (local.get $1) - (i32.const 24) - ) - ) - ) - (local.set $5 - (i32.and - (local.get $4) - (i32.const -4) - ) - ) - (loop $label$6 - (if - (i32.lt_s - (local.get $0) - (local.get $5) - ) - (block - (i32.store - (local.get $0) - (local.get $3) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - (br $label$6) - ) - ) - ) - ) - ) - (loop $label$8 - (if - (i32.lt_s - (local.get $0) - (local.get $4) - ) - (block - (i32.store8 - (local.get $0) - (local.get $1) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (br $label$8) - ) - ) - ) - (i32.sub - (local.get $0) - (local.get $2) - ) + (func $29 (;42;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + i32.const 74 + i32.add + local.tee $2 + i32.load8_s + local.set $1 + local.get $2 + local.get $1 + i32.const 255 + i32.add + local.get $1 + i32.or + i32.store8 + local.get $0 + i32.load + local.tee $1 + i32.const 8 + i32.and + if (result i32) ;; label = @2 + block (result i32) ;; label = @3 + local.get $0 + local.get $1 + i32.const 32 + i32.or + i32.store + i32.const -1 + end + else + block (result i32) ;; label = @3 + local.get $0 + i32.const 0 + i32.store offset=8 + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + local.get $0 + i32.load offset=44 + local.tee $1 + i32.store offset=28 + local.get $0 + local.get $1 + i32.store offset=20 + local.get $0 + local.get $1 + local.get $0 + i32.load offset=48 + i32.add + i32.store offset=16 + i32.const 0 + end + end + local.tee $0 + end ) - ) - (func $36 (; 49 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (block $label$1 (result i32) - (if - (i32.ge_s - (local.get $2) - (i32.const 4096) - ) - (return - (call $fimport$12 - (local.get $0) - (local.get $1) - (local.get $2) - ) - ) - ) - (local.set $3 - (local.get $0) - ) - (if - (i32.eq - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.and - (local.get $1) - (i32.const 3) - ) - ) - (block - (loop $label$4 - (if - (i32.and - (local.get $0) - (i32.const 3) - ) - (block - (if - (i32.eqz - (local.get $2) - ) - (return - (local.get $3) - ) - ) - (i32.store8 - (local.get $0) - (i32.load8_s - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 1) - ) - ) - (br $label$4) - ) - ) - ) - (loop $label$7 - (if - (i32.ge_s - (local.get $2) - (i32.const 4) - ) - (block - (i32.store - (local.get $0) - (i32.load - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 4) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 4) - ) - ) - (br $label$7) - ) - ) - ) - ) - ) - (loop $label$9 - (if - (i32.gt_s - (local.get $2) - (i32.const 0) - ) - (block - (i32.store8 - (local.get $0) - (i32.load8_s - (local.get $1) - ) - ) - (local.set $0 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (local.set $1 - (i32.add - (local.get $1) - (i32.const 1) - ) - ) - (local.set $2 - (i32.sub - (local.get $2) - (i32.const 1) - ) - ) - (br $label$9) - ) - ) - ) - (local.get $3) + (func $30 (;43;) (type $4) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $2 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $2 + local.tee $3 + local.get $1 + i32.store + i32.const 1024 + i32.load + local.get $0 + local.get $3 + call $17 + local.set $0 + local.get $2 + global.set $global$1 + local.get $0 + end ) - ) - (func $37 (; 50 ;) (type $3) (result i32) - (i32.const 0) - ) - (func $38 (; 51 ;) (type $4) (param $0 i32) (param $1 i32) (result i32) - (call_indirect (type $1) - (local.get $1) - (i32.add - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 0) - ) + (func $31 (;44;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$1 + local.set $14 + global.get $global$1 + i32.const 16 + i32.add + global.set $global$1 + local.get $14 + local.set $18 + block $label$2 ;; label = @2 + local.get $0 + i32.const 245 + i32.lt_u + if ;; label = @3 + block ;; label = @4 + local.get $0 + i32.const 11 + i32.add + i32.const -8 + i32.and + local.set $3 + i32.const 3644 + i32.load + local.tee $8 + local.get $0 + i32.const 11 + i32.lt_u + if (result i32) ;; label = @5 + i32.const 16 + local.tee $3 + else + local.get $3 + end + i32.const 3 + i32.shr_u + local.tee $2 + i32.shr_u + local.tee $0 + i32.const 3 + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $0 + i32.const 1 + i32.and + i32.const 1 + i32.xor + local.get $2 + i32.add + local.tee $5 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.tee $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $7 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.set $4 + local.get $2 + local.get $4 + i32.eq + if ;; label = @7 + i32.const 3644 + local.get $8 + i32.const 1 + local.get $5 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + else + block ;; label = @8 + local.get $4 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $4 + i32.const 12 + i32.add + local.tee $0 + i32.load + local.get $7 + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $0 + local.get $2 + i32.store + local.get $3 + local.get $4 + i32.store + end + else + call $fimport$10 + end + end + end + local.get $7 + local.get $5 + i32.const 3 + i32.shl + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $7 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + local.get $14 + global.set $global$1 + local.get $1 + return + end + end + local.get $3 + i32.const 3652 + i32.load + local.tee $16 + i32.gt_u + if ;; label = @5 + block ;; label = @6 + local.get $0 + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.get $2 + i32.shl + i32.const 2 + local.get $2 + i32.shl + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.or + i32.and + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $5 + local.get $0 + local.get $5 + i32.shr_u + local.tee $2 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $5 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + local.tee $2 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $2 + local.get $0 + i32.shr_u + i32.add + local.tee $11 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.tee $4 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $9 + i32.const 8 + i32.add + local.tee $5 + i32.load + local.set $12 + local.get $4 + local.get $12 + i32.eq + if ;; label = @9 + i32.const 3644 + local.get $8 + i32.const 1 + local.get $11 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $7 + i32.store + else + block ;; label = @10 + local.get $12 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$10 + end + local.get $12 + i32.const 12 + i32.add + local.tee $0 + i32.load + local.get $9 + i32.eq + if ;; label = @11 + block ;; label = @12 + local.get $0 + local.get $4 + i32.store + local.get $2 + local.get $12 + i32.store + local.get $8 + local.set $7 + end + else + call $fimport$10 + end + end + end + local.get $9 + local.get $3 + i32.const 3 + i32.or + i32.store offset=4 + local.get $9 + local.get $3 + i32.add + local.tee $4 + local.get $11 + i32.const 3 + i32.shl + local.get $3 + i32.sub + local.tee $11 + i32.const 1 + i32.or + i32.store offset=4 + local.get $4 + local.get $11 + i32.add + local.get $11 + i32.store + local.get $16 + if ;; label = @9 + block ;; label = @10 + i32.const 3664 + i32.load + local.set $9 + local.get $16 + i32.const 3 + i32.shr_u + local.tee $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.set $2 + local.get $7 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @11 + local.get $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $0 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @12 + call $fimport$10 + else + block ;; label = @13 + local.get $3 + local.set $6 + local.get $0 + local.set $1 + end + end + else + block ;; label = @12 + i32.const 3644 + local.get $7 + local.get $0 + i32.or + i32.store + local.get $2 + i32.const 8 + i32.add + local.set $6 + local.get $2 + local.set $1 + end + end + local.get $6 + local.get $9 + i32.store + local.get $1 + local.get $9 + i32.store offset=12 + local.get $9 + local.get $1 + i32.store offset=8 + local.get $9 + local.get $2 + i32.store offset=12 + end + end + i32.const 3652 + local.get $11 + i32.store + i32.const 3664 + local.get $4 + i32.store + local.get $14 + global.set $global$1 + local.get $5 + return + end + end + i32.const 3648 + i32.load + local.tee $6 + if ;; label = @7 + block ;; label = @8 + local.get $6 + i32.const 0 + local.get $6 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $2 + local.get $0 + local.get $2 + i32.shr_u + local.tee $1 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $2 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + local.tee $1 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $1 + local.get $0 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 3948 + i32.add + i32.load + local.tee $2 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.sub + local.set $9 + local.get $2 + local.set $1 + loop $label$25 ;; label = @9 + block $label$26 ;; label = @10 + local.get $1 + i32.load offset=16 + local.tee $0 + i32.eqz + if ;; label = @11 + local.get $1 + i32.load offset=20 + local.tee $0 + i32.eqz + br_if 1 (;@10;) + end + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.sub + local.tee $1 + local.get $9 + i32.lt_u + local.tee $7 + if ;; label = @11 + local.get $1 + local.set $9 + end + local.get $0 + local.set $1 + local.get $7 + if ;; label = @11 + local.get $0 + local.set $2 + end + br 1 (;@9;) + end + end + local.get $2 + i32.const 3660 + i32.load + local.tee $12 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $2 + local.get $2 + local.get $3 + i32.add + local.tee $13 + i32.ge_u + if ;; label = @9 + call $fimport$10 + end + local.get $2 + i32.load offset=24 + local.set $15 + block $label$32 ;; label = @9 + local.get $2 + i32.load offset=12 + local.tee $0 + local.get $2 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $2 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @12 + local.get $2 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @13 + block ;; label = @14 + i32.const 0 + local.set $4 + br 5 (;@9;) + end + end + end + loop $label$36 ;; label = @12 + local.get $0 + i32.const 20 + i32.add + local.tee $11 + i32.load + local.tee $7 + if ;; label = @13 + block ;; label = @14 + local.get $7 + local.set $0 + local.get $11 + local.set $1 + br 2 (;@12;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $11 + i32.load + local.tee $7 + if ;; label = @13 + block ;; label = @14 + local.get $7 + local.set $0 + local.get $11 + local.set $1 + br 2 (;@12;) + end + end + end + local.get $1 + local.get $12 + i32.lt_u + if ;; label = @12 + call $fimport$10 + else + block ;; label = @13 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $4 + end + end + end + else + block ;; label = @11 + local.get $2 + i32.load offset=8 + local.tee $11 + local.get $12 + i32.lt_u + if ;; label = @12 + call $fimport$10 + end + local.get $11 + i32.const 12 + i32.add + local.tee $7 + i32.load + local.get $2 + i32.ne + if ;; label = @12 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $2 + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $7 + local.get $0 + i32.store + local.get $1 + local.get $11 + i32.store + local.get $0 + local.set $4 + end + else + call $fimport$10 + end + end + end + end + block $label$46 ;; label = @9 + local.get $15 + if ;; label = @10 + block ;; label = @11 + local.get $2 + local.get $2 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 3948 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $0 + local.get $4 + i32.store + local.get $4 + i32.eqz + if ;; label = @14 + block ;; label = @15 + i32.const 3648 + local.get $6 + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 6 (;@9;) + end + end + end + else + block ;; label = @13 + local.get $15 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$10 + end + local.get $15 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $2 + i32.eq + if ;; label = @14 + local.get $0 + local.get $4 + i32.store + else + local.get $15 + local.get $4 + i32.store offset=20 + end + local.get $4 + i32.eqz + br_if 4 (;@9;) + end + end + local.get $4 + i32.const 3660 + i32.load + local.tee $0 + i32.lt_u + if ;; label = @12 + call $fimport$10 + end + local.get $4 + local.get $15 + i32.store offset=24 + local.get $2 + i32.load offset=16 + local.tee $1 + if ;; label = @12 + local.get $1 + local.get $0 + i32.lt_u + if ;; label = @13 + call $fimport$10 + else + block ;; label = @14 + local.get $4 + local.get $1 + i32.store offset=16 + local.get $1 + local.get $4 + i32.store offset=24 + end + end + end + local.get $2 + i32.load offset=20 + local.tee $0 + if ;; label = @12 + local.get $0 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @13 + call $fimport$10 + else + block ;; label = @14 + local.get $4 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $4 + i32.store offset=24 + end + end + end + end + end + end + local.get $9 + i32.const 16 + i32.lt_u + if ;; label = @9 + block ;; label = @10 + local.get $2 + local.get $9 + local.get $3 + i32.add + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + else + block ;; label = @10 + local.get $2 + local.get $3 + i32.const 3 + i32.or + i32.store offset=4 + local.get $13 + local.get $9 + i32.const 1 + i32.or + i32.store offset=4 + local.get $13 + local.get $9 + i32.add + local.get $9 + i32.store + local.get $16 + if ;; label = @11 + block ;; label = @12 + i32.const 3664 + i32.load + local.set $7 + local.get $16 + i32.const 3 + i32.shr_u + local.tee $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.set $3 + local.get $8 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @13 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$10 + else + block ;; label = @15 + local.get $1 + local.set $10 + local.get $0 + local.set $5 + end + end + else + block ;; label = @14 + i32.const 3644 + local.get $8 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $10 + local.get $3 + local.set $5 + end + end + local.get $10 + local.get $7 + i32.store + local.get $5 + local.get $7 + i32.store offset=12 + local.get $7 + local.get $5 + i32.store offset=8 + local.get $7 + local.get $3 + i32.store offset=12 + end + end + i32.const 3652 + local.get $9 + i32.store + i32.const 3664 + local.get $13 + i32.store + end + end + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + else + local.get $3 + local.set $0 + end + end + else + local.get $3 + local.set $0 + end + end + else + local.get $0 + i32.const -65 + i32.gt_u + if ;; label = @4 + i32.const -1 + local.set $0 + else + block ;; label = @5 + local.get $0 + i32.const 11 + i32.add + local.tee $0 + i32.const -8 + i32.and + local.set $7 + i32.const 3648 + i32.load + local.tee $5 + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @8 + local.get $7 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @9 + i32.const 31 + else + local.get $7 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $3 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $3 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.set $17 + i32.const 0 + local.get $7 + i32.sub + local.set $3 + block $label$78 ;; label = @8 + block $label$79 ;; label = @9 + block $label$80 ;; label = @10 + local.get $17 + i32.const 2 + i32.shl + i32.const 3948 + i32.add + i32.load + local.tee $1 + if ;; label = @11 + block ;; label = @12 + i32.const 25 + local.get $17 + i32.const 1 + i32.shr_u + i32.sub + local.set $0 + i32.const 0 + local.set $4 + local.get $7 + local.get $17 + i32.const 31 + i32.eq + if (result i32) ;; label = @13 + i32.const 0 + else + local.get $0 + end + i32.shl + local.set $10 + i32.const 0 + local.set $0 + loop $label$84 ;; label = @13 + local.get $1 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.sub + local.tee $6 + local.get $3 + i32.lt_u + if ;; label = @14 + local.get $6 + if ;; label = @15 + block ;; label = @16 + local.get $6 + local.set $3 + local.get $1 + local.set $0 + end + else + block ;; label = @16 + i32.const 0 + local.set $3 + local.get $1 + local.set $0 + br 7 (;@9;) + end + end + end + local.get $1 + i32.load offset=20 + local.tee $19 + i32.eqz + local.get $19 + local.get $1 + i32.const 16 + i32.add + local.get $10 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + i32.load + local.tee $6 + i32.eq + i32.or + if (result i32) ;; label = @14 + local.get $4 + else + local.get $19 + end + local.set $1 + local.get $10 + local.get $6 + i32.eqz + local.tee $4 + i32.const 1 + i32.and + i32.const 1 + i32.xor + i32.shl + local.set $10 + local.get $4 + if ;; label = @14 + block ;; label = @15 + local.get $1 + local.set $4 + local.get $0 + local.set $1 + br 5 (;@10;) + end + else + block ;; label = @15 + local.get $1 + local.set $4 + local.get $6 + local.set $1 + br 2 (;@13;) + end + end + end + end + else + block ;; label = @12 + i32.const 0 + local.set $4 + i32.const 0 + local.set $1 + end + end + end + local.get $4 + i32.eqz + local.get $1 + i32.eqz + i32.and + if (result i32) ;; label = @10 + block (result i32) ;; label = @11 + local.get $5 + i32.const 2 + local.get $17 + i32.shl + local.tee $0 + i32.const 0 + local.get $0 + i32.sub + i32.or + i32.and + local.tee $0 + i32.eqz + if ;; label = @12 + block ;; label = @13 + local.get $7 + local.set $0 + br 11 (;@2;) + end + end + local.get $0 + i32.const 0 + local.get $0 + i32.sub + i32.and + i32.const -1 + i32.add + local.tee $0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + local.set $10 + local.get $0 + local.get $10 + i32.shr_u + local.tee $4 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + local.get $10 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + local.tee $4 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + local.tee $0 + i32.or + local.get $4 + local.get $0 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 3948 + i32.add + i32.load + end + else + local.get $4 + end + local.tee $0 + br_if 0 (;@9;) + local.get $1 + local.set $4 + br 1 (;@8;) + end + loop $label$96 ;; label = @9 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.sub + local.tee $4 + local.get $3 + i32.lt_u + local.tee $10 + if ;; label = @10 + local.get $4 + local.set $3 + end + local.get $10 + if ;; label = @10 + local.get $0 + local.set $1 + end + local.get $0 + i32.load offset=16 + local.tee $4 + if ;; label = @10 + block ;; label = @11 + local.get $4 + local.set $0 + br 2 (;@9;) + end + end + local.get $0 + i32.load offset=20 + local.tee $0 + br_if 0 (;@9;) + local.get $1 + local.set $4 + end + end + local.get $4 + if ;; label = @8 + local.get $3 + i32.const 3652 + i32.load + local.get $7 + i32.sub + i32.lt_u + if ;; label = @9 + block ;; label = @10 + local.get $4 + i32.const 3660 + i32.load + local.tee $12 + i32.lt_u + if ;; label = @11 + call $fimport$10 + end + local.get $4 + local.get $4 + local.get $7 + i32.add + local.tee $6 + i32.ge_u + if ;; label = @11 + call $fimport$10 + end + local.get $4 + i32.load offset=24 + local.set $10 + block $label$104 ;; label = @11 + local.get $4 + i32.load offset=12 + local.tee $0 + local.get $4 + i32.eq + if ;; label = @12 + block ;; label = @13 + local.get $4 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @14 + local.get $4 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @15 + block ;; label = @16 + i32.const 0 + local.set $13 + br 5 (;@11;) + end + end + end + loop $label$108 ;; label = @14 + local.get $0 + i32.const 20 + i32.add + local.tee $9 + i32.load + local.tee $11 + if ;; label = @15 + block ;; label = @16 + local.get $11 + local.set $0 + local.get $9 + local.set $1 + br 2 (;@14;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $9 + i32.load + local.tee $11 + if ;; label = @15 + block ;; label = @16 + local.get $11 + local.set $0 + local.get $9 + local.set $1 + br 2 (;@14;) + end + end + end + local.get $1 + local.get $12 + i32.lt_u + if ;; label = @14 + call $fimport$10 + else + block ;; label = @15 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $13 + end + end + end + else + block ;; label = @13 + local.get $4 + i32.load offset=8 + local.tee $9 + local.get $12 + i32.lt_u + if ;; label = @14 + call $fimport$10 + end + local.get $9 + i32.const 12 + i32.add + local.tee $11 + i32.load + local.get $4 + i32.ne + if ;; label = @14 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $4 + i32.eq + if ;; label = @14 + block ;; label = @15 + local.get $11 + local.get $0 + i32.store + local.get $1 + local.get $9 + i32.store + local.get $0 + local.set $13 + end + else + call $fimport$10 + end + end + end + end + block $label$118 ;; label = @11 + local.get $10 + if ;; label = @12 + block ;; label = @13 + local.get $4 + local.get $4 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 3948 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @14 + block ;; label = @15 + local.get $0 + local.get $13 + i32.store + local.get $13 + i32.eqz + if ;; label = @16 + block ;; label = @17 + i32.const 3648 + local.get $5 + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $2 + i32.store + br 6 (;@11;) + end + end + end + else + block ;; label = @15 + local.get $10 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @16 + call $fimport$10 + end + local.get $10 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @16 + local.get $0 + local.get $13 + i32.store + else + local.get $10 + local.get $13 + i32.store offset=20 + end + local.get $13 + i32.eqz + if ;; label = @16 + block ;; label = @17 + local.get $5 + local.set $2 + br 6 (;@11;) + end + end + end + end + local.get $13 + i32.const 3660 + i32.load + local.tee $0 + i32.lt_u + if ;; label = @14 + call $fimport$10 + end + local.get $13 + local.get $10 + i32.store offset=24 + local.get $4 + i32.load offset=16 + local.tee $1 + if ;; label = @14 + local.get $1 + local.get $0 + i32.lt_u + if ;; label = @15 + call $fimport$10 + else + block ;; label = @16 + local.get $13 + local.get $1 + i32.store offset=16 + local.get $1 + local.get $13 + i32.store offset=24 + end + end + end + local.get $4 + i32.load offset=20 + local.tee $0 + if ;; label = @14 + local.get $0 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @15 + call $fimport$10 + else + block ;; label = @16 + local.get $13 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $13 + i32.store offset=24 + local.get $5 + local.set $2 + end + end + else + local.get $5 + local.set $2 + end + end + else + local.get $5 + local.set $2 + end + end + block $label$136 ;; label = @11 + local.get $3 + i32.const 16 + i32.lt_u + if ;; label = @12 + block ;; label = @13 + local.get $4 + local.get $3 + local.get $7 + i32.add + local.tee $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $4 + local.get $0 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + else + block ;; label = @13 + local.get $4 + local.get $7 + i32.const 3 + i32.or + i32.store offset=4 + local.get $6 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $3 + i32.add + local.get $3 + i32.store + local.get $3 + i32.const 3 + i32.shr_u + local.set $0 + local.get $3 + i32.const 256 + i32.lt_u + if ;; label = @14 + block ;; label = @15 + local.get $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.set $3 + i32.const 3644 + i32.load + local.tee $1 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @16 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @17 + call $fimport$10 + else + block ;; label = @18 + local.get $1 + local.set $16 + local.get $0 + local.set $8 + end + end + else + block ;; label = @17 + i32.const 3644 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $16 + local.get $3 + local.set $8 + end + end + local.get $16 + local.get $6 + i32.store + local.get $8 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $8 + i32.store offset=8 + local.get $6 + local.get $3 + i32.store offset=12 + br 4 (;@11;) + end + end + local.get $3 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @14 + local.get $3 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @15 + i32.const 31 + else + local.get $3 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $5 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $5 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $5 + i32.const 2 + i32.shl + i32.const 3948 + i32.add + local.set $1 + local.get $6 + local.get $5 + i32.store offset=28 + local.get $6 + i32.const 16 + i32.add + local.tee $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store + local.get $2 + i32.const 1 + local.get $5 + i32.shl + local.tee $0 + i32.and + i32.eqz + if ;; label = @14 + block ;; label = @15 + i32.const 3648 + local.get $2 + local.get $0 + i32.or + i32.store + local.get $1 + local.get $6 + i32.store + local.get $6 + local.get $1 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 4 (;@11;) + end + end + local.get $1 + i32.load + local.set $0 + i32.const 25 + local.get $5 + i32.const 1 + i32.shr_u + i32.sub + local.set $1 + local.get $3 + local.get $5 + i32.const 31 + i32.eq + if (result i32) ;; label = @14 + i32.const 0 + else + local.get $1 + end + i32.shl + local.set $5 + block $label$151 ;; label = @14 + block $label$152 ;; label = @15 + block $label$153 ;; label = @16 + loop $label$154 ;; label = @17 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $3 + i32.eq + br_if 2 (;@15;) + local.get $5 + i32.const 1 + i32.shl + local.set $2 + local.get $0 + i32.const 16 + i32.add + local.get $5 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $5 + i32.load + local.tee $1 + i32.eqz + br_if 1 (;@16;) + local.get $2 + local.set $5 + local.get $1 + local.set $0 + br 0 (;@17;) + end + end + local.get $5 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @16 + call $fimport$10 + else + block ;; label = @17 + local.get $5 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 6 (;@11;) + end + end + br 1 (;@14;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $2 + i32.const 3660 + i32.load + local.tee $1 + i32.ge_u + local.get $0 + local.get $1 + i32.ge_u + i32.and + if ;; label = @15 + block ;; label = @16 + local.get $2 + local.get $6 + i32.store offset=12 + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $2 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $6 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + end + end + local.get $14 + global.set $global$1 + local.get $4 + i32.const 8 + i32.add + return + end + else + local.get $7 + local.set $0 + end + else + local.get $7 + local.set $0 + end + end + else + local.get $7 + local.set $0 + end + end + end + end + end + i32.const 3652 + i32.load + local.tee $1 + local.get $0 + i32.ge_u + if ;; label = @2 + block ;; label = @3 + i32.const 3664 + i32.load + local.set $2 + local.get $1 + local.get $0 + i32.sub + local.tee $3 + i32.const 15 + i32.gt_u + if ;; label = @4 + block ;; label = @5 + i32.const 3664 + local.get $2 + local.get $0 + i32.add + local.tee $1 + i32.store + i32.const 3652 + local.get $3 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $1 + local.get $3 + i32.add + local.get $3 + i32.store + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + end + else + block ;; label = @5 + i32.const 3652 + i32.const 0 + i32.store + i32.const 3664 + i32.const 0 + i32.store + local.get $2 + local.get $1 + i32.const 3 + i32.or + i32.store offset=4 + local.get $2 + local.get $1 + i32.add + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const 1 + i32.or + i32.store + end + end + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + i32.const 3656 + i32.load + local.tee $10 + local.get $0 + i32.gt_u + if ;; label = @2 + block ;; label = @3 + i32.const 3656 + local.get $10 + local.get $0 + i32.sub + local.tee $3 + i32.store + i32.const 3668 + i32.const 3668 + i32.load + local.tee $2 + local.get $0 + i32.add + local.tee $1 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + i32.const 4116 + i32.load + if (result i32) ;; label = @2 + i32.const 4124 + i32.load + else + block (result i32) ;; label = @3 + i32.const 4124 + i32.const 4096 + i32.store + i32.const 4120 + i32.const 4096 + i32.store + i32.const 4128 + i32.const -1 + i32.store + i32.const 4132 + i32.const -1 + i32.store + i32.const 4136 + i32.const 0 + i32.store + i32.const 4088 + i32.const 0 + i32.store + local.get $18 + local.get $18 + i32.const -16 + i32.and + i32.const 1431655768 + i32.xor + local.tee $1 + i32.store + i32.const 4116 + local.get $1 + i32.store + i32.const 4096 + end + end + local.tee $1 + local.get $0 + i32.const 47 + i32.add + local.tee $13 + i32.add + local.tee $8 + i32.const 0 + local.get $1 + i32.sub + local.tee $4 + i32.and + local.tee $6 + local.get $0 + i32.le_u + if ;; label = @2 + block ;; label = @3 + local.get $14 + global.set $global$1 + i32.const 0 + return + end + end + i32.const 4084 + i32.load + local.tee $2 + if ;; label = @2 + i32.const 4076 + i32.load + local.tee $3 + local.get $6 + i32.add + local.tee $1 + local.get $3 + i32.le_u + local.get $1 + local.get $2 + i32.gt_u + i32.or + if ;; label = @3 + block ;; label = @4 + local.get $14 + global.set $global$1 + i32.const 0 + return + end + end + end + local.get $0 + i32.const 48 + i32.add + local.set $7 + block $label$171 ;; label = @2 + block $label$172 ;; label = @3 + i32.const 4088 + i32.load + i32.const 4 + i32.and + i32.eqz + if ;; label = @4 + block ;; label = @5 + block $label$174 ;; label = @6 + block $label$175 ;; label = @7 + block $label$176 ;; label = @8 + i32.const 3668 + i32.load + local.tee $3 + i32.eqz + br_if 0 (;@8;) + i32.const 4092 + local.set $2 + loop $label$177 ;; label = @9 + block $label$178 ;; label = @10 + local.get $2 + i32.load + local.tee $1 + local.get $3 + i32.le_u + if ;; label = @11 + local.get $1 + local.get $2 + i32.const 4 + i32.add + local.tee $5 + i32.load + i32.add + local.get $3 + i32.gt_u + br_if 1 (;@10;) + end + local.get $2 + i32.load offset=8 + local.tee $1 + i32.eqz + br_if 2 (;@8;) + local.get $1 + local.set $2 + br 1 (;@9;) + end + end + local.get $8 + local.get $10 + i32.sub + local.get $4 + i32.and + local.tee $3 + i32.const 2147483647 + i32.lt_u + if ;; label = @9 + local.get $3 + call $34 + local.tee $1 + local.get $2 + i32.load + local.get $5 + i32.load + i32.add + i32.eq + if ;; label = @10 + local.get $1 + i32.const -1 + i32.ne + br_if 7 (;@3;) + else + block ;; label = @11 + local.get $1 + local.set $2 + local.get $3 + local.set $1 + br 4 (;@7;) + end + end + end + br 2 (;@6;) + end + i32.const 0 + call $34 + local.tee $1 + i32.const -1 + i32.ne + if ;; label = @8 + block ;; label = @9 + i32.const 4120 + i32.load + local.tee $2 + i32.const -1 + i32.add + local.tee $5 + local.get $1 + local.tee $3 + i32.add + i32.const 0 + local.get $2 + i32.sub + i32.and + local.get $3 + i32.sub + local.set $2 + local.get $5 + local.get $3 + i32.and + if (result i32) ;; label = @10 + local.get $2 + else + i32.const 0 + end + local.get $6 + i32.add + local.tee $3 + i32.const 4076 + i32.load + local.tee $5 + i32.add + local.set $4 + local.get $3 + local.get $0 + i32.gt_u + local.get $3 + i32.const 2147483647 + i32.lt_u + i32.and + if ;; label = @10 + block ;; label = @11 + i32.const 4084 + i32.load + local.tee $2 + if ;; label = @12 + local.get $4 + local.get $5 + i32.le_u + local.get $4 + local.get $2 + i32.gt_u + i32.or + br_if 6 (;@6;) + end + local.get $3 + call $34 + local.tee $2 + local.get $1 + i32.eq + br_if 8 (;@3;) + local.get $3 + local.set $1 + br 4 (;@7;) + end + end + end + end + br 1 (;@6;) + end + i32.const 0 + local.get $1 + i32.sub + local.set $5 + local.get $7 + local.get $1 + i32.gt_u + local.get $1 + i32.const 2147483647 + i32.lt_u + local.get $2 + i32.const -1 + i32.ne + i32.and + i32.and + if ;; label = @7 + local.get $13 + local.get $1 + i32.sub + i32.const 4124 + i32.load + local.tee $3 + i32.add + i32.const 0 + local.get $3 + i32.sub + i32.and + local.tee $3 + i32.const 2147483647 + i32.lt_u + if ;; label = @8 + local.get $3 + call $34 + i32.const -1 + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $5 + call $34 + drop + br 4 (;@6;) + end + else + local.get $3 + local.get $1 + i32.add + local.set $3 + end + else + local.get $1 + local.set $3 + end + else + local.get $1 + local.set $3 + end + local.get $2 + i32.const -1 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $2 + local.set $1 + br 5 (;@3;) + end + end + end + i32.const 4088 + i32.const 4088 + i32.load + i32.const 4 + i32.or + i32.store + end + end + local.get $6 + i32.const 2147483647 + i32.lt_u + if ;; label = @4 + local.get $6 + call $34 + local.tee $1 + i32.const 0 + call $34 + local.tee $3 + i32.lt_u + local.get $1 + i32.const -1 + i32.ne + local.get $3 + i32.const -1 + i32.ne + i32.and + i32.and + if ;; label = @5 + local.get $3 + local.get $1 + i32.sub + local.tee $3 + local.get $0 + i32.const 40 + i32.add + i32.gt_u + br_if 2 (;@3;) + end + end + br 1 (;@2;) + end + i32.const 4076 + i32.const 4076 + i32.load + local.get $3 + i32.add + local.tee $2 + i32.store + local.get $2 + i32.const 4080 + i32.load + i32.gt_u + if ;; label = @3 + i32.const 4080 + local.get $2 + i32.store + end + block $label$198 ;; label = @3 + i32.const 3668 + i32.load + local.tee $8 + if ;; label = @4 + block ;; label = @5 + i32.const 4092 + local.set $2 + block $label$200 ;; label = @6 + block $label$201 ;; label = @7 + loop $label$202 ;; label = @8 + local.get $1 + local.get $2 + i32.load + local.tee $4 + local.get $2 + i32.const 4 + i32.add + local.tee $7 + i32.load + local.tee $5 + i32.add + i32.eq + br_if 1 (;@7;) + local.get $2 + i32.load offset=8 + local.tee $2 + br_if 0 (;@8;) + end + br 1 (;@6;) + end + local.get $2 + i32.load offset=12 + i32.const 8 + i32.and + i32.eqz + if ;; label = @7 + local.get $8 + local.get $1 + i32.lt_u + local.get $8 + local.get $4 + i32.ge_u + i32.and + if ;; label = @8 + block ;; label = @9 + local.get $7 + local.get $5 + local.get $3 + i32.add + i32.store + i32.const 3656 + i32.load + local.set $5 + i32.const 0 + local.get $8 + i32.const 8 + i32.add + local.tee $2 + i32.sub + i32.const 7 + i32.and + local.set $1 + i32.const 3668 + local.get $8 + local.get $2 + i32.const 7 + i32.and + if (result i32) ;; label = @10 + local.get $1 + else + i32.const 0 + local.tee $1 + end + i32.add + local.tee $2 + i32.store + i32.const 3656 + local.get $3 + local.get $1 + i32.sub + local.get $5 + i32.add + local.tee $1 + i32.store + local.get $2 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $1 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 3672 + i32.const 4132 + i32.load + i32.store + br 6 (;@3;) + end + end + end + end + local.get $1 + i32.const 3660 + i32.load + local.tee $2 + i32.lt_u + if ;; label = @6 + block ;; label = @7 + i32.const 3660 + local.get $1 + i32.store + local.get $1 + local.set $2 + end + end + local.get $1 + local.get $3 + i32.add + local.set $10 + i32.const 4092 + local.set $5 + block $label$208 ;; label = @6 + block $label$209 ;; label = @7 + loop $label$210 ;; label = @8 + local.get $5 + i32.load + local.get $10 + i32.eq + br_if 1 (;@7;) + local.get $5 + i32.load offset=8 + local.tee $5 + br_if 0 (;@8;) + i32.const 4092 + local.set $5 + end + br 1 (;@6;) + end + local.get $5 + i32.load offset=12 + i32.const 8 + i32.and + if ;; label = @7 + i32.const 4092 + local.set $5 + else + block ;; label = @8 + local.get $5 + local.get $1 + i32.store + local.get $5 + i32.const 4 + i32.add + local.tee $5 + local.get $5 + i32.load + local.get $3 + i32.add + i32.store + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $4 + i32.sub + i32.const 7 + i32.and + local.set $7 + i32.const 0 + local.get $10 + i32.const 8 + i32.add + local.tee $5 + i32.sub + i32.const 7 + i32.and + local.set $3 + local.get $1 + local.get $4 + i32.const 7 + i32.and + if (result i32) ;; label = @9 + local.get $7 + else + i32.const 0 + end + i32.add + local.tee $13 + local.get $0 + i32.add + local.set $6 + local.get $10 + local.get $5 + i32.const 7 + i32.and + if (result i32) ;; label = @9 + local.get $3 + else + i32.const 0 + end + i32.add + local.tee $4 + local.get $13 + i32.sub + local.get $0 + i32.sub + local.set $7 + local.get $13 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + block $label$217 ;; label = @9 + local.get $4 + local.get $8 + i32.eq + if ;; label = @10 + block ;; label = @11 + i32.const 3656 + i32.const 3656 + i32.load + local.get $7 + i32.add + local.tee $0 + i32.store + i32.const 3668 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + end + else + block ;; label = @11 + local.get $4 + i32.const 3664 + i32.load + i32.eq + if ;; label = @12 + block ;; label = @13 + i32.const 3652 + i32.const 3652 + i32.load + local.get $7 + i32.add + local.tee $0 + i32.store + i32.const 3664 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $0 + i32.add + local.get $0 + i32.store + br 4 (;@9;) + end + end + local.get $4 + i32.load offset=4 + local.tee $0 + i32.const 3 + i32.and + i32.const 1 + i32.eq + if (result i32) ;; label = @12 + block (result i32) ;; label = @13 + local.get $0 + i32.const -8 + i32.and + local.set $11 + local.get $0 + i32.const 3 + i32.shr_u + local.set $1 + block $label$222 ;; label = @14 + local.get $0 + i32.const 256 + i32.lt_u + if ;; label = @15 + block ;; label = @16 + local.get $4 + i32.load offset=12 + local.set $5 + block $label$224 ;; label = @17 + local.get $4 + i32.load offset=8 + local.tee $3 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.tee $0 + i32.ne + if ;; label = @18 + block ;; label = @19 + local.get $3 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $3 + i32.load offset=12 + local.get $4 + i32.eq + br_if 2 (;@17;) + call $fimport$10 + end + end + end + local.get $5 + local.get $3 + i32.eq + if ;; label = @17 + block ;; label = @18 + i32.const 3644 + i32.const 3644 + i32.load + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 4 (;@14;) + end + end + block $label$228 ;; label = @17 + local.get $5 + local.get $0 + i32.eq + if ;; label = @18 + local.get $5 + i32.const 8 + i32.add + local.set $20 + else + block ;; label = @19 + local.get $5 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $5 + i32.const 8 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + block ;; label = @21 + local.get $0 + local.set $20 + br 4 (;@17;) + end + end + call $fimport$10 + end + end + end + local.get $3 + local.get $5 + i32.store offset=12 + local.get $20 + local.get $3 + i32.store + end + else + block ;; label = @16 + local.get $4 + i32.load offset=24 + local.set $8 + block $label$234 ;; label = @17 + local.get $4 + i32.load offset=12 + local.tee $0 + local.get $4 + i32.eq + if ;; label = @18 + block ;; label = @19 + local.get $4 + i32.const 16 + i32.add + local.tee $3 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.eqz + if ;; label = @20 + local.get $3 + i32.load + local.tee $0 + if ;; label = @21 + local.get $3 + local.set $1 + else + block ;; label = @22 + i32.const 0 + local.set $12 + br 5 (;@17;) + end + end + end + loop $label$239 ;; label = @20 + local.get $0 + i32.const 20 + i32.add + local.tee $5 + i32.load + local.tee $3 + if ;; label = @21 + block ;; label = @22 + local.get $3 + local.set $0 + local.get $5 + local.set $1 + br 2 (;@20;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $5 + i32.load + local.tee $3 + if ;; label = @21 + block ;; label = @22 + local.get $3 + local.set $0 + local.get $5 + local.set $1 + br 2 (;@20;) + end + end + end + local.get $1 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + else + block ;; label = @21 + local.get $1 + i32.const 0 + i32.store + local.get $0 + local.set $12 + end + end + end + else + block ;; label = @19 + local.get $4 + i32.load offset=8 + local.tee $5 + local.get $2 + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $5 + i32.const 12 + i32.add + local.tee $3 + i32.load + local.get $4 + i32.ne + if ;; label = @20 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + block ;; label = @21 + local.get $3 + local.get $0 + i32.store + local.get $1 + local.get $5 + i32.store + local.get $0 + local.set $12 + end + else + call $fimport$10 + end + end + end + end + local.get $8 + i32.eqz + br_if 2 (;@14;) + block $label$249 ;; label = @17 + local.get $4 + local.get $4 + i32.load offset=28 + local.tee $1 + i32.const 2 + i32.shl + i32.const 3948 + i32.add + local.tee $0 + i32.load + i32.eq + if ;; label = @18 + block ;; label = @19 + local.get $0 + local.get $12 + i32.store + local.get $12 + br_if 2 (;@17;) + i32.const 3648 + i32.const 3648 + i32.load + i32.const 1 + local.get $1 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 5 (;@14;) + end + else + block ;; label = @19 + local.get $8 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @20 + call $fimport$10 + end + local.get $8 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $4 + i32.eq + if ;; label = @20 + local.get $0 + local.get $12 + i32.store + else + local.get $8 + local.get $12 + i32.store offset=20 + end + local.get $12 + i32.eqz + br_if 5 (;@14;) + end + end + end + local.get $12 + i32.const 3660 + i32.load + local.tee $1 + i32.lt_u + if ;; label = @17 + call $fimport$10 + end + local.get $12 + local.get $8 + i32.store offset=24 + local.get $4 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.tee $3 + if ;; label = @17 + local.get $3 + local.get $1 + i32.lt_u + if ;; label = @18 + call $fimport$10 + else + block ;; label = @19 + local.get $12 + local.get $3 + i32.store offset=16 + local.get $3 + local.get $12 + i32.store offset=24 + end + end + end + local.get $0 + i32.load offset=4 + local.tee $0 + i32.eqz + br_if 2 (;@14;) + local.get $0 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @17 + call $fimport$10 + else + block ;; label = @18 + local.get $12 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $12 + i32.store offset=24 + end + end + end + end + end + local.get $11 + local.get $7 + i32.add + local.set $7 + local.get $4 + local.get $11 + i32.add + end + else + local.get $4 + end + local.tee $0 + i32.const 4 + i32.add + local.tee $0 + local.get $0 + i32.load + i32.const -2 + i32.and + i32.store + local.get $6 + local.get $7 + i32.const 1 + i32.or + i32.store offset=4 + local.get $6 + local.get $7 + i32.add + local.get $7 + i32.store + local.get $7 + i32.const 3 + i32.shr_u + local.set $0 + local.get $7 + i32.const 256 + i32.lt_u + if ;; label = @12 + block ;; label = @13 + local.get $0 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.set $3 + block $label$263 ;; label = @14 + i32.const 3644 + i32.load + local.tee $1 + i32.const 1 + local.get $0 + i32.shl + local.tee $0 + i32.and + if ;; label = @15 + block ;; label = @16 + local.get $3 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 3660 + i32.load + i32.ge_u + if ;; label = @17 + block ;; label = @18 + local.get $1 + local.set $21 + local.get $0 + local.set $9 + br 4 (;@14;) + end + end + call $fimport$10 + end + else + block ;; label = @16 + i32.const 3644 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $21 + local.get $3 + local.set $9 + end + end + end + local.get $21 + local.get $6 + i32.store + local.get $9 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $9 + i32.store offset=8 + local.get $6 + local.get $3 + i32.store offset=12 + br 4 (;@9;) + end + end + block $label$267 (result i32) ;; label = @12 + local.get $7 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @13 + block (result i32) ;; label = @14 + i32.const 31 + local.get $7 + i32.const 16777215 + i32.gt_u + br_if 2 (;@12;) + drop + local.get $7 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $3 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $0 + local.get $3 + i32.or + local.get $1 + local.get $0 + i32.shl + local.tee $1 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $0 + i32.or + i32.sub + local.get $1 + local.get $0 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + end + local.tee $2 + i32.const 2 + i32.shl + i32.const 3948 + i32.add + local.set $3 + local.get $6 + local.get $2 + i32.store offset=28 + local.get $6 + i32.const 16 + i32.add + local.tee $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store + i32.const 3648 + i32.load + local.tee $1 + i32.const 1 + local.get $2 + i32.shl + local.tee $0 + i32.and + i32.eqz + if ;; label = @12 + block ;; label = @13 + i32.const 3648 + local.get $1 + local.get $0 + i32.or + i32.store + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $3 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 4 (;@9;) + end + end + local.get $3 + i32.load + local.set $0 + i32.const 25 + local.get $2 + i32.const 1 + i32.shr_u + i32.sub + local.set $1 + local.get $7 + local.get $2 + i32.const 31 + i32.eq + if (result i32) ;; label = @12 + i32.const 0 + else + local.get $1 + end + i32.shl + local.set $2 + block $label$273 ;; label = @12 + block $label$274 ;; label = @13 + block $label$275 ;; label = @14 + loop $label$276 ;; label = @15 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $7 + i32.eq + br_if 2 (;@13;) + local.get $2 + i32.const 1 + i32.shl + local.set $3 + local.get $0 + i32.const 16 + i32.add + local.get $2 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $2 + i32.load + local.tee $1 + i32.eqz + br_if 1 (;@14;) + local.get $3 + local.set $2 + local.get $1 + local.set $0 + br 0 (;@15;) + end + end + local.get $2 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @14 + call $fimport$10 + else + block ;; label = @15 + local.get $2 + local.get $6 + i32.store + local.get $6 + local.get $0 + i32.store offset=24 + local.get $6 + local.get $6 + i32.store offset=12 + local.get $6 + local.get $6 + i32.store offset=8 + br 6 (;@9;) + end + end + br 1 (;@12;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $2 + i32.const 3660 + i32.load + local.tee $1 + i32.ge_u + local.get $0 + local.get $1 + i32.ge_u + i32.and + if ;; label = @13 + block ;; label = @14 + local.get $2 + local.get $6 + i32.store offset=12 + local.get $3 + local.get $6 + i32.store + local.get $6 + local.get $2 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $6 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + end + end + local.get $14 + global.set $global$1 + local.get $13 + i32.const 8 + i32.add + return + end + end + end + loop $label$281 ;; label = @6 + block $label$282 ;; label = @7 + local.get $5 + i32.load + local.tee $2 + local.get $8 + i32.le_u + if ;; label = @8 + local.get $2 + local.get $5 + i32.load offset=4 + i32.add + local.tee $13 + local.get $8 + i32.gt_u + br_if 1 (;@7;) + end + local.get $5 + i32.load offset=8 + local.set $5 + br 1 (;@6;) + end + end + i32.const 0 + local.get $13 + i32.const -47 + i32.add + local.tee $7 + i32.const 8 + i32.add + local.tee $5 + i32.sub + i32.const 7 + i32.and + local.set $2 + local.get $7 + local.get $5 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $2 + else + i32.const 0 + end + i32.add + local.tee $2 + local.get $8 + i32.const 16 + i32.add + local.tee $12 + i32.lt_u + if (result i32) ;; label = @6 + local.get $8 + else + local.get $2 + end + local.tee $7 + i32.const 8 + i32.add + local.set $10 + local.get $7 + i32.const 24 + i32.add + local.set $5 + local.get $3 + i32.const -40 + i32.add + local.set $9 + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $4 + i32.sub + i32.const 7 + i32.and + local.set $2 + i32.const 3668 + local.get $1 + local.get $4 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $2 + else + i32.const 0 + local.tee $2 + end + i32.add + local.tee $4 + i32.store + i32.const 3656 + local.get $9 + local.get $2 + i32.sub + local.tee $2 + i32.store + local.get $4 + local.get $2 + i32.const 1 + i32.or + i32.store offset=4 + local.get $4 + local.get $2 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 3672 + i32.const 4132 + i32.load + i32.store + local.get $7 + i32.const 4 + i32.add + local.tee $2 + i32.const 27 + i32.store + local.get $10 + i32.const 4092 + i64.load align=4 + i64.store align=4 + local.get $10 + i32.const 4100 + i64.load align=4 + i64.store offset=8 align=4 + i32.const 4092 + local.get $1 + i32.store + i32.const 4096 + local.get $3 + i32.store + i32.const 4104 + i32.const 0 + i32.store + i32.const 4100 + local.get $10 + i32.store + local.get $5 + local.set $1 + loop $label$290 ;; label = @6 + local.get $1 + i32.const 4 + i32.add + local.tee $1 + i32.const 7 + i32.store + local.get $1 + i32.const 4 + i32.add + local.get $13 + i32.lt_u + br_if 0 (;@6;) + end + local.get $7 + local.get $8 + i32.ne + if ;; label = @6 + block ;; label = @7 + local.get $2 + local.get $2 + i32.load + i32.const -2 + i32.and + i32.store + local.get $8 + local.get $7 + local.get $8 + i32.sub + local.tee $4 + i32.const 1 + i32.or + i32.store offset=4 + local.get $7 + local.get $4 + i32.store + local.get $4 + i32.const 3 + i32.shr_u + local.set $1 + local.get $4 + i32.const 256 + i32.lt_u + if ;; label = @8 + block ;; label = @9 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.set $2 + i32.const 3644 + i32.load + local.tee $3 + i32.const 1 + local.get $1 + i32.shl + local.tee $1 + i32.and + if ;; label = @10 + local.get $2 + i32.const 8 + i32.add + local.tee $3 + i32.load + local.tee $1 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$10 + else + block ;; label = @12 + local.get $3 + local.set $15 + local.get $1 + local.set $11 + end + end + else + block ;; label = @11 + i32.const 3644 + local.get $3 + local.get $1 + i32.or + i32.store + local.get $2 + i32.const 8 + i32.add + local.set $15 + local.get $2 + local.set $11 + end + end + local.get $15 + local.get $8 + i32.store + local.get $11 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $11 + i32.store offset=8 + local.get $8 + local.get $2 + i32.store offset=12 + br 6 (;@3;) + end + end + local.get $4 + i32.const 8 + i32.shr_u + local.tee $1 + if (result i32) ;; label = @8 + local.get $4 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @9 + i32.const 31 + else + local.get $4 + i32.const 14 + local.get $1 + local.get $1 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $2 + i32.shl + local.tee $3 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $1 + local.get $2 + i32.or + local.get $3 + local.get $1 + i32.shl + local.tee $3 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $1 + i32.or + i32.sub + local.get $3 + local.get $1 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $1 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $1 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $5 + i32.const 2 + i32.shl + i32.const 3948 + i32.add + local.set $2 + local.get $8 + local.get $5 + i32.store offset=28 + local.get $8 + i32.const 0 + i32.store offset=20 + local.get $12 + i32.const 0 + i32.store + i32.const 3648 + i32.load + local.tee $3 + i32.const 1 + local.get $5 + i32.shl + local.tee $1 + i32.and + i32.eqz + if ;; label = @8 + block ;; label = @9 + i32.const 3648 + local.get $3 + local.get $1 + i32.or + i32.store + local.get $2 + local.get $8 + i32.store + local.get $8 + local.get $2 + i32.store offset=24 + local.get $8 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $8 + i32.store offset=8 + br 6 (;@3;) + end + end + local.get $2 + i32.load + local.set $1 + i32.const 25 + local.get $5 + i32.const 1 + i32.shr_u + i32.sub + local.set $3 + local.get $4 + local.get $5 + i32.const 31 + i32.eq + if (result i32) ;; label = @8 + i32.const 0 + else + local.get $3 + end + i32.shl + local.set $5 + block $label$304 ;; label = @8 + block $label$305 ;; label = @9 + block $label$306 ;; label = @10 + loop $label$307 ;; label = @11 + local.get $1 + i32.load offset=4 + i32.const -8 + i32.and + local.get $4 + i32.eq + br_if 2 (;@9;) + local.get $5 + i32.const 1 + i32.shl + local.set $2 + local.get $1 + i32.const 16 + i32.add + local.get $5 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $5 + i32.load + local.tee $3 + i32.eqz + br_if 1 (;@10;) + local.get $2 + local.set $5 + local.get $3 + local.set $1 + br 0 (;@11;) + end + end + local.get $5 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $5 + local.get $8 + i32.store + local.get $8 + local.get $1 + i32.store offset=24 + local.get $8 + local.get $8 + i32.store offset=12 + local.get $8 + local.get $8 + i32.store offset=8 + br 8 (;@3;) + end + end + br 1 (;@8;) + end + local.get $1 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $5 + i32.const 3660 + i32.load + local.tee $3 + i32.ge_u + local.get $1 + local.get $3 + i32.ge_u + i32.and + if ;; label = @9 + block ;; label = @10 + local.get $5 + local.get $8 + i32.store offset=12 + local.get $2 + local.get $8 + i32.store + local.get $8 + local.get $5 + i32.store offset=8 + local.get $8 + local.get $1 + i32.store offset=12 + local.get $8 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + end + end + else + block ;; label = @5 + i32.const 3660 + i32.load + local.tee $2 + i32.eqz + local.get $1 + local.get $2 + i32.lt_u + i32.or + if ;; label = @6 + i32.const 3660 + local.get $1 + i32.store + end + i32.const 4092 + local.get $1 + i32.store + i32.const 4096 + local.get $3 + i32.store + i32.const 4104 + i32.const 0 + i32.store + i32.const 3680 + i32.const 4116 + i32.load + i32.store + i32.const 3676 + i32.const -1 + i32.store + i32.const 0 + local.set $2 + loop $label$314 ;; label = @6 + local.get $2 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.tee $5 + local.get $5 + i32.store offset=12 + local.get $5 + local.get $5 + i32.store offset=8 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + i32.const 32 + i32.ne + br_if 0 (;@6;) + end + local.get $3 + i32.const -40 + i32.add + local.set $5 + i32.const 0 + local.get $1 + i32.const 8 + i32.add + local.tee $2 + i32.sub + i32.const 7 + i32.and + local.set $3 + i32.const 3668 + local.get $1 + local.get $2 + i32.const 7 + i32.and + if (result i32) ;; label = @6 + local.get $3 + else + i32.const 0 + end + local.tee $1 + i32.add + local.tee $3 + i32.store + i32.const 3656 + local.get $5 + local.get $1 + i32.sub + local.tee $1 + i32.store + local.get $3 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $1 + i32.add + i32.const 40 + i32.store offset=4 + i32.const 3672 + i32.const 4132 + i32.load + i32.store + end + end + end + i32.const 3656 + i32.load + local.tee $1 + local.get $0 + i32.gt_u + if ;; label = @3 + block ;; label = @4 + i32.const 3656 + local.get $1 + local.get $0 + i32.sub + local.tee $3 + i32.store + i32.const 3668 + i32.const 3668 + i32.load + local.tee $2 + local.get $0 + i32.add + local.tee $1 + i32.store + local.get $1 + local.get $3 + i32.const 1 + i32.or + i32.store offset=4 + local.get $2 + local.get $0 + i32.const 3 + i32.or + i32.store offset=4 + local.get $14 + global.set $global$1 + local.get $2 + i32.const 8 + i32.add + return + end + end + end + call $11 + i32.const 12 + i32.store + local.get $14 + global.set $global$1 + i32.const 0 + end ) - ) - (func $39 (; 52 ;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - (call_indirect (type $0) - (local.get $1) - (local.get $2) - (local.get $3) - (i32.add - (i32.and - (local.get $0) - (i32.const 3) - ) - (i32.const 2) - ) + (func $32 (;45;) (type $2) (param $0 i32) + (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) + block $label$1 ;; label = @1 + local.get $0 + i32.eqz + if ;; label = @2 + return + end + local.get $0 + i32.const -8 + i32.add + local.tee $1 + i32.const 3660 + i32.load + local.tee $11 + i32.lt_u + if ;; label = @2 + call $fimport$10 + end + local.get $0 + i32.const -4 + i32.add + i32.load + local.tee $0 + i32.const 3 + i32.and + local.tee $8 + i32.const 1 + i32.eq + if ;; label = @2 + call $fimport$10 + end + local.get $1 + local.get $0 + i32.const -8 + i32.and + local.tee $4 + i32.add + local.set $6 + block $label$5 ;; label = @2 + local.get $0 + i32.const 1 + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $1 + local.set $3 + local.get $4 + local.set $2 + end + else + block ;; label = @4 + local.get $8 + i32.eqz + if ;; label = @5 + return + end + local.get $1 + i32.const 0 + local.get $1 + i32.load + local.tee $8 + i32.sub + i32.add + local.tee $0 + local.get $11 + i32.lt_u + if ;; label = @5 + call $fimport$10 + end + local.get $8 + local.get $4 + i32.add + local.set $1 + local.get $0 + i32.const 3664 + i32.load + i32.eq + if ;; label = @5 + block ;; label = @6 + local.get $6 + i32.const 4 + i32.add + local.tee $2 + i32.load + local.tee $3 + i32.const 3 + i32.and + i32.const 3 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 6 (;@2;) + end + end + i32.const 3652 + local.get $1 + i32.store + local.get $2 + local.get $3 + i32.const -2 + i32.and + i32.store + local.get $0 + local.get $1 + i32.const 1 + i32.or + i32.store offset=4 + local.get $0 + local.get $1 + i32.add + local.get $1 + i32.store + return + end + end + local.get $8 + i32.const 3 + i32.shr_u + local.set $10 + local.get $8 + i32.const 256 + i32.lt_u + if ;; label = @5 + block ;; label = @6 + local.get $0 + i32.load offset=12 + local.set $3 + local.get $0 + i32.load offset=8 + local.tee $4 + local.get $10 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.tee $2 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $4 + local.get $11 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $4 + i32.load offset=12 + local.get $0 + i32.ne + if ;; label = @9 + call $fimport$10 + end + end + end + local.get $3 + local.get $4 + i32.eq + if ;; label = @7 + block ;; label = @8 + i32.const 3644 + i32.const 3644 + i32.load + i32.const 1 + local.get $10 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 6 (;@2;) + end + end + local.get $3 + local.get $2 + i32.eq + if ;; label = @7 + local.get $3 + i32.const 8 + i32.add + local.set $5 + else + block ;; label = @8 + local.get $3 + local.get $11 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $3 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.get $0 + i32.eq + if ;; label = @9 + local.get $2 + local.set $5 + else + call $fimport$10 + end + end + end + local.get $4 + local.get $3 + i32.store offset=12 + local.get $5 + local.get $4 + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 4 (;@2;) + end + end + local.get $0 + i32.load offset=24 + local.set $12 + block $label$22 ;; label = @5 + local.get $0 + i32.load offset=12 + local.tee $4 + local.get $0 + i32.eq + if ;; label = @6 + block ;; label = @7 + local.get $0 + i32.const 16 + i32.add + local.tee $5 + i32.const 4 + i32.add + local.tee $8 + i32.load + local.tee $4 + if ;; label = @8 + local.get $8 + local.set $5 + else + local.get $5 + i32.load + local.tee $4 + i32.eqz + if ;; label = @9 + block ;; label = @10 + i32.const 0 + local.set $7 + br 5 (;@5;) + end + end + end + loop $label$27 ;; label = @8 + local.get $4 + i32.const 20 + i32.add + local.tee $8 + i32.load + local.tee $10 + if ;; label = @9 + block ;; label = @10 + local.get $10 + local.set $4 + local.get $8 + local.set $5 + br 2 (;@8;) + end + end + local.get $4 + i32.const 16 + i32.add + local.tee $8 + i32.load + local.tee $10 + if ;; label = @9 + block ;; label = @10 + local.get $10 + local.set $4 + local.get $8 + local.set $5 + br 2 (;@8;) + end + end + end + local.get $5 + local.get $11 + i32.lt_u + if ;; label = @8 + call $fimport$10 + else + block ;; label = @9 + local.get $5 + i32.const 0 + i32.store + local.get $4 + local.set $7 + end + end + end + else + block ;; label = @7 + local.get $0 + i32.load offset=8 + local.tee $5 + local.get $11 + i32.lt_u + if ;; label = @8 + call $fimport$10 + end + local.get $5 + i32.const 12 + i32.add + local.tee $8 + i32.load + local.get $0 + i32.ne + if ;; label = @8 + call $fimport$10 + end + local.get $4 + i32.const 8 + i32.add + local.tee $10 + i32.load + local.get $0 + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $8 + local.get $4 + i32.store + local.get $10 + local.get $5 + i32.store + local.get $4 + local.set $7 + end + else + call $fimport$10 + end + end + end + end + local.get $12 + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $0 + i32.load offset=28 + local.tee $4 + i32.const 2 + i32.shl + i32.const 3948 + i32.add + local.tee $5 + i32.load + i32.eq + if ;; label = @7 + block ;; label = @8 + local.get $5 + local.get $7 + i32.store + local.get $7 + i32.eqz + if ;; label = @9 + block ;; label = @10 + i32.const 3648 + i32.const 3648 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 8 (;@2;) + end + end + end + else + block ;; label = @8 + local.get $12 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $12 + i32.const 16 + i32.add + local.tee $4 + i32.load + local.get $0 + i32.eq + if ;; label = @9 + local.get $4 + local.get $7 + i32.store + else + local.get $12 + local.get $7 + i32.store offset=20 + end + local.get $7 + i32.eqz + if ;; label = @9 + block ;; label = @10 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + br 8 (;@2;) + end + end + end + end + local.get $7 + i32.const 3660 + i32.load + local.tee $5 + i32.lt_u + if ;; label = @7 + call $fimport$10 + end + local.get $7 + local.get $12 + i32.store offset=24 + local.get $0 + i32.const 16 + i32.add + local.tee $8 + i32.load + local.tee $4 + if ;; label = @7 + local.get $4 + local.get $5 + i32.lt_u + if ;; label = @8 + call $fimport$10 + else + block ;; label = @9 + local.get $7 + local.get $4 + i32.store offset=16 + local.get $4 + local.get $7 + i32.store offset=24 + end + end + end + local.get $8 + i32.load offset=4 + local.tee $4 + if ;; label = @7 + local.get $4 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @8 + call $fimport$10 + else + block ;; label = @9 + local.get $7 + local.get $4 + i32.store offset=20 + local.get $4 + local.get $7 + i32.store offset=24 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + else + block ;; label = @8 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + end + else + block ;; label = @6 + local.get $0 + local.set $3 + local.get $1 + local.set $2 + end + end + end + end + end + local.get $3 + local.get $6 + i32.ge_u + if ;; label = @2 + call $fimport$10 + end + local.get $6 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + i32.const 1 + i32.and + i32.eqz + if ;; label = @2 + call $fimport$10 + end + local.get $0 + i32.const 2 + i32.and + if ;; label = @2 + block ;; label = @3 + local.get $1 + local.get $0 + i32.const -2 + i32.and + i32.store + local.get $3 + local.get $2 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $2 + i32.add + local.get $2 + i32.store + end + else + block ;; label = @3 + local.get $6 + i32.const 3668 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 3656 + i32.const 3656 + i32.load + local.get $2 + i32.add + local.tee $0 + i32.store + i32.const 3668 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + i32.const 3664 + i32.load + i32.ne + if ;; label = @6 + return + end + i32.const 3664 + i32.const 0 + i32.store + i32.const 3652 + i32.const 0 + i32.store + return + end + end + local.get $6 + i32.const 3664 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 3652 + i32.const 3652 + i32.load + local.get $2 + i32.add + local.tee $0 + i32.store + i32.const 3664 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $0 + i32.add + local.get $0 + i32.store + return + end + end + local.get $0 + i32.const -8 + i32.and + local.get $2 + i32.add + local.set $5 + local.get $0 + i32.const 3 + i32.shr_u + local.set $4 + block $label$61 ;; label = @4 + local.get $0 + i32.const 256 + i32.lt_u + if ;; label = @5 + block ;; label = @6 + local.get $6 + i32.load offset=12 + local.set $2 + local.get $6 + i32.load offset=8 + local.tee $1 + local.get $4 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.tee $0 + i32.ne + if ;; label = @7 + block ;; label = @8 + local.get $1 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $1 + i32.load offset=12 + local.get $6 + i32.ne + if ;; label = @9 + call $fimport$10 + end + end + end + local.get $2 + local.get $1 + i32.eq + if ;; label = @7 + block ;; label = @8 + i32.const 3644 + i32.const 3644 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 4 (;@4;) + end + end + local.get $2 + local.get $0 + i32.eq + if ;; label = @7 + local.get $2 + i32.const 8 + i32.add + local.set $14 + else + block ;; label = @8 + local.get $2 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $2 + i32.const 8 + i32.add + local.tee $0 + i32.load + local.get $6 + i32.eq + if ;; label = @9 + local.get $0 + local.set $14 + else + call $fimport$10 + end + end + end + local.get $1 + local.get $2 + i32.store offset=12 + local.get $14 + local.get $1 + i32.store + end + else + block ;; label = @6 + local.get $6 + i32.load offset=24 + local.set $7 + block $label$73 ;; label = @7 + local.get $6 + i32.load offset=12 + local.tee $0 + local.get $6 + i32.eq + if ;; label = @8 + block ;; label = @9 + local.get $6 + i32.const 16 + i32.add + local.tee $2 + i32.const 4 + i32.add + local.tee $1 + i32.load + local.tee $0 + if ;; label = @10 + local.get $1 + local.set $2 + else + local.get $2 + i32.load + local.tee $0 + i32.eqz + if ;; label = @11 + block ;; label = @12 + i32.const 0 + local.set $9 + br 5 (;@7;) + end + end + end + loop $label$78 ;; label = @10 + local.get $0 + i32.const 20 + i32.add + local.tee $1 + i32.load + local.tee $4 + if ;; label = @11 + block ;; label = @12 + local.get $4 + local.set $0 + local.get $1 + local.set $2 + br 2 (;@10;) + end + end + local.get $0 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $4 + if ;; label = @11 + block ;; label = @12 + local.get $4 + local.set $0 + local.get $1 + local.set $2 + br 2 (;@10;) + end + end + end + local.get $2 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $2 + i32.const 0 + i32.store + local.get $0 + local.set $9 + end + end + end + else + block ;; label = @9 + local.get $6 + i32.load offset=8 + local.tee $2 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + end + local.get $2 + i32.const 12 + i32.add + local.tee $1 + i32.load + local.get $6 + i32.ne + if ;; label = @10 + call $fimport$10 + end + local.get $0 + i32.const 8 + i32.add + local.tee $4 + i32.load + local.get $6 + i32.eq + if ;; label = @10 + block ;; label = @11 + local.get $1 + local.get $0 + i32.store + local.get $4 + local.get $2 + i32.store + local.get $0 + local.set $9 + end + else + call $fimport$10 + end + end + end + end + local.get $7 + if ;; label = @7 + block ;; label = @8 + local.get $6 + local.get $6 + i32.load offset=28 + local.tee $0 + i32.const 2 + i32.shl + i32.const 3948 + i32.add + local.tee $2 + i32.load + i32.eq + if ;; label = @9 + block ;; label = @10 + local.get $2 + local.get $9 + i32.store + local.get $9 + i32.eqz + if ;; label = @11 + block ;; label = @12 + i32.const 3648 + i32.const 3648 + i32.load + i32.const 1 + local.get $0 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + br 8 (;@4;) + end + end + end + else + block ;; label = @10 + local.get $7 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @11 + call $fimport$10 + end + local.get $7 + i32.const 16 + i32.add + local.tee $0 + i32.load + local.get $6 + i32.eq + if ;; label = @11 + local.get $0 + local.get $9 + i32.store + else + local.get $7 + local.get $9 + i32.store offset=20 + end + local.get $9 + i32.eqz + br_if 6 (;@4;) + end + end + local.get $9 + i32.const 3660 + i32.load + local.tee $2 + i32.lt_u + if ;; label = @9 + call $fimport$10 + end + local.get $9 + local.get $7 + i32.store offset=24 + local.get $6 + i32.const 16 + i32.add + local.tee $1 + i32.load + local.tee $0 + if ;; label = @9 + local.get $0 + local.get $2 + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $9 + local.get $0 + i32.store offset=16 + local.get $0 + local.get $9 + i32.store offset=24 + end + end + end + local.get $1 + i32.load offset=4 + local.tee $0 + if ;; label = @9 + local.get $0 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @10 + call $fimport$10 + else + block ;; label = @11 + local.get $9 + local.get $0 + i32.store offset=20 + local.get $0 + local.get $9 + i32.store offset=24 + end + end + end + end + end + end + end + end + local.get $3 + local.get $5 + i32.const 1 + i32.or + i32.store offset=4 + local.get $3 + local.get $5 + i32.add + local.get $5 + i32.store + local.get $3 + i32.const 3664 + i32.load + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 3652 + local.get $5 + i32.store + return + end + else + local.get $5 + local.set $2 + end + end + end + local.get $2 + i32.const 3 + i32.shr_u + local.set $1 + local.get $2 + i32.const 256 + i32.lt_u + if ;; label = @2 + block ;; label = @3 + local.get $1 + i32.const 1 + i32.shl + i32.const 2 + i32.shl + i32.const 3684 + i32.add + local.set $0 + i32.const 3644 + i32.load + local.tee $2 + i32.const 1 + local.get $1 + i32.shl + local.tee $1 + i32.and + if ;; label = @4 + local.get $0 + i32.const 8 + i32.add + local.tee $2 + i32.load + local.tee $1 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @5 + call $fimport$10 + else + block ;; label = @6 + local.get $2 + local.set $15 + local.get $1 + local.set $13 + end + end + else + block ;; label = @5 + i32.const 3644 + local.get $2 + local.get $1 + i32.or + i32.store + local.get $0 + i32.const 8 + i32.add + local.set $15 + local.get $0 + local.set $13 + end + end + local.get $15 + local.get $3 + i32.store + local.get $13 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $13 + i32.store offset=8 + local.get $3 + local.get $0 + i32.store offset=12 + return + end + end + local.get $2 + i32.const 8 + i32.shr_u + local.tee $0 + if (result i32) ;; label = @2 + local.get $2 + i32.const 16777215 + i32.gt_u + if (result i32) ;; label = @3 + i32.const 31 + else + local.get $2 + i32.const 14 + local.get $0 + local.get $0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + local.tee $0 + i32.shl + local.tee $1 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + local.tee $4 + local.get $0 + i32.or + local.get $1 + local.get $4 + i32.shl + local.tee $0 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + local.tee $1 + i32.or + i32.sub + local.get $0 + local.get $1 + i32.shl + i32.const 15 + i32.shr_u + i32.add + local.tee $0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + local.get $0 + i32.const 1 + i32.shl + i32.or + end + else + i32.const 0 + end + local.tee $1 + i32.const 2 + i32.shl + i32.const 3948 + i32.add + local.set $0 + local.get $3 + local.get $1 + i32.store offset=28 + local.get $3 + i32.const 0 + i32.store offset=20 + local.get $3 + i32.const 0 + i32.store offset=16 + block $label$113 ;; label = @2 + i32.const 3648 + i32.load + local.tee $4 + i32.const 1 + local.get $1 + i32.shl + local.tee $5 + i32.and + if ;; label = @3 + block ;; label = @4 + local.get $0 + i32.load + local.set $0 + i32.const 25 + local.get $1 + i32.const 1 + i32.shr_u + i32.sub + local.set $4 + local.get $2 + local.get $1 + i32.const 31 + i32.eq + if (result i32) ;; label = @5 + i32.const 0 + else + local.get $4 + end + i32.shl + local.set $1 + block $label$117 ;; label = @5 + block $label$118 ;; label = @6 + block $label$119 ;; label = @7 + loop $label$120 ;; label = @8 + local.get $0 + i32.load offset=4 + i32.const -8 + i32.and + local.get $2 + i32.eq + br_if 2 (;@6;) + local.get $1 + i32.const 1 + i32.shl + local.set $4 + local.get $0 + i32.const 16 + i32.add + local.get $1 + i32.const 31 + i32.shr_u + i32.const 2 + i32.shl + i32.add + local.tee $1 + i32.load + local.tee $5 + i32.eqz + br_if 1 (;@7;) + local.get $4 + local.set $1 + local.get $5 + local.set $0 + br 0 (;@8;) + end + end + local.get $1 + i32.const 3660 + i32.load + i32.lt_u + if ;; label = @7 + call $fimport$10 + else + block ;; label = @8 + local.get $1 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.store offset=24 + local.get $3 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $3 + i32.store offset=8 + br 6 (;@2;) + end + end + br 1 (;@5;) + end + local.get $0 + i32.const 8 + i32.add + local.tee $1 + i32.load + local.tee $2 + i32.const 3660 + i32.load + local.tee $4 + i32.ge_u + local.get $0 + local.get $4 + i32.ge_u + i32.and + if ;; label = @6 + block ;; label = @7 + local.get $2 + local.get $3 + i32.store offset=12 + local.get $1 + local.get $3 + i32.store + local.get $3 + local.get $2 + i32.store offset=8 + local.get $3 + local.get $0 + i32.store offset=12 + local.get $3 + i32.const 0 + i32.store offset=24 + end + else + call $fimport$10 + end + end + end + else + block ;; label = @4 + i32.const 3648 + local.get $4 + local.get $5 + i32.or + i32.store + local.get $0 + local.get $3 + i32.store + local.get $3 + local.get $0 + i32.store offset=24 + local.get $3 + local.get $3 + i32.store offset=12 + local.get $3 + local.get $3 + i32.store offset=8 + end + end + end + i32.const 3676 + i32.const 3676 + i32.load + i32.const -1 + i32.add + local.tee $0 + i32.store + local.get $0 + if ;; label = @2 + return + else + i32.const 4100 + local.set $0 + end + loop $label$128 ;; label = @2 + local.get $0 + i32.load + local.tee $2 + i32.const 8 + i32.add + local.set $0 + local.get $2 + br_if 0 (;@2;) + end + i32.const 3676 + i32.const -1 + i32.store + end ) - ) - (func $40 (; 53 ;) (type $5) (param $0 i32) (param $1 i32) - (call_indirect (type $2) - (local.get $1) - (i32.add - (i32.and - (local.get $0) - (i32.const 1) - ) - (i32.const 6) - ) + (func $33 (;46;) (type $6) + nop ) - ) - (func $41 (; 54 ;) (type $1) (param $0 i32) (result i32) - (block $label$1 (result i32) - (call $fimport$3 - (i32.const 0) - ) - (i32.const 0) + (func $34 (;47;) (type $1) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) + block $label$1 (result i32) ;; label = @1 + global.get $global$0 + i32.load + local.tee $2 + local.get $0 + i32.const 15 + i32.add + i32.const -16 + i32.and + local.tee $0 + i32.add + local.set $1 + local.get $0 + i32.const 0 + i32.gt_s + local.get $1 + local.get $2 + i32.lt_s + i32.and + local.get $1 + i32.const 0 + i32.lt_s + i32.or + if ;; label = @2 + block ;; label = @3 + call $fimport$6 + drop + i32.const 12 + call $fimport$11 + i32.const -1 + return + end + end + global.get $global$0 + local.get $1 + i32.store + local.get $1 + call $fimport$5 + i32.gt_s + if ;; label = @2 + call $fimport$4 + i32.eqz + if ;; label = @3 + block ;; label = @4 + i32.const 12 + call $fimport$11 + global.get $global$0 + local.get $2 + i32.store + i32.const -1 + return + end + end + end + local.get $2 + end ) - ) - (func $42 (; 55 ;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (block $label$1 (result i32) - (call $fimport$3 - (i32.const 1) - ) - (i32.const 0) + (func $35 (;48;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) (local $4 i32) (local $5 i32) + block $label$1 (result i32) ;; label = @1 + local.get $0 + local.get $2 + i32.add + local.set $4 + local.get $2 + i32.const 20 + i32.ge_s + if ;; label = @2 + block ;; label = @3 + local.get $1 + i32.const 255 + i32.and + local.set $1 + local.get $0 + i32.const 3 + i32.and + local.tee $3 + if ;; label = @4 + block ;; label = @5 + local.get $0 + i32.const 4 + i32.add + local.get $3 + i32.sub + local.set $3 + loop $label$4 ;; label = @6 + local.get $0 + local.get $3 + i32.lt_s + if ;; label = @7 + block ;; label = @8 + local.get $0 + local.get $1 + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br 2 (;@6;) + end + end + end + end + end + local.get $1 + local.get $1 + i32.const 8 + i32.shl + i32.or + local.get $1 + i32.const 16 + i32.shl + i32.or + local.get $1 + i32.const 24 + i32.shl + i32.or + local.set $3 + local.get $4 + i32.const -4 + i32.and + local.set $5 + loop $label$6 ;; label = @4 + local.get $0 + local.get $5 + i32.lt_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $3 + i32.store + local.get $0 + i32.const 4 + i32.add + local.set $0 + br 2 (;@4;) + end + end + end + end + end + loop $label$8 ;; label = @2 + local.get $0 + local.get $4 + i32.lt_s + if ;; label = @3 + block ;; label = @4 + local.get $0 + local.get $1 + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br 2 (;@2;) + end + end + end + local.get $0 + local.get $2 + i32.sub + end ) - ) - (func $43 (; 56 ;) (type $2) (param $0 i32) - (call $fimport$3 - (i32.const 2) + (func $36 (;49;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + block $label$1 (result i32) ;; label = @1 + local.get $2 + i32.const 4096 + i32.ge_s + if ;; label = @2 + local.get $0 + local.get $1 + local.get $2 + call $fimport$12 + return + end + local.get $0 + local.set $3 + local.get $0 + i32.const 3 + i32.and + local.get $1 + i32.const 3 + i32.and + i32.eq + if ;; label = @2 + block ;; label = @3 + loop $label$4 ;; label = @4 + local.get $0 + i32.const 3 + i32.and + if ;; label = @5 + block ;; label = @6 + local.get $2 + i32.eqz + if ;; label = @7 + local.get $3 + return + end + local.get $0 + local.get $1 + i32.load8_s + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + br 2 (;@4;) + end + end + end + loop $label$7 ;; label = @4 + local.get $2 + i32.const 4 + i32.ge_s + if ;; label = @5 + block ;; label = @6 + local.get $0 + local.get $1 + i32.load + i32.store + local.get $0 + i32.const 4 + i32.add + local.set $0 + local.get $1 + i32.const 4 + i32.add + local.set $1 + local.get $2 + i32.const 4 + i32.sub + local.set $2 + br 2 (;@4;) + end + end + end + end + end + loop $label$9 ;; label = @2 + local.get $2 + i32.const 0 + i32.gt_s + if ;; label = @3 + block ;; label = @4 + local.get $0 + local.get $1 + i32.load8_s + i32.store8 + local.get $0 + i32.const 1 + i32.add + local.set $0 + local.get $1 + i32.const 1 + i32.add + local.set $1 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + br 2 (;@2;) + end + end + end + local.get $3 + end ) - ) -) - + (func $37 (;50;) (type $3) (result i32) + i32.const 0 + ) + (func $38 (;51;) (type $4) (param $0 i32) (param $1 i32) (result i32) + local.get $1 + local.get $0 + i32.const 1 + i32.and + i32.const 0 + i32.add + call_indirect (type $1) + ) + (func $39 (;52;) (type $12) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) + local.get $1 + local.get $2 + local.get $3 + local.get $0 + i32.const 3 + i32.and + i32.const 2 + i32.add + call_indirect (type $0) + ) + (func $40 (;53;) (type $5) (param $0 i32) (param $1 i32) + local.get $1 + local.get $0 + i32.const 1 + i32.and + i32.const 6 + i32.add + call_indirect (type $2) + ) + (func $41 (;54;) (type $1) (param $0 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + i32.const 0 + call $fimport$3 + i32.const 0 + end + ) + (func $42 (;55;) (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + block $label$1 (result i32) ;; label = @1 + i32.const 1 + call $fimport$3 + i32.const 0 + end + ) + (func $43 (;56;) (type $2) (param $0 i32) + i32.const 2 + call $fimport$3 + ) + (global $global$0 (;5;) (mut i32) global.get $gimport$0) + (global $global$1 (;6;) (mut i32) global.get $gimport$1) + (global $global$2 (;7;) (mut i32) global.get $gimport$2) + (global $global$3 (;8;) (mut i32) i32.const 0) + (global $global$4 (;9;) (mut i32) i32.const 0) + (global $global$5 (;10;) (mut i32) i32.const 0) + (export "_sbrk" (func $34)) + (export "_free" (func $32)) + (export "_main" (func $7)) + (export "_pthread_self" (func $37)) + (export "_memset" (func $35)) + (export "_malloc" (func $31)) + (export "_memcpy" (func $36)) + (export "___errno_location" (func $11)) + (export "runPostSets" (func $33)) + (export "stackAlloc" (func $0)) + (export "stackSave" (func $1)) + (export "stackRestore" (func $2)) + (export "establishStackSpace" (func $3)) + (export "setThrew" (func $4)) + (export "setTempRet0" (func $5)) + (export "getTempRet0" (func $6)) + (export "dynCall_ii" (func $38)) + (export "dynCall_iiii" (func $39)) + (export "dynCall_vi" (func $40)) + (elem (;0;) (global.get $gimport$19) func $41 $8 $42 $13 $9 $14 $43 $15) + (data (;0;) (i32.const 1024) "\04\04\00\00\05") + (data (;1;) (i32.const 1040) "\01") + (data (;2;) (i32.const 1064) "\01\00\00\00\02\00\00\004\10\00\00\00\04") + (data (;3;) (i32.const 1088) "\01") + (data (;4;) (i32.const 1103) "\0a\ff\ff\ff\ff") + (data (;5;) (i32.const 1140) "error: %d\5cn\00lastprime: %d.\0a\00\11\00\0a\00\11\11\11\00\00\00\00\05\00\00\00\00\00\00\09\00\00\00\00\0b") + (data (;6;) (i32.const 1200) "\11\00\0f\0a\11\11\11\03\0a\07\00\01\13\09\0b\0b\00\00\09\06\0b\00\00\0b\00\06\11\00\00\00\11\11\11") + (data (;7;) (i32.const 1249) "\0b") + (data (;8;) (i32.const 1258) "\11\00\0a\0a\11\11\11\00\0a\00\00\02\00\09\0b\00\00\00\09\00\0b\00\00\0b") + (data (;9;) (i32.const 1307) "\0c") + (data (;10;) (i32.const 1319) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c") + (data (;11;) (i32.const 1365) "\0e") + (data (;12;) (i32.const 1377) "\0d\00\00\00\04\0d\00\00\00\00\09\0e\00\00\00\00\00\0e\00\00\0e") + (data (;13;) (i32.const 1423) "\10") + (data (;14;) (i32.const 1435) "\0f\00\00\00\00\0f\00\00\00\00\09\10\00\00\00\00\00\10\00\00\10\00\00\12\00\00\00\12\12\12") + (data (;15;) (i32.const 1490) "\12\00\00\00\12\12\12\00\00\00\00\00\00\09") + (data (;16;) (i32.const 1539) "\0b") + (data (;17;) (i32.const 1551) "\0a\00\00\00\00\0a\00\00\00\00\09\0b\00\00\00\00\00\0b\00\00\0b") + (data (;18;) (i32.const 1597) "\0c") + (data (;19;) (i32.const 1609) "\0c\00\00\00\00\0c\00\00\00\00\09\0c\00\00\00\00\00\0c\00\00\0c\00\000123456789ABCDEF-+ 0X0x\00(null)\00-0X+0X 0X-0x+0x 0x\00inf\00INF\00nan\00NAN\00.\00T!\22\19\0d\01\02\03\11K\1c\0c\10\04\0b\1d\12\1e'hnopqb \05\06\0f\13\14\15\1a\08\16\07($\17\18\09\0a\0e\1b\1f%#\83\82}&*+<=>?CGJMXYZ[\5c]^_`acdefgijklrstyz{|\00Illegal byte sequence\00Domain error\00Result not representable\00Not a tty\00Permission denied\00Operation not permitted\00No such file or directory\00No such process\00File exists\00Value too large for data type\00No space left on device\00Out of memory\00Resource busy\00Interrupted system call\00Resource temporarily unavailable\00Invalid seek\00Cross-device link\00Read-only file system\00Directory not empty\00Connection reset by peer\00Operation timed out\00Connection refused\00Host is down\00Host is unreachable\00Address in use\00Broken pipe\00I/O error\00No such device or address\00Block device required\00No such device\00Not a directory\00Is a directory\00Text file busy\00Exec format error\00Invalid argument\00Argument list too long\00Symbolic link loop\00Filename too long\00Too many open files in system\00No file descriptors available\00Bad file descriptor\00No child process\00Bad address\00File too large\00Too many links\00No locks available\00Resource deadlock would occur\00State not recoverable\00Previous owner died\00Operation canceled\00Function not implemented\00No message of desired type\00Identifier removed\00Device not a stream\00No data available\00Device timeout\00Out of streams resources\00Link has been severed\00Protocol error\00Bad message\00File descriptor in bad state\00Not a socket\00Destination address required\00Message too large\00Protocol wrong type for socket\00Protocol not available\00Protocol not supported\00Socket type not supported\00Not supported\00Protocol family not supported\00Address family not supported by protocol\00Address not available\00Network is down\00Network unreachable\00Connection reset by network\00Connection aborted\00No buffer space available\00Socket is connected\00Socket not connected\00Cannot send after socket shutdown\00Operation already in progress\00Operation in progress\00Stale file handle\00Remote I/O error\00Quota exceeded\00No medium found\00Wrong medium type\00No error information") +) \ No newline at end of file diff --git a/cranelift/wasm/wasmtests/rust_fannkuch.wat b/cranelift/wasm/wasmtests/rust_fannkuch.wat index cd47f661c82c..c61f08c67a98 100644 --- a/cranelift/wasm/wasmtests/rust_fannkuch.wat +++ b/cranelift/wasm/wasmtests/rust_fannkuch.wat @@ -1,2511 +1,1723 @@ (module - (type $0 (func (param i32 i32 i32) (result i32))) - (type $1 (func (param i32 i32) (result i32))) - (type $2 (func (param i32))) - (type $3 (func (param i32) (result i32))) - (type $4 (func (param i32 i32))) - (type $5 (func (param i64 i32) (result i32))) - (type $6 (func (param i32) (result i64))) - (type $7 (func)) - (type $8 (func (param i32 i32))) - (type $9 (func (param i32 i32 i32) (result i32))) - (memory $0 17) - (data (i32.const 1048576) "src/lib.rs\00\00\00\00\00\00attempt to divide by zero\00\00\00\00\00\00\00attempt to divide with overflow\00index out of bounds: the len is but the index is 00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899called `Option::unwrap()` on a `None` valuesrc/libcore/option.rssrc/lib.rs") - (data (i32.const 1048982) "\10\00\n\00\00\00%\00\00\00\1d\00\00\00\10\00\10\00\19\00\00\00\00\00\10\00\n\00\00\00&\00\00\00\15\00\00\000\00\10\00\1f\00\00\00\00\00\10\00\n\00\00\00&\00\00\00\15\00\00\00\00\00\10\00\n\00\00\00.\00\00\00\15\00\00\00\00\00\10\00\n\00\00\000\00\00\00\15\00\00\00\00\00\10\00\n\00\00\00-\00\00\00\11\00\00\00\00\00\10\00\n\00\00\00E\00\00\00\17\00\00\00\00\00\10\00\n\00\00\00q\00\00\00\"\00\00\00\00\00\10\00\n\00\00\00s\00\00\00\11\00\00\00P\00\10\00 \00\00\00p\00\10\00\12\00\00\00\02\00\00\00\00\00\00\00\01\00\00\00\03\00\00\00J\01\10\00+\00\00\00u\01\10\00\15\00\00\00Y\01\00\00\15\00\00\00\8a\01\10\00\n\00\00\00\08\00\00\00\t\00\00\00\8a\01\10\00\n\00\00\00\n\00\00\00\14") - (table $0 4 4 funcref) - (elem (i32.const 1) $4 $7 $8) - (global $global$0 (mut i32) (i32.const 1048576)) - (global $global$1 i32 (i32.const 1049244)) - (global $global$2 i32 (i32.const 1049244)) - (export "memory" (memory $0)) - (export "__heap_base" (global $global$1)) - (export "__data_end" (global $global$2)) - (export "run_fannkuch" (func $10)) - (func $0 (; 0 ;) (type $7) - (local $0 i32) - (local $1 i32) - (local.set $0 - (i32.const 1) + (type $0 (;0;) (func (param i32 i32 i32) (result i32))) + (type $1 (;1;) (func (param i32 i32) (result i32))) + (type $2 (;2;) (func (param i32))) + (type $3 (;3;) (func (param i32) (result i32))) + (type $4 (;4;) (func (param i32 i32))) + (type $5 (;5;) (func (param i64 i32) (result i32))) + (type $6 (;6;) (func (param i32) (result i64))) + (type $7 (;7;) (func)) + (type $8 (;8;) (func (param i32 i32))) + (type $9 (;9;) (func (param i32 i32 i32) (result i32))) + (func $0 (;0;) (type $7) + (local $0 i32) (local $1 i32) + i32.const 1 + local.set $0 + block $label$1 ;; label = @1 + block $label$2 ;; label = @2 + block $label$3 ;; label = @3 + i32.const 1049232 + i32.load + i32.const 1 + i32.eq + if ;; label = @4 + block ;; label = @5 + i32.const 1049236 + i32.const 1049236 + i32.load + i32.const 1 + i32.add + local.tee $0 + i32.store + local.get $0 + i32.const 3 + i32.lt_u + br_if 2 (;@3;) + br 3 (;@2;) + end + end + i32.const 1049232 + i64.const 4294967297 + i64.store + end + i32.const 1049240 + i32.load + local.tee $1 + i32.const -1 + i32.le_s + br_if 0 (;@2;) + i32.const 1049240 + local.get $1 + i32.store + local.get $0 + i32.const 2 + i32.lt_u + br_if 1 (;@1;) + end + unreachable + end + unreachable ) - (block $label$1 - (block $label$2 - (block $label$3 - (if - (i32.eq - (i32.load - (i32.const 1049232) - ) - (i32.const 1) - ) - (block - (i32.store - (i32.const 1049236) - (local.tee $0 - (i32.add - (i32.load - (i32.const 1049236) - ) - (i32.const 1) - ) - ) - ) - (br_if $label$3 - (i32.lt_u - (local.get $0) - (i32.const 3) - ) - ) - (br $label$2) - ) - ) - (i64.store - (i32.const 1049232) - (i64.const 4294967297) - ) - ) - (br_if $label$2 - (i32.le_s - (local.tee $1 - (i32.load - (i32.const 1049240) - ) - ) - (i32.const -1) - ) - ) - (i32.store - (i32.const 1049240) - (local.get $1) - ) - (br_if $label$1 - (i32.lt_u - (local.get $0) - (i32.const 2) - ) - ) - ) - (unreachable) + (func $1 (;1;) (type $2) (param $0 i32) + (local $1 i32) + global.get $global$0 + i32.const 16 + i32.sub + local.tee $1 + global.set $global$0 + local.get $0 + i32.load offset=8 + i32.eqz + if ;; label = @1 + block ;; label = @2 + i32.const 1049172 + call $2 + unreachable + end + end + local.get $1 + local.get $0 + i32.const 20 + i32.add + i64.load align=4 + i64.store offset=8 + local.get $1 + local.get $0 + i64.load offset=12 align=4 + i64.store + call $0 + unreachable ) - (unreachable) - ) - (func $1 (; 1 ;) (type $2) (param $0 i32) - (local $1 i32) - (global.set $global$0 - (local.tee $1 - (i32.sub - (global.get $global$0) - (i32.const 16) - ) - ) + (func $2 (;2;) (type $2) (param $0 i32) + (local $1 i32) (local $2 i64) (local $3 i64) (local $4 i64) + global.get $global$0 + i32.const 48 + i32.sub + local.tee $1 + global.set $global$0 + local.get $0 + i64.load offset=8 align=4 + local.set $2 + local.get $0 + i64.load offset=16 align=4 + local.set $3 + local.get $0 + i64.load align=4 + local.set $4 + local.get $1 + i32.const 20 + i32.add + i32.const 0 + i32.store + local.get $1 + local.get $4 + i64.store offset=24 + local.get $1 + i32.const 1048656 + i32.store offset=16 + local.get $1 + i64.const 1 + i64.store offset=4 align=4 + local.get $1 + local.get $1 + i32.const 24 + i32.add + i32.store + local.get $1 + local.get $3 + i64.store offset=40 + local.get $1 + local.get $2 + i64.store offset=32 + local.get $1 + local.get $1 + i32.const 32 + i32.add + call $5 + unreachable ) - (if - (i32.eqz - (i32.load offset=8 - (local.get $0) - ) - ) - (block - (call $2 - (i32.const 1049172) - ) - (unreachable) - ) + (func $3 (;3;) (type $8) (param $0 i32) (param $1 i32) + (local $2 i32) + global.get $global$0 + i32.const 48 + i32.sub + local.tee $2 + global.set $global$0 + local.get $2 + i32.const 16 + i32.store offset=4 + local.get $2 + local.get $1 + i32.store + local.get $2 + i32.const 44 + i32.add + i32.const 1 + i32.store + local.get $2 + i32.const 28 + i32.add + i32.const 2 + i32.store + local.get $2 + i32.const 1 + i32.store offset=36 + local.get $2 + i64.const 2 + i64.store offset=12 align=4 + local.get $2 + i32.const 1049140 + i32.store offset=8 + local.get $2 + local.get $2 + i32.store offset=40 + local.get $2 + local.get $2 + i32.const 4 + i32.add + i32.store offset=32 + local.get $2 + local.get $2 + i32.const 32 + i32.add + i32.store offset=24 + local.get $2 + i32.const 8 + i32.add + local.get $0 + call $5 + unreachable ) - (i64.store offset=8 - (local.get $1) - (i64.load align=4 - (i32.add - (local.get $0) - (i32.const 20) - ) - ) + (func $4 (;4;) (type $1) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + i64.load32_u + local.get $1 + call $6 ) - (i64.store - (local.get $1) - (i64.load offset=12 align=4 - (local.get $0) - ) + (func $5 (;5;) (type $4) (param $0 i32) (param $1 i32) + (local $2 i32) (local $3 i64) + global.get $global$0 + i32.const 32 + i32.sub + local.tee $2 + global.set $global$0 + local.get $1 + i64.load align=4 + local.set $3 + local.get $2 + i32.const 20 + i32.add + local.get $1 + i64.load offset=8 align=4 + i64.store align=4 + local.get $2 + local.get $3 + i64.store offset=12 align=4 + local.get $2 + local.get $0 + i32.store offset=8 + local.get $2 + i32.const 1049156 + i32.store offset=4 + local.get $2 + i32.const 1048656 + i32.store + local.get $2 + call $1 + unreachable ) - (call $0) - (unreachable) - ) - (func $2 (; 2 ;) (type $2) (param $0 i32) - (local $1 i32) - (local $2 i64) - (local $3 i64) - (local $4 i64) - (global.set $global$0 - (local.tee $1 - (i32.sub - (global.get $global$0) - (i32.const 48) - ) - ) + (func $6 (;6;) (type $5) (param $0 i64) (param $1 i32) (result i32) + (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i64) (local $14 i32) (local $15 i32) + global.get $global$0 + i32.const 48 + i32.sub + local.tee $6 + global.set $global$0 + i32.const 39 + local.set $2 + block $label$1 ;; label = @1 + block $label$2 ;; label = @2 + local.get $0 + i64.const 10000 + i64.ge_u + if ;; label = @3 + block ;; label = @4 + loop $label$4 ;; label = @5 + local.get $6 + i32.const 9 + i32.add + local.get $2 + i32.add + local.tee $3 + i32.const -4 + i32.add + local.get $0 + local.get $0 + i64.const 10000 + i64.div_u + local.tee $13 + i64.const -10000 + i64.mul + i64.add + i32.wrap_i64 + local.tee $4 + i32.const 100 + i32.div_u + local.tee $5 + i32.const 1 + i32.shl + i32.const 1048706 + i32.add + i32.load16_u align=1 + i32.store16 align=1 + local.get $3 + i32.const -2 + i32.add + local.get $5 + i32.const -100 + i32.mul + local.get $4 + i32.add + i32.const 1 + i32.shl + i32.const 1048706 + i32.add + i32.load16_u align=1 + i32.store16 align=1 + local.get $2 + i32.const -4 + i32.add + local.set $2 + block (result i32) ;; label = @6 + local.get $0 + i64.const 99999999 + i64.gt_u + local.set $14 + local.get $13 + local.set $0 + local.get $14 + end + br_if 0 (;@5;) + end + local.get $13 + i32.wrap_i64 + local.tee $3 + i32.const 99 + i32.le_s + br_if 3 (;@1;) + br 2 (;@2;) + end + end + local.get $0 + local.tee $13 + i32.wrap_i64 + local.tee $3 + i32.const 99 + i32.le_s + br_if 1 (;@1;) + end + local.get $2 + i32.const -2 + i32.add + local.tee $2 + local.get $6 + i32.const 9 + i32.add + i32.add + local.get $13 + i32.wrap_i64 + local.tee $4 + i32.const 65535 + i32.and + i32.const 100 + i32.div_u + local.tee $3 + i32.const -100 + i32.mul + local.get $4 + i32.add + i32.const 65535 + i32.and + i32.const 1 + i32.shl + i32.const 1048706 + i32.add + i32.load16_u align=1 + i32.store16 align=1 + end + block $label$5 ;; label = @1 + local.get $3 + i32.const 9 + i32.le_s + if ;; label = @2 + block ;; label = @3 + local.get $2 + i32.const -1 + i32.add + local.tee $2 + local.get $6 + i32.const 9 + i32.add + i32.add + local.get $3 + i32.const 48 + i32.add + i32.store8 + br 2 (;@1;) + end + end + local.get $2 + i32.const -2 + i32.add + local.tee $2 + local.get $6 + i32.const 9 + i32.add + i32.add + local.get $3 + i32.const 1 + i32.shl + i32.const 1048706 + i32.add + i32.load16_u align=1 + i32.store16 align=1 + end + i32.const 39 + local.get $2 + i32.sub + local.set $7 + i32.const 1 + local.set $3 + i32.const 43 + i32.const 1114112 + local.get $1 + i32.load + local.tee $4 + i32.const 1 + i32.and + local.tee $11 + select + local.set $8 + local.get $4 + i32.const 29 + i32.shl + i32.const 31 + i32.shr_s + i32.const 1048656 + i32.and + local.set $9 + local.get $6 + i32.const 9 + i32.add + local.get $2 + i32.add + local.set $10 + block $label$7 ;; label = @1 + block $label$8 ;; label = @2 + block $label$9 ;; label = @3 + block $label$10 ;; label = @4 + block $label$11 ;; label = @5 + block $label$12 ;; label = @6 + block $label$13 ;; label = @7 + block $label$14 ;; label = @8 + block $label$15 (result i32) ;; label = @9 + block $label$16 ;; label = @10 + block $label$17 ;; label = @11 + block $label$18 ;; label = @12 + block $label$19 ;; label = @13 + local.get $1 + i32.load offset=8 + i32.const 1 + i32.eq + if ;; label = @14 + block ;; label = @15 + local.get $1 + i32.const 12 + i32.add + i32.load + local.tee $5 + local.get $7 + local.get $11 + i32.add + local.tee $2 + i32.le_u + br_if 2 (;@13;) + local.get $4 + i32.const 8 + i32.and + br_if 3 (;@12;) + local.get $5 + local.get $2 + i32.sub + local.set $4 + i32.const 1 + local.get $1 + i32.load8_u offset=48 + local.tee $3 + local.get $3 + i32.const 3 + i32.eq + select + local.tee $3 + i32.const 3 + i32.and + i32.eqz + br_if 4 (;@11;) + local.get $3 + i32.const 2 + i32.eq + br_if 5 (;@10;) + i32.const 0 + local.set $5 + local.get $4 + br 6 (;@9;) + end + end + local.get $1 + local.get $8 + local.get $9 + call $9 + br_if 10 (;@3;) + br 11 (;@2;) + end + local.get $1 + local.get $8 + local.get $9 + call $9 + br_if 9 (;@3;) + br 10 (;@2;) + end + local.get $1 + i32.const 1 + i32.store8 offset=48 + local.get $1 + i32.const 48 + i32.store offset=4 + local.get $1 + local.get $8 + local.get $9 + call $9 + br_if 8 (;@3;) + local.get $5 + local.get $2 + i32.sub + local.set $3 + i32.const 1 + local.get $1 + i32.const 48 + i32.add + i32.load8_u + local.tee $4 + local.get $4 + i32.const 3 + i32.eq + select + local.tee $4 + i32.const 3 + i32.and + i32.eqz + br_if 3 (;@8;) + local.get $4 + i32.const 2 + i32.eq + br_if 4 (;@7;) + i32.const 0 + local.set $4 + br 5 (;@6;) + end + local.get $4 + local.set $5 + i32.const 0 + br 1 (;@9;) + end + local.get $4 + i32.const 1 + i32.add + i32.const 1 + i32.shr_u + local.set $5 + local.get $4 + i32.const 1 + i32.shr_u + end + local.set $3 + i32.const -1 + local.set $2 + local.get $1 + i32.const 4 + i32.add + local.set $4 + local.get $1 + i32.const 24 + i32.add + local.set $11 + local.get $1 + i32.const 28 + i32.add + local.set $12 + block $label$21 ;; label = @9 + loop $label$22 ;; label = @10 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + local.get $3 + i32.ge_u + br_if 1 (;@9;) + local.get $11 + i32.load + local.get $4 + i32.load + local.get $12 + i32.load + i32.load offset=16 + call_indirect (type $1) + i32.eqz + br_if 0 (;@10;) + end + br 8 (;@1;) + end + local.get $1 + i32.const 4 + i32.add + i32.load + local.set $4 + i32.const 1 + local.set $3 + local.get $1 + local.get $8 + local.get $9 + call $9 + br_if 5 (;@3;) + local.get $1 + i32.const 24 + i32.add + local.tee $2 + i32.load + local.get $10 + local.get $7 + local.get $1 + i32.const 28 + i32.add + local.tee $1 + i32.load + i32.load offset=12 + call_indirect (type $0) + br_if 5 (;@3;) + local.get $2 + i32.load + local.set $7 + i32.const -1 + local.set $2 + local.get $1 + i32.load + i32.const 16 + i32.add + local.set $1 + loop $label$23 ;; label = @9 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + local.get $5 + i32.ge_u + br_if 4 (;@5;) + local.get $7 + local.get $4 + local.get $1 + i32.load + call_indirect (type $1) + i32.eqz + br_if 0 (;@9;) + end + br 5 (;@3;) + end + local.get $3 + local.set $4 + i32.const 0 + local.set $3 + br 1 (;@6;) + end + local.get $3 + i32.const 1 + i32.add + i32.const 1 + i32.shr_u + local.set $4 + local.get $3 + i32.const 1 + i32.shr_u + local.set $3 + end + i32.const -1 + local.set $2 + local.get $1 + i32.const 4 + i32.add + local.set $5 + local.get $1 + i32.const 24 + i32.add + local.set $8 + local.get $1 + i32.const 28 + i32.add + local.set $9 + block $label$24 ;; label = @6 + loop $label$25 ;; label = @7 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + local.get $3 + i32.ge_u + br_if 1 (;@6;) + local.get $8 + i32.load + local.get $5 + i32.load + local.get $9 + i32.load + i32.load offset=16 + call_indirect (type $1) + i32.eqz + br_if 0 (;@7;) + end + br 5 (;@1;) + end + local.get $1 + i32.const 4 + i32.add + i32.load + local.set $5 + i32.const 1 + local.set $3 + local.get $1 + i32.const 24 + i32.add + local.tee $2 + i32.load + local.get $10 + local.get $7 + local.get $1 + i32.const 28 + i32.add + local.tee $1 + i32.load + i32.load offset=12 + call_indirect (type $0) + br_if 2 (;@3;) + local.get $2 + i32.load + local.set $7 + i32.const -1 + local.set $2 + local.get $1 + i32.load + i32.const 16 + i32.add + local.set $1 + loop $label$26 ;; label = @6 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + local.get $4 + i32.ge_u + br_if 2 (;@4;) + local.get $7 + local.get $5 + local.get $1 + i32.load + call_indirect (type $1) + i32.eqz + br_if 0 (;@6;) + end + br 2 (;@3;) + end + local.get $6 + i32.const 48 + i32.add + global.set $global$0 + i32.const 0 + return + end + i32.const 0 + local.set $3 + end + local.get $6 + i32.const 48 + i32.add + global.set $global$0 + local.get $3 + return + end + block (result i32) ;; label = @2 + local.get $1 + i32.load offset=24 + local.get $10 + local.get $7 + local.get $1 + i32.const 28 + i32.add + i32.load + i32.load offset=12 + call_indirect (type $0) + local.set $15 + local.get $6 + i32.const 48 + i32.add + global.set $global$0 + local.get $15 + end + return + end + local.get $6 + i32.const 48 + i32.add + global.set $global$0 + i32.const 1 ) - (local.set $2 - (i64.load offset=8 align=4 - (local.get $0) - ) + (func $7 (;7;) (type $2) (param $0 i32) + nop ) - (local.set $3 - (i64.load offset=16 align=4 - (local.get $0) - ) + (func $8 (;8;) (type $6) (param $0 i32) (result i64) + i64.const -2357177763932378009 ) - (local.set $4 - (i64.load align=4 - (local.get $0) - ) + (func $9 (;9;) (type $9) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + block $label$1 ;; label = @1 + block $label$2 (result i32) ;; label = @2 + local.get $1 + i32.const 1114112 + i32.ne + if ;; label = @3 + i32.const 1 + local.get $0 + i32.load offset=24 + local.get $1 + local.get $0 + i32.const 28 + i32.add + i32.load + i32.load offset=16 + call_indirect (type $1) + br_if 1 (;@2;) + drop + end + local.get $2 + i32.eqz + br_if 1 (;@1;) + local.get $0 + i32.load offset=24 + local.get $2 + i32.const 0 + local.get $0 + i32.const 28 + i32.add + i32.load + i32.load offset=12 + call_indirect (type $0) + end + return + end + i32.const 0 ) - (i32.store - (i32.add - (local.get $1) - (i32.const 20) - ) - (i32.const 0) + (func $10 (;10;) (type $3) (param $0 i32) (result i32) + (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) (local $7 i32) (local $8 i32) (local $9 i32) (local $10 i32) (local $11 i32) (local $12 i32) (local $13 i32) (local $14 i32) (local $15 i32) (local $16 i32) (local $17 i32) (local $18 i32) (local $19 i32) (local $20 i32) (local $21 i32) (local $22 i32) (local $23 i32) (local $24 i32) (local $25 i32) (local $26 i32) (local $27 i32) (local $28 i32) (local $29 i32) (local $30 i32) (local $31 i32) (local $32 i32) (local $33 i32) (local $34 i32) (local $35 i32) (local $36 i32) (local $37 i32) (local $38 i32) (local $39 i32) (local $40 i32) (local $41 i32) (local $42 i32) (local $43 i32) (local $44 i32) (local $45 i32) (local $46 i32) + global.get $global$0 + i32.const 256 + i32.sub + local.tee $1 + global.set $global$0 + local.get $1 + i64.const 4294967297 + i64.store offset=56 align=4 + local.get $1 + i64.const 4294967297 + i64.store offset=48 align=4 + local.get $1 + i64.const 4294967297 + i64.store offset=40 align=4 + local.get $1 + i64.const 4294967297 + i64.store offset=32 align=4 + local.get $1 + i64.const 4294967297 + i64.store offset=24 align=4 + local.get $1 + i64.const 4294967297 + i64.store offset=16 align=4 + local.get $1 + i64.const 4294967297 + i64.store offset=8 align=4 + local.get $1 + i64.const 4294967297 + i64.store align=4 + block $label$1 ;; label = @1 + local.get $0 + i32.const 1 + i32.add + local.tee $11 + i32.const 2 + i32.ge_u + if ;; label = @2 + block ;; label = @3 + local.get $1 + local.set $3 + i32.const 1 + local.set $2 + loop $label$3 ;; label = @4 + local.get $2 + i32.const 16 + i32.ge_u + br_if 3 (;@1;) + local.get $3 + i32.const 4 + i32.add + local.tee $4 + local.get $3 + i32.load + local.get $2 + i32.mul + i32.store + local.get $4 + local.set $3 + local.get $2 + i32.const 1 + i32.add + local.tee $4 + local.set $2 + local.get $4 + local.get $11 + i32.lt_u + br_if 0 (;@4;) + end + end + end + local.get $0 + i32.const 16 + i32.lt_u + if ;; label = @2 + block ;; label = @3 + i32.const 1 + local.set $20 + local.get $1 + local.get $0 + i32.const 2 + i32.shl + i32.add + i32.load + local.tee $9 + local.set $21 + local.get $9 + i32.const 24 + i32.ge_u + if ;; label = @4 + i32.const 24 + i32.const 25 + local.get $9 + local.get $9 + i32.const 24 + i32.div_u + local.tee $21 + i32.const 24 + i32.mul + i32.eq + select + local.set $20 + end + i32.const 0 + local.get $0 + i32.sub + local.set $40 + local.get $1 + i32.const 196 + i32.add + local.set $12 + local.get $1 + i32.const 132 + i32.add + local.set $41 + local.get $1 + i32.const 124 + i32.add + local.set $42 + local.get $1 + i32.const 68 + i32.add + local.set $11 + local.get $0 + i32.const 2 + i32.lt_u + local.set $43 + loop $label$6 ;; label = @4 + local.get $1 + i32.const 120 + i32.add + i64.const 0 + i64.store + local.get $1 + i32.const 112 + i32.add + i64.const 0 + i64.store + local.get $1 + i32.const 104 + i32.add + i64.const 0 + i64.store + local.get $1 + i32.const 96 + i32.add + i64.const 0 + i64.store + local.get $1 + i32.const 88 + i32.add + i64.const 0 + i64.store + local.get $1 + i32.const 80 + i32.add + i64.const 0 + i64.store + local.get $1 + i32.const 72 + i32.add + i64.const 0 + i64.store + local.get $1 + i64.const 0 + i64.store offset=64 + local.get $1 + i32.const 184 + i32.add + local.tee $26 + i64.const 0 + i64.store + local.get $1 + i32.const 176 + i32.add + local.tee $27 + i64.const 0 + i64.store + local.get $1 + i32.const 168 + i32.add + local.tee $28 + i64.const 0 + i64.store + local.get $1 + i32.const 160 + i32.add + local.tee $29 + i64.const 0 + i64.store + local.get $1 + i32.const 152 + i32.add + local.tee $30 + i64.const 0 + i64.store + local.get $1 + i32.const 144 + i32.add + local.tee $31 + i64.const 0 + i64.store + local.get $1 + i32.const 136 + i32.add + local.tee $32 + i64.const 0 + i64.store + local.get $1 + i64.const 0 + i64.store offset=128 + local.get $1 + i32.const 248 + i32.add + local.tee $33 + i64.const 64424509454 + i64.store align=4 + local.get $1 + i32.const 240 + i32.add + local.tee $34 + i64.const 55834574860 + i64.store align=4 + local.get $1 + i32.const 232 + i32.add + local.tee $35 + i64.const 47244640266 + i64.store align=4 + local.get $1 + i32.const 224 + i32.add + local.tee $36 + i64.const 38654705672 + i64.store align=4 + local.get $1 + i32.const 216 + i32.add + local.tee $37 + i64.const 30064771078 + i64.store align=4 + local.get $1 + i32.const 208 + i32.add + local.tee $38 + i64.const 21474836484 + i64.store align=4 + local.get $1 + i32.const 200 + i32.add + local.tee $39 + i64.const 12884901890 + i64.store align=4 + local.get $1 + i64.const 4294967296 + i64.store offset=192 align=4 + local.get $13 + local.get $21 + i32.mul + local.set $7 + block $label$7 (result i32) ;; label = @5 + block $label$8 ;; label = @6 + local.get $43 + i32.eqz + if ;; label = @7 + block ;; label = @8 + local.get $40 + local.set $23 + local.get $7 + local.set $14 + local.get $0 + local.set $15 + i32.const 0 + local.set $5 + br 2 (;@6;) + end + end + i32.const 0 + br 1 (;@5;) + end + i32.const 1 + end + local.set $2 + loop $label$10 ;; label = @5 + block $label$11 ;; label = @6 + block $label$12 ;; label = @7 + block $label$13 (result i32) ;; label = @8 + block $label$14 ;; label = @9 + block $label$15 ;; label = @10 + block $label$16 ;; label = @11 + block $label$17 ;; label = @12 + block $label$18 ;; label = @13 + block $label$19 ;; label = @14 + local.get $2 + i32.eqz + if ;; label = @15 + block ;; label = @16 + local.get $13 + i32.const 1 + i32.add + local.set $13 + local.get $9 + local.get $7 + local.get $21 + i32.add + local.tee $3 + local.get $3 + local.get $9 + i32.gt_u + select + i32.const -1 + i32.add + local.set $44 + i32.const 0 + local.set $24 + local.get $1 + i32.load offset=192 + local.tee $6 + i32.const 1 + i32.ge_s + br_if 2 (;@14;) + br 3 (;@13;) + end + end + block $label$21 ;; label = @15 + block $label$22 ;; label = @16 + block $label$23 ;; label = @17 + block $label$24 ;; label = @18 + block $label$25 ;; label = @19 + block $label$26 ;; label = @20 + block $label$27 ;; label = @21 + block $label$28 ;; label = @22 + block $label$29 ;; label = @23 + block $label$30 ;; label = @24 + block $label$31 ;; label = @25 + local.get $5 + br_table 0 (;@25;) 1 (;@24;) 2 (;@23;) + end + local.get $15 + i32.const -1 + i32.add + local.tee $4 + i32.const 16 + i32.ge_u + br_if 6 (;@18;) + local.get $1 + local.get $4 + i32.const 2 + i32.shl + local.tee $2 + i32.add + i32.load + local.tee $3 + i32.eqz + br_if 7 (;@17;) + local.get $14 + i32.const -2147483648 + i32.eq + if ;; label = @25 + local.get $3 + i32.const -1 + i32.eq + br_if 9 (;@16;) + end + local.get $1 + i32.const -64 + i32.sub + local.get $2 + i32.add + local.get $14 + local.get $3 + i32.div_s + local.tee $16 + i32.store + local.get $32 + local.get $39 + i64.load align=4 + i64.store + local.get $31 + local.get $38 + i64.load align=4 + i64.store + local.get $30 + local.get $37 + i64.load align=4 + i64.store + local.get $29 + local.get $36 + i64.load align=4 + i64.store + local.get $28 + local.get $35 + i64.load align=4 + i64.store + local.get $27 + local.get $34 + i64.load align=4 + i64.store + local.get $26 + local.get $33 + i64.load align=4 + i64.store + local.get $1 + local.get $1 + i64.load offset=192 align=4 + i64.store offset=128 + local.get $16 + local.get $23 + i32.add + local.set $45 + local.get $14 + local.get $3 + local.get $16 + i32.mul + i32.sub + local.set $14 + i32.const 0 + local.set $2 + local.get $1 + i32.const 192 + i32.add + local.set $8 + loop $label$33 ;; label = @25 + block $label$34 ;; label = @26 + local.get $2 + local.get $16 + i32.add + local.tee $3 + local.get $4 + i32.gt_u + if ;; label = @27 + block ;; label = @28 + local.get $2 + local.get $45 + i32.add + local.tee $46 + i32.const 15 + i32.gt_u + br_if 7 (;@21;) + local.get $3 + local.get $15 + i32.sub + local.set $3 + local.get $2 + i32.const 15 + i32.le_u + br_if 2 (;@26;) + br 6 (;@22;) + end + end + local.get $3 + i32.const 16 + i32.ge_u + br_if 6 (;@20;) + local.get $2 + i32.const 15 + i32.gt_u + br_if 4 (;@22;) + end + local.get $8 + local.get $1 + i32.const 128 + i32.add + local.get $3 + i32.const 2 + i32.shl + i32.add + i32.load + i32.store + local.get $8 + i32.const 4 + i32.add + local.set $8 + local.get $2 + i32.const 1 + i32.add + local.tee $2 + local.get $15 + i32.lt_u + br_if 0 (;@25;) + end + local.get $23 + i32.const 1 + i32.add + local.set $23 + local.get $4 + local.tee $15 + i32.const 1 + i32.gt_u + br_if 9 (;@15;) + i32.const 0 + local.set $2 + br 19 (;@5;) + end + local.get $26 + local.get $33 + i64.load align=4 + i64.store + local.get $27 + local.get $34 + i64.load align=4 + i64.store + local.get $28 + local.get $35 + i64.load align=4 + i64.store + local.get $29 + local.get $36 + i64.load align=4 + i64.store + local.get $30 + local.get $37 + i64.load align=4 + i64.store + local.get $31 + local.get $38 + i64.load align=4 + i64.store + local.get $32 + local.get $39 + i64.load align=4 + i64.store + local.get $1 + local.get $1 + i64.load offset=192 align=4 + i64.store offset=128 + local.get $6 + i32.const 15 + i32.gt_u + br_if 4 (;@19;) + i32.const 1 + local.set $17 + local.get $6 + local.set $10 + i32.const 0 + br 15 (;@8;) + end + local.get $7 + local.get $44 + i32.lt_u + if ;; label = @23 + block ;; label = @24 + local.get $12 + i32.load + local.set $25 + local.get $12 + local.get $6 + i32.store + local.get $1 + local.get $25 + i32.store offset=192 + local.get $11 + local.set $18 + local.get $1 + i32.load offset=68 + local.tee $2 + i32.const 1 + i32.lt_s + br_if 18 (;@6;) + i32.const 1 + local.set $19 + br 15 (;@9;) + end + end + local.get $22 + local.get $24 + i32.add + local.set $22 + local.get $13 + local.get $20 + i32.lt_u + br_if 18 (;@4;) + local.get $1 + i32.const 256 + i32.add + global.set $global$0 + local.get $22 + return + end + i32.const 1049076 + local.get $2 + call $3 + unreachable + end + i32.const 1049060 + local.get $46 + call $3 + unreachable + end + i32.const 1049044 + local.get $2 + local.get $16 + i32.add + call $3 + unreachable + end + local.get $6 + local.set $10 + br 11 (;@7;) + end + i32.const 1048980 + local.get $4 + call $3 + unreachable + end + i32.const 1048996 + call $2 + unreachable + end + i32.const 1049020 + call $2 + unreachable + end + i32.const 0 + local.set $5 + br 2 (;@12;) + end + i32.const 1 + local.set $5 + br 2 (;@11;) + end + i32.const 2 + local.set $5 + br 2 (;@10;) + end + i32.const 1 + local.set $2 + br 6 (;@5;) + end + i32.const 1 + local.set $2 + br 5 (;@5;) + end + i32.const 1 + local.set $2 + br 4 (;@5;) + end + i32.const 1 + end + local.set $2 + loop $label$37 ;; label = @8 + block $label$38 ;; label = @9 + block $label$39 ;; label = @10 + local.get $2 + i32.eqz + if ;; label = @11 + block ;; label = @12 + local.get $10 + local.tee $3 + i32.const 2 + i32.shl + local.tee $4 + local.get $1 + i32.const 128 + i32.add + i32.add + local.tee $5 + i32.load + local.tee $10 + if ;; label = @13 + block ;; label = @14 + local.get $5 + local.get $3 + i32.store + block $label$42 ;; label = @15 + local.get $3 + i32.const 3 + i32.lt_u + br_if 0 (;@15;) + local.get $3 + i32.const -1 + i32.add + i32.const 1 + i32.shr_u + local.tee $8 + i32.eqz + br_if 0 (;@15;) + local.get $4 + local.get $42 + i32.add + local.set $2 + local.get $41 + local.set $3 + loop $label$43 ;; label = @16 + local.get $3 + i32.load + local.set $4 + local.get $3 + local.get $2 + i32.load + i32.store + local.get $2 + local.get $4 + i32.store + local.get $3 + i32.const 4 + i32.add + local.set $3 + local.get $2 + i32.const -4 + i32.add + local.set $2 + local.get $8 + i32.const -1 + i32.add + local.tee $8 + br_if 0 (;@16;) + end + end + local.get $17 + i32.const 1 + i32.add + local.set $17 + local.get $10 + i32.const 16 + i32.lt_u + br_if 5 (;@9;) + br 7 (;@7;) + end + end + i32.const 0 + local.get $17 + i32.sub + local.get $17 + local.get $7 + i32.const 1 + i32.and + select + local.get $24 + i32.add + local.set $24 + i32.const 2 + local.set $5 + br 2 (;@10;) + end + end + i32.const 0 + local.set $2 + local.get $18 + i32.const 0 + i32.store + local.get $1 + local.get $6 + local.tee $4 + i32.store offset=192 + local.get $19 + i32.const 1 + i32.add + local.set $5 + local.get $12 + local.set $3 + block $label$44 ;; label = @11 + block $label$45 ;; label = @12 + loop $label$46 ;; label = @13 + local.get $2 + i32.const 2 + i32.add + i32.const 16 + i32.ge_u + br_if 1 (;@12;) + local.get $3 + local.get $3 + i32.const 4 + i32.add + local.tee $3 + i32.load + i32.store + local.get $2 + i32.const 1 + i32.add + local.tee $2 + local.get $19 + i32.lt_u + br_if 0 (;@13;) + end + local.get $5 + i32.const 16 + i32.ge_u + br_if 1 (;@11;) + local.get $5 + i32.const 2 + i32.shl + local.tee $3 + local.get $1 + i32.const 192 + i32.add + i32.add + local.get $25 + i32.store + local.get $1 + i32.const -64 + i32.sub + local.get $3 + i32.add + local.tee $18 + i32.load + local.tee $2 + local.get $19 + i32.le_s + br_if 6 (;@6;) + local.get $12 + i32.load + local.set $6 + local.get $5 + local.set $19 + local.get $4 + local.set $25 + i32.const 1 + local.set $2 + br 4 (;@8;) + end + i32.const 1049108 + local.get $2 + i32.const 2 + i32.add + call $3 + unreachable + end + i32.const 1049124 + local.get $5 + call $3 + unreachable + end + i32.const 1 + local.set $2 + br 4 (;@5;) + end + i32.const 0 + local.set $2 + br 0 (;@8;) + end + end + i32.const 1049092 + local.get $10 + call $3 + unreachable + end + local.get $7 + i32.const 1 + i32.add + local.set $7 + local.get $18 + local.get $2 + i32.const 1 + i32.add + i32.store + block $label$47 ;; label = @6 + block $label$48 ;; label = @7 + local.get $1 + i32.load offset=192 + local.tee $6 + i32.const 1 + i32.ge_s + if ;; label = @8 + block ;; label = @9 + i32.const 1 + local.set $5 + br 2 (;@7;) + end + end + i32.const 2 + local.set $5 + br 1 (;@6;) + end + i32.const 1 + local.set $2 + br 1 (;@5;) + end + i32.const 1 + local.set $2 + br 0 (;@5;) + end + end + end + end + i32.const 1049212 + local.get $0 + call $3 + unreachable + end + i32.const 1049196 + local.get $2 + call $3 + unreachable ) - (i64.store offset=24 - (local.get $1) - (local.get $4) - ) - (i32.store offset=16 - (local.get $1) - (i32.const 1048656) - ) - (i64.store offset=4 align=4 - (local.get $1) - (i64.const 1) - ) - (i32.store - (local.get $1) - (i32.add - (local.get $1) - (i32.const 24) - ) - ) - (i64.store offset=40 - (local.get $1) - (local.get $3) - ) - (i64.store offset=32 - (local.get $1) - (local.get $2) - ) - (call $5 - (local.get $1) - (i32.add - (local.get $1) - (i32.const 32) - ) - ) - (unreachable) - ) - (func $3 (; 3 ;) (type $8) (param $0 i32) (param $1 i32) - (local $2 i32) - (global.set $global$0 - (local.tee $2 - (i32.sub - (global.get $global$0) - (i32.const 48) - ) - ) - ) - (i32.store offset=4 - (local.get $2) - (i32.const 16) - ) - (i32.store - (local.get $2) - (local.get $1) - ) - (i32.store - (i32.add - (local.get $2) - (i32.const 44) - ) - (i32.const 1) - ) - (i32.store - (i32.add - (local.get $2) - (i32.const 28) - ) - (i32.const 2) - ) - (i32.store offset=36 - (local.get $2) - (i32.const 1) - ) - (i64.store offset=12 align=4 - (local.get $2) - (i64.const 2) - ) - (i32.store offset=8 - (local.get $2) - (i32.const 1049140) - ) - (i32.store offset=40 - (local.get $2) - (local.get $2) - ) - (i32.store offset=32 - (local.get $2) - (i32.add - (local.get $2) - (i32.const 4) - ) - ) - (i32.store offset=24 - (local.get $2) - (i32.add - (local.get $2) - (i32.const 32) - ) - ) - (call $5 - (i32.add - (local.get $2) - (i32.const 8) - ) - (local.get $0) - ) - (unreachable) - ) - (func $4 (; 4 ;) (type $1) (param $0 i32) (param $1 i32) (result i32) - (call $6 - (i64.load32_u - (local.get $0) - ) - (local.get $1) - ) - ) - (func $5 (; 5 ;) (type $4) (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i64) - (global.set $global$0 - (local.tee $2 - (i32.sub - (global.get $global$0) - (i32.const 32) - ) - ) - ) - (local.set $3 - (i64.load align=4 - (local.get $1) - ) - ) - (i64.store align=4 - (i32.add - (local.get $2) - (i32.const 20) - ) - (i64.load offset=8 align=4 - (local.get $1) - ) - ) - (i64.store offset=12 align=4 - (local.get $2) - (local.get $3) - ) - (i32.store offset=8 - (local.get $2) - (local.get $0) - ) - (i32.store offset=4 - (local.get $2) - (i32.const 1049156) - ) - (i32.store - (local.get $2) - (i32.const 1048656) - ) - (call $1 - (local.get $2) - ) - (unreachable) - ) - (func $6 (; 6 ;) (type $5) (param $0 i64) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i64) - (local $14 i32) - (local $15 i32) - (global.set $global$0 - (local.tee $6 - (i32.sub - (global.get $global$0) - (i32.const 48) - ) - ) - ) - (local.set $2 - (i32.const 39) - ) - (block $label$1 - (block $label$2 - (if - (i64.ge_u - (local.get $0) - (i64.const 10000) - ) - (block - (loop $label$4 - (i32.store16 align=1 - (i32.add - (local.tee $3 - (i32.add - (i32.add - (local.get $6) - (i32.const 9) - ) - (local.get $2) - ) - ) - (i32.const -4) - ) - (i32.load16_u align=1 - (i32.add - (i32.shl - (local.tee $5 - (i32.div_u - (local.tee $4 - (i32.wrap_i64 - (i64.add - (local.get $0) - (i64.mul - (local.tee $13 - (i64.div_u - (local.get $0) - (i64.const 10000) - ) - ) - (i64.const -10000) - ) - ) - ) - ) - (i32.const 100) - ) - ) - (i32.const 1) - ) - (i32.const 1048706) - ) - ) - ) - (i32.store16 align=1 - (i32.add - (local.get $3) - (i32.const -2) - ) - (i32.load16_u align=1 - (i32.add - (i32.shl - (i32.add - (i32.mul - (local.get $5) - (i32.const -100) - ) - (local.get $4) - ) - (i32.const 1) - ) - (i32.const 1048706) - ) - ) - ) - (local.set $2 - (i32.add - (local.get $2) - (i32.const -4) - ) - ) - (br_if $label$4 - (block (result i32) - (local.set $14 - (i64.gt_u - (local.get $0) - (i64.const 99999999) - ) - ) - (local.set $0 - (local.get $13) - ) - (local.get $14) - ) - ) - ) - (br_if $label$1 - (i32.le_s - (local.tee $3 - (i32.wrap_i64 - (local.get $13) - ) - ) - (i32.const 99) - ) - ) - (br $label$2) - ) - ) - (br_if $label$1 - (i32.le_s - (local.tee $3 - (i32.wrap_i64 - (local.tee $13 - (local.get $0) - ) - ) - ) - (i32.const 99) - ) - ) - ) - (i32.store16 align=1 - (i32.add - (local.tee $2 - (i32.add - (local.get $2) - (i32.const -2) - ) - ) - (i32.add - (local.get $6) - (i32.const 9) - ) - ) - (i32.load16_u align=1 - (i32.add - (i32.shl - (i32.and - (i32.add - (i32.mul - (local.tee $3 - (i32.div_u - (i32.and - (local.tee $4 - (i32.wrap_i64 - (local.get $13) - ) - ) - (i32.const 65535) - ) - (i32.const 100) - ) - ) - (i32.const -100) - ) - (local.get $4) - ) - (i32.const 65535) - ) - (i32.const 1) - ) - (i32.const 1048706) - ) - ) - ) - ) - (block $label$5 - (if - (i32.le_s - (local.get $3) - (i32.const 9) - ) - (block - (i32.store8 - (i32.add - (local.tee $2 - (i32.add - (local.get $2) - (i32.const -1) - ) - ) - (i32.add - (local.get $6) - (i32.const 9) - ) - ) - (i32.add - (local.get $3) - (i32.const 48) - ) - ) - (br $label$5) - ) - ) - (i32.store16 align=1 - (i32.add - (local.tee $2 - (i32.add - (local.get $2) - (i32.const -2) - ) - ) - (i32.add - (local.get $6) - (i32.const 9) - ) - ) - (i32.load16_u align=1 - (i32.add - (i32.shl - (local.get $3) - (i32.const 1) - ) - (i32.const 1048706) - ) - ) - ) - ) - (local.set $7 - (i32.sub - (i32.const 39) - (local.get $2) - ) - ) - (local.set $3 - (i32.const 1) - ) - (local.set $8 - (select - (i32.const 43) - (i32.const 1114112) - (local.tee $11 - (i32.and - (local.tee $4 - (i32.load - (local.get $1) - ) - ) - (i32.const 1) - ) - ) - ) - ) - (local.set $9 - (i32.and - (i32.shr_s - (i32.shl - (local.get $4) - (i32.const 29) - ) - (i32.const 31) - ) - (i32.const 1048656) - ) - ) - (local.set $10 - (i32.add - (i32.add - (local.get $6) - (i32.const 9) - ) - (local.get $2) - ) - ) - (block $label$7 - (block $label$8 - (block $label$9 - (block $label$10 - (block $label$11 - (block $label$12 - (block $label$13 - (block $label$14 - (local.set $3 - (block $label$15 (result i32) - (block $label$16 - (block $label$17 - (block $label$18 - (block $label$19 - (if - (i32.eq - (i32.load offset=8 - (local.get $1) - ) - (i32.const 1) - ) - (block - (br_if $label$19 - (i32.le_u - (local.tee $5 - (i32.load - (i32.add - (local.get $1) - (i32.const 12) - ) - ) - ) - (local.tee $2 - (i32.add - (local.get $7) - (local.get $11) - ) - ) - ) - ) - (br_if $label$18 - (i32.and - (local.get $4) - (i32.const 8) - ) - ) - (local.set $4 - (i32.sub - (local.get $5) - (local.get $2) - ) - ) - (br_if $label$17 - (i32.eqz - (i32.and - (local.tee $3 - (select - (i32.const 1) - (local.tee $3 - (i32.load8_u offset=48 - (local.get $1) - ) - ) - (i32.eq - (local.get $3) - (i32.const 3) - ) - ) - ) - (i32.const 3) - ) - ) - ) - (br_if $label$16 - (i32.eq - (local.get $3) - (i32.const 2) - ) - ) - (local.set $5 - (i32.const 0) - ) - (br $label$15 - (local.get $4) - ) - ) - ) - (br_if $label$9 - (call $9 - (local.get $1) - (local.get $8) - (local.get $9) - ) - ) - (br $label$8) - ) - (br_if $label$9 - (call $9 - (local.get $1) - (local.get $8) - (local.get $9) - ) - ) - (br $label$8) - ) - (i32.store8 offset=48 - (local.get $1) - (i32.const 1) - ) - (i32.store offset=4 - (local.get $1) - (i32.const 48) - ) - (br_if $label$9 - (call $9 - (local.get $1) - (local.get $8) - (local.get $9) - ) - ) - (local.set $3 - (i32.sub - (local.get $5) - (local.get $2) - ) - ) - (br_if $label$14 - (i32.eqz - (i32.and - (local.tee $4 - (select - (i32.const 1) - (local.tee $4 - (i32.load8_u - (i32.add - (local.get $1) - (i32.const 48) - ) - ) - ) - (i32.eq - (local.get $4) - (i32.const 3) - ) - ) - ) - (i32.const 3) - ) - ) - ) - (br_if $label$13 - (i32.eq - (local.get $4) - (i32.const 2) - ) - ) - (local.set $4 - (i32.const 0) - ) - (br $label$12) - ) - (local.set $5 - (local.get $4) - ) - (br $label$15 - (i32.const 0) - ) - ) - (local.set $5 - (i32.shr_u - (i32.add - (local.get $4) - (i32.const 1) - ) - (i32.const 1) - ) - ) - (i32.shr_u - (local.get $4) - (i32.const 1) - ) - ) - ) - (local.set $2 - (i32.const -1) - ) - (local.set $4 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (local.set $11 - (i32.add - (local.get $1) - (i32.const 24) - ) - ) - (local.set $12 - (i32.add - (local.get $1) - (i32.const 28) - ) - ) - (block $label$21 - (loop $label$22 - (br_if $label$21 - (i32.ge_u - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (local.get $3) - ) - ) - (br_if $label$22 - (i32.eqz - (call_indirect (type $1) - (i32.load - (local.get $11) - ) - (i32.load - (local.get $4) - ) - (i32.load offset=16 - (i32.load - (local.get $12) - ) - ) - ) - ) - ) - ) - (br $label$7) - ) - (local.set $4 - (i32.load - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - ) - (local.set $3 - (i32.const 1) - ) - (br_if $label$9 - (call $9 - (local.get $1) - (local.get $8) - (local.get $9) - ) - ) - (br_if $label$9 - (call_indirect (type $0) - (i32.load - (local.tee $2 - (i32.add - (local.get $1) - (i32.const 24) - ) - ) - ) - (local.get $10) - (local.get $7) - (i32.load offset=12 - (i32.load - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 28) - ) - ) - ) - ) - ) - ) - (local.set $7 - (i32.load - (local.get $2) - ) - ) - (local.set $2 - (i32.const -1) - ) - (local.set $1 - (i32.add - (i32.load - (local.get $1) - ) - (i32.const 16) - ) - ) - (loop $label$23 - (br_if $label$11 - (i32.ge_u - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (local.get $5) - ) - ) - (br_if $label$23 - (i32.eqz - (call_indirect (type $1) - (local.get $7) - (local.get $4) - (i32.load - (local.get $1) - ) - ) - ) - ) - ) - (br $label$9) - ) - (local.set $4 - (local.get $3) - ) - (local.set $3 - (i32.const 0) - ) - (br $label$12) - ) - (local.set $4 - (i32.shr_u - (i32.add - (local.get $3) - (i32.const 1) - ) - (i32.const 1) - ) - ) - (local.set $3 - (i32.shr_u - (local.get $3) - (i32.const 1) - ) - ) - ) - (local.set $2 - (i32.const -1) - ) - (local.set $5 - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - (local.set $8 - (i32.add - (local.get $1) - (i32.const 24) - ) - ) - (local.set $9 - (i32.add - (local.get $1) - (i32.const 28) - ) - ) - (block $label$24 - (loop $label$25 - (br_if $label$24 - (i32.ge_u - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (local.get $3) - ) - ) - (br_if $label$25 - (i32.eqz - (call_indirect (type $1) - (i32.load - (local.get $8) - ) - (i32.load - (local.get $5) - ) - (i32.load offset=16 - (i32.load - (local.get $9) - ) - ) - ) - ) - ) - ) - (br $label$7) - ) - (local.set $5 - (i32.load - (i32.add - (local.get $1) - (i32.const 4) - ) - ) - ) - (local.set $3 - (i32.const 1) - ) - (br_if $label$9 - (call_indirect (type $0) - (i32.load - (local.tee $2 - (i32.add - (local.get $1) - (i32.const 24) - ) - ) - ) - (local.get $10) - (local.get $7) - (i32.load offset=12 - (i32.load - (local.tee $1 - (i32.add - (local.get $1) - (i32.const 28) - ) - ) - ) - ) - ) - ) - (local.set $7 - (i32.load - (local.get $2) - ) - ) - (local.set $2 - (i32.const -1) - ) - (local.set $1 - (i32.add - (i32.load - (local.get $1) - ) - (i32.const 16) - ) - ) - (loop $label$26 - (br_if $label$10 - (i32.ge_u - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (local.get $4) - ) - ) - (br_if $label$26 - (i32.eqz - (call_indirect (type $1) - (local.get $7) - (local.get $5) - (i32.load - (local.get $1) - ) - ) - ) - ) - ) - (br $label$9) - ) - (global.set $global$0 - (i32.add - (local.get $6) - (i32.const 48) - ) - ) - (return - (i32.const 0) - ) - ) - (local.set $3 - (i32.const 0) - ) - ) - (global.set $global$0 - (i32.add - (local.get $6) - (i32.const 48) - ) - ) - (return - (local.get $3) - ) - ) - (return - (block (result i32) - (local.set $15 - (call_indirect (type $0) - (i32.load offset=24 - (local.get $1) - ) - (local.get $10) - (local.get $7) - (i32.load offset=12 - (i32.load - (i32.add - (local.get $1) - (i32.const 28) - ) - ) - ) - ) - ) - (global.set $global$0 - (i32.add - (local.get $6) - (i32.const 48) - ) - ) - (local.get $15) - ) - ) - ) - (global.set $global$0 - (i32.add - (local.get $6) - (i32.const 48) - ) - ) - (i32.const 1) - ) - (func $7 (; 7 ;) (type $2) (param $0 i32) - (nop) - ) - (func $8 (; 8 ;) (type $6) (param $0 i32) (result i64) - (i64.const -2357177763932378009) - ) - (func $9 (; 9 ;) (type $9) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (block $label$1 - (return - (block $label$2 (result i32) - (if - (i32.ne - (local.get $1) - (i32.const 1114112) - ) - (drop - (br_if $label$2 - (i32.const 1) - (call_indirect (type $1) - (i32.load offset=24 - (local.get $0) - ) - (local.get $1) - (i32.load offset=16 - (i32.load - (i32.add - (local.get $0) - (i32.const 28) - ) - ) - ) - ) - ) - ) - ) - (br_if $label$1 - (i32.eqz - (local.get $2) - ) - ) - (call_indirect (type $0) - (i32.load offset=24 - (local.get $0) - ) - (local.get $2) - (i32.const 0) - (i32.load offset=12 - (i32.load - (i32.add - (local.get $0) - (i32.const 28) - ) - ) - ) - ) - ) - ) - ) - (i32.const 0) - ) - (func $10 (; 10 ;) (type $3) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 i32) - (local $17 i32) - (local $18 i32) - (local $19 i32) - (local $20 i32) - (local $21 i32) - (local $22 i32) - (local $23 i32) - (local $24 i32) - (local $25 i32) - (local $26 i32) - (local $27 i32) - (local $28 i32) - (local $29 i32) - (local $30 i32) - (local $31 i32) - (local $32 i32) - (local $33 i32) - (local $34 i32) - (local $35 i32) - (local $36 i32) - (local $37 i32) - (local $38 i32) - (local $39 i32) - (local $40 i32) - (local $41 i32) - (local $42 i32) - (local $43 i32) - (local $44 i32) - (local $45 i32) - (local $46 i32) - (global.set $global$0 - (local.tee $1 - (i32.sub - (global.get $global$0) - (i32.const 256) - ) - ) - ) - (i64.store offset=56 align=4 - (local.get $1) - (i64.const 4294967297) - ) - (i64.store offset=48 align=4 - (local.get $1) - (i64.const 4294967297) - ) - (i64.store offset=40 align=4 - (local.get $1) - (i64.const 4294967297) - ) - (i64.store offset=32 align=4 - (local.get $1) - (i64.const 4294967297) - ) - (i64.store offset=24 align=4 - (local.get $1) - (i64.const 4294967297) - ) - (i64.store offset=16 align=4 - (local.get $1) - (i64.const 4294967297) - ) - (i64.store offset=8 align=4 - (local.get $1) - (i64.const 4294967297) - ) - (i64.store align=4 - (local.get $1) - (i64.const 4294967297) - ) - (block $label$1 - (if - (i32.ge_u - (local.tee $11 - (i32.add - (local.get $0) - (i32.const 1) - ) - ) - (i32.const 2) - ) - (block - (local.set $3 - (local.get $1) - ) - (local.set $2 - (i32.const 1) - ) - (loop $label$3 - (br_if $label$1 - (i32.ge_u - (local.get $2) - (i32.const 16) - ) - ) - (i32.store - (local.tee $4 - (i32.add - (local.get $3) - (i32.const 4) - ) - ) - (i32.mul - (i32.load - (local.get $3) - ) - (local.get $2) - ) - ) - (local.set $3 - (local.get $4) - ) - (local.set $2 - (local.tee $4 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - ) - (br_if $label$3 - (i32.lt_u - (local.get $4) - (local.get $11) - ) - ) - ) - ) - ) - (if - (i32.lt_u - (local.get $0) - (i32.const 16) - ) - (block - (local.set $20 - (i32.const 1) - ) - (local.set $21 - (local.tee $9 - (i32.load - (i32.add - (local.get $1) - (i32.shl - (local.get $0) - (i32.const 2) - ) - ) - ) - ) - ) - (if - (i32.ge_u - (local.get $9) - (i32.const 24) - ) - (local.set $20 - (select - (i32.const 24) - (i32.const 25) - (i32.eq - (local.get $9) - (i32.mul - (local.tee $21 - (i32.div_u - (local.get $9) - (i32.const 24) - ) - ) - (i32.const 24) - ) - ) - ) - ) - ) - (local.set $40 - (i32.sub - (i32.const 0) - (local.get $0) - ) - ) - (local.set $12 - (i32.add - (local.get $1) - (i32.const 196) - ) - ) - (local.set $41 - (i32.add - (local.get $1) - (i32.const 132) - ) - ) - (local.set $42 - (i32.add - (local.get $1) - (i32.const 124) - ) - ) - (local.set $11 - (i32.add - (local.get $1) - (i32.const 68) - ) - ) - (local.set $43 - (i32.lt_u - (local.get $0) - (i32.const 2) - ) - ) - (loop $label$6 - (i64.store - (i32.add - (local.get $1) - (i32.const 120) - ) - (i64.const 0) - ) - (i64.store - (i32.add - (local.get $1) - (i32.const 112) - ) - (i64.const 0) - ) - (i64.store - (i32.add - (local.get $1) - (i32.const 104) - ) - (i64.const 0) - ) - (i64.store - (i32.add - (local.get $1) - (i32.const 96) - ) - (i64.const 0) - ) - (i64.store - (i32.add - (local.get $1) - (i32.const 88) - ) - (i64.const 0) - ) - (i64.store - (i32.add - (local.get $1) - (i32.const 80) - ) - (i64.const 0) - ) - (i64.store - (i32.add - (local.get $1) - (i32.const 72) - ) - (i64.const 0) - ) - (i64.store offset=64 - (local.get $1) - (i64.const 0) - ) - (i64.store - (local.tee $26 - (i32.add - (local.get $1) - (i32.const 184) - ) - ) - (i64.const 0) - ) - (i64.store - (local.tee $27 - (i32.add - (local.get $1) - (i32.const 176) - ) - ) - (i64.const 0) - ) - (i64.store - (local.tee $28 - (i32.add - (local.get $1) - (i32.const 168) - ) - ) - (i64.const 0) - ) - (i64.store - (local.tee $29 - (i32.add - (local.get $1) - (i32.const 160) - ) - ) - (i64.const 0) - ) - (i64.store - (local.tee $30 - (i32.add - (local.get $1) - (i32.const 152) - ) - ) - (i64.const 0) - ) - (i64.store - (local.tee $31 - (i32.add - (local.get $1) - (i32.const 144) - ) - ) - (i64.const 0) - ) - (i64.store - (local.tee $32 - (i32.add - (local.get $1) - (i32.const 136) - ) - ) - (i64.const 0) - ) - (i64.store offset=128 - (local.get $1) - (i64.const 0) - ) - (i64.store align=4 - (local.tee $33 - (i32.add - (local.get $1) - (i32.const 248) - ) - ) - (i64.const 64424509454) - ) - (i64.store align=4 - (local.tee $34 - (i32.add - (local.get $1) - (i32.const 240) - ) - ) - (i64.const 55834574860) - ) - (i64.store align=4 - (local.tee $35 - (i32.add - (local.get $1) - (i32.const 232) - ) - ) - (i64.const 47244640266) - ) - (i64.store align=4 - (local.tee $36 - (i32.add - (local.get $1) - (i32.const 224) - ) - ) - (i64.const 38654705672) - ) - (i64.store align=4 - (local.tee $37 - (i32.add - (local.get $1) - (i32.const 216) - ) - ) - (i64.const 30064771078) - ) - (i64.store align=4 - (local.tee $38 - (i32.add - (local.get $1) - (i32.const 208) - ) - ) - (i64.const 21474836484) - ) - (i64.store align=4 - (local.tee $39 - (i32.add - (local.get $1) - (i32.const 200) - ) - ) - (i64.const 12884901890) - ) - (i64.store offset=192 align=4 - (local.get $1) - (i64.const 4294967296) - ) - (local.set $7 - (i32.mul - (local.get $13) - (local.get $21) - ) - ) - (local.set $2 - (block $label$7 (result i32) - (block $label$8 - (if - (i32.eqz - (local.get $43) - ) - (block - (local.set $23 - (local.get $40) - ) - (local.set $14 - (local.get $7) - ) - (local.set $15 - (local.get $0) - ) - (local.set $5 - (i32.const 0) - ) - (br $label$8) - ) - ) - (br $label$7 - (i32.const 0) - ) - ) - (i32.const 1) - ) - ) - (loop $label$10 - (block $label$11 - (block $label$12 - (local.set $2 - (block $label$13 (result i32) - (block $label$14 - (block $label$15 - (block $label$16 - (block $label$17 - (block $label$18 - (block $label$19 - (if - (i32.eqz - (local.get $2) - ) - (block - (local.set $13 - (i32.add - (local.get $13) - (i32.const 1) - ) - ) - (local.set $44 - (i32.add - (select - (local.get $9) - (local.tee $3 - (i32.add - (local.get $7) - (local.get $21) - ) - ) - (i32.gt_u - (local.get $3) - (local.get $9) - ) - ) - (i32.const -1) - ) - ) - (local.set $24 - (i32.const 0) - ) - (br_if $label$19 - (i32.ge_s - (local.tee $6 - (i32.load offset=192 - (local.get $1) - ) - ) - (i32.const 1) - ) - ) - (br $label$18) - ) - ) - (block $label$21 - (block $label$22 - (block $label$23 - (block $label$24 - (block $label$25 - (block $label$26 - (block $label$27 - (block $label$28 - (block $label$29 - (block $label$30 - (block $label$31 - (br_table $label$31 $label$30 $label$29 - (local.get $5) - ) - ) - (br_if $label$24 - (i32.ge_u - (local.tee $4 - (i32.add - (local.get $15) - (i32.const -1) - ) - ) - (i32.const 16) - ) - ) - (br_if $label$23 - (i32.eqz - (local.tee $3 - (i32.load - (i32.add - (local.get $1) - (local.tee $2 - (i32.shl - (local.get $4) - (i32.const 2) - ) - ) - ) - ) - ) - ) - ) - (if - (i32.eq - (local.get $14) - (i32.const -2147483648) - ) - (br_if $label$22 - (i32.eq - (local.get $3) - (i32.const -1) - ) - ) - ) - (i32.store - (i32.add - (i32.sub - (local.get $1) - (i32.const -64) - ) - (local.get $2) - ) - (local.tee $16 - (i32.div_s - (local.get $14) - (local.get $3) - ) - ) - ) - (i64.store - (local.get $32) - (i64.load align=4 - (local.get $39) - ) - ) - (i64.store - (local.get $31) - (i64.load align=4 - (local.get $38) - ) - ) - (i64.store - (local.get $30) - (i64.load align=4 - (local.get $37) - ) - ) - (i64.store - (local.get $29) - (i64.load align=4 - (local.get $36) - ) - ) - (i64.store - (local.get $28) - (i64.load align=4 - (local.get $35) - ) - ) - (i64.store - (local.get $27) - (i64.load align=4 - (local.get $34) - ) - ) - (i64.store - (local.get $26) - (i64.load align=4 - (local.get $33) - ) - ) - (i64.store offset=128 - (local.get $1) - (i64.load offset=192 align=4 - (local.get $1) - ) - ) - (local.set $45 - (i32.add - (local.get $16) - (local.get $23) - ) - ) - (local.set $14 - (i32.sub - (local.get $14) - (i32.mul - (local.get $3) - (local.get $16) - ) - ) - ) - (local.set $2 - (i32.const 0) - ) - (local.set $8 - (i32.add - (local.get $1) - (i32.const 192) - ) - ) - (loop $label$33 - (block $label$34 - (if - (i32.gt_u - (local.tee $3 - (i32.add - (local.get $2) - (local.get $16) - ) - ) - (local.get $4) - ) - (block - (br_if $label$27 - (i32.gt_u - (local.tee $46 - (i32.add - (local.get $2) - (local.get $45) - ) - ) - (i32.const 15) - ) - ) - (local.set $3 - (i32.sub - (local.get $3) - (local.get $15) - ) - ) - (br_if $label$34 - (i32.le_u - (local.get $2) - (i32.const 15) - ) - ) - (br $label$28) - ) - ) - (br_if $label$26 - (i32.ge_u - (local.get $3) - (i32.const 16) - ) - ) - (br_if $label$28 - (i32.gt_u - (local.get $2) - (i32.const 15) - ) - ) - ) - (i32.store - (local.get $8) - (i32.load - (i32.add - (i32.add - (local.get $1) - (i32.const 128) - ) - (i32.shl - (local.get $3) - (i32.const 2) - ) - ) - ) - ) - (local.set $8 - (i32.add - (local.get $8) - (i32.const 4) - ) - ) - (br_if $label$33 - (i32.lt_u - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (local.get $15) - ) - ) - ) - (local.set $23 - (i32.add - (local.get $23) - (i32.const 1) - ) - ) - (br_if $label$21 - (i32.gt_u - (local.tee $15 - (local.get $4) - ) - (i32.const 1) - ) - ) - (local.set $2 - (i32.const 0) - ) - (br $label$10) - ) - (i64.store - (local.get $26) - (i64.load align=4 - (local.get $33) - ) - ) - (i64.store - (local.get $27) - (i64.load align=4 - (local.get $34) - ) - ) - (i64.store - (local.get $28) - (i64.load align=4 - (local.get $35) - ) - ) - (i64.store - (local.get $29) - (i64.load align=4 - (local.get $36) - ) - ) - (i64.store - (local.get $30) - (i64.load align=4 - (local.get $37) - ) - ) - (i64.store - (local.get $31) - (i64.load align=4 - (local.get $38) - ) - ) - (i64.store - (local.get $32) - (i64.load align=4 - (local.get $39) - ) - ) - (i64.store offset=128 - (local.get $1) - (i64.load offset=192 align=4 - (local.get $1) - ) - ) - (br_if $label$25 - (i32.gt_u - (local.get $6) - (i32.const 15) - ) - ) - (local.set $17 - (i32.const 1) - ) - (local.set $10 - (local.get $6) - ) - (br $label$13 - (i32.const 0) - ) - ) - (if - (i32.lt_u - (local.get $7) - (local.get $44) - ) - (block - (local.set $25 - (i32.load - (local.get $12) - ) - ) - (i32.store - (local.get $12) - (local.get $6) - ) - (i32.store offset=192 - (local.get $1) - (local.get $25) - ) - (local.set $18 - (local.get $11) - ) - (br_if $label$11 - (i32.lt_s - (local.tee $2 - (i32.load offset=68 - (local.get $1) - ) - ) - (i32.const 1) - ) - ) - (local.set $19 - (i32.const 1) - ) - (br $label$14) - ) - ) - (local.set $22 - (i32.add - (local.get $22) - (local.get $24) - ) - ) - (br_if $label$6 - (i32.lt_u - (local.get $13) - (local.get $20) - ) - ) - (global.set $global$0 - (i32.add - (local.get $1) - (i32.const 256) - ) - ) - (return - (local.get $22) - ) - ) - (call $3 - (i32.const 1049076) - (local.get $2) - ) - (unreachable) - ) - (call $3 - (i32.const 1049060) - (local.get $46) - ) - (unreachable) - ) - (call $3 - (i32.const 1049044) - (i32.add - (local.get $2) - (local.get $16) - ) - ) - (unreachable) - ) - (local.set $10 - (local.get $6) - ) - (br $label$12) - ) - (call $3 - (i32.const 1048980) - (local.get $4) - ) - (unreachable) - ) - (call $2 - (i32.const 1048996) - ) - (unreachable) - ) - (call $2 - (i32.const 1049020) - ) - (unreachable) - ) - (local.set $5 - (i32.const 0) - ) - (br $label$17) - ) - (local.set $5 - (i32.const 1) - ) - (br $label$16) - ) - (local.set $5 - (i32.const 2) - ) - (br $label$15) - ) - (local.set $2 - (i32.const 1) - ) - (br $label$10) - ) - (local.set $2 - (i32.const 1) - ) - (br $label$10) - ) - (local.set $2 - (i32.const 1) - ) - (br $label$10) - ) - (i32.const 1) - ) - ) - (loop $label$37 - (block $label$38 - (block $label$39 - (if - (i32.eqz - (local.get $2) - ) - (block - (if - (local.tee $10 - (i32.load - (local.tee $5 - (i32.add - (local.tee $4 - (i32.shl - (local.tee $3 - (local.get $10) - ) - (i32.const 2) - ) - ) - (i32.add - (local.get $1) - (i32.const 128) - ) - ) - ) - ) - ) - (block - (i32.store - (local.get $5) - (local.get $3) - ) - (block $label$42 - (br_if $label$42 - (i32.lt_u - (local.get $3) - (i32.const 3) - ) - ) - (br_if $label$42 - (i32.eqz - (local.tee $8 - (i32.shr_u - (i32.add - (local.get $3) - (i32.const -1) - ) - (i32.const 1) - ) - ) - ) - ) - (local.set $2 - (i32.add - (local.get $4) - (local.get $42) - ) - ) - (local.set $3 - (local.get $41) - ) - (loop $label$43 - (local.set $4 - (i32.load - (local.get $3) - ) - ) - (i32.store - (local.get $3) - (i32.load - (local.get $2) - ) - ) - (i32.store - (local.get $2) - (local.get $4) - ) - (local.set $3 - (i32.add - (local.get $3) - (i32.const 4) - ) - ) - (local.set $2 - (i32.add - (local.get $2) - (i32.const -4) - ) - ) - (br_if $label$43 - (local.tee $8 - (i32.add - (local.get $8) - (i32.const -1) - ) - ) - ) - ) - ) - (local.set $17 - (i32.add - (local.get $17) - (i32.const 1) - ) - ) - (br_if $label$38 - (i32.lt_u - (local.get $10) - (i32.const 16) - ) - ) - (br $label$12) - ) - ) - (local.set $24 - (i32.add - (select - (i32.sub - (i32.const 0) - (local.get $17) - ) - (local.get $17) - (i32.and - (local.get $7) - (i32.const 1) - ) - ) - (local.get $24) - ) - ) - (local.set $5 - (i32.const 2) - ) - (br $label$39) - ) - ) - (local.set $2 - (i32.const 0) - ) - (i32.store - (local.get $18) - (i32.const 0) - ) - (i32.store offset=192 - (local.get $1) - (local.tee $4 - (local.get $6) - ) - ) - (local.set $5 - (i32.add - (local.get $19) - (i32.const 1) - ) - ) - (local.set $3 - (local.get $12) - ) - (block $label$44 - (block $label$45 - (loop $label$46 - (br_if $label$45 - (i32.ge_u - (i32.add - (local.get $2) - (i32.const 2) - ) - (i32.const 16) - ) - ) - (i32.store - (local.get $3) - (i32.load - (local.tee $3 - (i32.add - (local.get $3) - (i32.const 4) - ) - ) - ) - ) - (br_if $label$46 - (i32.lt_u - (local.tee $2 - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (local.get $19) - ) - ) - ) - (br_if $label$44 - (i32.ge_u - (local.get $5) - (i32.const 16) - ) - ) - (i32.store - (i32.add - (local.tee $3 - (i32.shl - (local.get $5) - (i32.const 2) - ) - ) - (i32.add - (local.get $1) - (i32.const 192) - ) - ) - (local.get $25) - ) - (br_if $label$11 - (i32.le_s - (local.tee $2 - (i32.load - (local.tee $18 - (i32.add - (i32.sub - (local.get $1) - (i32.const -64) - ) - (local.get $3) - ) - ) - ) - ) - (local.get $19) - ) - ) - (local.set $6 - (i32.load - (local.get $12) - ) - ) - (local.set $19 - (local.get $5) - ) - (local.set $25 - (local.get $4) - ) - (local.set $2 - (i32.const 1) - ) - (br $label$37) - ) - (call $3 - (i32.const 1049108) - (i32.add - (local.get $2) - (i32.const 2) - ) - ) - (unreachable) - ) - (call $3 - (i32.const 1049124) - (local.get $5) - ) - (unreachable) - ) - (local.set $2 - (i32.const 1) - ) - (br $label$10) - ) - (local.set $2 - (i32.const 0) - ) - (br $label$37) - ) - ) - (call $3 - (i32.const 1049092) - (local.get $10) - ) - (unreachable) - ) - (local.set $7 - (i32.add - (local.get $7) - (i32.const 1) - ) - ) - (i32.store - (local.get $18) - (i32.add - (local.get $2) - (i32.const 1) - ) - ) - (block $label$47 - (block $label$48 - (if - (i32.ge_s - (local.tee $6 - (i32.load offset=192 - (local.get $1) - ) - ) - (i32.const 1) - ) - (block - (local.set $5 - (i32.const 1) - ) - (br $label$48) - ) - ) - (local.set $5 - (i32.const 2) - ) - (br $label$47) - ) - (local.set $2 - (i32.const 1) - ) - (br $label$10) - ) - (local.set $2 - (i32.const 1) - ) - (br $label$10) - ) - ) - ) - ) - (call $3 - (i32.const 1049212) - (local.get $0) - ) - (unreachable) - ) - (call $3 - (i32.const 1049196) - (local.get $2) - ) - (unreachable) - ) -) - + (table $0 (;0;) 4 4 funcref) + (memory $0 (;0;) 17) + (global $global$0 (;0;) (mut i32) i32.const 1048576) + (global $global$1 (;1;) i32 i32.const 1049244) + (global $global$2 (;2;) i32 i32.const 1049244) + (export "memory" (memory $0)) + (export "__heap_base" (global $global$1)) + (export "__data_end" (global $global$2)) + (export "run_fannkuch" (func $10)) + (elem (;0;) (i32.const 1) func $4 $7 $8) + (data (;0;) (i32.const 1048576) "src/lib.rs\00\00\00\00\00\00attempt to divide by zero\00\00\00\00\00\00\00attempt to divide with overflow\00index out of bounds: the len is but the index is 00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899called `Option::unwrap()` on a `None` valuesrc/libcore/option.rssrc/lib.rs") + (data (;1;) (i32.const 1048982) "\10\00\0a\00\00\00%\00\00\00\1d\00\00\00\10\00\10\00\19\00\00\00\00\00\10\00\0a\00\00\00&\00\00\00\15\00\00\000\00\10\00\1f\00\00\00\00\00\10\00\0a\00\00\00&\00\00\00\15\00\00\00\00\00\10\00\0a\00\00\00.\00\00\00\15\00\00\00\00\00\10\00\0a\00\00\000\00\00\00\15\00\00\00\00\00\10\00\0a\00\00\00-\00\00\00\11\00\00\00\00\00\10\00\0a\00\00\00E\00\00\00\17\00\00\00\00\00\10\00\0a\00\00\00q\00\00\00\22\00\00\00\00\00\10\00\0a\00\00\00s\00\00\00\11\00\00\00P\00\10\00 \00\00\00p\00\10\00\12\00\00\00\02\00\00\00\00\00\00\00\01\00\00\00\03\00\00\00J\01\10\00+\00\00\00u\01\10\00\15\00\00\00Y\01\00\00\15\00\00\00\8a\01\10\00\0a\00\00\00\08\00\00\00\09\00\00\00\8a\01\10\00\0a\00\00\00\0a\00\00\00\14") +) \ No newline at end of file diff --git a/examples/fuel.wat b/examples/fuel.wat index 48622c2e219a..f7d312925b24 100644 --- a/examples/fuel.wat +++ b/examples/fuel.wat @@ -2,7 +2,7 @@ (func $fibonacci (param $n i32) (result i32) (if (i32.lt_s (local.get $n) (i32.const 2)) - (return (local.get $n)) + (then (return (local.get $n))) ) (i32.add (call $fibonacci (i32.sub (local.get $n) (i32.const 1))) diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 29747fbcc690..dabdf0fec634 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -1202,6 +1202,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wasm-encoder]] +version = "0.33.2" +when = "2023-09-26" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wasm-metadata]] version = "0.9.0" when = "2023-07-11" @@ -1237,6 +1244,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wasm-metadata]] +version = "0.10.6" +when = "2023-09-26" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wasm-mutate]] version = "0.2.30" when = "2023-07-26" @@ -1265,6 +1279,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wasm-mutate]] +version = "0.2.35" +when = "2023-09-26" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wasm-smith]] version = "0.12.13" when = "2023-07-26" @@ -1293,6 +1314,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wasm-smith]] +version = "0.12.18" +when = "2023-09-26" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wasmparser]] version = "0.108.0" when = "2023-07-11" @@ -1328,6 +1356,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wasmparser]] +version = "0.113.2" +when = "2023-09-26" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wasmprinter]] version = "0.2.62" when = "2023-07-26" @@ -1356,6 +1391,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wasmprinter]] +version = "0.2.67" +when = "2023-09-26" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wasmtime]] version = "11.0.1" when = "2023-07-24" @@ -1672,6 +1714,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wast]] +version = "65.0.2" +when = "2023-09-26" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wat]] version = "1.0.69" when = "2023-07-26" @@ -1700,6 +1749,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wat]] +version = "1.0.74" +when = "2023-09-26" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wiggle]] version = "11.0.1" when = "2023-07-24" @@ -1937,6 +1993,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wit-component]] +version = "0.14.3" +when = "2023-09-26" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wit-parser]] version = "0.9.2" when = "2023-07-26" @@ -1965,6 +2028,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wit-parser]] +version = "0.11.2" +when = "2023-09-26" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[audits.embark-studios.wildcard-audits.spdx]] who = "Jake Shadle " criteria = "safe-to-deploy" diff --git a/tests/all/cli_tests/print-arguments.wat b/tests/all/cli_tests/print-arguments.wat index 93e4558c46f0..59835de37b95 100644 --- a/tests/all/cli_tests/print-arguments.wat +++ b/tests/all/cli_tests/print-arguments.wat @@ -18,12 +18,12 @@ (if (i32.ne (call $args_get (local.get $argptrs) (local.get $argmem)) (i32.const 0)) - (unreachable)) + (then (unreachable))) (loop (local.set $arg (i32.load (local.get $argptrs))) (local.set $argptrs (i32.add (local.get $argptrs) (i32.const 4))) - (if (i32.eq (local.get $arg) (i32.const 0)) (return)) + (if (i32.eq (local.get $arg) (i32.const 0)) (then (return))) (call $write_all (local.get $arg) (call $strlen (local.get $arg))) (call $write_all (i32.const 10) (i32.const 1)) @@ -50,7 +50,7 @@ (local.get $iov) (i32.const 1) (local.get $written))) - (if (i32.ne (local.get $rc) (i32.const 0)) (unreachable)) + (if (i32.ne (local.get $rc) (i32.const 0)) (then (unreachable))) (local.set $len (i32.sub (local.get $len) (i32.load (local.get $written)))) (local.set $ptr (i32.add (local.get $ptr) (i32.load (local.get $written)))) diff --git a/tests/all/cli_tests/print_env.wat b/tests/all/cli_tests/print_env.wat index ddbe6f2f564b..5b649e562adc 100644 --- a/tests/all/cli_tests/print_env.wat +++ b/tests/all/cli_tests/print_env.wat @@ -18,12 +18,12 @@ (if (i32.ne (call $environ_get (local.get $envptrs) (local.get $envmem)) (i32.const 0)) - (unreachable)) + (then (unreachable))) (loop (local.set $env (i32.load (local.get $envptrs))) (local.set $envptrs (i32.add (local.get $envptrs) (i32.const 4))) - (if (i32.eq (local.get $env) (i32.const 0)) (return)) + (if (i32.eq (local.get $env) (i32.const 0)) (then (return))) (call $write_all (local.get $env) (call $strlen (local.get $env))) (call $write_all (i32.const 10) (i32.const 1)) @@ -50,7 +50,7 @@ (local.get $iov) (i32.const 1) (local.get $written))) - (if (i32.ne (local.get $rc) (i32.const 0)) (unreachable)) + (if (i32.ne (local.get $rc) (i32.const 0)) (then (unreachable))) (local.set $len (i32.sub (local.get $len) (i32.load (local.get $written)))) (local.set $ptr (i32.add (local.get $ptr) (i32.load (local.get $written)))) diff --git a/tests/all/component_model/resources.rs b/tests/all/component_model/resources.rs index 24487785cf91..ba821251f79c 100644 --- a/tests/all/component_model/resources.rs +++ b/tests/all/component_model/resources.rs @@ -1038,7 +1038,7 @@ fn pass_guest_back_as_borrow() -> Result<()> { ) (func (export "take") (param i32) - (if (i32.ne (local.get 0) (i32.const 100)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 100)) (then (unreachable))) ) ) (core instance $i (instantiate $m @@ -1228,8 +1228,8 @@ fn guest_different_host_same() -> Result<()> { (func (export "f") (param i32 i32) ;; separate tables both have initial index of 0 - (if (i32.ne (local.get 0) (i32.const 0)) (unreachable)) - (if (i32.ne (local.get 1) (i32.const 0)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0)) (then (unreachable))) + (if (i32.ne (local.get 1) (i32.const 0)) (then (unreachable))) ;; host should end up getting the same resource (call $f (local.get 0) (local.get 1)) diff --git a/tests/all/memory.rs b/tests/all/memory.rs index 3acdfc2c69c2..6375854507a6 100644 --- a/tests/all/memory.rs +++ b/tests/all/memory.rs @@ -346,7 +346,7 @@ fn tiny_static_heap() -> Result<()> { (loop (if (i32.eq (local.get $i) (i32.const 65536)) - (return)) + (then (return))) (drop (i32.load8_u (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))) br 0 diff --git a/tests/misc_testsuite/component-model/fused.wast b/tests/misc_testsuite/component-model/fused.wast index 4e6add9fb2ba..4ce18faef8c3 100644 --- a/tests/misc_testsuite/component-model/fused.wast +++ b/tests/misc_testsuite/component-model/fused.wast @@ -64,13 +64,13 @@ (call $assert_false (i32.const 0)) (if (i32.ne (call $ret_bool (i32.const 1)) (i32.const 1)) - (unreachable)) + (then (unreachable))) (if (i32.ne (call $ret_bool (i32.const 2)) (i32.const 1)) - (unreachable)) + (then (unreachable))) (if (i32.ne (call $ret_bool (i32.const -1)) (i32.const 1)) - (unreachable)) + (then (unreachable))) (if (i32.ne (call $ret_bool (i32.const 0)) (i32.const 0)) - (unreachable)) + (then (unreachable))) ) (start $start) ) @@ -108,28 +108,28 @@ (func (export "roundtrip") (param $src i32) (result i32) (local $dst i32) (if (i32.ne (local.get $src) (i32.const 16)) - (unreachable)) - - (if (i32.ne (i32.load offset=0 (local.get $src)) (i32.const 1)) (unreachable)) - (if (i32.ne (i32.load offset=4 (local.get $src)) (i32.const 2)) (unreachable)) - (if (i32.ne (i32.load offset=8 (local.get $src)) (i32.const 3)) (unreachable)) - (if (i32.ne (i32.load offset=12 (local.get $src)) (i32.const 4)) (unreachable)) - (if (i32.ne (i32.load offset=16 (local.get $src)) (i32.const 5)) (unreachable)) - (if (i32.ne (i32.load offset=20 (local.get $src)) (i32.const 6)) (unreachable)) - (if (i32.ne (i32.load offset=24 (local.get $src)) (i32.const 7)) (unreachable)) - (if (i32.ne (i32.load offset=28 (local.get $src)) (i32.const 8)) (unreachable)) - (if (i32.ne (i32.load offset=32 (local.get $src)) (i32.const 9)) (unreachable)) - (if (i32.ne (i32.load offset=36 (local.get $src)) (i32.const 10)) (unreachable)) - (if (i32.ne (i32.load offset=40 (local.get $src)) (i32.const 11)) (unreachable)) - (if (i32.ne (i32.load offset=44 (local.get $src)) (i32.const 12)) (unreachable)) - (if (i32.ne (i32.load offset=48 (local.get $src)) (i32.const 13)) (unreachable)) - (if (i32.ne (i32.load offset=52 (local.get $src)) (i32.const 14)) (unreachable)) - (if (i32.ne (i32.load offset=56 (local.get $src)) (i32.const 15)) (unreachable)) - (if (i32.ne (i32.load offset=60 (local.get $src)) (i32.const 16)) (unreachable)) - (if (i32.ne (i32.load offset=64 (local.get $src)) (i32.const 17)) (unreachable)) - (if (i32.ne (i32.load offset=68 (local.get $src)) (i32.const 18)) (unreachable)) - (if (i32.ne (i32.load offset=72 (local.get $src)) (i32.const 19)) (unreachable)) - (if (i32.ne (i32.load offset=76 (local.get $src)) (i32.const 20)) (unreachable)) + (then (unreachable))) + + (if (i32.ne (i32.load offset=0 (local.get $src)) (i32.const 1)) (then (unreachable))) + (if (i32.ne (i32.load offset=4 (local.get $src)) (i32.const 2)) (then (unreachable))) + (if (i32.ne (i32.load offset=8 (local.get $src)) (i32.const 3)) (then (unreachable))) + (if (i32.ne (i32.load offset=12 (local.get $src)) (i32.const 4)) (then (unreachable))) + (if (i32.ne (i32.load offset=16 (local.get $src)) (i32.const 5)) (then (unreachable))) + (if (i32.ne (i32.load offset=20 (local.get $src)) (i32.const 6)) (then (unreachable))) + (if (i32.ne (i32.load offset=24 (local.get $src)) (i32.const 7)) (then (unreachable))) + (if (i32.ne (i32.load offset=28 (local.get $src)) (i32.const 8)) (then (unreachable))) + (if (i32.ne (i32.load offset=32 (local.get $src)) (i32.const 9)) (then (unreachable))) + (if (i32.ne (i32.load offset=36 (local.get $src)) (i32.const 10)) (then (unreachable))) + (if (i32.ne (i32.load offset=40 (local.get $src)) (i32.const 11)) (then (unreachable))) + (if (i32.ne (i32.load offset=44 (local.get $src)) (i32.const 12)) (then (unreachable))) + (if (i32.ne (i32.load offset=48 (local.get $src)) (i32.const 13)) (then (unreachable))) + (if (i32.ne (i32.load offset=52 (local.get $src)) (i32.const 14)) (then (unreachable))) + (if (i32.ne (i32.load offset=56 (local.get $src)) (i32.const 15)) (then (unreachable))) + (if (i32.ne (i32.load offset=60 (local.get $src)) (i32.const 16)) (then (unreachable))) + (if (i32.ne (i32.load offset=64 (local.get $src)) (i32.const 17)) (then (unreachable))) + (if (i32.ne (i32.load offset=68 (local.get $src)) (i32.const 18)) (then (unreachable))) + (if (i32.ne (i32.load offset=72 (local.get $src)) (i32.const 19)) (then (unreachable))) + (if (i32.ne (i32.load offset=76 (local.get $src)) (i32.const 20)) (then (unreachable))) (local.set $dst (i32.const 500)) @@ -186,16 +186,16 @@ (local.set $retaddr (i32.const 200)) (call $roundtrip (local.get $addr) (local.get $retaddr)) - (if (i32.ne (i32.load offset=0 (local.get $retaddr)) (i32.const 21)) (unreachable)) - (if (i32.ne (i32.load offset=4 (local.get $retaddr)) (i32.const 22)) (unreachable)) - (if (i32.ne (i32.load offset=8 (local.get $retaddr)) (i32.const 23)) (unreachable)) - (if (i32.ne (i32.load offset=12 (local.get $retaddr)) (i32.const 24)) (unreachable)) - (if (i32.ne (i32.load offset=16 (local.get $retaddr)) (i32.const 25)) (unreachable)) - (if (i32.ne (i32.load offset=20 (local.get $retaddr)) (i32.const 26)) (unreachable)) - (if (i32.ne (i32.load offset=24 (local.get $retaddr)) (i32.const 27)) (unreachable)) - (if (i32.ne (i32.load offset=28 (local.get $retaddr)) (i32.const 28)) (unreachable)) - (if (i32.ne (i32.load offset=32 (local.get $retaddr)) (i32.const 29)) (unreachable)) - (if (i32.ne (i32.load offset=36 (local.get $retaddr)) (i32.const 30)) (unreachable)) + (if (i32.ne (i32.load offset=0 (local.get $retaddr)) (i32.const 21)) (then (unreachable))) + (if (i32.ne (i32.load offset=4 (local.get $retaddr)) (i32.const 22)) (then (unreachable))) + (if (i32.ne (i32.load offset=8 (local.get $retaddr)) (i32.const 23)) (then (unreachable))) + (if (i32.ne (i32.load offset=12 (local.get $retaddr)) (i32.const 24)) (then (unreachable))) + (if (i32.ne (i32.load offset=16 (local.get $retaddr)) (i32.const 25)) (then (unreachable))) + (if (i32.ne (i32.load offset=20 (local.get $retaddr)) (i32.const 26)) (then (unreachable))) + (if (i32.ne (i32.load offset=24 (local.get $retaddr)) (i32.const 27)) (then (unreachable))) + (if (i32.ne (i32.load offset=28 (local.get $retaddr)) (i32.const 28)) (then (unreachable))) + (if (i32.ne (i32.load offset=32 (local.get $retaddr)) (i32.const 29)) (then (unreachable))) + (if (i32.ne (i32.load offset=36 (local.get $retaddr)) (i32.const 30)) (then (unreachable))) ) (func $store_many (param $amt i32) (param $addr i32) @@ -205,7 +205,7 @@ (i32.store (local.get $addr) (local.get $c)) (local.set $addr (i32.add (local.get $addr) (i32.const 4))) - (if (i32.ne (local.get $amt) (local.get $c)) (br $loop)) + (if (i32.ne (local.get $amt) (local.get $c)) (then (br $loop))) ) ) (start $start) @@ -439,7 +439,7 @@ (core module $m (func (export "foo") (result i32) i32.const 100) (func (export "foo-post") (param i32) - (if (i32.ne (local.get 0) (i32.const 100)) (unreachable))) + (if (i32.ne (local.get 0) (i32.const 100)) (then (unreachable)))) ) (core instance $m (instantiate $m)) (func $foo (result u32) @@ -452,7 +452,7 @@ (core module $something (import "" "foo" (func $foo (result i32))) (func $start - (if (i32.ne (call $foo) (i32.const 100)) (unreachable))) + (if (i32.ne (call $foo) (i32.const 100)) (then (unreachable)))) (start $start) ) (core instance (instantiate $something @@ -645,7 +645,7 @@ (core module $m (func (export "r") (param i32) (result i32) - (if (i32.ne (local.get 0) (i32.const 0)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0)) (then (unreachable))) i32.const 0 ) ) @@ -763,10 +763,10 @@ (component $c1 (core module $m (func (export "u") (param i32) - (if (i32.ne (local.get 0) (i32.const 0)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0)) (then (unreachable))) ) (func (export "s") (param i32) - (if (i32.ne (local.get 0) (i32.const -1)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const -1)) (then (unreachable))) ) ) (core instance $m (instantiate $m)) @@ -846,51 +846,51 @@ (func (export "a") (param i32 i32 i32) (i32.eqz (local.get 0)) if - (if (i32.ne (local.get 1) (i32.const 0)) (unreachable)) - (if (i32.ne (local.get 2) (i32.const 2)) (unreachable)) + (if (i32.ne (local.get 1) (i32.const 0)) (then (unreachable))) + (if (i32.ne (local.get 2) (i32.const 2)) (then (unreachable))) else - (if (i32.ne (local.get 1) (i32.const 1)) (unreachable)) - (if (f32.ne (f32.reinterpret_i32 (local.get 2)) (f32.const 3)) (unreachable)) + (if (i32.ne (local.get 1) (i32.const 1)) (then (unreachable))) + (if (f32.ne (f32.reinterpret_i32 (local.get 2)) (f32.const 3)) (then (unreachable))) end ) (func (export "b") (param i32 i32 i64) (i32.eqz (local.get 0)) if - (if (i32.ne (local.get 1) (i32.const 0)) (unreachable)) - (if (i64.ne (local.get 2) (i64.const 4)) (unreachable)) + (if (i32.ne (local.get 1) (i32.const 0)) (then (unreachable))) + (if (i64.ne (local.get 2) (i64.const 4)) (then (unreachable))) else - (if (i32.ne (local.get 1) (i32.const 1)) (unreachable)) - (if (i64.ne (local.get 2) (i64.const 5)) (unreachable)) + (if (i32.ne (local.get 1) (i32.const 1)) (then (unreachable))) + (if (i64.ne (local.get 2) (i64.const 5)) (then (unreachable))) end ) (func (export "c") (param i32 i32 i64) (i32.eqz (local.get 0)) if - (if (i32.ne (local.get 1) (i32.const 0)) (unreachable)) - (if (i64.ne (local.get 2) (i64.const 6)) (unreachable)) + (if (i32.ne (local.get 1) (i32.const 0)) (then (unreachable))) + (if (i64.ne (local.get 2) (i64.const 6)) (then (unreachable))) else - (if (i32.ne (local.get 1) (i32.const 1)) (unreachable)) - (if (f64.ne (f64.reinterpret_i64 (local.get 2)) (f64.const 7)) (unreachable)) + (if (i32.ne (local.get 1) (i32.const 1)) (then (unreachable))) + (if (f64.ne (f64.reinterpret_i64 (local.get 2)) (f64.const 7)) (then (unreachable))) end ) (func (export "d") (param i32 i32 i64) (i32.eqz (local.get 0)) if - (if (i32.ne (local.get 1) (i32.const 0)) (unreachable)) - (if (f32.ne (f32.reinterpret_i32 (i32.wrap_i64 (local.get 2))) (f32.const 8)) (unreachable)) + (if (i32.ne (local.get 1) (i32.const 0)) (then (unreachable))) + (if (f32.ne (f32.reinterpret_i32 (i32.wrap_i64 (local.get 2))) (f32.const 8)) (then (unreachable))) else - (if (i32.ne (local.get 1) (i32.const 1)) (unreachable)) - (if (f64.ne (f64.reinterpret_i64 (local.get 2)) (f64.const 9)) (unreachable)) + (if (i32.ne (local.get 1) (i32.const 1)) (then (unreachable))) + (if (f64.ne (f64.reinterpret_i64 (local.get 2)) (f64.const 9)) (then (unreachable))) end ) (func (export "e") (param i32 i32 i64) (i32.eqz (local.get 0)) if - (if (i32.ne (local.get 1) (i32.const 0)) (unreachable)) - (if (f32.ne (f32.reinterpret_i32 (i32.wrap_i64 (local.get 2))) (f32.const 10)) (unreachable)) + (if (i32.ne (local.get 1) (i32.const 0)) (then (unreachable))) + (if (f32.ne (f32.reinterpret_i32 (i32.wrap_i64 (local.get 2))) (f32.const 10)) (then (unreachable))) else - (if (i32.ne (local.get 1) (i32.const 1)) (unreachable)) - (if (i64.ne (local.get 2) (i64.const 11)) (unreachable)) + (if (i32.ne (local.get 1) (i32.const 1)) (then (unreachable))) + (if (i64.ne (local.get 2) (i64.const 11)) (then (unreachable))) end ) ) @@ -987,39 +987,39 @@ (core module $m (func (export "a") (param i32 i32 f32 i64 i32) (if (i32.eq (local.get 0) (i32.const 0)) - (block - (if (i32.ne (local.get 1) (i32.const 0)) (unreachable)) - (if (f32.ne (local.get 2) (f32.const 0)) (unreachable)) - (if (i64.ne (local.get 3) (i64.const 0)) (unreachable)) - (if (i32.ne (local.get 4) (i32.const 0)) (unreachable)) - ) + (then (block + (if (i32.ne (local.get 1) (i32.const 0)) (then (unreachable))) + (if (f32.ne (local.get 2) (f32.const 0)) (then (unreachable))) + (if (i64.ne (local.get 3) (i64.const 0)) (then (unreachable))) + (if (i32.ne (local.get 4) (i32.const 0)) (then (unreachable))) + )) ) (if (i32.eq (local.get 0) (i32.const 1)) - (block - (if (i32.ne (local.get 1) (i32.const 1)) (unreachable)) - (if (f32.ne (local.get 2) (f32.const 1)) (unreachable)) - (if (i64.ne (local.get 3) (i64.const 0)) (unreachable)) - (if (i32.ne (local.get 4) (i32.const 0)) (unreachable)) - ) + (then (block + (if (i32.ne (local.get 1) (i32.const 1)) (then (unreachable))) + (if (f32.ne (local.get 2) (f32.const 1)) (then (unreachable))) + (if (i64.ne (local.get 3) (i64.const 0)) (then (unreachable))) + (if (i32.ne (local.get 4) (i32.const 0)) (then (unreachable))) + )) ) (if (i32.eq (local.get 0) (i32.const 2)) - (block - (if (i32.ne (local.get 1) (i32.const 2)) (unreachable)) - (if (f32.ne (local.get 2) (f32.const 2)) (unreachable)) - (if (i64.ne (local.get 3) (i64.const 2)) (unreachable)) - (if (i32.ne (local.get 4) (i32.const 0)) (unreachable)) - ) + (then (block + (if (i32.ne (local.get 1) (i32.const 2)) (then (unreachable))) + (if (f32.ne (local.get 2) (f32.const 2)) (then (unreachable))) + (if (i64.ne (local.get 3) (i64.const 2)) (then (unreachable))) + (if (i32.ne (local.get 4) (i32.const 0)) (then (unreachable))) + )) ) (if (i32.eq (local.get 0) (i32.const 3)) - (block - (if (i32.ne (local.get 1) (i32.const 3)) (unreachable)) - (if (f32.ne (local.get 2) (f32.const 3)) (unreachable)) - (if (i64.ne (local.get 3) (i64.const 3)) (unreachable)) - (if (i32.ne (local.get 4) (i32.const 3)) (unreachable)) - ) + (then (block + (if (i32.ne (local.get 1) (i32.const 3)) (then (unreachable))) + (if (f32.ne (local.get 2) (f32.const 3)) (then (unreachable))) + (if (i64.ne (local.get 3) (i64.const 3)) (then (unreachable))) + (if (i32.ne (local.get 4) (i32.const 3)) (then (unreachable))) + )) ) (if (i32.gt_u (local.get 0) (i32.const 3)) - (unreachable)) + (then (unreachable))) ) ) (core instance $m (instantiate $m)) @@ -1263,35 +1263,35 @@ (export $f65 "t-f65" (type $f65')) (core module $m (func (export "f1") (param i32) - (if (i32.ne (local.get 0) (i32.const 0x1)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0x1)) (then (unreachable))) ) (func (export "f8") (param i32) - (if (i32.ne (local.get 0) (i32.const 0x11)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0x11)) (then (unreachable))) ) (func (export "f9") (param i32) - (if (i32.ne (local.get 0) (i32.const 0x111)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0x111)) (then (unreachable))) ) (func (export "f16") (param i32) - (if (i32.ne (local.get 0) (i32.const 0x1111)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0x1111)) (then (unreachable))) ) (func (export "f17") (param i32) - (if (i32.ne (local.get 0) (i32.const 0x11111)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0x11111)) (then (unreachable))) ) (func (export "f32") (param i32) - (if (i32.ne (local.get 0) (i32.const 0x11111111)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0x11111111)) (then (unreachable))) ) (func (export "f33") (param i32 i32) - (if (i32.ne (local.get 0) (i32.const 0x11111111)) (unreachable)) - (if (i32.ne (local.get 1) (i32.const 0x1)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0x11111111)) (then (unreachable))) + (if (i32.ne (local.get 1) (i32.const 0x1)) (then (unreachable))) ) (func (export "f64") (param i32 i32) - (if (i32.ne (local.get 0) (i32.const 0x11111111)) (unreachable)) - (if (i32.ne (local.get 1) (i32.const 0x11111111)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0x11111111)) (then (unreachable))) + (if (i32.ne (local.get 1) (i32.const 0x11111111)) (then (unreachable))) ) (func (export "f65") (param i32 i32 i32) - (if (i32.ne (local.get 0) (i32.const 0x11111111)) (unreachable)) - (if (i32.ne (local.get 1) (i32.const 0x11111111)) (unreachable)) - (if (i32.ne (local.get 2) (i32.const 0x1)) (unreachable)) + (if (i32.ne (local.get 0) (i32.const 0x11111111)) (then (unreachable))) + (if (i32.ne (local.get 1) (i32.const 0x11111111)) (then (unreachable))) + (if (i32.ne (local.get 2) (i32.const 0x1)) (then (unreachable))) ) ) (core instance $m (instantiate $m)) diff --git a/tests/misc_testsuite/component-model/resources.wast b/tests/misc_testsuite/component-model/resources.wast index decb9554e85e..89b97536aebc 100644 --- a/tests/misc_testsuite/component-model/resources.wast +++ b/tests/misc_testsuite/component-model/resources.wast @@ -14,8 +14,8 @@ (local $r i32) (local.set $r (call $new (i32.const 100))) - (if (i32.ne (local.get $r) (i32.const 0)) (unreachable)) - (if (i32.ne (call $rep (local.get $r)) (i32.const 100)) (unreachable)) + (if (i32.ne (local.get $r) (i32.const 0)) (then (unreachable))) + (if (i32.ne (call $rep (local.get $r)) (i32.const 100)) (then (unreachable))) (call $drop (local.get $r)) ) @@ -95,30 +95,30 @@ ;; resources assigned sequentially (local.set $r1 (call $new (i32.const 100))) - (if (i32.ne (local.get $r1) (i32.const 0)) (unreachable)) + (if (i32.ne (local.get $r1) (i32.const 0)) (then (unreachable))) (local.set $r2 (call $new (i32.const 200))) - (if (i32.ne (local.get $r2) (i32.const 1)) (unreachable)) + (if (i32.ne (local.get $r2) (i32.const 1)) (then (unreachable))) (local.set $r3 (call $new (i32.const 300))) - (if (i32.ne (local.get $r3) (i32.const 2)) (unreachable)) + (if (i32.ne (local.get $r3) (i32.const 2)) (then (unreachable))) ;; representations all look good - (if (i32.ne (call $rep (local.get $r1)) (i32.const 100)) (unreachable)) - (if (i32.ne (call $rep (local.get $r2)) (i32.const 200)) (unreachable)) - (if (i32.ne (call $rep (local.get $r3)) (i32.const 300)) (unreachable)) + (if (i32.ne (call $rep (local.get $r1)) (i32.const 100)) (then (unreachable))) + (if (i32.ne (call $rep (local.get $r2)) (i32.const 200)) (then (unreachable))) + (if (i32.ne (call $rep (local.get $r3)) (i32.const 300)) (then (unreachable))) ;; reallocate r2 (call $drop (local.get $r2)) (local.set $r2 (call $new (i32.const 400))) ;; should have reused index 1 - (if (i32.ne (local.get $r2) (i32.const 1)) (unreachable)) + (if (i32.ne (local.get $r2) (i32.const 1)) (then (unreachable))) ;; representations all look good - (if (i32.ne (call $rep (local.get $r1)) (i32.const 100)) (unreachable)) - (if (i32.ne (call $rep (local.get $r2)) (i32.const 400)) (unreachable)) - (if (i32.ne (call $rep (local.get $r3)) (i32.const 300)) (unreachable)) + (if (i32.ne (call $rep (local.get $r1)) (i32.const 100)) (then (unreachable))) + (if (i32.ne (call $rep (local.get $r2)) (i32.const 400)) (then (unreachable))) + (if (i32.ne (call $rep (local.get $r3)) (i32.const 300)) (then (unreachable))) ;; deallocate, then reallocate (call $drop (local.get $r1)) @@ -130,18 +130,18 @@ (local.set $r3 (call $new (i32.const 700))) ;; representations all look good - (if (i32.ne (call $rep (local.get $r1)) (i32.const 500)) (unreachable)) - (if (i32.ne (call $rep (local.get $r2)) (i32.const 600)) (unreachable)) - (if (i32.ne (call $rep (local.get $r3)) (i32.const 700)) (unreachable)) + (if (i32.ne (call $rep (local.get $r1)) (i32.const 500)) (then (unreachable))) + (if (i32.ne (call $rep (local.get $r2)) (i32.const 600)) (then (unreachable))) + (if (i32.ne (call $rep (local.get $r3)) (i32.const 700)) (then (unreachable))) ;; indices should be lifo - (if (i32.ne (local.get $r1) (i32.const 2)) (unreachable)) - (if (i32.ne (local.get $r2) (i32.const 1)) (unreachable)) - (if (i32.ne (local.get $r3) (i32.const 0)) (unreachable)) + (if (i32.ne (local.get $r1) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get $r2) (i32.const 1)) (then (unreachable))) + (if (i32.ne (local.get $r3) (i32.const 0)) (then (unreachable))) ;; bump one more time (local.set $r4 (call $new (i32.const 800))) - (if (i32.ne (local.get $r4) (i32.const 3)) (unreachable)) + (if (i32.ne (local.get $r4) (i32.const 3)) (then (unreachable))) ;; deallocate everything (call $drop (local.get $r1)) @@ -241,13 +241,13 @@ (local.set $r2 (call $ctor (i32.const 200))) ;; assert r1/r2 are sequential - (if (i32.ne (local.get $r1) (i32.const 0)) (unreachable)) - (if (i32.ne (local.get $r2) (i32.const 1)) (unreachable)) + (if (i32.ne (local.get $r1) (i32.const 0)) (then (unreachable))) + (if (i32.ne (local.get $r2) (i32.const 1)) (then (unreachable))) ;; reallocate r1 and it should be reassigned the same index (call $drop (local.get $r1)) (local.set $r1 (call $ctor (i32.const 300))) - (if (i32.ne (local.get $r1) (i32.const 0)) (unreachable)) + (if (i32.ne (local.get $r1) (i32.const 0)) (then (unreachable))) ;; internal values should match (call $assert (local.get $r1) (i32.const 300)) @@ -399,7 +399,7 @@ (import "" "ctor" (func $ctor (param i32) (result i32))) (func $start - (if (i32.ne (call $ctor (i32.const 100)) (i32.const 0)) (unreachable)) + (if (i32.ne (call $ctor (i32.const 100)) (i32.const 0)) (then (unreachable))) ) ) (core instance $i (instantiate $m @@ -443,7 +443,7 @@ (import "" "ctor" (func $ctor (param i32) (result i32))) (func $start - (if (i32.ne (call $ctor (i32.const 100)) (i32.const 0)) (unreachable)) + (if (i32.ne (call $ctor (i32.const 100)) (i32.const 0)) (then (unreachable))) ) (start $start) ) @@ -492,7 +492,7 @@ (import "" "drop" (func $drop (param i32))) (func (export "alloc") - (if (i32.ne (call $ctor (i32.const 100)) (i32.const 0)) (unreachable)) + (if (i32.ne (call $ctor (i32.const 100)) (i32.const 0)) (then (unreachable))) ) (func (export "dealloc") (call $drop (i32.const 0)) @@ -552,7 +552,7 @@ (import "" "drop" (func $drop (param i32))) (func (export "alloc") - (if (i32.ne (call $ctor (i32.const 100)) (i32.const 0)) (unreachable)) + (if (i32.ne (call $ctor (i32.const 100)) (i32.const 0)) (then (unreachable))) ) (func (export "dealloc") (call $drop (i32.const 0)) @@ -617,8 +617,8 @@ (call $drop2 (call $new2 (i32.const 104))) ;; should be referencing the same namespace - (if (i32.ne (call $new1 (i32.const 105)) (i32.const 0)) (unreachable)) - (if (i32.ne (call $new2 (i32.const 105)) (i32.const 1)) (unreachable)) + (if (i32.ne (call $new1 (i32.const 105)) (i32.const 0)) (then (unreachable))) + (if (i32.ne (call $new2 (i32.const 105)) (i32.const 1)) (then (unreachable))) ;; use different drops out of order (call $drop2 (i32.const 0)) @@ -701,30 +701,30 @@ (local.set $r2 (call $new2 (i32.const 200))) ;; both should be index 0 - (if (i32.ne (local.get $r1) (i32.const 0)) (unreachable)) - (if (i32.ne (local.get $r2) (i32.const 0)) (unreachable)) + (if (i32.ne (local.get $r1) (i32.const 0)) (then (unreachable))) + (if (i32.ne (local.get $r2) (i32.const 0)) (then (unreachable))) ;; nothing should be dropped yet - (if (i32.ne (call $drops) (i32.const 0)) (unreachable)) - (if (i32.ne (call $last-drop) (i32.const -1)) (unreachable)) + (if (i32.ne (call $drops) (i32.const 0)) (then (unreachable))) + (if (i32.ne (call $last-drop) (i32.const -1)) (then (unreachable))) ;; dropping a resource without a destructor is ok, but shouldn't tamper ;; with anything. (call $drop1 (local.get $r1)) - (if (i32.ne (call $drops) (i32.const 0)) (unreachable)) - (if (i32.ne (call $last-drop) (i32.const -1)) (unreachable)) + (if (i32.ne (call $drops) (i32.const 0)) (then (unreachable))) + (if (i32.ne (call $last-drop) (i32.const -1)) (then (unreachable))) ;; drop r2 which should record a drop and additionally record the private ;; representation value which was dropped (call $drop2 (local.get $r2)) - (if (i32.ne (call $drops) (i32.const 1)) (unreachable)) - (if (i32.ne (call $last-drop) (i32.const 200)) (unreachable)) + (if (i32.ne (call $drops) (i32.const 1)) (then (unreachable))) + (if (i32.ne (call $last-drop) (i32.const 200)) (then (unreachable))) ;; do it all over again (local.set $r2 (call $new2 (i32.const 300))) (call $drop2 (local.get $r2)) - (if (i32.ne (call $drops) (i32.const 2)) (unreachable)) - (if (i32.ne (call $last-drop) (i32.const 300)) (unreachable)) + (if (i32.ne (call $drops) (i32.const 2)) (then (unreachable))) + (if (i32.ne (call $last-drop) (i32.const 300)) (then (unreachable))) ) (start $start) @@ -779,18 +779,18 @@ (local.set $r1 (call $ctor (i32.const 100))) ;; should be no drops yet - (if (i32.ne (call $drops) (i32.const 0)) (unreachable)) + (if (i32.ne (call $drops) (i32.const 0)) (then (unreachable))) ;; should count a drop (call $drop (local.get $r1)) - (if (i32.ne (call $drops) (i32.const 1)) (unreachable)) - (if (i32.ne (call $last-drop) (i32.const 100)) (unreachable)) + (if (i32.ne (call $drops) (i32.const 1)) (then (unreachable))) + (if (i32.ne (call $last-drop) (i32.const 100)) (then (unreachable))) ;; do it again to be sure (local.set $r1 (call $ctor (i32.const 200))) (call $drop (local.get $r1)) - (if (i32.ne (call $drops) (i32.const 2)) (unreachable)) - (if (i32.ne (call $last-drop) (i32.const 200)) (unreachable)) + (if (i32.ne (call $drops) (i32.const 2)) (then (unreachable))) + (if (i32.ne (call $last-drop) (i32.const 200)) (then (unreachable))) ) (start $start) @@ -866,7 +866,7 @@ ;; table should be empty at this point, so a fresh allocation should get ;; index 0 - (if (i32.ne (call $ctor (i32.const 600)) (i32.const 0)) (unreachable)) + (if (i32.ne (call $ctor (i32.const 600)) (i32.const 0)) (then (unreachable))) ) (start $start) @@ -971,10 +971,10 @@ (import "" "rep" (func $rep (param i32) (result i32))) (func (export "[method]t.assert") (param i32 i32) - (if (i32.ne (local.get 0) (local.get 1)) (unreachable)) + (if (i32.ne (local.get 0) (local.get 1)) (then (unreachable))) ) (func (export "[static]t.assert-own") (param i32 i32) - (if (i32.ne (call $rep (local.get 0)) (local.get 1)) (unreachable)) + (if (i32.ne (call $rep (local.get 0)) (local.get 1)) (then (unreachable))) (call $dtor (local.get 0)) ) ) @@ -1024,8 +1024,8 @@ (local.set $r1 (call $ctor (i32.const 100))) (local.set $r2 (call $ctor (i32.const 200))) - (if (i32.ne (local.get $r1) (i32.const 0)) (unreachable)) - (if (i32.ne (local.get $r2) (i32.const 1)) (unreachable)) + (if (i32.ne (local.get $r1) (i32.const 0)) (then (unreachable))) + (if (i32.ne (local.get $r2) (i32.const 1)) (then (unreachable))) (call $assert-borrow (local.get $r2) (i32.const 200)) (call $assert-borrow (local.get $r1) (i32.const 100)) diff --git a/tests/misc_testsuite/many_table_gets_lead_to_gc.wast b/tests/misc_testsuite/many_table_gets_lead_to_gc.wast index b0827dbdabbc..d911eb3844e7 100644 --- a/tests/misc_testsuite/many_table_gets_lead_to_gc.wast +++ b/tests/misc_testsuite/many_table_gets_lead_to_gc.wast @@ -9,7 +9,7 @@ (loop $continue ;; Exit when our loop counter `$i` reaches zero. (if (i32.eqz (local.get $i)) - (return) + (then (return)) ) ;; Get an `externref` out of the table. This could cause the From 96f00830e7c16e283c06cd059a71ce832ed5f3e8 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 27 Sep 2023 10:30:39 -0700 Subject: [PATCH 016/199] Minor preview2 impl cleanups (#7097) * delete preview2::pipe::pipe this is not used anywhere, not documented, and trivial for the user to build on their own if they need it. i suspect they probably only want one end of their pipe wrapped with AsyncReadStream or AsyncWriteStream. * fix cfg_attr to ignore forking tests on qemu * delete "async fd stdin" test, which is just the worker thread one anyway --- crates/wasi/src/preview2/pipe.rs | 11 ----------- crates/wasi/src/preview2/stdio.rs | 9 +-------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/crates/wasi/src/preview2/pipe.rs b/crates/wasi/src/preview2/pipe.rs index 69749db9d43c..cfde46d8c127 100644 --- a/crates/wasi/src/preview2/pipe.rs +++ b/crates/wasi/src/preview2/pipe.rs @@ -105,17 +105,6 @@ impl HostOutputStream for MemoryOutputPipe { } } -/// FIXME: this needs docs -pub fn pipe(size: usize) -> (AsyncReadStream, AsyncWriteStream) { - let (a, b) = tokio::io::duplex(size); - let (_read_half, write_half) = tokio::io::split(a); - let (read_half, _write_half) = tokio::io::split(b); - ( - AsyncReadStream::new(read_half), - AsyncWriteStream::new(size, write_half), - ) -} - /// Provides a [`HostInputStream`] impl from a [`tokio::io::AsyncRead`] impl pub struct AsyncReadStream { state: StreamState, diff --git a/crates/wasi/src/preview2/stdio.rs b/crates/wasi/src/preview2/stdio.rs index f385329c1a37..08f4350462cc 100644 --- a/crates/wasi/src/preview2/stdio.rs +++ b/crates/wasi/src/preview2/stdio.rs @@ -333,14 +333,7 @@ mod test { // This test doesn't work under qemu because of the use of fork in the test helper. #[test] - #[cfg_attr(not(target = "x86_64"), ignore)] - fn test_async_fd_stdin() { - test_stdin_by_forking(super::stdin); - } - - // This test doesn't work under qemu because of the use of fork in the test helper. - #[test] - #[cfg_attr(not(target = "x86_64"), ignore)] + #[cfg_attr(not(target_arch = "x86_64"), ignore)] fn test_worker_thread_stdin() { test_stdin_by_forking(super::worker_thread_stdin::stdin); } From b7c0eae6ddfae9dbd6a07990dd97cf6bc61b6dd2 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 27 Sep 2023 17:18:23 -0700 Subject: [PATCH 017/199] Add MPK-protected stripes to the pooling allocator (#7072) * Add memory protection keys (MPK) In order to use MPK on an x86_64 Linux system, we need access to the underlying `pkey_*` system calls (`sys`), control of the x86 PKRU register (`pkru`), and a way of determining if MPK is even supported (`is_supported`). These various parts are wrapped in a `ProtectionKey` abstraction along with a `ProtectionMask` that can be used `allow` the CPU to access protected regions. * Integrate MPK into the pooling allocator This change adds "stripes" to the pooling allocator's `MemoryPool`. Now, when requesting a slot in which to instantiate, the user (i.e., `InstanceAllocationRequest`) will be transparently assigned to one of the stripes, each of which is associated with a protection key. The user can also request a specific protection key to use, which will override the original "find me a slot logic". This has implications for how instances get allocated: once a store is assigned a protection key, it will only allocate requests with that key, limiting how many slots it has access to. E.g., if 15 keys are active, the store can only ever access 1/15th of the slots. This change also includes a tri-bool configuration field, `memory_protection_keys`, which is disabled by default for the time being. * Address review comments This is a rollup of 43 commits addressing review comments of various kinds: bug fixes, refactorings, documentation improvements, etc. It also ensures that CI runs all checks. A big thanks to @fitzgen and @alexcrichton for the review! prtest:full Co-authored-by: Nick Fitzgerald Co-authored-by: Alex Crichton --------- Co-authored-by: Nick Fitzgerald Co-authored-by: Alex Crichton --- Cargo.lock | 1 + crates/runtime/Cargo.toml | 6 +- .../allocator/pooling/memory_pool.txt | 8 + crates/runtime/src/instance/allocator.rs | 23 + .../src/instance/allocator/on_demand.rs | 22 + .../runtime/src/instance/allocator/pooling.rs | 88 +- .../allocator/pooling/index_allocator.rs | 30 +- .../instance/allocator/pooling/memory_pool.rs | 792 +++++++++++++++--- crates/runtime/src/lib.rs | 2 + crates/runtime/src/mpk/disabled.rs | 39 + crates/runtime/src/mpk/enabled.rs | 193 +++++ crates/runtime/src/mpk/mod.rs | 54 ++ crates/runtime/src/mpk/pkru.rs | 88 ++ crates/runtime/src/mpk/sys.rs | 116 +++ crates/wasmtime/src/config.rs | 53 +- crates/wasmtime/src/instance.rs | 1 + crates/wasmtime/src/store.rs | 30 +- crates/wasmtime/src/trampoline.rs | 1 + crates/wasmtime/src/trampoline/memory.rs | 14 + tests/all/pooling_allocator.rs | 2 +- 20 files changed, 1412 insertions(+), 151 deletions(-) create mode 100644 crates/runtime/proptest-regressions/instance/allocator/pooling/memory_pool.txt create mode 100644 crates/runtime/src/mpk/disabled.rs create mode 100644 crates/runtime/src/mpk/enabled.rs create mode 100644 crates/runtime/src/mpk/mod.rs create mode 100644 crates/runtime/src/mpk/pkru.rs create mode 100644 crates/runtime/src/mpk/sys.rs diff --git a/Cargo.lock b/Cargo.lock index 9887f11ac7e3..3597329678f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3681,6 +3681,7 @@ dependencies = [ "memoffset", "once_cell", "paste", + "proptest", "rand", "rustix 0.38.8", "sptr", diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml index 50002b88dead..0ff9f69ae8c1 100644 --- a/crates/runtime/Cargo.toml +++ b/crates/runtime/Cargo.toml @@ -50,6 +50,7 @@ features = [ [dev-dependencies] once_cell = { workspace = true } +proptest = "1.0.0" [build-dependencies] cc = "1.0" @@ -61,9 +62,6 @@ async = ["wasmtime-fiber"] # Enables support for the pooling instance allocator pooling-allocator = [] -component-model = [ - "wasmtime-environ/component-model", - "dep:encoding_rs", -] +component-model = ["wasmtime-environ/component-model", "dep:encoding_rs"] wmemcheck = [] diff --git a/crates/runtime/proptest-regressions/instance/allocator/pooling/memory_pool.txt b/crates/runtime/proptest-regressions/instance/allocator/pooling/memory_pool.txt new file mode 100644 index 000000000000..f95bfcd3875f --- /dev/null +++ b/crates/runtime/proptest-regressions/instance/allocator/pooling/memory_pool.txt @@ -0,0 +1,8 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 696808084287d5d58b85c60c4720227ab4dd83ada7be6841a67162023aaf4914 # shrinks to c = SlabConstraints { max_memory_bytes: 0, num_memory_slots: 1, num_pkeys_available: 0, guard_bytes: 9223372036854775808 } +cc cf9f6c36659f7f56ed8ea646e8c699cbf46708cef6911cdd376418ad69ea1388 # shrinks to c = SlabConstraints { max_memory_bytes: 14161452635954640438, num_memory_slots: 0, num_pkeys_available: 0, guard_bytes: 4285291437754911178 } diff --git a/crates/runtime/src/instance/allocator.rs b/crates/runtime/src/instance/allocator.rs index 35c1201495d6..5205d87c3444 100644 --- a/crates/runtime/src/instance/allocator.rs +++ b/crates/runtime/src/instance/allocator.rs @@ -1,6 +1,7 @@ use crate::imports::Imports; use crate::instance::{Instance, InstanceHandle}; use crate::memory::Memory; +use crate::mpk::ProtectionKey; use crate::table::Table; use crate::{CompiledModuleId, ModuleRuntimeInfo, Store}; use anyhow::{anyhow, bail, Result}; @@ -59,6 +60,10 @@ pub struct InstanceAllocationRequest<'a> { /// Indicates '--wmemcheck' flag. pub wmemcheck: bool, + + /// Request that the instance's memories be protected by a specific + /// protection key. + pub pkey: Option, } /// A pointer to a Store. This Option<*mut dyn Store> is wrapped in a struct @@ -267,6 +272,24 @@ pub unsafe trait InstanceAllocatorImpl { /// Primarily present for the pooling allocator to remove mappings of /// this module from slots in linear memory. fn purge_module(&self, module: CompiledModuleId); + + /// Use the next available protection key. + /// + /// The pooling allocator can use memory protection keys (MPK) for + /// compressing the guard regions protecting against OOB. Each + /// pool-allocated store needs its own key. + fn next_available_pkey(&self) -> Option; + + /// Restrict access to memory regions protected by `pkey`. + /// + /// This is useful for the pooling allocator, which can use memory + /// protection keys (MPK). Note: this may still allow access to other + /// protection keys, such as the default kernel key; see implementations of + /// this. + fn restrict_to_pkey(&self, pkey: ProtectionKey); + + /// Allow access to memory regions protected by any protection key. + fn allow_all_pkeys(&self); } /// A thing that can allocate instances. diff --git a/crates/runtime/src/instance/allocator/on_demand.rs b/crates/runtime/src/instance/allocator/on_demand.rs index ad4d951bf57a..935074729fd3 100644 --- a/crates/runtime/src/instance/allocator/on_demand.rs +++ b/crates/runtime/src/instance/allocator/on_demand.rs @@ -3,6 +3,7 @@ use super::{ }; use crate::instance::RuntimeMemoryCreator; use crate::memory::{DefaultMemoryCreator, Memory}; +use crate::mpk::ProtectionKey; use crate::table::Table; use crate::CompiledModuleId; use anyhow::Result; @@ -151,4 +152,25 @@ unsafe impl InstanceAllocatorImpl for OnDemandInstanceAllocator { } fn purge_module(&self, _: CompiledModuleId) {} + + fn next_available_pkey(&self) -> Option { + // The on-demand allocator cannot use protection keys--it requires + // back-to-back allocation of memory slots that this allocator cannot + // guarantee. + None + } + + fn restrict_to_pkey(&self, _: ProtectionKey) { + // The on-demand allocator cannot use protection keys; an on-demand + // allocator will never hand out protection keys to the stores its + // engine creates. + unreachable!() + } + + fn allow_all_pkeys(&self) { + // The on-demand allocator cannot use protection keys; an on-demand + // allocator will never hand out protection keys to the stores its + // engine creates. + unreachable!() + } } diff --git a/crates/runtime/src/instance/allocator/pooling.rs b/crates/runtime/src/instance/allocator/pooling.rs index 214e66c8c5f5..7d00a59052bb 100644 --- a/crates/runtime/src/instance/allocator/pooling.rs +++ b/crates/runtime/src/instance/allocator/pooling.rs @@ -1,11 +1,62 @@ //! Implements the pooling instance allocator. //! -//! The pooling instance allocator maps memory in advance -//! and allocates instances, memories, tables, and stacks from -//! a pool of available resources. +//! The pooling instance allocator maps memory in advance and allocates +//! instances, memories, tables, and stacks from a pool of available resources. +//! Using the pooling instance allocator can speed up module instantiation when +//! modules can be constrained based on configurable limits +//! ([`InstanceLimits`]). Each new instance is stored in a "slot"; as instances +//! are allocated and freed, these slots are either filled or emptied: //! -//! Using the pooling instance allocator can speed up module instantiation -//! when modules can be constrained based on configurable limits. +//! ```text +//! ┌──────┬──────┬──────┬──────┬──────┐ +//! │Slot 0│Slot 1│Slot 2│Slot 3│......│ +//! └──────┴──────┴──────┴──────┴──────┘ +//! ``` +//! +//! Note that these slots are a useful abstraction but not exactly how this is +//! mapped to memory in fact. Each new instance _does_ get associated with a +//! slot number (see uses of `index` and [`SlotId`] in this module) but the +//! parts of the instances are stored in separate pools: memories in the +//! [`MemoryPool`], tables in the [`TablePool`], etc. What ties these various +//! parts together is the slot number generated by an [`IndexAllocator`] . +//! +//! The [`MemoryPool`] protects Wasmtime from out-of-bounds memory accesses by +//! inserting inaccessible guard regions between memory slots. The +//! [`MemoryPool`] documentation has a more in-depth chart but one can think of +//! memories being laid out like the following: +//! +//! ```text +//! ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ +//! │Guard│Mem 0│Guard│Mem 1│Guard│Mem 2│.....│Guard│ +//! └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ +//! ``` +//! +//! To complicate matters, each instance can have multiple memories, multiple +//! tables, etc. You might think these would be stored consecutively in their +//! respective pools (for instance `n`, table 0 is at table pool slot `n + 0` +//! and table 1 is at `n + 1`), but for memories this is not the case. With +//! protection keys enabled, memories do not need interleaved guard regions +//! because the protection key will signal a fault if the wrong memory is +//! accessed. Instead, the pooling allocator "stripes" the memories with +//! different protection keys. +//! +//! This concept, dubbed [ColorGuard] in the original paper, relies on careful +//! calculation of the memory sizes to prevent any "overlapping access": there +//! are limited protection keys available (15) so the next memory using the same +//! key must be at least as far away as the guard region we would insert +//! otherwise. This ends up looking like the following, where a store for +//! instance 0 (`I0`) "stripes" two memories (`M0` and `M1`) with the same +//! protection key 1 and far enough apart to signal an OOB access: +//! +//! ```text +//! ┌─────┬─────┬─────┬─────┬────────────────┬─────┬─────┬─────┐ +//! │.....│I0:M1│.....│.....│..│I0:M2│.....│.....│ +//! ├─────┼─────┼─────┼─────┼────────────────┼─────┼─────┼─────┤ +//! │.....│key 1│key 2│key 3│.....│key 1│key 2│.....│ +//! └─────┴─────┴─────┴─────┴────────────────┴─────┴─────┴─────┘ +//! ``` +//! +//! [ColorGuard]: https://plas2022.github.io/files/pdf/SegueColorGuard.pdf mod index_allocator; mod memory_pool; @@ -27,7 +78,11 @@ cfg_if::cfg_if! { use super::{ InstanceAllocationRequest, InstanceAllocatorImpl, MemoryAllocationIndex, TableAllocationIndex, }; -use crate::{instance::Instance, CompiledModuleId, Memory, Table}; +use crate::{ + instance::Instance, + mpk::{self, MpkEnabled, ProtectionKey, ProtectionMask}, + CompiledModuleId, Memory, Table, +}; use anyhow::{bail, Result}; use memory_pool::MemoryPool; use std::{ @@ -162,6 +217,8 @@ pub struct PoolingInstanceAllocatorConfig { pub linear_memory_keep_resident: usize, /// Same as `linear_memory_keep_resident` but for tables. pub table_keep_resident: usize, + /// Whether to enable memory protection keys. + pub memory_protection_keys: MpkEnabled, } impl Default for PoolingInstanceAllocatorConfig { @@ -174,15 +231,18 @@ impl Default for PoolingInstanceAllocatorConfig { async_stack_keep_resident: 0, linear_memory_keep_resident: 0, table_keep_resident: 0, + memory_protection_keys: MpkEnabled::Disable, } } } /// Implements the pooling instance allocator. /// -/// This allocator internally maintains pools of instances, memories, tables, and stacks. +/// This allocator internally maintains pools of instances, memories, tables, +/// and stacks. /// -/// Note: the resource pools are manually dropped so that the fault handler terminates correctly. +/// Note: the resource pools are manually dropped so that the fault handler +/// terminates correctly. #[derive(Debug)] pub struct PoolingInstanceAllocator { limits: InstanceLimits, @@ -533,6 +593,18 @@ unsafe impl InstanceAllocatorImpl for PoolingInstanceAllocator { fn purge_module(&self, module: CompiledModuleId) { self.memories.purge_module(module); } + + fn next_available_pkey(&self) -> Option { + self.memories.next_available_pkey() + } + + fn restrict_to_pkey(&self, pkey: ProtectionKey) { + mpk::allow(ProtectionMask::zero().or(pkey)); + } + + fn allow_all_pkeys(&self) { + mpk::allow(ProtectionMask::all()); + } } #[cfg(test)] diff --git a/crates/runtime/src/instance/allocator/pooling/index_allocator.rs b/crates/runtime/src/instance/allocator/pooling/index_allocator.rs index d4079680be89..7d35f8e3f757 100644 --- a/crates/runtime/src/instance/allocator/pooling/index_allocator.rs +++ b/crates/runtime/src/instance/allocator/pooling/index_allocator.rs @@ -167,6 +167,12 @@ impl ModuleAffinityIndexAllocator { })) } + /// How many slots can this allocator allocate? + pub fn len(&self) -> usize { + let inner = self.0.lock().unwrap(); + inner.slot_state.len() + } + /// Are zero slots in use right now? pub fn is_empty(&self) -> bool { let inner = self.0.lock().unwrap(); @@ -299,8 +305,16 @@ impl ModuleAffinityIndexAllocator { }); } - /// For testing only, we want to be able to assert what is on the - /// single freelist, for the policies that keep just one. + /// Return the number of empty slots available in this allocator. + #[cfg(test)] + pub fn num_empty_slots(&self) -> usize { + let inner = self.0.lock().unwrap(); + let total_slots = inner.slot_state.len(); + (total_slots - inner.last_cold as usize) + inner.unused_warm_slots as usize + } + + /// For testing only, we want to be able to assert what is on the single + /// freelist, for the policies that keep just one. #[cfg(test)] #[allow(unused)] pub(crate) fn testing_freelist(&self) -> Vec { @@ -311,8 +325,8 @@ impl ModuleAffinityIndexAllocator { .collect() } - /// For testing only, get the list of all modules with at least - /// one slot with affinity for that module. + /// For testing only, get the list of all modules with at least one slot + /// with affinity for that module. #[cfg(test)] pub(crate) fn testing_module_affinity_list(&self) -> Vec { let inner = self.0.lock().unwrap(); @@ -475,7 +489,9 @@ mod test { fn test_next_available_allocation_strategy() { for size in 0..20 { let state = ModuleAffinityIndexAllocator::new(size, 0); + assert_eq!(state.num_empty_slots() as u32, size); for i in 0..size { + assert_eq!(state.num_empty_slots() as u32, size - i); assert_eq!(state.alloc(None).unwrap().index(), i as usize); } assert!(state.alloc(None).is_none()); @@ -496,6 +512,9 @@ mod test { assert_ne!(index1, index2); state.free(index1); + assert_eq!(state.num_empty_slots(), 99); + + // Allocate to the same `index1` slot again. let index3 = state.alloc(Some(id1)).unwrap(); assert_eq!(index3, index1); state.free(index3); @@ -512,13 +531,14 @@ mod test { // for id1, and 98 empty. Allocate 100 for id2. The first // should be equal to the one we know was previously used for // id2. The next 99 are arbitrary. - + assert_eq!(state.num_empty_slots(), 100); let mut indices = vec![]; for _ in 0..100 { indices.push(state.alloc(Some(id2)).unwrap()); } assert!(state.alloc(None).is_none()); assert_eq!(indices[0], index2); + assert_eq!(state.num_empty_slots(), 0); for i in indices { state.free(i); diff --git a/crates/runtime/src/instance/allocator/pooling/memory_pool.rs b/crates/runtime/src/instance/allocator/pooling/memory_pool.rs index 45038a21e860..47455a0e3558 100644 --- a/crates/runtime/src/instance/allocator/pooling/memory_pool.rs +++ b/crates/runtime/src/instance/allocator/pooling/memory_pool.rs @@ -1,65 +1,128 @@ +//! Implements a memory pool using a single allocated memory slab. +//! +//! The pooling instance allocator maps memory in advance and allocates +//! instances, memories, tables, and stacks from a pool of available resources. +//! Using the pooling instance allocator can speed up module instantiation when +//! modules can be constrained based on configurable limits +//! ([`InstanceLimits`]). Each new instance is stored in a "slot"; as instances +//! are allocated and freed, these slots are either filled or emptied: +//! +//! ```text +//! ┌──────┬──────┬──────┬──────┬──────┐ +//! │Slot 0│Slot 1│Slot 2│Slot 3│......│ +//! └──────┴──────┴──────┴──────┴──────┘ +//! ``` +//! +//! Note that these slots are a useful abstraction but not exactly how this is +//! mapped to memory in fact. Each new instance _does_ get associated with a +//! slot number (see uses of `index` and [`SlotId`] in this module) but the +//! parts of the instances are stored in separate pools: memories in the +//! [`MemoryPool`], tables in the [`TablePool`], etc. What ties these various +//! parts together is the slot number generated by an [`IndexAllocator`] . +//! +//! The [`MemoryPool`] protects Wasmtime from out-of-bounds memory accesses by +//! inserting inaccessible guard regions between memory slots. The +//! [`MemoryPool`] documentation has a more in-depth chart but one can think of +//! memories being laid out like the following: +//! +//! ```text +//! ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ +//! │Guard│Mem 0│Guard│Mem 1│Guard│Mem 2│.....│Guard│ +//! └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ +//! ``` +//! +//! To complicate matters, each instance can have multiple memories, multiple +//! tables, etc. You might think these would be stored consecutively in their +//! respective pools (for instance `n`, table 0 is at table pool slot `n + 0` +//! and table 1 is at `n + 1`), but for memories this is not the case. With +//! protection keys enabled, memories do not need interleaved guard regions +//! because the protection key will signal a fault if the wrong memory is +//! accessed. Instead, the pooling allocator "stripes" the memories with +//! different protection keys. +//! +//! This concept, dubbed [ColorGuard] in the original paper, relies on careful +//! calculation of the memory sizes to prevent any "overlapping access": there +//! are limited protection keys available (15) so the next memory using the same +//! key must be at least as far away as the guard region we would insert +//! otherwise. This ends up looking like the following, where a store for +//! instance 0 (`I0`) "stripes" two memories (`M0` and `M1`) with the same +//! protection key 1 and far enough apart to signal an OOB access: +//! +//! ```text +//! ┌─────┬─────┬─────┬─────┬────────────────┬─────┬─────┬─────┐ +//! │.....│I0:M1│.....│.....│..│I0:M2│.....│.....│ +//! ├─────┼─────┼─────┼─────┼────────────────┼─────┼─────┼─────┤ +//! │.....│key 1│key 2│key 3│.....│key 1│key 2│.....│ +//! └─────┴─────┴─────┴─────┴────────────────┴─────┴─────┴─────┘ +//! ``` +//! +//! [ColorGuard]: https://plas2022.github.io/files/pdf/SegueColorGuard.pdf + use super::{ index_allocator::{MemoryInModule, ModuleAffinityIndexAllocator, SlotId}, MemoryAllocationIndex, }; +use crate::mpk::{self, ProtectionKey, ProtectionMask}; use crate::{ - CompiledModuleId, InstanceAllocationRequest, Memory, MemoryImageSlot, Mmap, - PoolingInstanceAllocatorConfig, + CompiledModuleId, InstanceAllocationRequest, InstanceLimits, Memory, MemoryImageSlot, Mmap, + MpkEnabled, PoolingInstanceAllocatorConfig, }; use anyhow::{anyhow, bail, Context, Result}; use libc::c_void; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Mutex; use wasmtime_environ::{ DefinedMemoryIndex, MemoryPlan, MemoryStyle, Module, Tunables, WASM_PAGE_SIZE, }; +/// A set of allocator slots. +/// +/// The allocated slots can be split by striping them: e.g., with two stripe +/// colors 0 and 1, we would allocate all even slots using stripe 0 and all odd +/// slots using stripe 1. +/// +/// This is helpful for the use of protection keys: (a) if a request comes to +/// allocate multiple instances, we can allocate them all from the same stripe +/// and (b) if a store wants to allocate more from the same stripe it can. +#[derive(Debug)] +struct Stripe { + allocator: ModuleAffinityIndexAllocator, + pkey: Option, +} + /// Represents a pool of WebAssembly linear memories. /// /// A linear memory is divided into accessible pages and guard pages. /// /// A diagram for this struct's fields is: /// -/// ```ignore -/// memory_size -/// / -/// max_accessible / memory_and_guard_size -/// | / | -/// <--+---> / <-----------+----------> -/// <--------+-> -/// -/// +-----------+--------+---+-----------+ +--------+---+-----------+ -/// | PROT_NONE | | PROT_NONE | ... | | PROT_NONE | -/// +-----------+--------+---+-----------+ +--------+---+-----------+ -/// | |<------------------+----------------------------------> -/// \ | \ -/// mapping | `max_total_memories` memories +/// ```text +/// layout.max_memory_bytes layout.slot_bytes +/// | | +/// <-----+----> <-----------+-----------> +/// +-----------+------------+-----------+ +--------+---+-----------+-----------+ +/// | PROT_NONE | | PROT_NONE | ... | | PROT_NONE | PROT_NONE | +/// +-----------+------------+-----------+ +--------+---+-----------+-----------+ +/// | |<------------------+----------------------------------> <----+----> +/// \ | \ | +/// mapping | `layout.num_slots` memories layout.post_slab_guard_size /// / -/// initial_memory_offset +/// layout.pre_slab_guard_size /// ``` #[derive(Debug)] pub struct MemoryPool { mapping: Mmap, - index_allocator: ModuleAffinityIndexAllocator, + /// This memory pool is stripe-aware. If using memory protection keys, this + /// will contain one stripe per available key; otherwise, a single stripe + /// with an empty key. + stripes: Vec, // If using a copy-on-write allocation scheme, the slot management. We // dynamically transfer ownership of a slot to a Memory when in // use. image_slots: Vec>>, - // The size, in bytes, of each linear memory's reservation, not including - // any guard region. - memory_size: usize, - // The size, in bytes, of each linear memory's reservation plus the trailing - // guard region allocated for it. - memory_and_guard_size: usize, - // The maximum size that can become accessible, in bytes, of each linear - // memory. Guaranteed to be a whole number of wasm pages. - max_accessible: usize, - // The size, in bytes, of the offset to the first linear memory in this - // pool. This is here to help account for the first region of guard pages, - // if desired, before the first linear memory. - initial_memory_offset: usize, - // The maximum number of memories that can be allocated concurrently, aka - // our pool's capacity. - max_total_memories: usize, + /// A description of the various memory sizes used in allocating the + /// `mapping` slab. + layout: SlabLayout, // The maximum number of memories that a single core module instance may // use. // @@ -74,6 +137,9 @@ pub struct MemoryPool { // // Only applicable on Linux. keep_resident: usize, + // Keep track of protection keys handed out to initialized stores; this + // allows us to round-robin the assignment of stores to stripes. + next_available_pkey: AtomicUsize, } impl MemoryPool { @@ -87,78 +153,109 @@ impl MemoryPool { ); } - // Interpret the larger of the maximal size of memory or the static - // memory bound as the size of the virtual address space reservation for - // memory itself. Typically `static_memory_bound` is 4G which helps - // elide most bounds checks in wasm. If `memory_pages` is larger, - // though, then this is a non-moving pooling allocator so create larger - // reservations for account for that. - let memory_size = config.limits.memory_pages.max(tunables.static_memory_bound) - * u64::from(WASM_PAGE_SIZE); - - let memory_and_guard_size = - usize::try_from(memory_size + tunables.static_memory_offset_guard_size) - .map_err(|_| anyhow!("memory reservation size exceeds addressable memory"))?; - - assert!( - memory_and_guard_size % crate::page_size() == 0, - "memory size {} is not a multiple of system page size", - memory_and_guard_size - ); - - let max_total_memories = config.limits.total_memories as usize; - let initial_memory_offset = if tunables.guard_before_linear_memory { - usize::try_from(tunables.static_memory_offset_guard_size).unwrap() - } else { - 0 + let pkeys = match config.memory_protection_keys { + MpkEnabled::Auto => { + if mpk::is_supported() { + mpk::keys() + } else { + &[] + } + } + MpkEnabled::Enable => { + if mpk::is_supported() { + mpk::keys() + } else { + bail!("mpk is disabled on this system") + } + } + MpkEnabled::Disable => &[], }; - // The entire allocation here is the size of each memory (with guard - // regions) times the total number of memories in the pool. - // - // Note, though, that guard regions are required to be after each linear - // memory. If the `guard_before_linear_memory` setting is specified, - // then due to the contiguous layout of linear memories the guard pages - // after one memory are also guard pages preceding the next linear - // memory. This means that we only need to handle pre-guard-page sizes - // specially for the first linear memory, hence the - // `initial_memory_offset` variable here. If guards aren't specified - // before linear memories this is set to `0`, otherwise it's set to - // the same size as guard regions for other memories. - let allocation_size = memory_and_guard_size - .checked_mul(max_total_memories) - .and_then(|c| c.checked_add(initial_memory_offset)) - .ok_or_else(|| { - anyhow!("total size of memory reservation exceeds addressable memory") - })?; + // This is a tricky bit of global state: when creating a memory pool + // that uses memory protection keys, we ensure here that any host code + // will have access to all keys (i.e., stripes). It's only when we enter + // the WebAssembly guest code (see `StoreInner::call_hook`) that we + // enforce which keys/stripes can be accessed. Be forewarned about the + // assumptions here: + // - we expect this "allow all" configuration to reset the default + // process state (only allow key 0) _before_ any memories are accessed + // - and we expect no other code (e.g., host-side code) to modify this + // global MPK configuration + if !pkeys.is_empty() { + mpk::allow(ProtectionMask::all()); + } - // Create a completely inaccessible region to start - let mapping = Mmap::accessible_reserved(0, allocation_size) + // Create a slab layout and allocate it as a completely inaccessible + // region to start--`PROT_NONE`. + let constraints = SlabConstraints::new(&config.limits, tunables, pkeys.len())?; + let layout = calculate(&constraints)?; + log::debug!("creating memory pool: {constraints:?} -> {layout:?}"); + let mut mapping = Mmap::accessible_reserved(0, layout.total_slab_bytes()?) .context("failed to create memory pool mapping")?; + // Then, stripe the memory with the available protection keys. This is + // unnecessary if there is only one stripe color. + if layout.num_stripes >= 2 { + let mut cursor = layout.pre_slab_guard_bytes; + let pkeys = &pkeys[..layout.num_stripes]; + for i in 0..constraints.num_slots { + let pkey = &pkeys[i % pkeys.len()]; + let region = unsafe { mapping.slice_mut(cursor..cursor + layout.slot_bytes) }; + pkey.protect(region)?; + cursor += layout.slot_bytes; + } + debug_assert_eq!( + cursor + layout.post_slab_guard_bytes, + layout.total_slab_bytes()? + ); + } + let image_slots: Vec<_> = std::iter::repeat_with(|| Mutex::new(None)) - .take(max_total_memories) + .take(constraints.num_slots) .collect(); - let pool = Self { - index_allocator: ModuleAffinityIndexAllocator::new( - config.limits.total_memories, + let create_stripe = |i| { + let num_slots = constraints.num_slots / layout.num_stripes + + usize::from(constraints.num_slots % layout.num_stripes > i); + let allocator = ModuleAffinityIndexAllocator::new( + num_slots.try_into().unwrap(), config.max_unused_warm_slots, - ), + ); + Stripe { + allocator, + pkey: pkeys.get(i).cloned(), + } + }; + + debug_assert!(layout.num_stripes > 0); + let stripes: Vec<_> = (0..layout.num_stripes) + .into_iter() + .map(create_stripe) + .collect(); + + let pool = Self { + stripes, mapping, image_slots, - memory_size: memory_size.try_into().unwrap(), - memory_and_guard_size, - initial_memory_offset, - max_total_memories, + layout, memories_per_instance: usize::try_from(config.limits.max_memories_per_module).unwrap(), - max_accessible: (config.limits.memory_pages as usize) * (WASM_PAGE_SIZE as usize), keep_resident: config.linear_memory_keep_resident, + next_available_pkey: AtomicUsize::new(0), }; Ok(pool) } + /// Return a protection key that stores can use for requesting new + pub fn next_available_pkey(&self) -> Option { + let index = self.next_available_pkey.fetch_add(1, Ordering::SeqCst) % self.stripes.len(); + debug_assert!( + self.stripes.len() < 2 || self.stripes[index].pkey.is_some(), + "if we are using stripes, we cannot have an empty protection key" + ); + self.stripes[index].pkey.clone() + } + /// Validate whether this memory pool supports the given module. pub fn validate(&self, module: &Module) -> Result<()> { let memories = module.memory_plans.len() - module.num_imported_memories; @@ -170,6 +267,7 @@ impl MemoryPool { ); } + let max_memory_pages = self.layout.max_memory_bytes / WASM_PAGE_SIZE as usize; for (i, plan) in module .memory_plans .iter() @@ -177,7 +275,7 @@ impl MemoryPool { { match plan.style { MemoryStyle::Static { bound } => { - if u64::try_from(self.memory_size).unwrap() < bound { + if u64::try_from(self.layout.pages_to_next_stripe_slot()).unwrap() < bound { bail!( "memory size allocated per-memory is too small to \ satisfy static bound of {bound:#x} pages" @@ -186,13 +284,12 @@ impl MemoryPool { } MemoryStyle::Dynamic { .. } => {} } - let max = self.max_accessible / (WASM_PAGE_SIZE as usize); - if plan.memory.minimum > u64::try_from(max).unwrap() { + if plan.memory.minimum > u64::try_from(max_memory_pages).unwrap() { bail!( "memory index {} has a minimum page size of {} which exceeds the limit of {}", i.as_u32(), plan.memory.minimum, - max, + max_memory_pages, ); } } @@ -201,7 +298,7 @@ impl MemoryPool { /// Are zero slots in use right now? pub fn is_empty(&self) -> bool { - self.index_allocator.is_empty() + self.stripes.iter().all(|s| s.allocator.is_empty()) } /// Allocate a single memory for the given instance allocation request. @@ -211,21 +308,31 @@ impl MemoryPool { memory_plan: &MemoryPlan, memory_index: DefinedMemoryIndex, ) -> Result<(MemoryAllocationIndex, Memory)> { - let allocation_index = self - .index_allocator + let stripe_index = if let Some(pkey) = &request.pkey { + pkey.as_stripe() + } else { + debug_assert!(self.stripes.len() < 2); + 0 + }; + + let striped_allocation_index = self.stripes[stripe_index] + .allocator .alloc( request .runtime_info .unique_id() .map(|id| MemoryInModule(id, memory_index)), ) - .map(|slot| MemoryAllocationIndex(u32::try_from(slot.index()).unwrap())) + .map(|slot| StripedAllocationIndex(u32::try_from(slot.index()).unwrap())) .ok_or_else(|| { anyhow!( - "maximum concurrent memory limit of {} reached", - self.max_total_memories + "maximum concurrent memory limit of {} reached for stripe {}", + self.stripes[stripe_index].allocator.len(), + stripe_index ) })?; + let allocation_index = + striped_allocation_index.as_unstriped_slot_index(stripe_index, self.stripes.len()); match (|| { // Double-check that the runtime requirements of the memory are @@ -235,13 +342,13 @@ impl MemoryPool { match memory_plan.style { MemoryStyle::Static { bound } => { let bound = bound * u64::from(WASM_PAGE_SIZE); - assert!(bound <= u64::try_from(self.memory_size).unwrap()); + assert!(bound <= u64::try_from(self.layout.slot_bytes).unwrap()); } MemoryStyle::Dynamic { .. } => {} } let base_ptr = self.get_base(allocation_index); - let base_capacity = self.max_accessible; + let base_capacity = self.layout.max_memory_bytes; let mut slot = self.take_memory_image_slot(allocation_index); let image = request.runtime_info.memory_image(memory_index)?; @@ -267,13 +374,15 @@ impl MemoryPool { base_ptr, base_capacity, slot, - self.memory_and_guard_size, + self.layout.slot_bytes, unsafe { &mut *request.store.get().unwrap() }, ) })() { Ok(memory) => Ok((allocation_index, memory)), Err(e) => { - self.index_allocator.free(SlotId(allocation_index.0)); + self.stripes[stripe_index] + .allocator + .free(SlotId(striped_allocation_index.0)); Err(e) } } @@ -297,7 +406,11 @@ impl MemoryPool { self.return_memory_image_slot(allocation_index, image); } - self.index_allocator.free(SlotId(allocation_index.0)); + let (stripe_index, striped_allocation_index) = + StripedAllocationIndex::from_unstriped_slot_index(allocation_index, self.stripes.len()); + self.stripes[stripe_index] + .allocator + .free(SlotId(striped_allocation_index.0)); } /// Purging everything related to `module`. @@ -323,31 +436,33 @@ impl MemoryPool { // associated with a module (not just module and memory). The latter // would require care to make sure that its maintenance wouldn't be too // expensive for normal allocation/free operations. - for i in 0..self.memories_per_instance { - use wasmtime_environ::EntityRef; - let memory_index = DefinedMemoryIndex::new(i); - while let Some(id) = self - .index_allocator - .alloc_affine_and_clear_affinity(module, memory_index) - { - // Clear the image from the slot and, if successful, return it back - // to our state. Note that on failure here the whole slot will get - // paved over with an anonymous mapping. - let index = MemoryAllocationIndex(id.0); - let mut slot = self.take_memory_image_slot(index); - if slot.remove_image().is_ok() { - self.return_memory_image_slot(index, slot); - } + for stripe in &self.stripes { + for i in 0..self.memories_per_instance { + use wasmtime_environ::EntityRef; + let memory_index = DefinedMemoryIndex::new(i); + while let Some(id) = stripe + .allocator + .alloc_affine_and_clear_affinity(module, memory_index) + { + // Clear the image from the slot and, if successful, return it back + // to our state. Note that on failure here the whole slot will get + // paved over with an anonymous mapping. + let index = MemoryAllocationIndex(id.0); + let mut slot = self.take_memory_image_slot(index); + if slot.remove_image().is_ok() { + self.return_memory_image_slot(index, slot); + } - self.index_allocator.free(id); + stripe.allocator.free(id); + } } } } fn get_base(&self, allocation_index: MemoryAllocationIndex) -> *mut u8 { - assert!(allocation_index.index() < self.max_total_memories); + assert!(allocation_index.index() < self.layout.num_slots); let offset = - self.initial_memory_offset + allocation_index.index() * self.memory_and_guard_size; + self.layout.pre_slab_guard_bytes + allocation_index.index() * self.layout.slot_bytes; unsafe { self.mapping.as_ptr().offset(offset as isize).cast_mut() } } @@ -363,7 +478,7 @@ impl MemoryPool { MemoryImageSlot::create( self.get_base(allocation_index) as *mut c_void, 0, - self.max_accessible, + self.layout.max_memory_bytes, ) }) } @@ -393,10 +508,254 @@ impl Drop for MemoryPool { } } +/// The index of a memory allocation within an `InstanceAllocator`. +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct StripedAllocationIndex(u32); + +impl StripedAllocationIndex { + fn from_unstriped_slot_index( + index: MemoryAllocationIndex, + num_stripes: usize, + ) -> (usize, Self) { + let stripe_index = index.index() % num_stripes; + let num_stripes: u32 = num_stripes.try_into().unwrap(); + let index_within_stripe = Self(index.0 / num_stripes); + (stripe_index, index_within_stripe) + } + + fn as_unstriped_slot_index(self, stripe: usize, num_stripes: usize) -> MemoryAllocationIndex { + let num_stripes: u32 = num_stripes.try_into().unwrap(); + let stripe: u32 = stripe.try_into().unwrap(); + MemoryAllocationIndex(self.0 * num_stripes + stripe) + } +} + +#[derive(Clone, Debug)] +struct SlabConstraints { + /// Essentially, the `static_memory_bound`: this is an assumption that the + /// runtime and JIT compiler make about how much space will be guarded + /// between slots. + expected_slot_bytes: usize, + /// The maximum size of any memory in the pool. + max_memory_bytes: usize, + num_slots: usize, + num_pkeys_available: usize, + guard_bytes: usize, + guard_before_slots: bool, +} + +impl SlabConstraints { + fn new( + limits: &InstanceLimits, + tunables: &Tunables, + num_pkeys_available: usize, + ) -> Result { + // The maximum size a memory can grow to in this pool. + let max_memory_bytes = limits.memory_pages * u64::from(WASM_PAGE_SIZE); + + // `static_memory_bound` is the configured number of Wasm pages for a + // static memory slot (see `Config::static_memory_maximum_size`); even + // if the memory never grows to this size (e.g., it has a lower memory + // maximum), codegen will assume that this unused memory is mapped + // `PROT_NONE`. Typically `static_memory_bound` is 4G which helps elide + // most bounds checks. `MemoryPool` must respect this bound, though not + // explicitly: if we can achieve the same effect via MPK-protected + // stripes, the slot size can be lower than the `static_memory_bound`. + let expected_slot_bytes = tunables.static_memory_bound * u64::from(WASM_PAGE_SIZE); + + let constraints = SlabConstraints { + max_memory_bytes: max_memory_bytes + .try_into() + .context("max memory is too large")?, + num_slots: limits + .total_memories + .try_into() + .context("too many memories")?, + expected_slot_bytes: expected_slot_bytes + .try_into() + .context("static memory bound is too large")?, + num_pkeys_available, + guard_bytes: tunables + .static_memory_offset_guard_size + .try_into() + .context("guard region is too large")?, + guard_before_slots: tunables.guard_before_linear_memory, + }; + Ok(constraints) + } +} + +#[derive(Debug)] +struct SlabLayout { + /// The total number of slots available in the memory pool slab. + num_slots: usize, + /// The size of each slot in the memory pool; this contains the maximum + /// memory size (i.e., from WebAssembly or Wasmtime configuration) plus any + /// guard region after the memory to catch OOB access. On these guard + /// regions, note that: + /// - users can configure how aggressively (or not) to elide bounds checks + /// via `Config::static_memory_guard_size` + /// - memory protection keys can compress the size of the guard region by + /// placing slots from a different key (i.e., a stripe) in the guard + /// region; this means the slot itself can be smaller and we can allocate + /// more of them. + slot_bytes: usize, + // The maximum size that can become accessible, in bytes, for each linear + // memory. Guaranteed to be a whole number of wasm pages. + max_memory_bytes: usize, + /// The total number of bytes to allocate for the memory pool slab. + // total_slab_bytes: usize, + /// If necessary, the number of bytes to reserve as a guard region at the + /// beginning of the slab. + pre_slab_guard_bytes: usize, + /// Like `pre_slab_guard_bytes`, but at the end of the slab. + post_slab_guard_bytes: usize, + /// The number of stripes needed in the slab layout. + num_stripes: usize, +} + +impl SlabLayout { + /// Return the total size of the slab, using the final layout (where `n = + /// num_slots`): + /// + /// ```text + /// ┌────────────────────┬──────┬──────┬───┬──────┬─────────────────────┐ + /// │pre_slab_guard_bytes│slot 1│slot 2│...│slot n│post_slab_guard_bytes│ + /// └────────────────────┴──────┴──────┴───┴──────┴─────────────────────┘ + /// ``` + fn total_slab_bytes(&self) -> Result { + self.slot_bytes + .checked_mul(self.num_slots) + .and_then(|c| c.checked_add(self.pre_slab_guard_bytes)) + .and_then(|c| c.checked_add(self.post_slab_guard_bytes)) + .ok_or_else(|| anyhow!("total size of memory reservation exceeds addressable memory")) + } + + /// Returns the number of Wasm pages from the beginning of one slot to the + /// next slot in the same stripe--this is the striped equivalent of + /// `static_memory_bound`. Recall that between slots of the same stripe we + /// will see a slot from every other stripe. + /// + /// For example, in a 3-stripe pool, this function measures the distance + /// from the beginning of slot 1 to slot 4, which are of the same stripe: + /// + /// ```text + /// ┌────────┬──────┬──────┬────────┬───┐ + /// │*slot 1*│slot 2│slot 3│*slot 4*│...| + /// └────────┴──────┴──────┴────────┴───┘ + /// ``` + fn pages_to_next_stripe_slot(&self) -> usize { + let slot_pages = self.slot_bytes / WASM_PAGE_SIZE as usize; + slot_pages * self.num_stripes + } +} + +fn calculate(constraints: &SlabConstraints) -> Result { + let SlabConstraints { + max_memory_bytes, + num_slots, + expected_slot_bytes, + num_pkeys_available, + guard_bytes, + guard_before_slots, + } = *constraints; + + // If the user specifies a guard region, we always need to allocate a + // `PROT_NONE` region for it before any memory slots. Recall that we can + // avoid bounds checks for loads and stores with immediates up to + // `guard_bytes`, but we rely on Wasmtime to emit bounds checks for any + // accesses greater than this. + let pre_slab_guard_bytes = if guard_before_slots { guard_bytes } else { 0 }; + + // To calculate the slot size, we start with the default configured size and + // attempt to chip away at this via MPK protection. Note here how we begin + // to define a slot as "all of the memory and guard region." + let slot_bytes = expected_slot_bytes + .max(max_memory_bytes) + .checked_add(guard_bytes) + .unwrap_or(usize::MAX); + + let (num_stripes, slot_bytes) = if guard_bytes == 0 || max_memory_bytes == 0 || num_slots == 0 { + // In the uncommon case where the memory/guard regions are empty or we don't need any slots , we + // will not need any stripes: we just lay out the slots back-to-back + // using a single stripe. + (1, slot_bytes) + } else if num_pkeys_available < 2 { + // If we do not have enough protection keys to stripe the memory, we do + // the same. We can't elide any of the guard bytes because we aren't + // overlapping guard regions with other stripes... + (1, slot_bytes) + } else { + // ...but if we can create at least two stripes, we can use another + // stripe (i.e., a different pkey) as this slot's guard region--this + // reduces the guard bytes each slot has to allocate. We must make + // sure, though, that if the size of that other stripe(s) does not + // fully cover `guard_bytes`, we keep those around to prevent OOB + // access. + + // We first calculate the number of stripes we need: we want to + // minimize this so that there is less chance of a single store + // running out of slots with its stripe--we need at least two, + // though. But this is not just an optimization; we need to handle + // the case when there are fewer slots than stripes. E.g., if our + // pool is configured with only three slots (`num_memory_slots = + // 3`), we will run into failures if we attempt to set up more than + // three stripes. + let needed_num_stripes = + slot_bytes / max_memory_bytes + usize::from(slot_bytes % max_memory_bytes != 0) + 1; + let num_stripes = num_pkeys_available.min(needed_num_stripes).min(num_slots); + + // Next, we try to reduce the slot size by "overlapping" the + // stripes: we can make slot `n` smaller since we know that slot + // `n+1` and following are in different stripes and will look just + // like `PROT_NONE` memory. + let next_slots_overlapping_bytes = max_memory_bytes + .checked_mul(num_stripes - 1) + .unwrap_or(usize::MAX); + let needed_slot_bytes = slot_bytes + .checked_sub(next_slots_overlapping_bytes) + .unwrap_or(0) + .max(max_memory_bytes); + (num_stripes, needed_slot_bytes) + }; + + // The page-aligned slot size; equivalent to `memory_and_guard_size`. + let page_alignment = crate::page_size() - 1; + let slot_bytes = slot_bytes + .checked_add(page_alignment) + .and_then(|slot_bytes| Some(slot_bytes & !page_alignment)) + .ok_or_else(|| anyhow!("slot size is too large"))?; + + // We may need another guard region (like `pre_slab_guard_bytes`) at the end + // of our slab. We could be conservative and just create it as large as + // `guard_bytes`, but because we know that the last slot already has + // `guard_bytes` factored in to its guard region, we can reduce the final + // guard region by that much. + let post_slab_guard_bytes = guard_bytes + .checked_sub(slot_bytes - max_memory_bytes) + .unwrap_or(0); + + // Check that we haven't exceeded the slab we can calculate given the limits + // of `usize`. + let layout = SlabLayout { + num_slots, + slot_bytes, + max_memory_bytes, + pre_slab_guard_bytes, + post_slab_guard_bytes, + num_stripes, + }; + match layout.total_slab_bytes() { + Ok(_) => Ok(layout), + Err(e) => Err(e), + } +} + #[cfg(test)] mod tests { use super::*; use crate::{InstanceLimits, PoolingInstanceAllocator}; + use proptest::prelude::*; use wasmtime_environ::WASM_PAGE_SIZE; #[cfg(target_pointer_width = "64")] @@ -421,16 +780,16 @@ mod tests { }, )?; - assert_eq!(pool.memory_and_guard_size, WASM_PAGE_SIZE as usize); - assert_eq!(pool.max_total_memories, 5); - assert_eq!(pool.max_accessible, WASM_PAGE_SIZE as usize); + assert_eq!(pool.layout.slot_bytes, WASM_PAGE_SIZE as usize); + assert_eq!(pool.layout.num_slots, 5); + assert_eq!(pool.layout.max_memory_bytes, WASM_PAGE_SIZE as usize); let base = pool.mapping.as_ptr() as usize; for i in 0..5 { let index = MemoryAllocationIndex(i); let ptr = pool.get_base(index); - assert_eq!(ptr as usize - base, i as usize * pool.memory_and_guard_size); + assert_eq!(ptr as usize - base, i as usize * pool.layout.slot_bytes); } Ok(()) @@ -455,6 +814,185 @@ mod tests { }, ) .unwrap(); - assert_eq!(pool.memories.memory_size, 2 * 65536); + assert_eq!(pool.memories.layout.max_memory_bytes, 2 * 65536); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn test_pooling_allocator_striping() { + if !mpk::is_supported() { + println!("skipping `test_pooling_allocator_striping` test; mpk is not supported"); + return; + } + + // Force the use of MPK. + let config = PoolingInstanceAllocatorConfig { + memory_protection_keys: MpkEnabled::Enable, + ..PoolingInstanceAllocatorConfig::default() + }; + let pool = MemoryPool::new(&config, &Tunables::default()).unwrap(); + assert!(pool.stripes.len() >= 2); + + let max_memory_slots = config.limits.total_memories; + dbg!(pool.stripes[0].allocator.num_empty_slots()); + dbg!(pool.stripes[1].allocator.num_empty_slots()); + let available_memory_slots: usize = pool + .stripes + .iter() + .map(|s| s.allocator.num_empty_slots()) + .sum(); + assert_eq!(max_memory_slots, available_memory_slots.try_into().unwrap()); + } + + #[test] + fn check_known_layout_calculations() { + for num_pkeys_available in 0..16 { + for num_memory_slots in [0, 1, 10, 64] { + for expected_slot_bytes in [0, 1 << 30 /* 1GB */, 4 << 30 /* 4GB */] { + for max_memory_bytes in + [0, 1 * WASM_PAGE_SIZE as usize, 10 * WASM_PAGE_SIZE as usize] + { + for guard_bytes in [0, 2 << 30 /* 2GB */] { + for guard_before_slots in [true, false] { + let constraints = SlabConstraints { + max_memory_bytes, + num_slots: num_memory_slots, + expected_slot_bytes, + num_pkeys_available, + guard_bytes, + guard_before_slots, + }; + let layout = calculate(&constraints); + assert_slab_layout_invariants(constraints, layout.unwrap()); + } + } + } + } + } + } + } + + proptest! { + #[test] + #[cfg_attr(miri, ignore)] + fn check_random_layout_calculations(c in constraints()) { + if let Ok(l) = calculate(&c) { + assert_slab_layout_invariants(c, l); + } + } + } + + fn constraints() -> impl Strategy { + ( + any::(), + any::(), + any::(), + any::(), + any::(), + any::(), + ) + .prop_map( + |( + max_memory_bytes, + num_memory_slots, + expected_slot_bytes, + num_pkeys_available, + guard_bytes, + guard_before_slots, + )| { + SlabConstraints { + max_memory_bytes, + num_slots: num_memory_slots, + expected_slot_bytes, + num_pkeys_available, + guard_bytes, + guard_before_slots, + } + }, + ) + } + + fn assert_slab_layout_invariants(c: SlabConstraints, s: SlabLayout) { + // Check that all the sizes add up. + assert_eq!( + s.total_slab_bytes().unwrap(), + s.pre_slab_guard_bytes + s.slot_bytes * c.num_slots + s.post_slab_guard_bytes, + "the slab size does not add up: {c:?} => {s:?}" + ); + assert!( + s.slot_bytes >= s.max_memory_bytes, + "slot is not big enough: {c:?} => {s:?}" + ); + + // Check that the various memory values are page-aligned. + assert!( + is_aligned(s.slot_bytes), + "slot is not page-aligned: {c:?} => {s:?}", + ); + assert!( + is_aligned(s.max_memory_bytes), + "slot guard region is not page-aligned: {c:?} => {s:?}", + ); + assert!( + is_aligned(s.pre_slab_guard_bytes), + "pre-slab guard region is not page-aligned: {c:?} => {s:?}" + ); + assert!( + is_aligned(s.post_slab_guard_bytes), + "post-slab guard region is not page-aligned: {c:?} => {s:?}" + ); + assert!( + is_aligned(s.total_slab_bytes().unwrap()), + "slab is not page-aligned: {c:?} => {s:?}" + ); + + // Check that we use no more or less stripes than needed. + assert!(s.num_stripes >= 1, "not enough stripes: {c:?} => {s:?}"); + if c.num_pkeys_available == 0 || c.num_slots == 0 { + assert_eq!( + s.num_stripes, 1, + "expected at least one stripe: {c:?} => {s:?}" + ); + } else { + assert!( + s.num_stripes <= c.num_pkeys_available, + "layout has more stripes than available pkeys: {c:?} => {s:?}" + ); + assert!( + s.num_stripes <= c.num_slots, + "layout has more stripes than memory slots: {c:?} => {s:?}" + ); + } + + // Check that we use the minimum number of stripes/protection keys. + // - if the next MPK-protected slot is bigger or the same as the + // required guard region, we only need two stripes + // - if the next slot is smaller than the guard region, we only need + // enough stripes to add up to at least that guard region size. + if c.num_pkeys_available > 1 && c.max_memory_bytes > 0 { + assert!( + s.num_stripes <= (c.guard_bytes / c.max_memory_bytes) + 2, + "calculated more stripes than needed: {c:?} => {s:?}" + ); + } + + // Check that the memory-striping will not allow OOB access. + // - we may have reduced the slot size from `expected_slot_bytes` to + // `slot_bytes` assuming MPK striping; we check that the expectation + // still holds + // - the last slot won't have MPK striping after it; we check that the + // `post_slab_guard_bytes` accounts for this + assert!( + s.slot_bytes * s.num_stripes >= c.expected_slot_bytes + c.guard_bytes, + "slot may allow OOB access: {c:?} => {s:?}" + ); + assert!( + s.slot_bytes + s.post_slab_guard_bytes >= c.expected_slot_bytes, + "last slot may allow OOB access: {c:?} => {s:?}" + ); + } + + fn is_aligned(bytes: usize) -> bool { + bytes % crate::page_size() == 0 } } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index ed2c4415ded6..9b5e631768be 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -31,6 +31,7 @@ mod vmcontext; pub mod debug_builtins; pub mod libcalls; +pub mod mpk; pub use wasmtime_jit_debug::gdb_jit_int::GdbJitImageRegistration; @@ -50,6 +51,7 @@ pub use crate::memory::{ }; pub use crate::mmap::Mmap; pub use crate::mmap_vec::MmapVec; +pub use crate::mpk::MpkEnabled; pub use crate::store_box::*; pub use crate::table::{Table, TableElement}; pub use crate::traphandlers::*; diff --git a/crates/runtime/src/mpk/disabled.rs b/crates/runtime/src/mpk/disabled.rs new file mode 100644 index 000000000000..bbeccc80e893 --- /dev/null +++ b/crates/runtime/src/mpk/disabled.rs @@ -0,0 +1,39 @@ +//! Noop implementations of MPK primitives for environments that do not support +//! the feature. + +#![allow(missing_docs)] + +use anyhow::Result; + +pub fn is_supported() -> bool { + false +} +pub fn keys() -> &'static [ProtectionKey] { + &[] +} +pub fn allow(_: ProtectionMask) {} + +#[derive(Clone, Copy, Debug)] +pub enum ProtectionKey {} +impl ProtectionKey { + pub fn protect(&self, _: &mut [u8]) -> Result<()> { + match *self {} + } + pub fn as_stripe(&self) -> usize { + match *self {} + } +} + +#[derive(Clone, Copy, Debug)] +pub struct ProtectionMask; +impl ProtectionMask { + pub fn all() -> Self { + Self + } + pub fn zero() -> Self { + Self + } + pub fn or(self, _: ProtectionKey) -> Self { + Self + } +} diff --git a/crates/runtime/src/mpk/enabled.rs b/crates/runtime/src/mpk/enabled.rs new file mode 100644 index 000000000000..37456ef13c44 --- /dev/null +++ b/crates/runtime/src/mpk/enabled.rs @@ -0,0 +1,193 @@ +//! + +use super::{pkru, sys}; +use anyhow::{Context, Result}; +use std::sync::OnceLock; + +/// Check if the MPK feature is supported. +pub fn is_supported() -> bool { + cfg!(target_os = "linux") && cfg!(target_arch = "x86_64") && pkru::has_cpuid_bit_set() +} + +/// Allocate all protection keys available to this process. +/// +/// This asks the kernel for all available keys (we expect 1-15; 0 is +/// kernel-reserved) in a thread-safe way. This avoids interference when +/// multiple threads try to allocate keys at the same time (e.g., during +/// testing). It also ensures that a single copy of the keys are reserved for +/// the lifetime of the process. +/// +/// TODO: this is not the best-possible design. This creates global state that +/// would prevent any other code in the process from using protection keys; the +/// `KEYS` are never deallocated from the system with `pkey_dealloc`. +pub fn keys() -> &'static [ProtectionKey] { + let keys = KEYS.get_or_init(|| { + let mut allocated = vec![]; + if is_supported() { + while let Ok(key_id) = sys::pkey_alloc(0, 0) { + debug_assert!(key_id < 16); + // UNSAFETY: here we unsafely assume that the system-allocated pkey + // will exist forever. + let pkey = ProtectionKey(key_id); + debug_assert_eq!(pkey.as_stripe(), allocated.len()); + allocated.push(pkey); + } + } + allocated + }); + &keys +} +static KEYS: OnceLock> = OnceLock::new(); + +/// Only allow access to pages marked by the keys set in `mask`. +/// +/// Any accesses to pages marked by another key will result in a `SIGSEGV` +/// fault. +pub fn allow(mask: ProtectionMask) { + let previous = pkru::read(); + pkru::write(mask.0); + log::trace!("PKRU change: {:#034b} => {:#034b}", previous, pkru::read()); +} + +/// An MPK protection key. +/// +/// The expected usage is: +/// - receive system-allocated keys from [`keys`] +/// - mark some regions of memory as accessible with [`ProtectionKey::protect`] +/// - [`allow`] or disallow access to the memory regions using a +/// [`ProtectionMask`]; any accesses to unmarked pages result in a fault +/// - drop the key +#[derive(Clone, Copy, Debug)] +pub struct ProtectionKey(u32); + +impl ProtectionKey { + /// Mark a page as protected by this [`ProtectionKey`]. + /// + /// This "colors" the pages of `region` via a kernel `pkey_mprotect` call to + /// only allow reads and writes when this [`ProtectionKey`] is activated + /// (see [`allow`]). + /// + /// # Errors + /// + /// This will fail if the region is not page aligned or for some unknown + /// kernel reason. + pub fn protect(&self, region: &mut [u8]) -> Result<()> { + let addr = region.as_mut_ptr() as usize; + let len = region.len(); + let prot = sys::PROT_READ | sys::PROT_WRITE; + sys::pkey_mprotect(addr, len, prot, self.0).with_context(|| { + format!( + "failed to mark region with pkey (addr = {addr:#x}, len = {len}, prot = {prot:#b})" + ) + }) + } + + /// Convert the [`ProtectionKey`] to its 0-based index; this is useful for + /// determining which allocation "stripe" a key belongs to. + /// + /// This function assumes that the kernel has allocated key 0 for itself. + pub fn as_stripe(&self) -> usize { + debug_assert!(self.0 != 0); + self.0 as usize - 1 + } +} + +/// A bit field indicating which protection keys should be allowed and disabled. +/// +/// The internal representation makes it easy to use [`ProtectionMask`] directly +/// with the PKRU register. When bits `n` and `n+1` are set, it means the +/// protection key is *not* allowed (see the PKRU write and access disabled +/// bits). +pub struct ProtectionMask(u32); +impl ProtectionMask { + /// Allow access from all protection keys. + #[inline] + pub fn all() -> Self { + Self(pkru::ALLOW_ACCESS) + } + + /// Only allow access to memory protected with protection key 0; note that + /// this does not mean "none" but rather allows access from the default + /// kernel protection key. + #[inline] + pub fn zero() -> Self { + Self(pkru::DISABLE_ACCESS ^ 0b11) + } + + /// Include `pkey` as another allowed protection key in the mask. + #[inline] + pub fn or(self, pkey: ProtectionKey) -> Self { + let mask = pkru::DISABLE_ACCESS ^ 0b11 << (pkey.0 * 2); + Self(self.0 & mask) + } +} + +/// Helper macro for skipping tests on systems that do not have MPK enabled +/// (e.g., older architecture, disabled by kernel, etc.) +#[cfg(test)] +macro_rules! skip_if_mpk_unavailable { + () => { + if !crate::mpk::is_supported() { + println!("> mpk is not supported: ignoring test"); + return; + } + }; +} +/// Necessary for inter-module access. +#[cfg(test)] +pub(crate) use skip_if_mpk_unavailable; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check_is_supported() { + println!("is pku supported = {}", is_supported()); + } + + #[test] + fn check_initialized_keys() { + if is_supported() { + assert!(!keys().is_empty()) + } + } + + #[test] + fn check_invalid_mark() { + skip_if_mpk_unavailable!(); + let pkey = keys()[0]; + let unaligned_region = unsafe { + let addr = 1 as *mut u8; // this is not page-aligned! + let len = 1; + std::slice::from_raw_parts_mut(addr, len) + }; + let result = pkey.protect(unaligned_region); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "failed to mark region with pkey (addr = 0x1, len = 1, prot = 0b11)" + ); + } + + #[test] + fn check_masking() { + skip_if_mpk_unavailable!(); + let original = pkru::read(); + + allow(ProtectionMask::all()); + assert_eq!(0, pkru::read()); + + allow(ProtectionMask::all().or(ProtectionKey(5))); + assert_eq!(0, pkru::read()); + + allow(ProtectionMask::zero()); + assert_eq!(0b11111111_11111111_11111111_11111100, pkru::read()); + + allow(ProtectionMask::zero().or(ProtectionKey(5))); + assert_eq!(0b11111111_11111111_11110011_11111100, pkru::read()); + + // Reset the PKRU state to what we originally observed. + pkru::write(original); + } +} diff --git a/crates/runtime/src/mpk/mod.rs b/crates/runtime/src/mpk/mod.rs new file mode 100644 index 000000000000..bfffe24118f7 --- /dev/null +++ b/crates/runtime/src/mpk/mod.rs @@ -0,0 +1,54 @@ +//! Memory Protection Keys (MPK) implementation for use in striped memory +//! allocation. +//! +//! MPK is an x86 feature available on relatively recent versions of Intel and +//! AMD CPUs. In Linux, this feature is named `pku` (protection keys userspace) +//! and consists of three new system calls: `pkey_alloc`, `pkey_free`, and +//! `pkey_mprotect` (see the [Linux documentation]). This crate provides an +//! abstraction, [`ProtectionKey`], that the [pooling allocator] applies to +//! contiguous memory allocations, allowing it to avoid guard pages in some +//! cases and more efficiently use memory. This technique was first presented in +//! a 2022 paper: [Segue and ColorGuard: Optimizing SFI Performance and +//! Scalability on Modern x86][colorguard]. +//! +//! [pooling allocator]: crate::PoolingInstanceAllocator +//! [Linux documentation]: +//! https://www.kernel.org/doc/html/latest/core-api/protection-keys.html +//! [colorguard]: https://plas2022.github.io/files/pdf/SegueColorGuard.pdf +//! +//! On x86_64 Linux systems, this module implements the various parts necessary +//! to use MPK in Wasmtime: +//! - [`is_supported`] indicates whether the feature is available at runtime +//! - [`ProtectionKey`] provides access to the kernel-allocated protection keys +//! (see [`keys`]) +//! - [`allow`] sets the CPU state to prevent access to regions outside the +//! [`ProtectionMask`] +//! - the `sys` module bridges the gap to Linux's `pkey_*` system calls +//! - the `pkru` module controls the x86 `PKRU` register (and other CPU state) +//! +//! On any other kind of machine, this module exposes noop implementations of +//! the public interface. + +cfg_if::cfg_if! { + if #[cfg(all(target_arch = "x86_64", target_os = "linux", not(miri)))] { + mod enabled; + mod pkru; + mod sys; + pub use enabled::{allow, is_supported, keys, ProtectionKey, ProtectionMask}; + } else { + mod disabled; + pub use disabled::{allow, is_supported, keys, ProtectionKey, ProtectionMask}; + } +} + +/// Describe the tri-state configuration of memory protection keys (MPK). +#[derive(Clone, Copy, Debug)] +pub enum MpkEnabled { + /// Use MPK if supported by the current system; fall back to guard regions + /// otherwise. + Auto, + /// Use MPK or fail if not supported. + Enable, + /// Do not use MPK. + Disable, +} diff --git a/crates/runtime/src/mpk/pkru.rs b/crates/runtime/src/mpk/pkru.rs new file mode 100644 index 000000000000..99dee74fa0f7 --- /dev/null +++ b/crates/runtime/src/mpk/pkru.rs @@ -0,0 +1,88 @@ +//! Control access to the x86 `PKRU` register. +//! +//! As documented in the Intel Software Development Manual, vol 3a, section 2.7, +//! the 32 bits of the `PKRU` register laid out as follows (note the +//! little-endianness): +//! +//! ```text +//! ┌───┬───┬───┬───┬───┬───┐ +//! │...│AD2│WD1│AD1│WD0│AD0│ +//! └───┴───┴───┴───┴───┴───┘ +//! ``` +//! +//! - `ADn = 1` means "access disable key `n`"--no reads or writes allowed to +//! pages marked with key `n`. +//! - `WDn = 1` means "write disable key `n`"--only reads are prevented to pages +//! marked with key `n` +//! - it is unclear what it means to have both `ADn` and `WDn` set +//! +//! Note that this only handles the user-mode `PKRU` register; there is an +//! equivalent supervisor-mode MSR, `IA32_PKRS`. + +use core::arch::asm; + +/// This `PKRU` register mask allows access to any pages marked with any +/// key--in other words, reading and writing is permitted to all pages. +pub const ALLOW_ACCESS: u32 = 0; + +/// This `PKRU` register mask disables access to any page marked with any +/// key--in other words, no reading or writing to all pages. +pub const DISABLE_ACCESS: u32 = 0b11111111_11111111_11111111_11111111; + +/// Read the value of the `PKRU` register. +pub fn read() -> u32 { + // ECX must be 0 to prevent a general protection exception (#GP). + let ecx: u32 = 0; + let pkru: u32; + unsafe { + asm!("rdpkru", in("ecx") ecx, out("eax") pkru, out("edx") _, + options(nomem, nostack, preserves_flags)); + } + return pkru; +} + +/// Write a value to the `PKRU` register. +pub fn write(pkru: u32) { + // Both ECX and EDX must be 0 to prevent a general protection exception + // (#GP). + let ecx: u32 = 0; + let edx: u32 = 0; + unsafe { + asm!("wrpkru", in("eax") pkru, in("ecx") ecx, in("edx") edx, + options(nomem, nostack, preserves_flags)); + } +} + +/// Check the `ECX.PKU` flag (bit 3) of the `07h` `CPUID` leaf; see the +/// Intel Software Development Manual, vol 3a, section 2.7. +pub fn has_cpuid_bit_set() -> bool { + let result = unsafe { std::arch::x86_64::__cpuid(0x07) }; + (result.ecx & 0b100) != 0 +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mpk::enabled::skip_if_mpk_unavailable; + + #[test] + #[ignore = "cannot be run with other tests that munge the PKRU register"] + fn check_read() { + skip_if_mpk_unavailable!(); + assert_eq!(read(), DISABLE_ACCESS ^ 1); + // By default, the Linux kernel only allows a process to access key 0, + // the default kernel key. + } + + #[test] + fn check_roundtrip() { + skip_if_mpk_unavailable!(); + let pkru = read(); + // Allow access to pages marked with any key. + write(ALLOW_ACCESS); + assert_eq!(read(), ALLOW_ACCESS); + // Restore the original value. + write(pkru); + assert_eq!(read(), pkru); + } +} diff --git a/crates/runtime/src/mpk/sys.rs b/crates/runtime/src/mpk/sys.rs new file mode 100644 index 000000000000..fe68decce8c2 --- /dev/null +++ b/crates/runtime/src/mpk/sys.rs @@ -0,0 +1,116 @@ +//! Expose the `pkey_*` Linux system calls. See the kernel documentation for +//! more information: +//! - [`pkeys`] overview +//! - [`pkey_alloc`] (with `pkey_free`) +//! - [`pkey_mprotect`] +//! - `pkey_set` is implemented directly in assembly. +//! +//! [`pkey_alloc`]: https://man7.org/linux/man-pages/man2/pkey_alloc.2.html +//! [`pkey_mprotect`]: https://man7.org/linux/man-pages/man2/pkey_mprotect.2.html +//! [`pkeys`]: https://man7.org/linux/man-pages/man7/pkeys.7.html + +use crate::page_size; +use anyhow::Result; +use std::io::Error; + +/// Protection mask allowing reads of pkey-protected memory (see `prot` in +/// [`pkey_mprotect`]). +pub const PROT_READ: u32 = libc::PROT_READ as u32; // == 0b0001. + +/// Protection mask allowing writes of pkey-protected memory (see `prot` in +/// [`pkey_mprotect`]). +pub const PROT_WRITE: u32 = libc::PROT_WRITE as u32; // == 0b0010; + +/// Allocate a new protection key in the Linux kernel ([docs]); returns the +/// key ID. +/// +/// [docs]: https://man7.org/linux/man-pages/man2/pkey_alloc.2.html +/// +/// Each process has its own separate pkey index; e.g., if process `m` +/// allocates key 1, process `n` can as well. +pub fn pkey_alloc(flags: u32, access_rights: u32) -> Result { + assert_eq!(flags, 0); // reserved for future use--must be 0. + let result = unsafe { libc::syscall(libc::SYS_pkey_alloc, flags, access_rights) }; + if result >= 0 { + Ok(result + .try_into() + .expect("only pkey IDs between 0 and 15 are expected")) + } else { + debug_assert_eq!(result, -1); // only this error result is expected. + Err(Error::last_os_error().into()) + } +} + +/// Free a kernel protection key ([docs]). +/// +/// [docs]: https://man7.org/linux/man-pages/man2/pkey_alloc.2.html +#[allow(dead_code)] +pub fn pkey_free(key: u32) -> Result<()> { + let result = unsafe { libc::syscall(libc::SYS_pkey_free, key) }; + if result == 0 { + Ok(()) + } else { + debug_assert_eq!(result, -1); // only this error result is expected. + Err(Error::last_os_error().into()) + } +} + +/// Change the access protections for a page-aligned memory region ([docs]). +/// +/// [docs]: https://man7.org/linux/man-pages/man2/pkey_mprotect.2.html +pub fn pkey_mprotect(addr: usize, len: usize, prot: u32, key: u32) -> Result<()> { + let page_size = page_size(); + if addr % page_size != 0 { + log::warn!( + "memory must be page-aligned for MPK (addr = {addr:#x}, page size = {page_size}" + ); + } + let result = unsafe { libc::syscall(libc::SYS_pkey_mprotect, addr, len, prot, key) }; + if result == 0 { + Ok(()) + } else { + debug_assert_eq!(result, -1); // only this error result is expected. + Err(Error::last_os_error().into()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[ignore = "cannot be run when keys() has already allocated all keys"] + #[test] + fn check_allocate_and_free() { + let key = pkey_alloc(0, 0).unwrap(); + assert_eq!(key, 1); + // It may seem strange to assert the key ID here, but we already + // make some assumptions: + // 1. we are running on Linux with `pku` enabled + // 2. Linux will allocate key 0 for itself + // 3. we are running this test in non-MPK mode and no one else is + // using pkeys + // If these assumptions are incorrect, this test can be removed. + pkey_free(key).unwrap() + } + + #[test] + fn check_invalid_free() { + let result = pkey_free(42); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Invalid argument (os error 22)" + ); + } + + #[test] + #[should_panic] + fn check_invalid_alloc_flags() { + let _ = pkey_alloc(42, 0); + } + + #[test] + fn check_invalid_alloc_rights() { + assert!(pkey_alloc(0, 42).is_err()); + } +} diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 2f657e462cd6..1fdec89379a7 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -14,9 +14,10 @@ use wasmparser::WasmFeatures; use wasmtime_cache::CacheConfig; use wasmtime_environ::Tunables; use wasmtime_jit::profiling::{self, ProfilingAgent}; -use wasmtime_runtime::{InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator}; +use wasmtime_runtime::{mpk, InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator}; pub use wasmtime_environ::CacheStore; +pub use wasmtime_runtime::MpkEnabled; /// Represents the module instance allocation strategy to use. #[derive(Clone)] @@ -1183,6 +1184,10 @@ impl Config { /// always be static memories, they are never dynamic. This setting /// configures the size of linear memory to reserve for each memory in the /// pooling allocator. + /// + /// Note that the pooling allocator can reduce the amount of memory needed + /// for pooling allocation by using memory protection; see + /// `PoolingAllocatorConfig::memory_protection_keys` for details. pub fn static_memory_maximum_size(&mut self, max_size: u64) -> &mut Self { let max_pages = max_size / u64::from(wasmtime_environ::WASM_PAGE_SIZE); self.tunables.static_memory_bound = max_pages; @@ -1195,7 +1200,7 @@ impl Config { /// linear memories created within this `Config`. This means that all /// memories will be allocated up-front and will never move. Additionally /// this means that all memories are synthetically limited by the - /// [`Config::static_memory_maximum_size`] option, irregardless of what the + /// [`Config::static_memory_maximum_size`] option, regardless of what the /// actual maximum size is on the memory's original type. /// /// For the difference between static and dynamic memories, see the @@ -1237,7 +1242,7 @@ impl Config { /// immediate offsets will generate bounds checks based on how big the guard /// page is. /// - /// For 32-bit memories a 4GB static memory is required to even start + /// For 32-bit wasm memories a 4GB static memory is required to even start /// removing bounds checks. A 4GB guard size will guarantee that the module /// has zero bounds checks for memory accesses. A 2GB guard size will /// eliminate all bounds checks with an immediate offset less than 2GB. A @@ -2303,6 +2308,48 @@ impl PoolingAllocationConfig { self.config.limits.memory_pages = pages; self } + + /// Configures whether memory protection keys (MPK) should be used for more + /// efficient layout of pool-allocated memories. + /// + /// When using the pooling allocator (see [`Config::allocation_strategy`], + /// [`InstanceAllocationStrategy::Pooling`]), memory protection keys can + /// reduce the total amount of allocated virtual memory by eliminating guard + /// regions between WebAssembly memories in the pool. It does so by + /// "coloring" memory regions with different memory keys and setting which + /// regions are accessible each time executions switches from host to guest + /// (or vice versa). + /// + /// MPK is only available on Linux (called `pku` there) and recent x86 + /// systems; we check for MPK support at runtime by examining the `CPUID` + /// register. This configuration setting can be in three states: + /// + /// - `auto`: if MPK support is available the guard regions are removed; if + /// not, the guard regions remain + /// - `enable`: use MPK to eliminate guard regions; fail if MPK is not + /// supported + /// - `disable`: never use MPK + /// + /// By default this value is `disabled`, but may become `auto` in future releases. + /// + /// __WARNING__: this configuration options is still experimental--use at + /// your own risk! MPK uses kernel and CPU features to protect memory + /// regions; you may observe segmentation faults if anything is + /// misconfigured. + pub fn memory_protection_keys(&mut self, enable: MpkEnabled) -> &mut Self { + self.config.memory_protection_keys = enable; + self + } + + /// Check if memory protection keys (MPK) are available on the current host. + /// + /// This is a convenience method for determining MPK availability using the + /// same method that [`MpkEnabled::Auto`] does. See + /// [`PoolingAllocationConfig::memory_protection_keys`] for more + /// information. + pub fn are_memory_protection_keys_available(&self) -> bool { + mpk::is_supported() + } } pub(crate) fn probestack_supported(arch: Architecture) -> bool { diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index 0adb96c8d2ed..af070727af1b 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -284,6 +284,7 @@ impl Instance { host_state: Box::new(Instance(instance_to_be)), store: StorePtr::new(store.traitobj()), wmemcheck: store.engine().config().wmemcheck, + pkey: store.get_pkey(), })?; // The instance still has lots of setup, for example diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index d6f2bc574475..48436c04f639 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -96,9 +96,9 @@ use std::sync::atomic::AtomicU64; use std::sync::Arc; use std::task::{Context, Poll}; use wasmtime_runtime::{ - ExportGlobal, InstanceAllocationRequest, InstanceAllocator, InstanceHandle, ModuleInfo, - OnDemandInstanceAllocator, SignalHandler, StoreBox, StorePtr, VMContext, VMExternRef, - VMExternRefActivationsTable, VMFuncRef, VMRuntimeLimits, WasmFault, + mpk::ProtectionKey, ExportGlobal, InstanceAllocationRequest, InstanceAllocator, InstanceHandle, + ModuleInfo, OnDemandInstanceAllocator, SignalHandler, StoreBox, StorePtr, VMContext, + VMExternRef, VMExternRefActivationsTable, VMFuncRef, VMRuntimeLimits, WasmFault, }; mod context; @@ -343,6 +343,11 @@ pub struct StoreOpaque { /// `store_data` above, where the function pointers are stored. rooted_host_funcs: ManuallyDrop>>, + /// Keep track of what protection key is being used during allocation so + /// that the right memory pages can be enabled when entering WebAssembly + /// guest code. + pkey: Option, + /// Runtime state for components used in the handling of resources, borrow, /// and calls. These also interact with the `ResourceAny` type and its /// internal representation. @@ -473,6 +478,7 @@ impl Store { /// tables created to 10,000. This can be overridden with the /// [`Store::limiter`] configuration method. pub fn new(engine: &Engine, data: T) -> Self { + let pkey = engine.allocator().next_available_pkey(); let mut inner = Box::new(StoreInner { inner: StoreOpaque { _marker: marker::PhantomPinned, @@ -504,6 +510,7 @@ impl Store { hostcall_val_storage: Vec::new(), wasm_val_raw_storage: Vec::new(), rooted_host_funcs: ManuallyDrop::new(Vec::new()), + pkey, #[cfg(feature = "component-model")] component_host_table: Default::default(), #[cfg(feature = "component-model")] @@ -537,6 +544,7 @@ impl Store { store: StorePtr::empty(), runtime_info: &shim, wmemcheck: engine.config().wmemcheck, + pkey: None, }) .expect("failed to allocate default callee") }; @@ -1164,6 +1172,16 @@ impl StoreInner { } pub fn call_hook(&mut self, s: CallHook) -> Result<()> { + if let Some(pkey) = &self.inner.pkey { + let allocator = self.engine().allocator(); + match s { + CallHook::CallingWasm | CallHook::ReturningFromHost => { + allocator.restrict_to_pkey(*pkey) + } + CallHook::ReturningFromWasm | CallHook::CallingHost => allocator.allow_all_pkeys(), + } + } + match &mut self.call_hook { Some(CallHookInner::Sync(hook)) => hook(&mut self.data, s), @@ -1669,6 +1687,12 @@ at https://bytecodealliance.org/security. std::process::abort(); } + /// Retrieve the store's protection key. + #[inline] + pub(crate) fn get_pkey(&self) -> Option { + self.pkey.clone() + } + #[inline] #[cfg(feature = "component-model")] pub(crate) fn component_calls_and_host_table( diff --git a/crates/wasmtime/src/trampoline.rs b/crates/wasmtime/src/trampoline.rs index 3822f6027777..7f2499fb3ef2 100644 --- a/crates/wasmtime/src/trampoline.rs +++ b/crates/wasmtime/src/trampoline.rs @@ -48,6 +48,7 @@ fn create_handle( store: StorePtr::new(store.traitobj()), runtime_info, wmemcheck: false, + pkey: None, })?; Ok(store.add_dummy_instance(handle)) diff --git a/crates/wasmtime/src/trampoline/memory.rs b/crates/wasmtime/src/trampoline/memory.rs index bc1423991783..72c7eb891f81 100644 --- a/crates/wasmtime/src/trampoline/memory.rs +++ b/crates/wasmtime/src/trampoline/memory.rs @@ -10,6 +10,7 @@ use wasmtime_environ::{ DefinedMemoryIndex, DefinedTableIndex, EntityIndex, HostPtr, MemoryPlan, MemoryStyle, Module, VMOffsets, WASM_PAGE_SIZE, }; +use wasmtime_runtime::mpk::ProtectionKey; use wasmtime_runtime::{ CompiledModuleId, Imports, InstanceAllocationRequest, InstanceAllocator, InstanceAllocatorImpl, Memory, MemoryAllocationIndex, MemoryImage, OnDemandInstanceAllocator, RuntimeLinearMemory, @@ -63,6 +64,7 @@ pub fn create_memory( store: StorePtr::new(store.traitobj()), runtime_info, wmemcheck: false, + pkey: None, }; unsafe { @@ -252,4 +254,16 @@ unsafe impl InstanceAllocatorImpl for SingleMemoryInstance<'_> { fn purge_module(&self, _: CompiledModuleId) { unreachable!() } + + fn next_available_pkey(&self) -> Option { + unreachable!() + } + + fn restrict_to_pkey(&self, _: ProtectionKey) { + unreachable!() + } + + fn allow_all_pkeys(&self) { + unreachable!() + } } diff --git a/tests/all/pooling_allocator.rs b/tests/all/pooling_allocator.rs index f6fcc2a541c0..dc23686de885 100644 --- a/tests/all/pooling_allocator.rs +++ b/tests/all/pooling_allocator.rs @@ -1169,7 +1169,7 @@ fn total_memories_limit() -> Result<()> { Err(e) => assert_eq!( e.to_string(), format!( - "maximum concurrent memory limit of {} reached", + "maximum concurrent memory limit of {} reached for stripe 0", TOTAL_MEMORIES ), ), From 998299267f22eae612a846604daae86f17451c1f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Sep 2023 09:53:01 -0500 Subject: [PATCH 018/199] x64: Fix false dependencies in int-to-float conversions (#7098) * x64: Fix false dependencies in int-to-float conversions This commit is a result of the investigation on #7085. The int-to-float conversion instructions used right now on the x64 backend will implicitly source the upper bits of the result from a different register. This implicitly creates a dependency on further consumers using the conversion result on whatever previously defined the upper bits, even though they aren't used. This false dependency is the primary reason for the slowdown witnessed in #7085. The fix chosen in this commit is to model the int-to-float instructions with a new shape of instruction instead of the previous `GprToXmm{,Vex}`. This previous shape was modeled as single-input and single-output, but this does not reflect the actual nature of the `cvtsi2s{s,d}` instructions. Instead these now use `CvtIntToFloat{,Vex}` which have two source operands and one destination operand, modeling how the upper bits of a different register are used. In lowerings using this instruction the upper bits to preserver are always sourced from a zero'd out register to force breaking dependencies between instructions. Closes #7085 * Remove now dead code * Remove outdated test Golden test output covers this test case anyway nowadays * Review comments * Fix emit tests --- cranelift/codegen/src/isa/x64/inst.isle | 49 +++++-- cranelift/codegen/src/isa/x64/inst/emit.rs | 93 ++++++++++--- .../codegen/src/isa/x64/inst/emit_tests.rs | 20 --- cranelift/codegen/src/isa/x64/inst/mod.rs | 48 ++++++- cranelift/codegen/src/isa/x64/lower.isle | 33 +++-- .../filetests/filetests/isa/x64/fastcall.clif | 26 +++- .../filetests/filetests/isa/x64/fcvt-avx.clif | 28 ++-- .../filetests/filetests/isa/x64/fcvt.clif | 127 +++++++++++------- 8 files changed, 306 insertions(+), 118 deletions(-) diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle index e9fb7779cadf..a3c7bb654c1c 100644 --- a/cranelift/codegen/src/isa/x64/inst.isle +++ b/cranelift/codegen/src/isa/x64/inst.isle @@ -437,6 +437,23 @@ (dst WritableXmm) (src_size OperandSize)) + ;; Conversion from signed integers to floats, the `{v,}`cvtsi2s{s,d}` + ;; instructions. + ;; + ;; Note that this is special in that `src1` is an xmm/float register + ;; while `src2` is a general purpose register as this is converting an + ;; integer in a gpr to an equivalent float in an xmm reg. + (CvtIntToFloat (op SseOpcode) + (src1 Xmm) + (src2 GprMem) + (dst WritableXmm) + (src2_size OperandSize)) + (CvtIntToFloatVex (op AvxOpcode) + (src1 Xmm) + (src2 GprMem) + (dst WritableXmm) + (src2_size OperandSize)) + ;; Converts an unsigned int64 to a float32/float64. (CvtUint64ToFloatSeq (dst_size OperandSize) ;; 4 or 8 (src Gpr) @@ -2095,6 +2112,18 @@ (_ Unit (emit (MInst.UnaryRmRImmVex size op src dst imm)))) dst)) +(decl cvt_int_to_float (SseOpcode Xmm GprMem OperandSize) Xmm) +(rule (cvt_int_to_float op src1 src2 size) + (let ((dst WritableXmm (temp_writable_xmm)) + (_ Unit (emit (MInst.CvtIntToFloat op src1 src2 dst size)))) + dst)) + +(decl cvt_int_to_float_vex (AvxOpcode Xmm GprMem OperandSize) Xmm) +(rule (cvt_int_to_float_vex op src1 src2 size) + (let ((dst WritableXmm (temp_writable_xmm)) + (_ Unit (emit (MInst.CvtIntToFloatVex op src1 src2 dst size)))) + dst)) + (decl cvt_u64_to_float_seq (Type Gpr) Xmm) (rule (cvt_u64_to_float_seq ty src) (let ((size OperandSize (raw_operand_size_of_type ty)) @@ -4351,20 +4380,20 @@ (xmm_unary_rm_r_vex (AvxOpcode.Vcvtdq2pd) x)) ;; Helper for creating `cvtsi2ss` instructions. -(decl x64_cvtsi2ss (Type GprMem) Xmm) -(rule (x64_cvtsi2ss ty x) - (gpr_to_xmm (SseOpcode.Cvtsi2ss) x (raw_operand_size_of_type ty))) -(rule 1 (x64_cvtsi2ss ty x) +(decl x64_cvtsi2ss (Type Xmm GprMem) Xmm) +(rule (x64_cvtsi2ss ty x y) + (cvt_int_to_float (SseOpcode.Cvtsi2ss) x y (raw_operand_size_of_type ty))) +(rule 1 (x64_cvtsi2ss ty x y) (if-let $true (use_avx)) - (gpr_to_xmm_vex (AvxOpcode.Vcvtsi2ss) x (raw_operand_size_of_type ty))) + (cvt_int_to_float_vex (AvxOpcode.Vcvtsi2ss) x y (raw_operand_size_of_type ty))) ;; Helper for creating `cvtsi2sd` instructions. -(decl x64_cvtsi2sd (Type GprMem) Xmm) -(rule (x64_cvtsi2sd ty x) - (gpr_to_xmm (SseOpcode.Cvtsi2sd) x (raw_operand_size_of_type ty))) -(rule 1 (x64_cvtsi2sd ty x) +(decl x64_cvtsi2sd (Type Xmm GprMem) Xmm) +(rule (x64_cvtsi2sd ty x y) + (cvt_int_to_float (SseOpcode.Cvtsi2sd) x y (raw_operand_size_of_type ty))) +(rule 1 (x64_cvtsi2sd ty x y) (if-let $true (use_avx)) - (gpr_to_xmm_vex (AvxOpcode.Vcvtsi2sd) x (raw_operand_size_of_type ty))) + (cvt_int_to_float_vex (AvxOpcode.Vcvtsi2sd) x y (raw_operand_size_of_type ty))) ;; Helper for creating `cvttps2dq` instructions. (decl x64_cvttps2dq (XmmMem) Xmm) diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index 0be0db116c2e..6bd65154eb23 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -32,8 +32,14 @@ fn emit_signed_cvt( } else { SseOpcode::Cvtsi2ss }; - let inst = Inst::gpr_to_xmm(op, RegMem::reg(src), OperandSize::Size64, dst); - inst.emit(&[], sink, info, state); + Inst::CvtIntToFloat { + op, + dst: Writable::from_reg(Xmm::new(dst.to_reg()).unwrap()), + src1: Xmm::new(dst.to_reg()).unwrap(), + src2: GprMem::new(RegMem::reg(src)).unwrap(), + src2_size: OperandSize::Size64, + } + .emit(&[], sink, info, state); } /// Emits a one way conditional jump if CC is set (true). @@ -2872,30 +2878,21 @@ pub(crate) fn emit( let (prefix, map, opcode) = match op { // vmovd/vmovq are differentiated by `w` AvxOpcode::Vmovd | AvxOpcode::Vmovq => (LegacyPrefixes::_66, OpcodeMap::_0F, 0x6E), - AvxOpcode::Vcvtsi2ss => (LegacyPrefixes::_F3, OpcodeMap::_0F, 0x2A), - AvxOpcode::Vcvtsi2sd => (LegacyPrefixes::_F2, OpcodeMap::_0F, 0x2A), _ => unimplemented!("Opcode {:?} not implemented", op), }; let w = match src_size { OperandSize::Size64 => true, _ => false, }; - let mut insn = VexInstruction::new() + VexInstruction::new() .length(VexVectorLength::V128) .w(w) .prefix(prefix) .map(map) .opcode(opcode) .rm(src) - .reg(dst.to_real_reg().unwrap().hw_enc()); - // These opcodes technically take a second operand which is the - // upper bits to preserve during the float conversion. We don't - // actually use this in this backend right now so reuse the - // destination register. This at least matches what LLVM does. - if let AvxOpcode::Vcvtsi2ss | AvxOpcode::Vcvtsi2sd = op { - insn = insn.vvvv(dst.to_real_reg().unwrap().hw_enc()); - } - insn.encode(sink); + .reg(dst.to_real_reg().unwrap().hw_enc()) + .encode(sink); } Inst::XmmRmREvex { @@ -3200,8 +3197,6 @@ pub(crate) fn emit( // Movd and movq use the same opcode; the presence of the REX prefix (set below) // actually determines which is used. SseOpcode::Movd | SseOpcode::Movq => (LegacyPrefixes::_66, 0x0F6E), - SseOpcode::Cvtsi2ss => (LegacyPrefixes::_F3, 0x0F2A), - SseOpcode::Cvtsi2sd => (LegacyPrefixes::_F2, 0x0F2A), _ => panic!("unexpected opcode {:?}", op), }; let rex = RexFlags::from(*src_size); @@ -3239,6 +3234,72 @@ pub(crate) fn emit( } } + Inst::CvtIntToFloat { + op, + src1, + src2, + dst, + src2_size, + } => { + let src1 = allocs.next(src1.to_reg()); + let dst = allocs.next(dst.to_reg().to_reg()); + assert_eq!(src1, dst); + let src2 = src2.clone().to_reg_mem().with_allocs(allocs); + + let (prefix, opcode) = match op { + SseOpcode::Cvtsi2ss => (LegacyPrefixes::_F3, 0x0F2A), + SseOpcode::Cvtsi2sd => (LegacyPrefixes::_F2, 0x0F2A), + _ => panic!("unexpected opcode {:?}", op), + }; + let rex = RexFlags::from(*src2_size); + match src2 { + RegMem::Reg { reg: src2 } => { + emit_std_reg_reg(sink, prefix, opcode, 2, dst, src2, rex); + } + RegMem::Mem { addr } => { + let addr = &addr.finalize(state, sink); + emit_std_reg_mem(sink, prefix, opcode, 2, dst, addr, rex, 0); + } + } + } + + Inst::CvtIntToFloatVex { + op, + src1, + src2, + dst, + src2_size, + } => { + let dst = allocs.next(dst.to_reg().to_reg()); + let src1 = allocs.next(src1.to_reg()); + let src2 = match src2.clone().to_reg_mem().with_allocs(allocs) { + RegMem::Reg { reg } => { + RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into()) + } + RegMem::Mem { addr } => RegisterOrAmode::Amode(addr.finalize(state, sink)), + }; + + let (prefix, map, opcode) = match op { + AvxOpcode::Vcvtsi2ss => (LegacyPrefixes::_F3, OpcodeMap::_0F, 0x2A), + AvxOpcode::Vcvtsi2sd => (LegacyPrefixes::_F2, OpcodeMap::_0F, 0x2A), + _ => unimplemented!("Opcode {:?} not implemented", op), + }; + let w = match src2_size { + OperandSize::Size64 => true, + _ => false, + }; + VexInstruction::new() + .length(VexVectorLength::V128) + .w(w) + .prefix(prefix) + .map(map) + .opcode(opcode) + .rm(src2) + .reg(dst.to_real_reg().unwrap().hw_enc()) + .vvvv(src1.to_real_reg().unwrap().hw_enc()) + .encode(sink); + } + Inst::CvtUint64ToFloatSeq { dst_size, src, diff --git a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs index d0050b8d4499..147f61deb832 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs @@ -5051,26 +5051,6 @@ fn test_x64_emit() { "664C0F6EFF", "movq %rdi, %xmm15", )); - insns.push(( - Inst::gpr_to_xmm( - SseOpcode::Cvtsi2ss, - RegMem::reg(rdi), - OperandSize::Size32, - w_xmm15, - ), - "F3440F2AFF", - "cvtsi2ss %edi, %xmm15", - )); - insns.push(( - Inst::gpr_to_xmm( - SseOpcode::Cvtsi2sd, - RegMem::reg(rsi), - OperandSize::Size64, - w_xmm1, - ), - "F2480F2ACE", - "cvtsi2sd %rsi, %xmm1", - )); // ======================================================== // XmmRmi diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index ca72a434bb87..2a321c7f44f6 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -178,7 +178,8 @@ impl Inst { | Inst::XmmToGprImm { op, .. } | Inst::XmmUnaryRmRImm { op, .. } | Inst::XmmUnaryRmRUnaligned { op, .. } - | Inst::XmmUnaryRmR { op, .. } => smallvec![op.available_from()], + | Inst::XmmUnaryRmR { op, .. } + | Inst::CvtIntToFloat { op, .. } => smallvec![op.available_from()], Inst::XmmUnaryRmREvex { op, .. } | Inst::XmmRmREvex { op, .. } @@ -196,7 +197,8 @@ impl Inst { | Inst::XmmMovRMImmVex { op, .. } | Inst::XmmToGprImmVex { op, .. } | Inst::XmmToGprVex { op, .. } - | Inst::GprToXmmVex { op, .. } => op.available_from(), + | Inst::GprToXmmVex { op, .. } + | Inst::CvtIntToFloatVex { op, .. } => op.available_from(), } } } @@ -1296,6 +1298,34 @@ impl PrettyPrint for Inst { format!("{op} {src}, {dst}") } + Inst::CvtIntToFloat { + op, + src1, + src2, + dst, + src2_size, + } => { + let src1 = pretty_print_reg(src1.to_reg(), 8, allocs); + let dst = pretty_print_reg(*dst.to_reg(), 8, allocs); + let src2 = src2.pretty_print(src2_size.to_bytes(), allocs); + let op = ljustify(op.to_string()); + format!("{op} {src1}, {src2}, {dst}") + } + + Inst::CvtIntToFloatVex { + op, + src1, + src2, + dst, + src2_size, + } => { + let dst = pretty_print_reg(*dst.to_reg(), 8, allocs); + let src1 = pretty_print_reg(src1.to_reg(), 8, allocs); + let src2 = src2.pretty_print(src2_size.to_bytes(), allocs); + let op = ljustify(op.to_string()); + format!("{op} {src1}, {src2}, {dst}") + } + Inst::CvtUint64ToFloatSeq { src, dst, @@ -2164,6 +2194,20 @@ fn x64_get_operands VReg>(inst: &Inst, collector: &mut OperandCol collector.reg_def(dst.to_writable_reg()); src.get_operands(collector); } + Inst::CvtIntToFloat { + src1, src2, dst, .. + } => { + collector.reg_use(src1.to_reg()); + collector.reg_reuse_def(dst.to_writable_reg(), 0); + src2.get_operands(collector); + } + Inst::CvtIntToFloatVex { + src1, src2, dst, .. + } => { + collector.reg_def(dst.to_writable_reg()); + collector.reg_use(src1.to_reg()); + src2.get_operands(collector); + } Inst::CvtUint64ToFloatSeq { src, dst, diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle index 69e9f229f15c..9691cec469ef 100644 --- a/cranelift/codegen/src/isa/x64/lower.isle +++ b/cranelift/codegen/src/isa/x64/lower.isle @@ -3336,23 +3336,40 @@ ;; Rules for `fcvt_from_sint` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Note that the `cvtsi2s{s,d}` instruction is not just an int-to-float +;; conversion instruction in isolation, it also takes the upper 64-bits of an +;; xmm register and places it into the destination. We don't actually want that +;; to happen as it could accidentally create a false dependency with a +;; previous instruction defining the register's upper 64-bits. See #7085 for +;; an instance of this. +;; +;; This means that the first operand to all of the int-to-float conversions here +;; are `(xmm_zero)` operands which is a guaranteed zero register that has no +;; dependencies on other instructions. +;; +;; Ideally this would be lifted out to a higher level to get deduplicated +;; between consecutive int-to-float operations but that's not easy +;; to do at this time. One possibility would be a mid-end rule which rewrites +;; `fcvt_from_sint` to an x86-specific opcode using a zero constant which would +;; be subject to normal LICM, but that's not feasible today. + (rule 2 (lower (has_type $F32 (fcvt_from_sint a @ (value_type $I8)))) - (x64_cvtsi2ss $I32 (extend_to_gpr a $I32 (ExtendKind.Sign)))) + (x64_cvtsi2ss $I32 (xmm_zero $F32X4) (extend_to_gpr a $I32 (ExtendKind.Sign)))) (rule 2 (lower (has_type $F32 (fcvt_from_sint a @ (value_type $I16)))) - (x64_cvtsi2ss $I32 (extend_to_gpr a $I32 (ExtendKind.Sign)))) + (x64_cvtsi2ss $I32 (xmm_zero $F32X4) (extend_to_gpr a $I32 (ExtendKind.Sign)))) (rule 1 (lower (has_type $F32 (fcvt_from_sint a @ (value_type (ty_int (fits_in_64 ty)))))) - (x64_cvtsi2ss ty a)) + (x64_cvtsi2ss ty (xmm_zero $F32X4) a)) (rule 2 (lower (has_type $F64 (fcvt_from_sint a @ (value_type $I8)))) - (x64_cvtsi2sd $I32 (extend_to_gpr a $I32 (ExtendKind.Sign)))) + (x64_cvtsi2sd $I32 (xmm_zero $F64X2) (extend_to_gpr a $I32 (ExtendKind.Sign)))) (rule 2 (lower (has_type $F64 (fcvt_from_sint a @ (value_type $I16)))) - (x64_cvtsi2sd $I32 (extend_to_gpr a $I32 (ExtendKind.Sign)))) + (x64_cvtsi2sd $I32 (xmm_zero $F64X2) (extend_to_gpr a $I32 (ExtendKind.Sign)))) (rule 1 (lower (has_type $F64 (fcvt_from_sint a @ (value_type (ty_int (fits_in_64 ty)))))) - (x64_cvtsi2sd ty a)) + (x64_cvtsi2sd ty (xmm_zero $F64X2) a)) (rule 0 (lower (fcvt_from_sint a @ (value_type $I32X4))) (x64_cvtdq2ps a)) @@ -3363,10 +3380,10 @@ ;; Rules for `fcvt_from_uint` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule 1 (lower (has_type $F32 (fcvt_from_uint val @ (value_type (fits_in_32 (ty_int ty)))))) - (x64_cvtsi2ss $I64 (extend_to_gpr val $I64 (ExtendKind.Zero)))) + (x64_cvtsi2ss $I64 (xmm_zero $F32X4) (extend_to_gpr val $I64 (ExtendKind.Zero)))) (rule 1 (lower (has_type $F64 (fcvt_from_uint val @ (value_type (fits_in_32 (ty_int ty)))))) - (x64_cvtsi2sd $I64 (extend_to_gpr val $I64 (ExtendKind.Zero)))) + (x64_cvtsi2sd $I64 (xmm_zero $F64X2) (extend_to_gpr val $I64 (ExtendKind.Zero)))) (rule (lower (has_type ty (fcvt_from_uint val @ (value_type $I64)))) (cvt_u64_to_float_seq ty val)) diff --git a/cranelift/filetests/filetests/isa/x64/fastcall.clif b/cranelift/filetests/filetests/isa/x64/fastcall.clif index 86e865b61493..d83c9fbee0f0 100644 --- a/cranelift/filetests/filetests/isa/x64/fastcall.clif +++ b/cranelift/filetests/filetests/isa/x64/fastcall.clif @@ -241,20 +241,27 @@ block0(v0: i64): ; pushq %rbp ; unwind PushFrameRegs { offset_upward_to_caller_sp: 16 } ; movq %rsp, %rbp -; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 } +; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 16 } +; subq %rsp, $16, %rsp +; movq %rdi, 0(%rsp) +; unwind SaveReg { clobber_offset: 0, reg: p7i } ; block0: -; cvtsi2sd %rcx, %xmm3 +; uninit %xmm3 +; xorpd %xmm3, %xmm3, %xmm3 +; cvtsi2sd %xmm3, %rcx, %xmm3 ; subq %rsp, $48, %rsp ; virtual_sp_offset_adjust 48 ; movq %rcx, 32(%rsp) ; movq %rcx, 40(%rsp) ; movq %rcx, %rdx -; load_ext_name %g+0, %r11 +; load_ext_name %g+0, %rdi ; movq %rdx, %rcx ; movdqa %xmm3, %xmm2 -; call *%r11 +; call *%rdi ; addq %rsp, $48, %rsp ; virtual_sp_offset_adjust -48 +; movq 0(%rsp), %rdi +; addq %rsp, $16, %rsp ; movq %rbp, %rsp ; popq %rbp ; ret @@ -263,17 +270,22 @@ block0(v0: i64): ; block0: ; offset 0x0 ; pushq %rbp ; movq %rsp, %rbp -; block1: ; offset 0x4 +; subq $0x10, %rsp +; movq %rdi, (%rsp) +; block1: ; offset 0xc +; xorpd %xmm3, %xmm3 ; cvtsi2sdq %rcx, %xmm3 ; subq $0x30, %rsp ; movq %rcx, 0x20(%rsp) ; movq %rcx, 0x28(%rsp) ; movq %rcx, %rdx -; movabsq $0, %r11 ; reloc_external Abs8 %g 0 +; movabsq $0, %rdi ; reloc_external Abs8 %g 0 ; movq %rdx, %rcx ; movdqa %xmm3, %xmm2 -; callq *%r11 +; callq *%rdi ; addq $0x30, %rsp +; movq (%rsp), %rdi +; addq $0x10, %rsp ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/fcvt-avx.clif b/cranelift/filetests/filetests/isa/x64/fcvt-avx.clif index 98e47d2b79f8..d46212e18597 100644 --- a/cranelift/filetests/filetests/isa/x64/fcvt-avx.clif +++ b/cranelift/filetests/filetests/isa/x64/fcvt-avx.clif @@ -11,7 +11,9 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; vcvtsi2ss %edi, %xmm0 +; uninit %xmm2 +; vxorps %xmm2, %xmm2, %xmm4 +; vcvtsi2ss %xmm4, %edi, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -21,7 +23,8 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; vcvtsi2ssl %edi, %xmm0, %xmm0 +; vxorps %xmm2, %xmm2, %xmm4 +; vcvtsi2ssl %edi, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -36,7 +39,9 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; vcvtsi2ss %rdi, %xmm0 +; uninit %xmm2 +; vxorps %xmm2, %xmm2, %xmm4 +; vcvtsi2ss %xmm4, %rdi, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -46,7 +51,8 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; vcvtsi2ssq %rdi, %xmm0, %xmm0 +; vxorps %xmm2, %xmm2, %xmm4 +; vcvtsi2ssq %rdi, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -61,7 +67,9 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; vcvtsi2sd %edi, %xmm0 +; uninit %xmm2 +; vxorpd %xmm2, %xmm2, %xmm4 +; vcvtsi2sd %xmm4, %edi, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -71,7 +79,8 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; vcvtsi2sdl %edi, %xmm0, %xmm0 +; vxorpd %xmm2, %xmm2, %xmm4 +; vcvtsi2sdl %edi, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -86,7 +95,9 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; vcvtsi2sd %rdi, %xmm0 +; uninit %xmm2 +; vxorpd %xmm2, %xmm2, %xmm4 +; vcvtsi2sd %xmm4, %rdi, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -96,7 +107,8 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; vcvtsi2sdq %rdi, %xmm0, %xmm0 +; vxorpd %xmm2, %xmm2, %xmm4 +; vcvtsi2sdq %rdi, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/fcvt.clif b/cranelift/filetests/filetests/isa/x64/fcvt.clif index 0dfa8637e0cd..1a04881cf022 100644 --- a/cranelift/filetests/filetests/isa/x64/fcvt.clif +++ b/cranelift/filetests/filetests/isa/x64/fcvt.clif @@ -11,8 +11,10 @@ block0(v0: i8): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movsbl %dil, %eax -; cvtsi2ss %eax, %xmm0 +; uninit %xmm0 +; xorps %xmm0, %xmm0, %xmm0 +; movsbl %dil, %r9d +; cvtsi2ss %xmm0, %r9d, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -22,8 +24,9 @@ block0(v0: i8): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movsbl %dil, %eax -; cvtsi2ssl %eax, %xmm0 +; xorps %xmm0, %xmm0 +; movsbl %dil, %r9d +; cvtsi2ssl %r9d, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -38,8 +41,10 @@ block0(v0: i16): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movswl %di, %eax -; cvtsi2ss %eax, %xmm0 +; uninit %xmm0 +; xorps %xmm0, %xmm0, %xmm0 +; movswl %di, %r9d +; cvtsi2ss %xmm0, %r9d, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -49,8 +54,9 @@ block0(v0: i16): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movswl %di, %eax -; cvtsi2ssl %eax, %xmm0 +; xorps %xmm0, %xmm0 +; movswl %di, %r9d +; cvtsi2ssl %r9d, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -65,7 +71,9 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; cvtsi2ss %edi, %xmm0 +; uninit %xmm0 +; xorps %xmm0, %xmm0, %xmm0 +; cvtsi2ss %xmm0, %edi, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -75,6 +83,7 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 +; xorps %xmm0, %xmm0 ; cvtsi2ssl %edi, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -90,7 +99,9 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; cvtsi2ss %rdi, %xmm0 +; uninit %xmm0 +; xorps %xmm0, %xmm0, %xmm0 +; cvtsi2ss %xmm0, %rdi, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -100,6 +111,7 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 +; xorps %xmm0, %xmm0 ; cvtsi2ssq %rdi, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -115,8 +127,10 @@ block0(v0: i8): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movsbl %dil, %eax -; cvtsi2sd %eax, %xmm0 +; uninit %xmm0 +; xorpd %xmm0, %xmm0, %xmm0 +; movsbl %dil, %r9d +; cvtsi2sd %xmm0, %r9d, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -126,8 +140,9 @@ block0(v0: i8): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movsbl %dil, %eax -; cvtsi2sdl %eax, %xmm0 +; xorpd %xmm0, %xmm0 +; movsbl %dil, %r9d +; cvtsi2sdl %r9d, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -142,8 +157,10 @@ block0(v0: i16): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movswl %di, %eax -; cvtsi2sd %eax, %xmm0 +; uninit %xmm0 +; xorpd %xmm0, %xmm0, %xmm0 +; movswl %di, %r9d +; cvtsi2sd %xmm0, %r9d, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -153,8 +170,9 @@ block0(v0: i16): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movswl %di, %eax -; cvtsi2sdl %eax, %xmm0 +; xorpd %xmm0, %xmm0 +; movswl %di, %r9d +; cvtsi2sdl %r9d, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -169,7 +187,9 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; cvtsi2sd %edi, %xmm0 +; uninit %xmm0 +; xorpd %xmm0, %xmm0, %xmm0 +; cvtsi2sd %xmm0, %edi, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -179,6 +199,7 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 +; xorpd %xmm0, %xmm0 ; cvtsi2sdl %edi, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -194,7 +215,9 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; cvtsi2sd %rdi, %xmm0 +; uninit %xmm0 +; xorpd %xmm0, %xmm0, %xmm0 +; cvtsi2sd %xmm0, %rdi, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -204,6 +227,7 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 +; xorpd %xmm0, %xmm0 ; cvtsi2sdq %rdi, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -251,16 +275,22 @@ block0(v0: i8, v1: i16, v2: i32, v3: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movzbq %dil, %r9 -; cvtsi2ss %r9, %xmm0 -; movzwq %si, %r9 -; cvtsi2ss %r9, %xmm1 -; movl %edx, %r9d -; cvtsi2ss %r9, %xmm2 -; u64_to_f32_seq %rcx, %xmm6, %r9, %r10 -; addss %xmm0, %xmm1, %xmm0 -; addss %xmm0, %xmm2, %xmm0 +; uninit %xmm0 +; xorps %xmm0, %xmm0, %xmm0 +; movzbq %dil, %r8 +; cvtsi2ss %xmm0, %r8, %xmm0 +; uninit %xmm6 +; xorps %xmm6, %xmm6, %xmm6 +; movzwq %si, %r8 +; cvtsi2ss %xmm6, %r8, %xmm6 +; uninit %xmm7 +; xorps %xmm7, %xmm7, %xmm7 +; movl %edx, %r8d +; cvtsi2ss %xmm7, %r8, %xmm7 +; u64_to_f32_seq %rcx, %xmm4, %r8, %rdx ; addss %xmm0, %xmm6, %xmm0 +; addss %xmm0, %xmm7, %xmm0 +; addss %xmm0, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -270,26 +300,29 @@ block0(v0: i8, v1: i16, v2: i32, v3: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movzbq %dil, %r9 -; cvtsi2ssq %r9, %xmm0 -; movzwq %si, %r9 -; cvtsi2ssq %r9, %xmm1 -; movl %edx, %r9d -; cvtsi2ssq %r9, %xmm2 +; xorps %xmm0, %xmm0 +; movzbq %dil, %r8 +; cvtsi2ssq %r8, %xmm0 +; xorps %xmm6, %xmm6 +; movzwq %si, %r8 +; cvtsi2ssq %r8, %xmm6 +; xorps %xmm7, %xmm7 +; movl %edx, %r8d +; cvtsi2ssq %r8, %xmm7 ; cmpq $0, %rcx -; jl 0x32 -; cvtsi2ssq %rcx, %xmm6 -; jmp 0x4c -; movq %rcx, %r9 -; shrq $1, %r9 -; movq %rcx, %r10 -; andq $1, %r10 -; orq %r9, %r10 -; cvtsi2ssq %r10, %xmm6 -; addss %xmm6, %xmm6 -; addss %xmm1, %xmm0 -; addss %xmm2, %xmm0 +; jl 0x3b +; cvtsi2ssq %rcx, %xmm4 +; jmp 0x55 +; movq %rcx, %r8 +; shrq $1, %r8 +; movq %rcx, %rdx +; andq $1, %rdx +; orq %r8, %rdx +; cvtsi2ssq %rdx, %xmm4 +; addss %xmm4, %xmm4 ; addss %xmm6, %xmm0 +; addss %xmm7, %xmm0 +; addss %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq From 7f79a09d2f445e89f0082c417f53b8bad9276dfc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Sep 2023 11:34:37 -0500 Subject: [PATCH 019/199] Update wit-{parser,component} deps (#7101) Pull in bytecodealliance/wasm-tools#1221 --- Cargo.lock | 8 ++++---- Cargo.toml | 4 ++-- supply-chain/imports.lock | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3597329678f8..28ec4893c185 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4179,9 +4179,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0477d36188eeba8e8f2579220ec385086f188de99453c6689814d4dad6a4c22c" +checksum = "ee23614740bf871dac9856e3062c7a308506eb3f0a2759939ab8d0aa8436a1c0" dependencies = [ "anyhow", "bitflags 2.3.3", @@ -4197,9 +4197,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4419bc240aa8c421e44a0c180e33891d5556b8b2327f432265a2c382cca9d294" +checksum = "a39edca9abb16309def3843af73b58d47d243fe33a9ceee572446bcc57556b9a" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index 4ce888943c6d..05ef6d4718f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -216,8 +216,8 @@ wasmprinter = "0.2.67" wasm-encoder = "0.33.2" wasm-smith = "0.12.18" wasm-mutate = "0.2.35" -wit-parser = "0.11.2" -wit-component = "0.14.3" +wit-parser = "0.11.3" +wit-component = "0.14.4" # Non-Bytecode Alliance maintained dependencies: # -------------------------- diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index dabdf0fec634..80e95f2bf4e4 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -2000,6 +2000,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wit-component]] +version = "0.14.4" +when = "2023-09-27" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wit-parser]] version = "0.9.2" when = "2023-07-26" @@ -2035,6 +2042,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wit-parser]] +version = "0.11.3" +when = "2023-09-27" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[audits.embark-studios.wildcard-audits.spdx]] who = "Jake Shadle " criteria = "safe-to-deploy" From e9cd1318a167ff88dbcaa812bb4a7a7b6f044365 Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Thu, 28 Sep 2023 12:09:15 -0700 Subject: [PATCH 020/199] wasmtime: Misc optimizations (#7102) * Mark some frequently used methods as `inline` * Seed the preview2 context with the thread_rng, instead of using a syscall --- crates/jit/src/instantiate.rs | 2 ++ crates/runtime/src/mmap.rs | 1 + crates/runtime/src/mmap_vec.rs | 1 + crates/wasi/src/preview2/ctx.rs | 5 ++++- crates/wasmtime/src/signatures.rs | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index 2a3c7bd42619..d3a260deb563 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -509,6 +509,7 @@ impl CompiledModule { /// Returns the text section of the ELF image for this compiled module. /// /// This memory should have the read/execute permissions. + #[inline] pub fn text(&self) -> &[u8] { self.code_memory.text() } @@ -575,6 +576,7 @@ impl CompiledModule { /// /// These trampolines are used for native callers (e.g. `Func::wrap`) /// calling Wasm callees. + #[inline] pub fn native_to_wasm_trampoline(&self, index: DefinedFuncIndex) -> Option<&[u8]> { let loc = self.funcs[index].native_to_wasm_trampoline?; Some(&self.text()[loc.start as usize..][..loc.length as usize]) diff --git a/crates/runtime/src/mmap.rs b/crates/runtime/src/mmap.rs index 1c077890c35b..4f9f4c64d4e5 100644 --- a/crates/runtime/src/mmap.rs +++ b/crates/runtime/src/mmap.rs @@ -122,6 +122,7 @@ impl Mmap { /// # Panics /// /// Panics of the `range` provided is outside of the limits of this mmap. + #[inline] pub unsafe fn slice(&self, range: Range) -> &[u8] { assert!(range.start <= range.end); assert!(range.end <= self.len()); diff --git a/crates/runtime/src/mmap_vec.rs b/crates/runtime/src/mmap_vec.rs index 68d0e49f2c2b..c7822ffe9b4e 100644 --- a/crates/runtime/src/mmap_vec.rs +++ b/crates/runtime/src/mmap_vec.rs @@ -127,6 +127,7 @@ impl MmapVec { impl Deref for MmapVec { type Target = [u8]; + #[inline] fn deref(&self) -> &[u8] { // SAFETY: this mmap owns its own range of the underlying mmap so it // should be all good-to-read. diff --git a/crates/wasi/src/preview2/ctx.rs b/crates/wasi/src/preview2/ctx.rs index 03b69da3b795..785ce43be2c1 100644 --- a/crates/wasi/src/preview2/ctx.rs +++ b/crates/wasi/src/preview2/ctx.rs @@ -54,7 +54,10 @@ impl WasiCtxBuilder { pub fn new() -> Self { // For the insecure random API, use `SmallRng`, which is fast. It's // also insecure, but that's the deal here. - let insecure_random = Box::new(cap_rand::rngs::SmallRng::from_entropy()); + let insecure_random = Box::new( + cap_rand::rngs::SmallRng::from_rng(cap_rand::thread_rng(cap_rand::ambient_authority())) + .unwrap(), + ); // For the insecure random seed, use a `u128` generated from // `thread_rng()`, so that it's not guessable from the insecure_random diff --git a/crates/wasmtime/src/signatures.rs b/crates/wasmtime/src/signatures.rs index e34de4638842..6ce6b0150423 100644 --- a/crates/wasmtime/src/signatures.rs +++ b/crates/wasmtime/src/signatures.rs @@ -45,6 +45,7 @@ impl SignatureCollection { } /// Gets the shared signature index given a module signature index. + #[inline] pub fn shared_signature(&self, index: SignatureIndex) -> Option { self.signatures.get(index).copied() } From cf28c228d280d5e30817002cb886a78104325ba3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Sep 2023 14:18:59 -0500 Subject: [PATCH 021/199] Remove stray `component_impl.rs` file (#7103) This has no longer been used since #7056 and I believe it's just an accident that it wasn't removed as part of that PR. --- crates/wasi-http/src/component_impl.rs | 1853 ------------------------ 1 file changed, 1853 deletions(-) delete mode 100644 crates/wasi-http/src/component_impl.rs diff --git a/crates/wasi-http/src/component_impl.rs b/crates/wasi-http/src/component_impl.rs deleted file mode 100644 index b33e1e07b9f2..000000000000 --- a/crates/wasi-http/src/component_impl.rs +++ /dev/null @@ -1,1853 +0,0 @@ -use crate::bindings::http::{ - outgoing_handler, - types::{Error, Host, Method, RequestOptions, Scheme}, -}; -use crate::WasiHttpView; -use anyhow::anyhow; -use std::str; -use std::vec::Vec; -use wasmtime::{AsContext, AsContextMut, Caller, Extern, Memory}; -use wasmtime_wasi::preview2::bindings::{io, poll}; - -const MEMORY: &str = "memory"; - -#[derive(Debug, thiserror::Error)] -enum HttpError { - #[error("Memory not found")] - MemoryNotFound, - #[error("Memory access error")] - MemoryAccessError(#[from] wasmtime::MemoryAccessError), - #[error("Buffer too small")] - BufferTooSmall, - #[error("UTF-8 error")] - Utf8Error(#[from] std::str::Utf8Error), -} - -fn memory_get(caller: &mut Caller<'_, T>) -> Result { - if let Some(Extern::Memory(mem)) = caller.get_export(MEMORY) { - Ok(mem) - } else { - Err(HttpError::MemoryNotFound) - } -} - -/// Get a slice of length `len` from `memory`, starting at `offset`. -/// This will return an `HttpError::BufferTooSmall` if the size of the -/// requested slice is larger than the memory size. -fn slice_from_memory( - memory: &Memory, - mut ctx: impl AsContextMut, - offset: u32, - len: u32, -) -> Result, HttpError> { - let required_memory_size = offset.checked_add(len).ok_or(HttpError::BufferTooSmall)? as usize; - - if required_memory_size > memory.data_size(&mut ctx) { - return Err(HttpError::BufferTooSmall); - } - - let mut buf = vec![0u8; len as usize]; - memory.read(&mut ctx, offset as usize, buf.as_mut_slice())?; - Ok(buf) -} - -fn u32_from_memory(memory: &Memory, ctx: impl AsContextMut, ptr: u32) -> Result { - let slice = slice_from_memory(memory, ctx, ptr, 4)?; - let mut dst = [0u8; 4]; - dst.clone_from_slice(&slice[0..4]); - Ok(u32::from_le_bytes(dst)) -} - -/// Read a string of byte length `len` from `memory`, starting at `offset`. -fn string_from_memory( - memory: &Memory, - ctx: impl AsContextMut, - offset: u32, - len: u32, -) -> Result { - let slice = slice_from_memory(memory, ctx, offset, len)?; - Ok(std::str::from_utf8(&slice)?.to_string()) -} - -fn read_option_string( - memory: &Memory, - ctx: impl AsContextMut, - is_some: i32, - ptr: u32, - len: u32, -) -> Result, HttpError> { - if is_some == 1 { - Ok(Some(string_from_memory(&memory, ctx, ptr, len)?)) - } else { - Ok(None) - } -} - -async fn allocate_guest_pointer( - caller: &mut Caller<'_, T>, - size: u32, -) -> anyhow::Result { - let realloc = caller - .get_export("cabi_realloc") - .ok_or_else(|| anyhow!("missing required export cabi_realloc"))?; - let func = realloc - .into_func() - .ok_or_else(|| anyhow!("cabi_realloc must be a func"))?; - let typed = func.typed::<(u32, u32, u32, u32), u32>(caller.as_context())?; - Ok(typed - .call_async(caller.as_context_mut(), (0, 0, 4, size)) - .await?) -} - -fn u32_array_to_u8(arr: &[u32]) -> Vec { - let mut result = std::vec::Vec::new(); - for val in arr.iter() { - let bytes = val.to_le_bytes(); - for b in bytes.iter() { - result.push(*b); - } - } - result -} - -pub fn add_component_to_linker( - linker: &mut wasmtime::Linker, - get_cx: impl Fn(&mut T) -> &mut T + Send + Sync + Copy + 'static, -) -> anyhow::Result<()> { - linker.func_wrap8_async( - "wasi:http/outgoing-handler", - "handle", - move |mut caller: Caller<'_, T>, - request: u32, - has_options: i32, - has_timeout: i32, - timeout_ms: u32, - has_first_byte_timeout: i32, - first_byte_timeout_ms: u32, - has_between_bytes_timeout: i32, - between_bytes_timeout_ms: u32| { - Box::new(async move { - let options = if has_options == 1 { - Some(RequestOptions { - connect_timeout_ms: if has_timeout == 1 { - Some(timeout_ms) - } else { - None - }, - first_byte_timeout_ms: if has_first_byte_timeout == 1 { - Some(first_byte_timeout_ms) - } else { - None - }, - between_bytes_timeout_ms: if has_between_bytes_timeout == 1 { - Some(between_bytes_timeout_ms) - } else { - None - }, - }) - } else { - None - }; - - let ctx = get_cx(caller.data_mut()); - tracing::trace!("[module='wasi:http/outgoing-handler' function='handle'] call request={:?} options={:?}", request, options); - let result = outgoing_handler::Host::handle(ctx, request, options).await; - tracing::trace!( - "[module='wasi:http/outgoing-handler' function='handle'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap14_async( - "wasi:http/types", - "new-outgoing-request", - move |mut caller: Caller<'_, T>, - method: i32, - method_ptr: i32, - method_len: i32, - path_is_some: i32, - path_ptr: u32, - path_len: u32, - scheme_is_some: i32, - scheme: i32, - scheme_ptr: i32, - scheme_len: i32, - authority_is_some: i32, - authority_ptr: u32, - authority_len: u32, - headers: u32| { - Box::new(async move { - let memory = memory_get(&mut caller)?; - let path = read_option_string( - &memory, - caller.as_context_mut(), - path_is_some, - path_ptr, - path_len, - )?; - let authority = read_option_string( - &memory, - caller.as_context_mut(), - authority_is_some, - authority_ptr, - authority_len, - )?; - - let mut s = Some(Scheme::Https); - if scheme_is_some == 1 { - s = Some(match scheme { - 0 => Scheme::Http, - 1 => Scheme::Https, - _ => { - let value = string_from_memory( - &memory, - caller.as_context_mut(), - scheme_ptr.try_into()?, - scheme_len.try_into()?, - )?; - Scheme::Other(value) - } - }); - } - let m = match method { - 0 => Method::Get, - 1 => Method::Head, - 2 => Method::Post, - 3 => Method::Put, - 4 => Method::Delete, - 5 => Method::Connect, - 6 => Method::Options, - 7 => Method::Trace, - 8 => Method::Patch, - _ => { - let value = string_from_memory( - &memory, - caller.as_context_mut(), - method_ptr.try_into()?, - method_len.try_into()?, - )?; - Method::Other(value) - } - }; - - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='new-outgoing-request'] call method={:?} path={:?} scheme={:?} authority={:?} headers={:?}", - m, - path, - s, - authority, - headers - ); - let result = - Host::new_outgoing_request(ctx, m, path, s, authority, headers).await; - tracing::trace!( - "[module='wasi:http/types' function='new-outgoing-request'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap1_async( - "wasi:http/types", - "incoming-response-status", - move |mut caller: Caller<'_, T>, id: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-status'] call id={:?}", - id - ); - let result = Ok(u32::from(Host::incoming_response_status(ctx, id).await?)); - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-status'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap1_async( - "wasi:http/types", - "drop-future-incoming-response", - move |mut caller: Caller<'_, T>, id: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='drop-future-incoming-response'] call id={:?}", - id - ); - let result = Host::drop_future_incoming_response(ctx, id).await; - tracing::trace!( - "[module='wasi:http/types' function='drop-future-incoming-response'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap2_async( - "wasi:http/types", - "future-incoming-response-get", - move |mut caller: Caller<'_, T>, future: u32, ptr: i32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='future-incoming-response-get'] call future={:?}", - future - ); - let result = Host::future_incoming_response_get(ctx, future).await; - tracing::trace!( - "[module='wasi:http/types' function='future-incoming-response-get'] return result={:?}", - result - ); - let response = result?; - - let memory = memory_get(&mut caller)?; - - // First == is_some - // Second == is_err - // Third == {ok: is_err = false, tag: is_err = true} - // Fourth == string ptr - // Fifth == string len - let result: [u32; 5] = match response { - Some(inner) => match inner { - Ok(value) => [1, 0, value, 0, 0], - Err(error) => { - let (tag, err_string) = match error { - Error::InvalidUrl(e) => (0u32, e), - Error::TimeoutError(e) => (1u32, e), - Error::ProtocolError(e) => (2u32, e), - Error::UnexpectedError(e) => (3u32, e), - }; - let bytes = err_string.as_bytes(); - let len = bytes.len().try_into().unwrap(); - let ptr = allocate_guest_pointer(&mut caller, len).await?; - memory.write(caller.as_context_mut(), ptr as _, bytes)?; - [1, 1, tag, ptr, len] - } - }, - None => [0, 0, 0, 0, 0], - }; - let raw = u32_array_to_u8(&result); - - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - Ok(()) - }) - }, - )?; - linker.func_wrap1_async( - "wasi:http/types", - "listen-to-future-incoming-response", - move |mut caller: Caller<'_, T>, future: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='listen-to-future-incoming-response'] call future={:?}", - future - ); - let result = Host::listen_to_future_incoming_response(ctx, future).await; - tracing::trace!( - "[module='wasi:http/types' function='listen-to-future-incoming-response'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap2_async( - "wasi:http/types", - "incoming-response-consume", - move |mut caller: Caller<'_, T>, response: u32, ptr: i32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-consume'] call response={:?}", - response - ); - let result = Host::incoming_response_consume(ctx, response).await; - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-consume'] return result={:?}", - result - ); - let stream = result?.unwrap_or(0); - - let memory = memory_get(&mut caller).unwrap(); - - // First == is_some - // Second == stream_id - let result: [u32; 2] = [0, stream]; - let raw = u32_array_to_u8(&result); - - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - Ok(()) - }) - }, - )?; - linker.func_wrap1_async( - "wasi:poll/poll", - "drop-pollable", - move |mut caller: Caller<'_, T>, id: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:poll/poll' function='drop-pollable'] call id={:?}", - id - ); - let result = poll::poll::Host::drop_pollable(ctx, id); - tracing::trace!( - "[module='wasi:poll/poll' function='drop-pollable'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap3_async( - "wasi:poll/poll", - "poll-oneoff", - move |mut caller: Caller<'_, T>, base_ptr: u32, len: u32, out_ptr: u32| { - Box::new(async move { - let memory = memory_get(&mut caller)?; - - let mut vec = Vec::new(); - let mut i = 0; - while i < len { - let ptr = base_ptr + i * 4; - let pollable_ptr = u32_from_memory(&memory, caller.as_context_mut(), ptr)?; - vec.push(pollable_ptr); - i = i + 1; - } - - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:poll/poll' function='poll-oneoff'] call in={:?}", - vec - ); - let result = poll::poll::Host::poll_oneoff(ctx, vec).await; - tracing::trace!( - "[module='wasi:poll/poll' function='poll-oneoff'] return result={:?}", - result - ); - let result = result?; - - let result_len = result.len(); - let result_ptr = - allocate_guest_pointer(&mut caller, (4 * result_len).try_into()?).await?; - let mut ptr = result_ptr; - for item in result.iter() { - let completion: u32 = match item { - true => 1, - false => 0, - }; - memory.write(caller.as_context_mut(), ptr as _, &completion.to_be_bytes())?; - - ptr = ptr + 4; - } - - let result: [u32; 2] = [result_ptr, result_len.try_into()?]; - let raw = u32_array_to_u8(&result); - memory.write(caller.as_context_mut(), out_ptr as _, &raw)?; - Ok(()) - }) - }, - )?; - linker.func_wrap1_async( - "wasi:io/streams", - "drop-input-stream", - move |mut caller: Caller<'_, T>, id: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='drop-input-stream'] call id={:?}", - id - ); - let result = io::streams::Host::drop_input_stream(ctx, id); - tracing::trace!( - "[module='wasi:io/streams' function='drop-input-stream'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap1_async( - "wasi:io/streams", - "drop-output-stream", - move |mut caller: Caller<'_, T>, id: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='drop-output-stream'] call id={:?}", - id - ); - let result = io::streams::Host::drop_output_stream(ctx, id); - tracing::trace!( - "[module='wasi:io/streams' function='drop-output-stream'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap3_async( - "wasi:io/streams", - "read", - move |mut caller: Caller<'_, T>, stream: u32, len: u64, ptr: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='read'] call this={:?} len={:?}", - stream, - len - ); - let result = io::streams::Host::read(ctx, stream, len).await; - tracing::trace!( - "[module='wasi:io/streams' function='read'] return result={:?}", - result - ); - let (bytes, status) = result?.map_err(|_| anyhow!("read failed"))?; - - let done = match status { - io::streams::StreamStatus::Open => 0, - io::streams::StreamStatus::Ended => 1, - }; - let body_len: u32 = bytes.len().try_into()?; - let out_ptr = allocate_guest_pointer(&mut caller, body_len).await?; - - // First == is_err - // Second == {ok: is_err = false, tag: is_err = true} - // Third == bytes length - // Fourth == enum status - let result: [u32; 4] = [0, out_ptr, body_len, done]; - let raw = u32_array_to_u8(&result); - - let memory = memory_get(&mut caller)?; - memory.write(caller.as_context_mut(), out_ptr as _, &bytes)?; - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - Ok(()) - }) - }, - )?; - linker.func_wrap3_async( - "wasi:io/streams", - "blocking-read", - move |mut caller: Caller<'_, T>, stream: u32, len: u64, ptr: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='blocking-read'] call this={:?} len={:?}", - stream, - len - ); - let result = io::streams::Host::blocking_read(ctx, stream, len).await; - tracing::trace!( - "[module='wasi:io/streams' function='blocking-read'] return result={:?}", - result - ); - let (bytes, status) = result?.map_err(|_| anyhow!("read failed"))?; - - let done = match status { - io::streams::StreamStatus::Open => 0, - io::streams::StreamStatus::Ended => 1, - }; - let body_len: u32 = bytes.len().try_into()?; - let out_ptr = allocate_guest_pointer(&mut caller, body_len).await?; - - // First == is_err - // Second == {ok: is_err = false, tag: is_err = true} - // Third == bytes length - // Fourth == enum status - let result: [u32; 4] = [0, out_ptr, body_len, done]; - let raw = u32_array_to_u8(&result); - - let memory = memory_get(&mut caller)?; - memory.write(caller.as_context_mut(), out_ptr as _, &bytes)?; - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - Ok(()) - }) - }, - )?; - linker.func_wrap1_async( - "wasi:io/streams", - "subscribe-to-input-stream", - move |mut caller: Caller<'_, T>, stream: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='subscribe-to-input-stream'] call stream={:?}", - stream - ); - let result = io::streams::Host::subscribe_to_input_stream(ctx, stream); - tracing::trace!( - "[module='wasi:io/streams' function='subscribe-to-input-stream'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap1_async( - "wasi:io/streams", - "subscribe-to-output-stream", - move |mut caller: Caller<'_, T>, stream: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='subscribe-to-output-stream'] call stream={:?}", - stream - ); - let result = io::streams::Host::subscribe_to_output_stream(ctx, stream); - tracing::trace!( - "[module='wasi:io/streams' function='subscribe-to-output-stream'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap2_async( - "wasi:io/streams", - "check-write", - move |mut caller: Caller<'_, T>, stream: u32, ptr: u32| { - Box::new(async move { - let memory = memory_get(&mut caller)?; - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='check-write'] call stream={:?}", - stream, - ); - let result = io::streams::Host::check_write(ctx, stream); - tracing::trace!( - "[module='wasi:io/streams' function='check-write'] return result={:?}", - result - ); - - let result: [u32; 3] = match result { - // 0 == outer result tag (success) - // 1 == result value (u64 upper 32 bits) - // 2 == result value (u64 lower 32 bits) - Ok(len) => [0, (len >> 32) as u32, len as u32], - - // 0 == outer result tag (failure) - // 1 == result value (unused) - // 2 == result value (error type) - Err(_) => todo!("how do we extract runtime error cases?"), - }; - - let raw = u32_array_to_u8(&result); - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - - Ok(()) - }) - }, - )?; - linker.func_wrap2_async( - "wasi:io/streams", - "flush", - move |mut caller: Caller<'_, T>, stream: u32, ptr: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='flush'] call stream={:?}", - stream - ); - let result = io::streams::Host::flush(ctx, stream); - tracing::trace!( - "[module='wasi:io/streams' function='flush'] return result={:?}", - result - ); - - let result: [u32; 2] = match result { - // 0 == outer result tag - // 1 == unused - Ok(_) => [0, 0], - - // 0 == outer result tag - // 1 == inner result tag - Err(_) => todo!("how do we extract runtime error cases?"), - }; - - let raw = u32_array_to_u8(&result); - let memory = memory_get(&mut caller)?; - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - - Ok(()) - }) - }, - )?; - linker.func_wrap2_async( - "wasi:io/streams", - "blocking-flush", - move |mut caller: Caller<'_, T>, stream: u32, ptr: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='blocking-flush'] call stream={:?}", - stream - ); - let result = io::streams::Host::blocking_flush(ctx, stream).await; - tracing::trace!( - "[module='wasi:io/streams' function='blocking-flush'] return result={:?}", - result - ); - - let result: [u32; 2] = match result { - // 0 == outer result tag - // 1 == unused - Ok(_) => [0, 0], - - // 0 == outer result tag - // 1 == inner result tag - Err(_) => todo!("how do we extract runtime error cases?"), - }; - - let raw = u32_array_to_u8(&result); - let memory = memory_get(&mut caller)?; - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - - Ok(()) - }) - }, - )?; - linker.func_wrap4_async( - "wasi:io/streams", - "write", - move |mut caller: Caller<'_, T>, stream: u32, body_ptr: u32, body_len: u32, ptr: u32| { - Box::new(async move { - let memory = memory_get(&mut caller)?; - let body = slice_from_memory(&memory, caller.as_context_mut(), body_ptr, body_len)?; - - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='write'] call stream={:?} body={:?}", - stream, - body - ); - let result = io::streams::Host::write(ctx, stream, body.into()).await; - tracing::trace!( - "[module='wasi:io/streams' function='write'] return result={:?}", - result - ); - result?; - - // First == is_err - // Second == {ok: is_err = false, tag: is_err = true} - let result: [u32; 2] = [0, 0]; - let raw = u32_array_to_u8(&result); - - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - - Ok(()) - }) - }, - )?; - linker.func_wrap4_async( - "wasi:io/streams", - "blocking-write-and-flush", - move |mut caller: Caller<'_, T>, stream: u32, body_ptr: u32, body_len: u32, ptr: u32| { - Box::new(async move { - let memory = memory_get(&mut caller)?; - let body = slice_from_memory(&memory, caller.as_context_mut(), body_ptr, body_len)?; - - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='blocking-write-and-flush'] call stream={:?} body={:?}", - stream, - body - ); - let result = io::streams::Host::blocking_write_and_flush(ctx, stream, body.into()).await; - tracing::trace!( - "[module='wasi:io/streams' function='blocking-write-and-flush'] return result={:?}", - result - ); - result?; - - // First == is_err - // Second == {ok: is_err = false, tag: is_err = true} - let result: [u32; 2] = [0, 0]; - let raw = u32_array_to_u8(&result); - - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - - Ok(()) - }) - }, - )?; - linker.func_wrap1_async( - "wasi:http/types", - "drop-fields", - move |mut caller: Caller<'_, T>, id: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='drop-fields'] call id={:?}", - id - ); - let result = Host::drop_fields(ctx, id).await; - tracing::trace!( - "[module='wasi:http/types' function='drop-fields'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap2_async( - "wasi:http/types", - "outgoing-request-write", - move |mut caller: Caller<'_, T>, request: u32, ptr: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='outgoing-request-write'] call request={:?}", - request - ); - let result = Host::outgoing_request_write(ctx, request).await; - tracing::trace!( - "[module='wasi:http/types' function='outgoing-request-write'] return result={:?}", - result - ); - let stream = result? - .map_err(|_| anyhow!("no outgoing stream present"))?; - - let memory = memory_get(&mut caller)?; - // First == is_some - // Second == stream_id - let result: [u32; 2] = [0, stream]; - let raw = u32_array_to_u8(&result); - - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - Ok(()) - }) - }, - )?; - linker.func_wrap1_async( - "wasi:http/types", - "drop-outgoing-request", - move |mut caller: Caller<'_, T>, id: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='drop-outgoing-request'] call id={:?}", - id - ); - let result = Host::drop_outgoing_request(ctx, id).await; - tracing::trace!( - "[module='wasi:http/types' function='drop-outgoing-request'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap1_async( - "wasi:http/types", - "drop-incoming-response", - move |mut caller: Caller<'_, T>, id: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='drop-incoming-response'] call id={:?}", - id - ); - let result = Host::drop_incoming_response(ctx, id).await; - tracing::trace!( - "[module='wasi:http/types' function='drop-incoming-response'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap2_async( - "wasi:http/types", - "new-fields", - move |mut caller: Caller<'_, T>, base_ptr: u32, len: u32| { - Box::new(async move { - let memory = memory_get(&mut caller)?; - - let mut vec = Vec::new(); - let mut i = 0; - // TODO: read this more efficiently as a single block. - while i < len { - let ptr = base_ptr + i * 16; - let name_ptr = u32_from_memory(&memory, caller.as_context_mut(), ptr)?; - let name_len = u32_from_memory(&memory, caller.as_context_mut(), ptr + 4)?; - let value_ptr = u32_from_memory(&memory, caller.as_context_mut(), ptr + 8)?; - let value_len = u32_from_memory(&memory, caller.as_context_mut(), ptr + 12)?; - - let name = - string_from_memory(&memory, caller.as_context_mut(), name_ptr, name_len)?; - let value = - string_from_memory(&memory, caller.as_context_mut(), value_ptr, value_len)?; - - vec.push((name, value)); - i = i + 1; - } - - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='new-fields'] call entries={:?}", - vec - ); - let result = Host::new_fields(ctx, vec).await; - tracing::trace!( - "[module='wasi:http/types' function='new-fields'] return result={:?}", - result - ); - result - }) - }, - )?; - linker.func_wrap2_async( - "wasi:http/types", - "fields-entries", - move |mut caller: Caller<'_, T>, fields: u32, out_ptr: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='fields-entries'] call fields={:?}", - fields - ); - let result = Host::fields_entries(ctx, fields).await; - tracing::trace!( - "[module='wasi:http/types' function='fields-entries'] return result={:?}", - result - ); - let entries = result?; - - let header_len = entries.len(); - let tuple_ptr = - allocate_guest_pointer(&mut caller, (16 * header_len).try_into()?).await?; - let mut ptr = tuple_ptr; - for item in entries.iter() { - let name = &item.0; - let value = &item.1; - let name_len: u32 = name.len().try_into()?; - let value_len: u32 = value.len().try_into()?; - - let name_ptr = allocate_guest_pointer(&mut caller, name_len).await?; - let value_ptr = allocate_guest_pointer(&mut caller, value_len).await?; - - let memory = memory_get(&mut caller)?; - memory.write(caller.as_context_mut(), name_ptr as _, &name.as_bytes())?; - memory.write(caller.as_context_mut(), value_ptr as _, value)?; - - let pair: [u32; 4] = [name_ptr, name_len, value_ptr, value_len]; - let raw_pair = u32_array_to_u8(&pair); - memory.write(caller.as_context_mut(), ptr as _, &raw_pair)?; - - ptr = ptr + 16; - } - - let memory = memory_get(&mut caller)?; - let result: [u32; 2] = [tuple_ptr, header_len.try_into()?]; - let raw = u32_array_to_u8(&result); - memory.write(caller.as_context_mut(), out_ptr as _, &raw)?; - Ok(()) - }) - }, - )?; - linker.func_wrap1_async( - "wasi:http/types", - "incoming-response-headers", - move |mut caller: Caller<'_, T>, handle: u32| { - Box::new(async move { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-headers'] call handle={:?}", - handle - ); - let result = Host::incoming_response_headers(ctx, handle).await; - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-headers'] return result={:?}", - result - ); - result - }) - }, - )?; - Ok(()) -} - -pub mod sync { - use super::{ - memory_get, read_option_string, string_from_memory, u32_array_to_u8, u32_from_memory, - }; - use crate::bindings::sync::http::{ - outgoing_handler, - types::{Error, Host, Method, RequestOptions, Scheme}, - }; - use crate::WasiHttpView; - use anyhow::anyhow; - use wasmtime::{AsContext, AsContextMut, Caller}; - use wasmtime_wasi::preview2::bindings::sync_io::{io, poll}; - - fn allocate_guest_pointer( - caller: &mut Caller<'_, T>, - size: u32, - ) -> anyhow::Result { - let realloc = caller - .get_export("cabi_realloc") - .ok_or_else(|| anyhow!("missing required export cabi_realloc"))?; - let func = realloc - .into_func() - .ok_or_else(|| anyhow!("cabi_realloc must be a func"))?; - let typed = func.typed::<(u32, u32, u32, u32), u32>(caller.as_context())?; - Ok(typed.call(caller.as_context_mut(), (0, 0, 4, size))?) - } - - pub fn add_component_to_linker( - linker: &mut wasmtime::Linker, - get_cx: impl Fn(&mut T) -> &mut T + Send + Sync + Copy + 'static, - ) -> anyhow::Result<()> { - linker.func_wrap( - "wasi:http/outgoing-handler", - "handle", - move |mut caller: Caller<'_, T>, - request: u32, - has_options: i32, - has_timeout: i32, - timeout_ms: u32, - has_first_byte_timeout: i32, - first_byte_timeout_ms: u32, - has_between_bytes_timeout: i32, - between_bytes_timeout_ms: u32| - -> anyhow::Result { - let options = if has_options == 1 { - Some(RequestOptions { - connect_timeout_ms: if has_timeout == 1 { - Some(timeout_ms) - } else { - None - }, - first_byte_timeout_ms: if has_first_byte_timeout == 1 { - Some(first_byte_timeout_ms) - } else { - None - }, - between_bytes_timeout_ms: if has_between_bytes_timeout == 1 { - Some(between_bytes_timeout_ms) - } else { - None - }, - }) - } else { - None - }; - - let ctx = get_cx(caller.data_mut()); - tracing::trace!("[module='wasi:http/outgoing-handler' function='handle'] call request={:?} options={:?}", request, options); - let result = outgoing_handler::Host::handle(ctx, request, options); - tracing::trace!( - "[module='wasi:http/outgoing-handler' function='handle'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:http/types", - "new-outgoing-request", - move |mut caller: Caller<'_, T>, - method: i32, - method_ptr: i32, - method_len: i32, - path_is_some: i32, - path_ptr: u32, - path_len: u32, - scheme_is_some: i32, - scheme: i32, - scheme_ptr: i32, - scheme_len: i32, - authority_is_some: i32, - authority_ptr: u32, - authority_len: u32, - headers: u32| - -> anyhow::Result { - let memory = memory_get(&mut caller)?; - let path = read_option_string( - &memory, - caller.as_context_mut(), - path_is_some, - path_ptr, - path_len, - )?; - let authority = read_option_string( - &memory, - caller.as_context_mut(), - authority_is_some, - authority_ptr, - authority_len, - )?; - - let mut s = Some(Scheme::Https); - if scheme_is_some == 1 { - s = Some(match scheme { - 0 => Scheme::Http, - 1 => Scheme::Https, - _ => { - let value = string_from_memory( - &memory, - caller.as_context_mut(), - scheme_ptr.try_into()?, - scheme_len.try_into()?, - )?; - Scheme::Other(value) - } - }); - } - let m = match method { - 0 => Method::Get, - 1 => Method::Head, - 2 => Method::Post, - 3 => Method::Put, - 4 => Method::Delete, - 5 => Method::Connect, - 6 => Method::Options, - 7 => Method::Trace, - 8 => Method::Patch, - _ => { - let value = string_from_memory( - &memory, - caller.as_context_mut(), - method_ptr.try_into()?, - method_len.try_into()?, - )?; - Method::Other(value) - } - }; - - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='new-outgoing-request'] call method={:?} path={:?} scheme={:?} authority={:?} headers={:?}", - m, - path, - s, - authority, - headers - ); - let result = - Host::new_outgoing_request(ctx, m, path, s, authority, headers); - tracing::trace!( - "[module='wasi:http/types' function='new-outgoing-request'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:http/types", - "incoming-response-status", - move |mut caller: Caller<'_, T>, id: u32| -> anyhow::Result { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-status'] call id={:?}", - id - ); - let result = Ok(u32::from(Host::incoming_response_status(ctx, id)?)); - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-status'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:http/types", - "drop-future-incoming-response", - move |mut caller: Caller<'_, T>, id: u32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='drop-future-incoming-response'] call id={:?}", - id - ); - let result = Host::drop_future_incoming_response(ctx, id); - tracing::trace!( - "[module='wasi:http/types' function='drop-future-incoming-response'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:http/types", - "future-incoming-response-get", - move |mut caller: Caller<'_, T>, future: u32, ptr: i32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - - tracing::trace!( - "[module='wasi:http/types' function='future-incoming-response-get'] call future={:?}", - future - ); - let result = Host::future_incoming_response_get(ctx, future); - tracing::trace!( - "[module='wasi:http/types' function='future-incoming-response-get'] return result={:?}", - result - ); - let response = result?; - - let memory = memory_get(&mut caller)?; - - // First == is_some - // Second == is_err - // Third == {ok: is_err = false, tag: is_err = true} - // Fourth == string ptr - // Fifth == string len - let result: [u32; 5] = match response { - Some(inner) => match inner { - Ok(value) => [1, 0, value, 0, 0], - Err(error) => { - let (tag, err_string) = match error { - Error::InvalidUrl(e) => (0u32, e), - Error::TimeoutError(e) => (1u32, e), - Error::ProtocolError(e) => (2u32, e), - Error::UnexpectedError(e) => (3u32, e), - }; - let bytes = err_string.as_bytes(); - let len = bytes.len().try_into().unwrap(); - let ptr = allocate_guest_pointer(&mut caller, len)?; - memory.write(caller.as_context_mut(), ptr as _, bytes)?; - [1, 1, tag, ptr, len] - } - }, - None => [0, 0, 0, 0, 0], - }; - let raw = u32_array_to_u8(&result); - - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:http/types", - "listen-to-future-incoming-response", - move |mut caller: Caller<'_, T>, future: u32| -> anyhow::Result { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='listen-to-future-incoming-response'] call future={:?}", - future - ); - let result = Host::listen_to_future_incoming_response(ctx, future); - tracing::trace!( - "[module='wasi:http/types' function='listen-to-future-incoming-response'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:http/types", - "incoming-response-consume", - move |mut caller: Caller<'_, T>, response: u32, ptr: i32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-consume'] call response={:?}", - response - ); - let result = Host::incoming_response_consume(ctx, response); - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-consume'] return result={:?}", - result - ); - let stream = result?.unwrap_or(0); - - let memory = memory_get(&mut caller).unwrap(); - - // First == is_some - // Second == stream_id - let result: [u32; 2] = [0, stream]; - let raw = u32_array_to_u8(&result); - - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:poll/poll", - "drop-pollable", - move |mut caller: Caller<'_, T>, id: u32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:poll/poll' function='drop-pollable'] call id={:?}", - id - ); - let result = poll::poll::Host::drop_pollable(ctx, id); - tracing::trace!( - "[module='wasi:poll/poll' function='drop-pollable'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:poll/poll", - "poll-oneoff", - move |mut caller: Caller<'_, T>, - base_ptr: u32, - len: u32, - out_ptr: u32| - -> anyhow::Result<()> { - let memory = memory_get(&mut caller)?; - - let mut vec = Vec::new(); - let mut i = 0; - while i < len { - let ptr = base_ptr + i * 4; - let pollable_ptr = u32_from_memory(&memory, caller.as_context_mut(), ptr)?; - vec.push(pollable_ptr); - i = i + 1; - } - - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:poll/poll' function='poll-oneoff'] call in={:?}", - vec - ); - let result = poll::poll::Host::poll_oneoff(ctx, vec); - tracing::trace!( - "[module='wasi:poll/poll' function='poll-oneoff'] return result={:?}", - result - ); - let result = result?; - - let result_len = result.len(); - let result_ptr = allocate_guest_pointer(&mut caller, (4 * result_len).try_into()?)?; - let mut ptr = result_ptr; - for item in result.iter() { - let completion: u32 = match item { - true => 1, - false => 0, - }; - memory.write(caller.as_context_mut(), ptr as _, &completion.to_be_bytes())?; - - ptr = ptr + 4; - } - - let result: [u32; 2] = [result_ptr, result_len.try_into()?]; - let raw = u32_array_to_u8(&result); - memory.write(caller.as_context_mut(), out_ptr as _, &raw)?; - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "drop-input-stream", - move |mut caller: Caller<'_, T>, id: u32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='drop-input-stream'] call id={:?}", - id - ); - let result = io::streams::Host::drop_input_stream(ctx, id); - tracing::trace!( - "[module='wasi:io/streams' function='drop-input-stream'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "drop-output-stream", - move |mut caller: Caller<'_, T>, id: u32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='drop-output-stream'] call id={:?}", - id - ); - let result = io::streams::Host::drop_output_stream(ctx, id); - tracing::trace!( - "[module='wasi:io/streams' function='drop-output-stream'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "read", - move |mut caller: Caller<'_, T>, - stream: u32, - len: u64, - ptr: u32| - -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='read'] call this={:?} len={:?}", - stream, - len - ); - let result = io::streams::Host::read(ctx, stream, len); - tracing::trace!( - "[module='wasi:io/streams' function='read'] return result={:?}", - result - ); - let (bytes, status) = result?.map_err(|_| anyhow!("read failed"))?; - - let done = match status { - io::streams::StreamStatus::Open => 0, - io::streams::StreamStatus::Ended => 1, - }; - let body_len: u32 = bytes.len().try_into()?; - let out_ptr = allocate_guest_pointer(&mut caller, body_len)?; - - // First == is_err - // Second == {ok: is_err = false, tag: is_err = true} - // Third == bytes length - // Fourth == enum status - let result: [u32; 4] = [0, out_ptr, body_len, done]; - let raw = u32_array_to_u8(&result); - - let memory = memory_get(&mut caller)?; - memory.write(caller.as_context_mut(), out_ptr as _, &bytes)?; - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "blocking-read", - move |mut caller: Caller<'_, T>, - stream: u32, - len: u64, - ptr: u32| - -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - - tracing::trace!( - "[module='wasi:io/streams' function='blocking-read'] call this={:?} len={:?}", - stream, - len - ); - let result = io::streams::Host::blocking_read(ctx, stream, len); - tracing::trace!( - "[module='wasi:io/streams' function='blocking-read'] return result={:?}", - result - ); - let (bytes, status) = result?.map_err(|_| anyhow!("read failed"))?; - - let done = match status { - io::streams::StreamStatus::Open => 0, - io::streams::StreamStatus::Ended => 1, - }; - let body_len: u32 = bytes.len().try_into()?; - let out_ptr = allocate_guest_pointer(&mut caller, body_len)?; - - // First == is_err - // Second == {ok: is_err = false, tag: is_err = true} - // Third == bytes length - // Fourth == enum status - let result: [u32; 4] = [0, out_ptr, body_len, done]; - let raw = u32_array_to_u8(&result); - - let memory = memory_get(&mut caller)?; - memory.write(caller.as_context_mut(), out_ptr as _, &bytes)?; - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "subscribe-to-input-stream", - move |mut caller: Caller<'_, T>, stream: u32| -> anyhow::Result { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='subscribe-to-input-stream'] call stream={:?}", - stream - ); - let result = io::streams::Host::subscribe_to_input_stream(ctx, stream)?; - // TODO: necessary until this PR has been merged: - // https://github.com/bytecodealliance/wasmtime/pull/6877 - let oneoff_result = poll::poll::Host::poll_oneoff(ctx, vec![result])?; - tracing::trace!( - "[module='wasi:poll/poll' function='poll-oneoff'] return result={:?}", - oneoff_result - ); - tracing::trace!( - "[module='wasi:io/streams' function='subscribe-to-input-stream'] return result=Ok({:?})", - result - ); - Ok(result) - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "subscribe-to-output-stream", - move |mut caller: Caller<'_, T>, stream: u32| -> anyhow::Result { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='subscribe-to-output-stream'] call stream={:?}", - stream - ); - let result = io::streams::Host::subscribe_to_output_stream(ctx, stream)?; - // TODO: necessary until this PR has been merged: - // https://github.com/bytecodealliance/wasmtime/pull/6877 - let oneoff_result = poll::poll::Host::poll_oneoff(ctx, vec![result])?; - tracing::trace!( - "[module='wasi:poll/poll' function='poll-oneoff'] return result={:?}", - oneoff_result - ); - tracing::trace!( - "[module='wasi:io/streams' function='subscribe-to-output-stream'] return result=Ok({:?})", - result - ); - Ok(result) - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "write", - move |mut caller: Caller<'_, T>, - stream: u32, - body_ptr: u32, - body_len: u32, - ptr: u32| - -> anyhow::Result<()> { - let memory = memory_get(&mut caller)?; - let body = - string_from_memory(&memory, caller.as_context_mut(), body_ptr, body_len)?; - - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='write'] call stream={:?} body={:?}", - stream, - body - ); - let result = io::streams::Host::write(ctx, stream, body.into()); - tracing::trace!( - "[module='wasi:io/streams' function='write'] return result={:?}", - result - ); - result?; - - // First == is_err - // Second == {ok: is_err = false, tag: is_err = true} - let result: [u32; 2] = [0, 0]; - let raw = u32_array_to_u8(&result); - - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "blocking-write-and-flush", - move |mut caller: Caller<'_, T>, - stream: u32, - body_ptr: u32, - body_len: u32, - ptr: u32| - -> anyhow::Result<()> { - let memory = memory_get(&mut caller)?; - let body = - string_from_memory(&memory, caller.as_context_mut(), body_ptr, body_len)?; - - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='blocking-write-and-flush'] call stream={:?} body={:?}", - stream, - body - ); - let result = io::streams::Host::blocking_write_and_flush(ctx, stream, body.into()); - tracing::trace!( - "[module='wasi:io/streams' function='blocking-write-and-flush'] return result={:?}", - result - ); - result?; - - // First == is_err - // Second == {ok: is_err = false, tag: is_err = true} - let result: [u32; 2] = [0, 0]; - let raw = u32_array_to_u8(&result); - - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "check-write", - move |mut caller: Caller<'_, T>, stream: u32, ptr: u32| { - let memory = memory_get(&mut caller)?; - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='check-write'] call stream={:?}", - stream - ); - let result = io::streams::Host::check_write(ctx, stream); - tracing::trace!( - "[module='wasi:io/streams' function='check-write'] return result={:?}", - result - ); - - let result: [u32; 3] = match result { - // 0 == outer result tag (success) - // 1 == result value (u64 upper 32 bits) - // 2 == result value (u64 lower 32 bits) - Ok(len) => [0, (len >> 32) as u32, len as u32], - - // 0 == outer result tag (failure) - // 1 == result value (unused) - // 2 == result value (error type) - Err(_) => todo!("how do we extract runtime error cases?"), - }; - - let raw = u32_array_to_u8(&result); - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "flush", - move |mut caller: Caller<'_, T>, stream: u32, ptr: u32| { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='flush'] call stream={:?}", - stream - ); - let result = io::streams::Host::flush(ctx, stream); - tracing::trace!( - "[module='wasi:io/streams' function='flush'] return result={:?}", - result - ); - - let result: [u32; 2] = match result { - // 0 == outer result tag - // 1 == unused - Ok(_) => [0, 0], - - // 0 == outer result tag - // 1 == inner result tag - Err(_) => todo!("how do we extract runtime error cases?"), - }; - - let raw = u32_array_to_u8(&result); - let memory = memory_get(&mut caller)?; - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:io/streams", - "blocking-flush", - move |mut caller: Caller<'_, T>, stream: u32, ptr: u32| { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:io/streams' function='blocking-flush'] call stream={:?}", - stream - ); - let result = io::streams::Host::blocking_flush(ctx, stream); - tracing::trace!( - "[module='wasi:io/streams' function='blocking-flush'] return result={:?}", - result - ); - - let result: [u32; 2] = match result { - // 0 == outer result tag - // 1 == unused - Ok(_) => [0, 0], - - // 0 == outer result tag - // 1 == inner result tag - Err(_) => todo!("how do we extract runtime error cases?"), - }; - - let raw = u32_array_to_u8(&result); - let memory = memory_get(&mut caller)?; - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:http/types", - "drop-fields", - move |mut caller: Caller<'_, T>, id: u32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='drop-fields'] call id={:?}", - id - ); - let result = Host::drop_fields(ctx, id); - tracing::trace!( - "[module='wasi:http/types' function='drop-fields'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:http/types", - "outgoing-request-write", - move |mut caller: Caller<'_, T>, request: u32, ptr: u32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='outgoing-request-write'] call request={:?}", - request - ); - let result = Host::outgoing_request_write(ctx, request); - tracing::trace!( - "[module='wasi:http/types' function='outgoing-request-write'] return result={:?}", - result - ); - let stream = result? - .map_err(|_| anyhow!("no outgoing stream present"))?; - - let memory = memory_get(&mut caller)?; - // First == is_some - // Second == stream_id - let result: [u32; 2] = [0, stream]; - let raw = u32_array_to_u8(&result); - - memory.write(caller.as_context_mut(), ptr as _, &raw)?; - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:http/types", - "drop-outgoing-request", - move |mut caller: Caller<'_, T>, id: u32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='drop-outgoing-request'] call id={:?}", - id - ); - let result = Host::drop_outgoing_request(ctx, id); - tracing::trace!( - "[module='wasi:http/types' function='drop-outgoing-request'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:http/types", - "drop-incoming-response", - move |mut caller: Caller<'_, T>, id: u32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='drop-incoming-response'] call id={:?}", - id - ); - let result = Host::drop_incoming_response(ctx, id); - tracing::trace!( - "[module='wasi:http/types' function='drop-incoming-response'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:http/types", - "new-fields", - move |mut caller: Caller<'_, T>, base_ptr: u32, len: u32| -> anyhow::Result { - let memory = memory_get(&mut caller)?; - - let mut vec = Vec::new(); - let mut i = 0; - // TODO: read this more efficiently as a single block. - while i < len { - let ptr = base_ptr + i * 16; - let name_ptr = u32_from_memory(&memory, caller.as_context_mut(), ptr)?; - let name_len = u32_from_memory(&memory, caller.as_context_mut(), ptr + 4)?; - let value_ptr = u32_from_memory(&memory, caller.as_context_mut(), ptr + 8)?; - let value_len = u32_from_memory(&memory, caller.as_context_mut(), ptr + 12)?; - - let name = - string_from_memory(&memory, caller.as_context_mut(), name_ptr, name_len)?; - let value = - string_from_memory(&memory, caller.as_context_mut(), value_ptr, value_len)?; - - vec.push((name, value)); - i = i + 1; - } - - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='new-fields'] call entries={:?}", - vec - ); - let result = Host::new_fields(ctx, vec); - tracing::trace!( - "[module='wasi:http/types' function='new-fields'] return result={:?}", - result - ); - result - }, - )?; - linker.func_wrap( - "wasi:http/types", - "fields-entries", - move |mut caller: Caller<'_, T>, fields: u32, out_ptr: u32| -> anyhow::Result<()> { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='fields-entries'] call fields={:?}", - fields - ); - let result = Host::fields_entries(ctx, fields); - tracing::trace!( - "[module='wasi:http/types' function='fields-entries'] return result={:?}", - result - ); - let entries = result?; - - let header_len = entries.len(); - let tuple_ptr = allocate_guest_pointer(&mut caller, (16 * header_len).try_into()?)?; - let mut ptr = tuple_ptr; - for item in entries.iter() { - let name = &item.0; - let value = &item.1; - let name_len: u32 = name.len().try_into()?; - let value_len: u32 = value.len().try_into()?; - - let name_ptr = allocate_guest_pointer(&mut caller, name_len)?; - let value_ptr = allocate_guest_pointer(&mut caller, value_len)?; - - let memory = memory_get(&mut caller)?; - memory.write(caller.as_context_mut(), name_ptr as _, &name.as_bytes())?; - memory.write(caller.as_context_mut(), value_ptr as _, value)?; - - let pair: [u32; 4] = [name_ptr, name_len, value_ptr, value_len]; - let raw_pair = u32_array_to_u8(&pair); - memory.write(caller.as_context_mut(), ptr as _, &raw_pair)?; - - ptr = ptr + 16; - } - - let memory = memory_get(&mut caller)?; - let result: [u32; 2] = [tuple_ptr, header_len.try_into()?]; - let raw = u32_array_to_u8(&result); - memory.write(caller.as_context_mut(), out_ptr as _, &raw)?; - Ok(()) - }, - )?; - linker.func_wrap( - "wasi:http/types", - "incoming-response-headers", - move |mut caller: Caller<'_, T>, handle: u32| -> anyhow::Result { - let ctx = get_cx(caller.data_mut()); - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-headers'] call handle={:?}", - handle - ); - let result = Host::incoming_response_headers(ctx, handle); - tracing::trace!( - "[module='wasi:http/types' function='incoming-response-headers'] return result={:?}", - result - ); - result - }, - )?; - Ok(()) - } -} From 40c1f9b8b4f962ed763e47943e6ce0a3be8d1966 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Thu, 28 Sep 2023 21:03:39 +0100 Subject: [PATCH 022/199] ci: Upgrade QEMU to `8.1.1` (#7096) * ci: Upgrade QEMU to `8.1.1` This adds support for RISC-V's Zcb extension that includes some extra compressed instructions. It also removes the current cpuinfo patch, that has been released in 8.1 * wasmtime: Don't assert the exact faulting address for wasm traps --- .github/workflows/main.yml | 3 +- ci/build-test-matrix.js | 2 +- ci/qemu-cpuinfo.patch | 115 ------------------------------------- tests/all/traps.rs | 15 ++--- 4 files changed, 7 insertions(+), 128 deletions(-) delete mode 100644 ci/qemu-cpuinfo.patch diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ec244179fd59..11a7701a6d57 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -374,7 +374,7 @@ jobs: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} env: - QEMU_BUILD_VERSION: 8.0.4 + QEMU_BUILD_VERSION: 8.1.1 strategy: fail-fast: true matrix: ${{ fromJson(needs.determine.outputs.test-matrix) }} @@ -435,7 +435,6 @@ jobs: # quickly. curl https://download.qemu.org/qemu-$QEMU_BUILD_VERSION.tar.xz | tar xJf - cd qemu-$QEMU_BUILD_VERSION - patch -p1 < $GITHUB_WORKSPACE/ci/qemu-cpuinfo.patch ./configure --target-list=${{ matrix.qemu_target }} --prefix=${{ runner.tool_cache}}/qemu --disable-tools --disable-slirp --disable-fdt --disable-capstone --disable-docs ninja -C build install touch ${{ runner.tool_cache }}/qemu/built diff --git a/ci/build-test-matrix.js b/ci/build-test-matrix.js index 35c2c00efd57..298203f5d4de 100644 --- a/ci/build-test-matrix.js +++ b/ci/build-test-matrix.js @@ -91,7 +91,7 @@ const array = [ "target": "riscv64gc-unknown-linux-gnu", "gcc_package": "gcc-riscv64-linux-gnu", "gcc": "riscv64-linux-gnu-gcc", - "qemu": "qemu-riscv64 -cpu rv64,v=true,vlen=256,vext_spec=v1.0,zba=true,zbb=true,zbc=true,zbs=true,zbkb=true -L /usr/riscv64-linux-gnu", + "qemu": "qemu-riscv64 -cpu rv64,v=true,vlen=256,vext_spec=v1.0,zba=true,zbb=true,zbc=true,zbs=true,zbkb=true,zcb=true -L /usr/riscv64-linux-gnu", "qemu_target": "riscv64-linux-user", "name": "Test Linux riscv64", "filter": "linux-riscv64", diff --git a/ci/qemu-cpuinfo.patch b/ci/qemu-cpuinfo.patch deleted file mode 100644 index 261bcb593409..000000000000 --- a/ci/qemu-cpuinfo.patch +++ /dev/null @@ -1,115 +0,0 @@ -From 5d05b046efb079360d0175164ee04947f20f9e66 Mon Sep 17 00:00:00 2001 -From: Afonso Bordado -Date: Tue, 21 Mar 2023 18:45:20 +0000 -Subject: [PATCH] linux-user: Emulate /proc/cpuinfo output for riscv - -RISC-V does not expose all extensions via hwcaps, thus some userspace -applications may want to query these via /proc/cpuinfo. - -Currently when querying this file the host's file is shown instead -which is slightly confusing. Emulate a basic /proc/cpuinfo file -with mmu info and an ISA string. ---- - linux-user/syscall.c | 34 ++++++++++++++++++++++++++++++++-- - tests/tcg/riscv64/cpuinfo.c | 30 ++++++++++++++++++++++++++++++ - 2 files changed, 62 insertions(+), 2 deletions(-) - create mode 100644 tests/tcg/riscv64/cpuinfo.c - -diff --git a/linux-user/syscall.c b/linux-user/syscall.c -index 333e6b7026..e6d85e0e8a 100644 ---- a/linux-user/syscall.c -+++ b/linux-user/syscall.c -@@ -8231,7 +8231,8 @@ void target_exception_dump(CPUArchState *env, const char *fmt, int code) - } - - #if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN || \ -- defined(TARGET_SPARC) || defined(TARGET_M68K) || defined(TARGET_HPPA) -+ defined(TARGET_SPARC) || defined(TARGET_M68K) || defined(TARGET_HPPA) || \ -+ defined(TARGET_RISCV) - static int is_proc(const char *filename, const char *entry) - { - return strcmp(filename, entry) == 0; -@@ -8309,6 +8310,35 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) - } - #endif - -+#if defined(TARGET_RISCV) -+static int open_cpuinfo(CPUArchState *cpu_env, int fd) -+{ -+ int i; -+ int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); -+ RISCVCPU *cpu = env_archcpu(cpu_env); -+ const RISCVCPUConfig *cfg = riscv_cpu_cfg((CPURISCVState *) cpu_env); -+ char *isa_string = riscv_isa_string(cpu); -+ const char *mmu; -+ -+ if (cfg->mmu) { -+ mmu = (cpu_env->xl == MXL_RV32) ? "sv32" : "sv48"; -+ } else { -+ mmu = "none"; -+ } -+ -+ for (i = 0; i < num_cpus; i++) { -+ dprintf(fd, "processor\t: %d\n", i); -+ dprintf(fd, "hart\t\t: %d\n", i); -+ dprintf(fd, "isa\t\t: %s\n", isa_string); -+ dprintf(fd, "mmu\t\t: %s\n", mmu); -+ dprintf(fd, "uarch\t\t: qemu\n\n"); -+ } -+ -+ g_free(isa_string); -+ return 0; -+} -+#endif -+ - #if defined(TARGET_M68K) - static int open_hardware(CPUArchState *cpu_env, int fd) - { -@@ -8333,7 +8363,7 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int - #if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN - { "/proc/net/route", open_net_route, is_proc }, - #endif --#if defined(TARGET_SPARC) || defined(TARGET_HPPA) -+#if defined(TARGET_SPARC) || defined(TARGET_HPPA) || defined(TARGET_RISCV) - { "/proc/cpuinfo", open_cpuinfo, is_proc }, - #endif - #if defined(TARGET_M68K) -diff --git a/tests/tcg/riscv64/cpuinfo.c b/tests/tcg/riscv64/cpuinfo.c -new file mode 100644 -index 0000000000..296abd0a8c ---- /dev/null -+++ b/tests/tcg/riscv64/cpuinfo.c -@@ -0,0 +1,30 @@ -+#include -+#include -+#include -+#include -+ -+#define BUFFER_SIZE 1024 -+ -+int main(void) -+{ -+ char buffer[BUFFER_SIZE]; -+ FILE *fp = fopen("/proc/cpuinfo", "r"); -+ assert(fp != NULL); -+ -+ while (fgets(buffer, BUFFER_SIZE, fp) != NULL) { -+ if (strstr(buffer, "processor") != NULL) { -+ assert(strstr(buffer, "processor\t: ") == buffer); -+ } else if (strstr(buffer, "hart") != NULL) { -+ assert(strstr(buffer, "hart\t\t: ") == buffer); -+ } else if (strstr(buffer, "isa") != NULL) { -+ assert(strcmp(buffer, "isa\t\t: rv64imafdc_zicsr_zifencei\n") == 0); -+ } else if (strstr(buffer, "mmu") != NULL) { -+ assert(strcmp(buffer, "mmu\t\t: sv48\n") == 0); -+ } else if (strstr(buffer, "uarch") != NULL) { -+ assert(strcmp(buffer, "uarch\t\t: qemu\n") == 0); -+ } -+ } -+ -+ fclose(fp); -+ return 0; -+} --- -2.34.1 - diff --git a/tests/all/traps.rs b/tests/all/traps.rs index a77443ac209c..cb33af235b49 100644 --- a/tests/all/traps.rs +++ b/tests/all/traps.rs @@ -1347,23 +1347,18 @@ fn wasm_fault_address_reported_by_default() -> Result<()> { )?; let err = Instance::new(&mut store, &module, &[]).unwrap_err(); - // On s390x faulting addressess are rounded to the nearest page boundary - // instead of having the precise address reported. - let mut expected_addr = 0xdeadbeef_u32; - if cfg!(target_arch = "s390x") { - expected_addr &= 0xfffff000; - } - // NB: at this time there's no programmatic access to the fault address // because it's not always available for load/store traps. Only static // memories on 32-bit have this information, but bounds-checked memories // use manual trapping instructions and otherwise don't have a means of // communicating the faulting address at this time. + // + // It looks like the exact reported fault address may not be deterministic, + // so assert that we have the right error message, but not the exact address. let err = format!("{err:?}"); assert!( - err.contains(&format!( - "memory fault at wasm address 0x{expected_addr:x} in linear memory of size 0x10000" - )), + err.contains("memory fault at wasm address ") + && err.contains(" in linear memory of size 0x10000"), "bad error: {err}" ); Ok(()) From 8a88ff6eb8803f5efcd41fbab64b823ef4c3ba5f Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 28 Sep 2023 15:51:51 -0700 Subject: [PATCH 023/199] Wasi-http: support inbound requests (proxy world) (#7091) * Move the incoming_handler impl into http_impl * Remove the incoming handler -- we need to use it as a guest export * Start adding a test-programs test for the server side of wasi-http * Progress towards running a server test * Implement incoming-request-method * Validate outparam value * Initial incoming handler test * Implement more of the incoming api * Finish the incoming api implementations * Initial cut at `wasmtime serve` * fix warning * wasmtime-cli: invoke ServeCommand, and add enough stuff to the linker to run trivial test * fix warnings * fix warnings * argument parsing: allow --addr to specify sockaddr * rustfmt * sync wit definitions between wasmtime-wasi and wasmtime-wasi-http * cargo vet: add an import config and wildcard audit for wasmtime-wmemcheck * cargo vet: audit signal-hook-registry * Remove duplicate add_to_linker calls for preview2 interfaces prtest:full * Add a method to finish outgoing responses Co-authored-by: Adam Foltzer Co-authored-by: Pat Hickey * Mark the result of the incoming_{request,response}_consume methods as own * Explicit versions for http-body and http-body-util * Explicit `serve` feature for the `wasmtime serve` command * Move the spawn outside of the future returned by `ProxyHandler::call` * Review feedback --------- Co-authored-by: Trevor Elliott Co-authored-by: Adam Foltzer --- Cargo.lock | 22 +- Cargo.toml | 20 +- crates/test-programs/Cargo.toml | 1 + crates/test-programs/build.rs | 10 +- .../tests/wasi-http-components-sync.rs | 8 +- .../tests/wasi-http-components.rs | 5 +- crates/test-programs/tests/wasi-http-proxy.rs | 146 +++++++++++ .../wasi-http-proxy-tests/Cargo.toml | 12 + .../wasi-http-proxy-tests/src/lib.rs | 33 +++ .../test-programs/wasi-http-tests/src/lib.rs | 2 +- crates/wasi-http/Cargo.toml | 11 +- crates/wasi-http/src/body.rs | 53 ++-- crates/wasi-http/src/http_impl.rs | 13 +- crates/wasi-http/src/incoming_handler.rs | 12 - crates/wasi-http/src/lib.rs | 1 - crates/wasi-http/src/proxy.rs | 49 +++- crates/wasi-http/src/types.rs | 168 +++++++++++-- crates/wasi-http/src/types_impl.rs | 231 +++++++++++++----- crates/wasi-http/wit/deps/http/types.wit | 14 +- crates/wasi/wit/deps/http/types.wit | 14 +- src/bin/wasmtime.rs | 7 + src/commands.rs | 6 + src/commands/serve.rs | 199 +++++++++++++++ supply-chain/audits.toml | 13 + supply-chain/config.toml | 3 + supply-chain/imports.lock | 10 + 26 files changed, 905 insertions(+), 158 deletions(-) create mode 100644 crates/test-programs/tests/wasi-http-proxy.rs create mode 100644 crates/test-programs/wasi-http-proxy-tests/Cargo.toml create mode 100644 crates/test-programs/wasi-http-proxy-tests/src/lib.rs delete mode 100644 crates/wasi-http/src/incoming_handler.rs create mode 100644 src/commands/serve.rs diff --git a/Cargo.lock b/Cargo.lock index 28ec4893c185..edd189ea6a03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2466,6 +2466,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "similar" version = "2.2.0" @@ -2659,6 +2668,7 @@ name = "test-programs" version = "0.0.0" dependencies = [ "anyhow", + "bytes", "cap-rand", "cap-std", "cargo_metadata", @@ -2750,6 +2760,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys", @@ -3042,6 +3053,13 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "wasi-http-proxy-tests" +version = "0.0.0" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasi-http-tests" version = "0.0.0" @@ -3394,6 +3412,9 @@ dependencies = [ "criterion", "env_logger 0.10.0", "filecheck", + "http-body", + "http-body-util", + "hyper", "libc", "listenfd", "log", @@ -3763,7 +3784,6 @@ dependencies = [ "http-body-util", "hyper", "rustls", - "thiserror", "tokio", "tokio-rustls", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 05ef6d4718f9..db8b4ff5836c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,12 @@ serde_json = { workspace = true } wasmparser = { workspace = true } wasm-encoder = { workspace = true } +async-trait = { workspace = true } +tokio = { workspace = true, optional = true, features = [ "signal", "macros" ] } +hyper = { workspace = true, optional = true } +http-body = { workspace = true } +http-body-util = { workspace = true } + [target.'cfg(unix)'.dependencies] rustix = { workspace = true, features = ["mm", "param"] } @@ -60,7 +66,7 @@ filecheck = { workspace = true } tempfile = { workspace = true } test-programs = { path = "crates/test-programs" } wasmtime-runtime = { workspace = true } -tokio = { version = "1.8.0", features = ["rt", "time", "macros", "rt-multi-thread"] } +tokio = { workspace = true, features = ["rt", "time", "macros", "rt-multi-thread"] } wast = { workspace = true } criterion = "0.5.0" num_cpus = "1.13.0" @@ -101,6 +107,7 @@ members = [ "crates/jit-icache-coherence", "crates/test-programs/wasi-tests", "crates/test-programs/wasi-http-tests", + "crates/test-programs/wasi-http-proxy-tests", "crates/test-programs/wasi-sockets-tests", "crates/test-programs/command-tests", "crates/test-programs/reactor-tests", @@ -251,7 +258,11 @@ tempfile = "3.1.0" filecheck = "0.5.0" libc = "0.2.60" file-per-thread-logger = "0.2.0" -tokio = { version = "1.26.0" } +tokio = { version = "1.26.0", features = [ "rt", "time" ] } +hyper = "=1.0.0-rc.3" +http = "0.2.9" +http-body = "=1.0.0-rc.2" +http-body-util = "=0.1.0-rc.2" bytes = "1.4" futures = { version = "0.3.27", default-features = false } indexmap = "2.0.0" @@ -275,7 +286,7 @@ jitdump = ["wasmtime/jitdump"] vtune = ["wasmtime/vtune"] wasi-nn = ["dep:wasmtime-wasi-nn"] wasi-threads = ["dep:wasmtime-wasi-threads"] -wasi-http = ["dep:wasmtime-wasi-http", "wasmtime-wasi-http?/sync"] +wasi-http = ["component-model", "dep:wasmtime-wasi-http", "dep:tokio", "dep:hyper", "wasmtime-wasi-http?/sync"] pooling-allocator = ["wasmtime/pooling-allocator", "wasmtime-cli-flags/pooling-allocator"] all-arch = ["wasmtime/all-arch"] component-model = [ @@ -286,6 +297,9 @@ component-model = [ winch = ["wasmtime/winch"] wmemcheck = ["wasmtime/wmemcheck"] +# Enable the `wasmtime serve` command +serve = ["wasi-http", "component-model"] + [[test]] name = "host_segfault" harness = false diff --git a/crates/test-programs/Cargo.toml b/crates/test-programs/Cargo.toml index 4808b8e462e7..12ad3188e2ef 100644 --- a/crates/test-programs/Cargo.toml +++ b/crates/test-programs/Cargo.toml @@ -15,6 +15,7 @@ heck = { workspace = true } [dependencies] anyhow = { workspace = true } +bytes = { workspace = true } http = { version = "0.2.9" } http-body = "1.0.0-rc.2" http-body-util = "0.1.0-rc.2" diff --git a/crates/test-programs/build.rs b/crates/test-programs/build.rs index 4062b02fea13..0de514283caa 100644 --- a/crates/test-programs/build.rs +++ b/crates/test-programs/build.rs @@ -33,6 +33,7 @@ fn build_and_generate_tests() { println!("cargo:rerun-if-changed=./wasi-sockets-tests"); if BUILD_WASI_HTTP_TESTS { println!("cargo:rerun-if-changed=./wasi-http-tests"); + println!("cargo:rerun-if-changed=./wasi-http-proxy-tests"); } else { println!("cargo:rustc-cfg=skip_wasi_http_tests"); } @@ -50,6 +51,7 @@ fn build_and_generate_tests() { .env_remove("CARGO_ENCODED_RUSTFLAGS"); if BUILD_WASI_HTTP_TESTS { cmd.arg("--package=wasi-http-tests"); + cmd.arg("--package=wasi-http-proxy-tests"); } let status = cmd.status().unwrap(); assert!(status.success()); @@ -60,8 +62,14 @@ fn build_and_generate_tests() { components_rs(&meta, "wasi-tests", "bin", &command_adapter, &out_dir); if BUILD_WASI_HTTP_TESTS { - modules_rs(&meta, "wasi-http-tests", "bin", &out_dir); components_rs(&meta, "wasi-http-tests", "bin", &command_adapter, &out_dir); + components_rs( + &meta, + "wasi-http-proxy-tests", + "cdylib", + &reactor_adapter, + &out_dir, + ); } components_rs(&meta, "command-tests", "bin", &command_adapter, &out_dir); diff --git a/crates/test-programs/tests/wasi-http-components-sync.rs b/crates/test-programs/tests/wasi-http-components-sync.rs index 33c638fc6883..222fc800edce 100644 --- a/crates/test-programs/tests/wasi-http-components-sync.rs +++ b/crates/test-programs/tests/wasi-http-components-sync.rs @@ -4,9 +4,8 @@ use wasmtime::{ Config, Engine, Store, }; use wasmtime_wasi::preview2::{ - command::sync::{add_to_linker, Command}, - pipe::MemoryOutputPipe, - IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView, + command::sync::Command, pipe::MemoryOutputPipe, IsATTY, Table, WasiCtx, WasiCtxBuilder, + WasiView, }; use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; @@ -60,8 +59,7 @@ fn instantiate_component( ctx: Ctx, ) -> Result<(Store, Command), anyhow::Error> { let mut linker = Linker::new(&ENGINE); - add_to_linker(&mut linker)?; - wasmtime_wasi_http::proxy::add_to_linker(&mut linker)?; + wasmtime_wasi_http::proxy::sync::add_to_linker(&mut linker)?; let mut store = Store::new(&ENGINE, ctx); diff --git a/crates/test-programs/tests/wasi-http-components.rs b/crates/test-programs/tests/wasi-http-components.rs index 8d1fa1f96c52..11fb6bd498ca 100644 --- a/crates/test-programs/tests/wasi-http-components.rs +++ b/crates/test-programs/tests/wasi-http-components.rs @@ -4,9 +4,7 @@ use wasmtime::{ Config, Engine, Store, }; use wasmtime_wasi::preview2::{ - command::{add_to_linker, Command}, - pipe::MemoryOutputPipe, - IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView, + command::Command, pipe::MemoryOutputPipe, IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView, }; use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; @@ -60,7 +58,6 @@ async fn instantiate_component( ctx: Ctx, ) -> Result<(Store, Command), anyhow::Error> { let mut linker = Linker::new(&ENGINE); - add_to_linker(&mut linker)?; wasmtime_wasi_http::proxy::add_to_linker(&mut linker)?; let mut store = Store::new(&ENGINE, ctx); diff --git a/crates/test-programs/tests/wasi-http-proxy.rs b/crates/test-programs/tests/wasi-http-proxy.rs new file mode 100644 index 000000000000..f344dc15071f --- /dev/null +++ b/crates/test-programs/tests/wasi-http-proxy.rs @@ -0,0 +1,146 @@ +#![cfg(all(feature = "test_programs", not(skip_wasi_http_tests)))] +use anyhow::Context; +use wasmtime::{ + component::{Component, Linker}, + Config, Engine, Store, +}; +use wasmtime_wasi::preview2::{ + self, pipe::MemoryOutputPipe, IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView, +}; +use wasmtime_wasi_http::{proxy::Proxy, WasiHttpCtx, WasiHttpView}; + +lazy_static::lazy_static! { + static ref ENGINE: Engine = { + let mut config = Config::new(); + config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); + config.wasm_component_model(true); + config.async_support(true); + let engine = Engine::new(&config).unwrap(); + engine + }; +} +// uses ENGINE, creates a fn get_module(&str) -> Module +include!(concat!( + env!("OUT_DIR"), + "/wasi_http_proxy_tests_components.rs" +)); + +struct Ctx { + table: Table, + wasi: WasiCtx, + http: WasiHttpCtx, +} + +impl WasiView for Ctx { + fn table(&self) -> &Table { + &self.table + } + fn table_mut(&mut self) -> &mut Table { + &mut self.table + } + fn ctx(&self) -> &WasiCtx { + &self.wasi + } + fn ctx_mut(&mut self) -> &mut WasiCtx { + &mut self.wasi + } +} + +impl WasiHttpView for Ctx { + fn table(&mut self) -> &mut Table { + &mut self.table + } + fn ctx(&mut self) -> &mut WasiHttpCtx { + &mut self.http + } +} + +async fn instantiate(component: Component, ctx: Ctx) -> Result<(Store, Proxy), anyhow::Error> { + let mut linker = Linker::new(&ENGINE); + wasmtime_wasi_http::proxy::add_to_linker(&mut linker)?; + + let mut store = Store::new(&ENGINE, ctx); + + let (proxy, _instance) = Proxy::instantiate_async(&mut store, &component, &linker).await?; + Ok((store, proxy)) +} + +#[test_log::test(tokio::test)] +async fn wasi_http_proxy_tests() -> anyhow::Result<()> { + let stdout = MemoryOutputPipe::new(4096); + let stderr = MemoryOutputPipe::new(4096); + + let mut table = Table::new(); + let component = get_component("wasi_http_proxy_tests"); + + // Create our wasi context. + let mut builder = WasiCtxBuilder::new(); + builder.stdout(stdout.clone(), IsATTY::No); + builder.stderr(stderr.clone(), IsATTY::No); + for (var, val) in test_programs::wasi_tests_environment() { + builder.env(var, val); + } + let wasi = builder.build(&mut table)?; + let http = WasiHttpCtx; + + let ctx = Ctx { table, wasi, http }; + + let (mut store, proxy) = instantiate(component, ctx).await?; + + let req = { + use http_body_util::{BodyExt, Empty}; + + let req = hyper::Request::builder().method(http::Method::GET).body( + Empty::::new() + .map_err(|e| anyhow::anyhow!(e)) + .boxed(), + )?; + store.data_mut().new_incoming_request(req)? + }; + + let (sender, receiver) = tokio::sync::oneshot::channel(); + let out = store.data_mut().new_response_outparam(sender)?; + + let handle = preview2::spawn(async move { + proxy + .wasi_http_incoming_handler() + .call_handle(&mut store, req, out) + .await?; + + Ok::<_, anyhow::Error>(()) + }); + + let resp = match receiver.await { + Ok(Ok(resp)) => { + use http_body_util::BodyExt; + let (parts, body) = resp.into_parts(); + let collected = BodyExt::collect(body).await?; + Ok(hyper::Response::from_parts(parts, collected)) + } + + Ok(Err(e)) => Err(e), + + // This happens if the wasm never calls `set-response-outparam` + Err(e) => panic!("Failed to receive a response: {e:?}"), + }; + + // Now that the response has been processed, we can wait on the wasm to finish without + // deadlocking. + handle.await.context("Component execution")?; + + let stdout = stdout.contents(); + if !stdout.is_empty() { + println!("[guest] stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); + } + let stderr = stderr.contents(); + if !stderr.is_empty() { + println!("[guest] stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); + } + + match resp { + Ok(resp) => println!("response: {resp:?}"), + Err(e) => panic!("Error given in response: {e:?}"), + }; + + Ok(()) +} diff --git a/crates/test-programs/wasi-http-proxy-tests/Cargo.toml b/crates/test-programs/wasi-http-proxy-tests/Cargo.toml new file mode 100644 index 000000000000..fc87dc1bce7d --- /dev/null +++ b/crates/test-programs/wasi-http-proxy-tests/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wasi-http-proxy-tests" +version = "0.0.0" +readme = "README.md" +edition = "2021" +publish = false + +[lib] +crate-type=["cdylib"] + +[dependencies] +wit-bindgen = { workspace = true, features = ["macros", "realloc"] } diff --git a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs b/crates/test-programs/wasi-http-proxy-tests/src/lib.rs new file mode 100644 index 000000000000..239f1754327d --- /dev/null +++ b/crates/test-programs/wasi-http-proxy-tests/src/lib.rs @@ -0,0 +1,33 @@ +pub mod bindings { + use super::T; + + wit_bindgen::generate!({ + path: "../../wasi-http/wit", + world: "wasi:http/proxy", + exports: { + "wasi:http/incoming-handler": T, + }, + }); +} + +use bindings::wasi::http::types::{IncomingRequest, ResponseOutparam}; + +struct T; + +impl bindings::exports::wasi::http::incoming_handler::Guest for T { + fn handle(_request: IncomingRequest, outparam: ResponseOutparam) { + let hdrs = bindings::wasi::http::types::new_fields(&[]); + let resp = bindings::wasi::http::types::new_outgoing_response(200, hdrs); + let body = + bindings::wasi::http::types::outgoing_response_write(resp).expect("outgoing response"); + + bindings::wasi::http::types::set_response_outparam(outparam, Ok(resp)); + + let out = bindings::wasi::http::types::outgoing_body_write(body).expect("outgoing stream"); + bindings::wasi::io::streams::blocking_write_and_flush(out, b"hello, world!") + .expect("writing response"); + + bindings::wasi::io::streams::drop_output_stream(out); + bindings::wasi::http::types::outgoing_body_finish(body, None); + } +} diff --git a/crates/test-programs/wasi-http-tests/src/lib.rs b/crates/test-programs/wasi-http-tests/src/lib.rs index d516af03da16..e2ae84de6998 100644 --- a/crates/test-programs/wasi-http-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-tests/src/lib.rs @@ -119,7 +119,7 @@ pub async fn request( // TODO: The current implementation requires this drop after the request is sent. // The ownership semantics are unclear in wasi-http we should clarify exactly what is // supposed to happen here. - http_types::drop_outgoing_body(outgoing_body); + http_types::outgoing_body_finish(outgoing_body, None); let incoming_response = match http_types::future_incoming_response_get(future_response) { Some(result) => result.map_err(|_| anyhow!("incoming response errored"))?, diff --git a/crates/wasi-http/Cargo.toml b/crates/wasi-http/Cargo.toml index 3ebbea483adc..354d00624525 100644 --- a/crates/wasi-http/Cargo.toml +++ b/crates/wasi-http/Cargo.toml @@ -12,16 +12,15 @@ anyhow = { workspace = true } async-trait = { workspace = true } bytes = { workspace = true } futures = { workspace = true, default-features = false } -hyper = { version = "=1.0.0-rc.3", features = ["full"] } -tokio = { version = "1", default-features = false, features = [ +hyper = { workspace = true, features = ["full"] } +tokio = { workspace = true, features = [ "net", "rt-multi-thread", "time", ] } -http = { version = "0.2.9" } -http-body = "1.0.0-rc.2" -http-body-util = "0.1.0-rc.2" -thiserror = { workspace = true } +http = { workspace = true } +http-body = { workspace = true } +http-body-util = { workspace = true } tracing = { workspace = true } wasmtime-wasi = { workspace = true, default-features = false, features = [ "preview2", diff --git a/crates/wasi-http/src/body.rs b/crates/wasi-http/src/body.rs index 298fe7b92bfe..fa021a3ddd61 100644 --- a/crates/wasi-http/src/body.rs +++ b/crates/wasi-http/src/body.rs @@ -4,7 +4,6 @@ use bytes::Bytes; use http_body_util::combinators::BoxBody; use std::future::Future; use std::{ - convert::Infallible, pin::Pin, sync::{Arc, Mutex}, time::Duration, @@ -15,11 +14,13 @@ use wasmtime_wasi::preview2::{ StreamRuntimeError, StreamState, }; +pub type HyperIncomingBody = BoxBody; + /// Holds onto the things needed to construct a [`HostIncomingBody`] until we are ready to build /// one. The HostIncomingBody spawns a task that starts consuming the incoming body, and we don't /// want to do that unless the user asks to consume the body. pub struct HostIncomingBodyBuilder { - pub body: hyper::body::Incoming, + pub body: HyperIncomingBody, pub between_bytes_timeout: Duration, } @@ -45,7 +46,7 @@ impl HostIncomingBodyBuilder { Ok(Some(Ok(frame))) => frame, Ok(Some(Err(e))) => { - match body_writer.send(Err(anyhow::anyhow!(e))).await { + match body_writer.send(Err(e)).await { Ok(_) => {} // If the body read end has dropped, then we report this error with the // trailers. unwrap and rewrap Err because the Ok side of these two Results @@ -239,29 +240,32 @@ impl HostFutureTrailers { } } -pub type HyperBody = BoxBody; +pub type HyperOutgoingBody = BoxBody; + +pub enum FinishMessage { + Finished, + Trailers(hyper::HeaderMap), + Abort, +} pub struct HostOutgoingBody { pub body_output_stream: Option>, - pub trailers_sender: Option>, + pub finish_sender: Option>, } impl HostOutgoingBody { - pub fn new() -> (Self, HyperBody) { + pub fn new() -> (Self, HyperOutgoingBody) { use http_body_util::BodyExt; - use hyper::{ - body::{Body, Frame}, - HeaderMap, - }; + use hyper::body::{Body, Frame}; use std::task::{Context, Poll}; use tokio::sync::oneshot::error::RecvError; struct BodyImpl { body_receiver: mpsc::Receiver, - trailers_receiver: Option>, + finish_receiver: Option>, } impl Body for BodyImpl { type Data = Bytes; - type Error = Infallible; + type Error = anyhow::Error; fn poll_frame( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -272,16 +276,21 @@ impl HostOutgoingBody { // This means that the `body_sender` end of the channel has been dropped. Poll::Ready(None) => { - if let Some(mut trailers_receiver) = self.as_mut().trailers_receiver.take() - { - match Pin::new(&mut trailers_receiver).poll(cx) { + if let Some(mut finish_receiver) = self.as_mut().finish_receiver.take() { + match Pin::new(&mut finish_receiver).poll(cx) { Poll::Pending => { - self.as_mut().trailers_receiver = Some(trailers_receiver); + self.as_mut().finish_receiver = Some(finish_receiver); Poll::Pending } - Poll::Ready(Ok(trailers)) => { - Poll::Ready(Some(Ok(Frame::trailers(trailers)))) - } + Poll::Ready(Ok(message)) => match message { + FinishMessage::Finished => Poll::Ready(None), + FinishMessage::Trailers(trailers) => { + Poll::Ready(Some(Ok(Frame::trailers(trailers)))) + } + FinishMessage::Abort => Poll::Ready(Some(Err( + anyhow::anyhow!("response corrupted"), + ))), + }, Poll::Ready(Err(RecvError { .. })) => Poll::Ready(None), } } else { @@ -293,17 +302,17 @@ impl HostOutgoingBody { } let (body_sender, body_receiver) = mpsc::channel(1); - let (trailers_sender, trailers_receiver) = oneshot::channel(); + let (finish_sender, finish_receiver) = oneshot::channel(); let body_impl = BodyImpl { body_receiver, - trailers_receiver: Some(trailers_receiver), + finish_receiver: Some(finish_receiver), } .boxed(); ( Self { // TODO: this capacity constant is arbitrary, and should be configurable body_output_stream: Some(Box::new(BodyWriteStream::new(1024 * 1024, body_sender))), - trailers_sender: Some(trailers_sender), + finish_sender: Some(finish_sender), }, body_impl, ) diff --git a/crates/wasi-http/src/http_impl.rs b/crates/wasi-http/src/http_impl.rs index 8c9e6358fb55..ae05c998d4ad 100644 --- a/crates/wasi-http/src/http_impl.rs +++ b/crates/wasi-http/src/http_impl.rs @@ -2,7 +2,7 @@ use crate::bindings::http::{ outgoing_handler, types::{FutureIncomingResponse, OutgoingRequest, RequestOptions, Scheme}, }; -use crate::types::{HostFutureIncomingResponse, IncomingResponseInternal, TableHttpExt}; +use crate::types::{self, HostFutureIncomingResponse, IncomingResponseInternal, TableHttpExt}; use crate::WasiHttpView; use anyhow::Context; use bytes::Bytes; @@ -37,7 +37,7 @@ impl outgoing_handler::Host for T { .unwrap_or(600 * 1000) as u64, ); - let req = self.table().delete_outgoing_request(request_id)?; + let req = types::OutgoingRequestLens::from(request_id).delete(self.table())?; let method = match req.method { crate::bindings::http::types::Method::Get => Method::GET, @@ -81,7 +81,11 @@ impl outgoing_handler::Host for T { builder = builder.header(k, v); } - let body = req.body.unwrap_or_else(|| Empty::::new().boxed()); + let body = req.body.unwrap_or_else(|| { + Empty::::new() + .map_err(|_| anyhow::anyhow!("empty error")) + .boxed() + }); let request = builder.body(body).map_err(http_protocol_error)?; @@ -159,7 +163,8 @@ impl outgoing_handler::Host for T { let resp = timeout(first_byte_timeout, sender.send_request(request)) .await .map_err(|_| timeout_error("first byte"))? - .map_err(hyper_protocol_error)?; + .map_err(hyper_protocol_error)? + .map(|body| body.map_err(|e| anyhow::anyhow!(e)).boxed()); Ok(IncomingResponseInternal { resp, diff --git a/crates/wasi-http/src/incoming_handler.rs b/crates/wasi-http/src/incoming_handler.rs deleted file mode 100644 index 3fd26bf7d0af..000000000000 --- a/crates/wasi-http/src/incoming_handler.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::bindings::http::types::{IncomingRequest, ResponseOutparam}; -use crate::WasiHttpView; - -impl crate::bindings::http::incoming_handler::Host for T { - fn handle( - &mut self, - _request: IncomingRequest, - _response_out: ResponseOutparam, - ) -> wasmtime::Result<()> { - anyhow::bail!("unimplemented: [incoming_handler] handle") - } -} diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs index 96a6fb835fb1..8501a0cabb81 100644 --- a/crates/wasi-http/src/lib.rs +++ b/crates/wasi-http/src/lib.rs @@ -2,7 +2,6 @@ pub use crate::types::{WasiHttpCtx, WasiHttpView}; pub mod body; pub mod http_impl; -pub mod incoming_handler; pub mod proxy; pub mod types; pub mod types_impl; diff --git a/crates/wasi-http/src/proxy.rs b/crates/wasi-http/src/proxy.rs index 9f156fd09a14..b276a6545265 100644 --- a/crates/wasi-http/src/proxy.rs +++ b/crates/wasi-http/src/proxy.rs @@ -4,7 +4,7 @@ use wasmtime_wasi::preview2; wasmtime::component::bindgen!({ world: "wasi:http/proxy", tracing: true, - async: false, + async: true, with: { "wasi:cli/stderr": preview2::bindings::cli::stderr, "wasi:cli/stdin": preview2::bindings::cli::stdin, @@ -23,10 +23,53 @@ wasmtime::component::bindgen!({ pub fn add_to_linker(l: &mut wasmtime::component::Linker) -> anyhow::Result<()> where - T: WasiHttpView + bindings::http::types::Host, + T: WasiHttpView + preview2::WasiView + bindings::http::types::Host, { - bindings::http::incoming_handler::add_to_linker(l, |t| t)?; + // TODO: this shouldn't be required, but the adapter unconditionally pulls in all of these + // dependencies. + preview2::command::add_to_linker(l)?; + bindings::http::outgoing_handler::add_to_linker(l, |t| t)?; bindings::http::types::add_to_linker(l, |t| t)?; + Ok(()) } + +pub mod sync { + use crate::{bindings, WasiHttpView}; + use wasmtime_wasi::preview2; + + wasmtime::component::bindgen!({ + world: "wasi:http/proxy", + tracing: true, + async: false, + with: { + "wasi:cli/stderr": preview2::bindings::cli::stderr, + "wasi:cli/stdin": preview2::bindings::cli::stdin, + "wasi:cli/stdout": preview2::bindings::cli::stdout, + "wasi:clocks/monotonic-clock": preview2::bindings::clocks::monotonic_clock, + "wasi:clocks/timezone": preview2::bindings::clocks::timezone, + "wasi:clocks/wall-clock": preview2::bindings::clocks::wall_clock, + "wasi:http/incoming-handler": bindings::http::incoming_handler, + "wasi:http/outgoing-handler": bindings::http::outgoing_handler, + "wasi:http/types": bindings::http::types, + "wasi:io/streams": preview2::bindings::io::streams, + "wasi:poll/poll": preview2::bindings::poll::poll, + "wasi:random/random": preview2::bindings::random::random, + }, + }); + + pub fn add_to_linker(l: &mut wasmtime::component::Linker) -> anyhow::Result<()> + where + T: WasiHttpView + preview2::WasiView + bindings::http::types::Host, + { + // TODO: this shouldn't be required, but the adapter unconditionally pulls in all of these + // dependencies. + preview2::command::sync::add_to_linker(l)?; + + bindings::http::outgoing_handler::add_to_linker(l, |t| t)?; + bindings::http::types::add_to_linker(l, |t| t)?; + + Ok(()) + } +} diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs index 3b73f7f3fd49..49baba25aac1 100644 --- a/crates/wasi-http/src/types.rs +++ b/crates/wasi-http/src/types.rs @@ -2,9 +2,13 @@ //! implementation of the wasi-http API. use crate::{ - bindings::http::types::{FutureTrailers, IncomingBody, Method, OutgoingBody, Scheme}, + bindings::http::types::{ + self, FutureTrailers, IncomingBody, IncomingRequest, Method, OutgoingBody, + OutgoingResponse, ResponseOutparam, Scheme, + }, body::{ - HostFutureTrailers, HostIncomingBody, HostIncomingBodyBuilder, HostOutgoingBody, HyperBody, + HostFutureTrailers, HostIncomingBody, HostIncomingBodyBuilder, HostOutgoingBody, + HyperIncomingBody, HyperOutgoingBody, }, }; use std::any::Any; @@ -18,15 +22,60 @@ pub struct WasiHttpCtx; pub trait WasiHttpView: Send { fn ctx(&mut self) -> &mut WasiHttpCtx; fn table(&mut self) -> &mut Table; + + fn new_incoming_request( + &mut self, + req: hyper::Request, + ) -> wasmtime::Result { + let (parts, body) = req.into_parts(); + let body = HostIncomingBodyBuilder { + body, + // TODO: this needs to be plumbed through + between_bytes_timeout: std::time::Duration::from_millis(600 * 1000), + }; + Ok(IncomingRequestLens::push( + self.table(), + HostIncomingRequest { + parts, + body: Some(body), + }, + )? + .id) + } + + fn new_response_outparam( + &mut self, + result: tokio::sync::oneshot::Sender< + Result, types::Error>, + >, + ) -> wasmtime::Result { + Ok(ResponseOutparamLens::push(self.table(), HostResponseOutparam { result })?.id) + } +} + +pub type IncomingRequestLens = TableLens; + +pub struct HostIncomingRequest { + pub parts: http::request::Parts, + pub body: Option, +} + +pub type ResponseOutparamLens = TableLens; + +pub struct HostResponseOutparam { + pub result: + tokio::sync::oneshot::Sender, types::Error>>, } +pub type OutgoingRequestLens = TableLens; + pub struct HostOutgoingRequest { pub method: Method, pub scheme: Option, pub path_with_query: String, pub authority: String, pub headers: FieldMap, - pub body: Option, + pub body: Option, } pub struct HostIncomingResponse { @@ -36,6 +85,37 @@ pub struct HostIncomingResponse { pub worker: AbortOnDropJoinHandle>, } +pub type OutgoingResponseLens = TableLens; + +pub struct HostOutgoingResponse { + pub status: u16, + pub headers: FieldMap, + pub body: Option, +} + +impl TryFrom for hyper::Response { + type Error = http::Error; + + fn try_from( + resp: HostOutgoingResponse, + ) -> Result, Self::Error> { + use http_body_util::{BodyExt, Empty}; + + let mut builder = hyper::Response::builder().status(resp.status); + + *builder.headers_mut().unwrap() = resp.headers; + + match resp.body { + Some(body) => builder.body(body), + None => builder.body( + Empty::::new() + .map_err(|_| anyhow::anyhow!("empty error")) + .boxed(), + ), + } + } +} + pub type FieldMap = hyper::HeaderMap; pub enum HostFields { @@ -54,7 +134,7 @@ pub enum HostFields { } pub struct IncomingResponseInternal { - pub resp: hyper::Response, + pub resp: hyper::Response, pub worker: AbortOnDropJoinHandle>, pub between_bytes_timeout: std::time::Duration, } @@ -105,13 +185,52 @@ impl std::future::Future for HostFutureIncomingResponse { } } -#[async_trait::async_trait] +pub struct TableLens { + id: u32, + _unused: std::marker::PhantomData, +} + +impl TableLens { + pub fn from(id: u32) -> Self { + Self { + id, + _unused: std::marker::PhantomData {}, + } + } + + pub fn into(self) -> u32 { + self.id + } + + #[inline(always)] + pub fn push(table: &mut Table, val: T) -> Result { + let id = table.push(Box::new(val))?; + Ok(Self::from(id)) + } + + #[inline(always)] + pub fn get<'t>(&self, table: &'t Table) -> Result<&'t T, TableError> { + table.get(self.id) + } + + #[inline(always)] + pub fn get_mut<'t>(&self, table: &'t mut Table) -> Result<&'t mut T, TableError> { + table.get_mut(self.id) + } + + #[inline(always)] + pub fn delete(&self, table: &mut Table) -> Result { + table.delete(self.id) + } +} + pub trait TableHttpExt { - fn push_outgoing_response(&mut self, request: HostOutgoingRequest) -> Result; - fn get_outgoing_request(&self, id: u32) -> Result<&HostOutgoingRequest, TableError>; - fn get_outgoing_request_mut(&mut self, id: u32) - -> Result<&mut HostOutgoingRequest, TableError>; - fn delete_outgoing_request(&mut self, id: u32) -> Result; + fn push_outgoing_response( + &mut self, + resp: HostOutgoingResponse, + ) -> Result; + fn get_outgoing_response(&mut self, id: u32) -> Result<&mut HostOutgoingResponse, TableError>; + fn delete_outgoing_response(&mut self, id: u32) -> Result; fn push_incoming_response(&mut self, response: HostIncomingResponse) -> Result; @@ -165,23 +284,26 @@ pub trait TableHttpExt { ) -> Result; } -#[async_trait::async_trait] impl TableHttpExt for Table { - fn push_outgoing_response(&mut self, request: HostOutgoingRequest) -> Result { - self.push(Box::new(request)) - } - fn get_outgoing_request(&self, id: u32) -> Result<&HostOutgoingRequest, TableError> { - self.get::(id) + fn push_outgoing_response( + &mut self, + response: HostOutgoingResponse, + ) -> Result { + self.push(Box::new(response)) } - fn get_outgoing_request_mut( + + fn get_outgoing_response( &mut self, - id: u32, - ) -> Result<&mut HostOutgoingRequest, TableError> { - self.get_mut::(id) + id: OutgoingResponse, + ) -> Result<&mut HostOutgoingResponse, TableError> { + self.get_mut(id) } - fn delete_outgoing_request(&mut self, id: u32) -> Result { - let req = self.delete::(id)?; - Ok(req) + + fn delete_outgoing_response( + &mut self, + id: OutgoingResponse, + ) -> Result { + self.delete(id) } fn push_incoming_response( diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index 1b2326f3553b..4baea8f364bf 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -3,17 +3,17 @@ use crate::bindings::http::types::{ IncomingResponse, Method, OutgoingBody, OutgoingRequest, OutgoingResponse, ResponseOutparam, Scheme, StatusCode, Trailers, }; -use crate::body::{HostFutureTrailers, HostFutureTrailersState}; -use crate::types::FieldMap; +use crate::body::{FinishMessage, HostFutureTrailers, HostFutureTrailersState}; +use crate::types::{HostIncomingRequest, HostOutgoingResponse}; use crate::WasiHttpView; use crate::{ body::{HostIncomingBodyBuilder, HostOutgoingBody}, types::{ - HostFields, HostFutureIncomingResponse, HostIncomingResponse, HostOutgoingRequest, - TableHttpExt, + self, FieldMap, HostFields, HostFutureIncomingResponse, HostIncomingResponse, + HostOutgoingRequest, TableHttpExt, }, }; -use anyhow::{anyhow, Context}; +use anyhow::Context; use std::any::Any; use wasmtime_wasi::preview2::{ bindings::io::streams::{InputStream, OutputStream}, @@ -113,42 +113,110 @@ impl crate::bindings::http::types::Host for T { .context("[fields_clone] pushing fields")?; Ok(id) } - fn drop_incoming_request(&mut self, _request: IncomingRequest) -> wasmtime::Result<()> { - todo!("we haven't implemented the server side of wasi-http yet") + fn drop_incoming_request(&mut self, id: IncomingRequest) -> wasmtime::Result<()> { + let _ = types::IncomingRequestLens::from(id).delete(self.table())?; + Ok(()) } fn drop_outgoing_request(&mut self, request: OutgoingRequest) -> wasmtime::Result<()> { - self.table().delete_outgoing_request(request)?; + types::OutgoingRequestLens::from(request).delete(self.table())?; Ok(()) } - fn incoming_request_method(&mut self, _request: IncomingRequest) -> wasmtime::Result { - todo!("we haven't implemented the server side of wasi-http yet") + fn incoming_request_method(&mut self, request: IncomingRequest) -> wasmtime::Result { + let method = types::IncomingRequestLens::from(request) + .get(self.table())? + .parts + .method + .as_ref(); + + if method == hyper::Method::GET { + Ok(Method::Get) + } else if method == hyper::Method::HEAD { + Ok(Method::Head) + } else if method == hyper::Method::POST { + Ok(Method::Post) + } else if method == hyper::Method::PUT { + Ok(Method::Put) + } else if method == hyper::Method::DELETE { + Ok(Method::Delete) + } else if method == hyper::Method::CONNECT { + Ok(Method::Connect) + } else if method == hyper::Method::OPTIONS { + Ok(Method::Options) + } else if method == hyper::Method::TRACE { + Ok(Method::Trace) + } else if method == hyper::Method::PATCH { + Ok(Method::Patch) + } else { + Ok(Method::Other(method.to_owned())) + } } fn incoming_request_path_with_query( &mut self, - _request: IncomingRequest, + id: IncomingRequest, ) -> wasmtime::Result> { - todo!("we haven't implemented the server side of wasi-http yet") - } - fn incoming_request_scheme( - &mut self, - _request: IncomingRequest, - ) -> wasmtime::Result> { - todo!("we haven't implemented the server side of wasi-http yet") + let req = types::IncomingRequestLens::from(id).get(self.table())?; + Ok(req + .parts + .uri + .path_and_query() + .map(|path_and_query| path_and_query.as_str().to_owned())) + } + fn incoming_request_scheme(&mut self, id: IncomingRequest) -> wasmtime::Result> { + let req = types::IncomingRequestLens::from(id).get(self.table())?; + Ok(req.parts.uri.scheme().map(|scheme| { + if scheme == &http::uri::Scheme::HTTP { + return Scheme::Http; + } + + if scheme == &http::uri::Scheme::HTTPS { + return Scheme::Https; + } + + Scheme::Other(req.parts.uri.scheme_str().unwrap().to_owned()) + })) } fn incoming_request_authority( &mut self, - _request: IncomingRequest, + id: IncomingRequest, ) -> wasmtime::Result> { - todo!("we haven't implemented the server side of wasi-http yet") + let req = types::IncomingRequestLens::from(id).get(self.table())?; + Ok(req + .parts + .uri + .authority() + .map(|auth| auth.as_str().to_owned())) } - fn incoming_request_headers(&mut self, _request: IncomingRequest) -> wasmtime::Result { - todo!("we haven't implemented the server side of wasi-http yet") + fn incoming_request_headers(&mut self, id: IncomingRequest) -> wasmtime::Result { + let _ = types::IncomingRequestLens::from(id).get(self.table())?; + + fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { + &mut elem + .downcast_mut::() + .unwrap() + .parts + .headers + } + + let headers = self.table().push_fields(HostFields::Ref { + parent: id, + get_fields, + })?; + + Ok(headers) } fn incoming_request_consume( &mut self, - _request: IncomingRequest, - ) -> wasmtime::Result> { - todo!("we haven't implemented the server side of wasi-http yet") + id: IncomingRequest, + ) -> wasmtime::Result> { + let req = types::IncomingRequestLens::from(id).get_mut(self.table())?; + match req.body.take() { + Some(builder) => { + let id = self.table().push_incoming_body(builder.build())?; + Ok(Ok(id)) + } + + None => Ok(Err(())), + } } fn new_outgoing_request( &mut self, @@ -168,19 +236,17 @@ impl crate::bindings::http::types::Host for T { scheme, body: None, }; - let id = self - .table() - .push_outgoing_response(req) - .context("[new_outgoing_request] pushing request")?; + let id = types::OutgoingRequestLens::push(self.table(), req) + .context("[new_outgoing_request] pushing request")? + .into(); Ok(id) } fn outgoing_request_write( &mut self, request: OutgoingRequest, ) -> wasmtime::Result> { - let req = self - .table() - .get_outgoing_request_mut(request) + let req = types::OutgoingRequestLens::from(request) + .get_mut(self.table()) .context("[outgoing_request_write] getting request")?; if req.body.is_some() { @@ -197,15 +263,25 @@ impl crate::bindings::http::types::Host for T { Ok(Ok(outgoing_body)) } - fn drop_response_outparam(&mut self, _response: ResponseOutparam) -> wasmtime::Result<()> { - todo!("we haven't implemented the server side of wasi-http yet") + fn drop_response_outparam(&mut self, id: ResponseOutparam) -> wasmtime::Result<()> { + let _ = types::ResponseOutparamLens::from(id).delete(self.table())?; + Ok(()) } fn set_response_outparam( &mut self, - _outparam: ResponseOutparam, - _response: Result, + id: ResponseOutparam, + resp: Result, ) -> wasmtime::Result<()> { - todo!("we haven't implemented the server side of wasi-http yet") + let val = match resp { + Ok(resp) => Ok(self.table().delete_outgoing_response(resp)?.try_into()?), + Err(e) => Err(e), + }; + + types::ResponseOutparamLens::from(id) + .delete(self.table())? + .result + .send(val) + .map_err(|_| anyhow::anyhow!("failed to initialize response")) } fn drop_incoming_response(&mut self, response: IncomingResponse) -> wasmtime::Result<()> { self.table() @@ -213,8 +289,9 @@ impl crate::bindings::http::types::Host for T { .context("[drop_incoming_response] deleting response")?; Ok(()) } - fn drop_outgoing_response(&mut self, _response: OutgoingResponse) -> wasmtime::Result<()> { - todo!("we haven't implemented the server side of wasi-http yet") + fn drop_outgoing_response(&mut self, id: OutgoingResponse) -> wasmtime::Result<()> { + types::OutgoingResponseLens::from(id).delete(self.table())?; + Ok(()) } fn incoming_response_status( &mut self, @@ -315,16 +392,41 @@ impl crate::bindings::http::types::Host for T { fn new_outgoing_response( &mut self, - _status_code: StatusCode, - _headers: Headers, + status: StatusCode, + headers: Headers, ) -> wasmtime::Result { - todo!("we haven't implemented the server side of wasi-http yet") + let fields = self.table().get_fields(headers)?.clone(); + self.table().delete_fields(headers)?; + + let id = types::OutgoingResponseLens::push( + self.table(), + HostOutgoingResponse { + status, + headers: fields, + body: None, + }, + )? + .into(); + + Ok(id) } fn outgoing_response_write( &mut self, - _response: OutgoingResponse, - ) -> wasmtime::Result> { - todo!("we haven't implemented the server side of wasi-http yet") + id: OutgoingResponse, + ) -> wasmtime::Result> { + let resp = types::OutgoingResponseLens::from(id).get_mut(self.table())?; + + if resp.body.is_some() { + return Ok(Err(())); + } + + let (host, body) = HostOutgoingBody::new(); + + resp.body.replace(body); + + let id = self.table().push_outgoing_body(host)?; + + Ok(Ok(id)) } fn drop_future_incoming_response( &mut self, @@ -431,32 +533,41 @@ impl crate::bindings::http::types::Host for T { } } - fn outgoing_body_write_trailers( + fn outgoing_body_finish( &mut self, id: OutgoingBody, - ts: Trailers, + ts: Option, ) -> wasmtime::Result<()> { let mut body = self.table().delete_outgoing_body(id)?; - let trailers = self.table().get_fields(ts)?.clone(); - match body - .trailers_sender + let sender = body + .finish_sender .take() - // Should be unreachable - this is the only place we take the trailers sender, - // at the end of the HostOutgoingBody's lifetime - .ok_or_else(|| anyhow!("trailers_sender missing"))? - .send(trailers.into()) - { - Ok(()) => {} - Err(_) => {} // Ignoring failure: receiver died sending body, but we can't report that - // here. - } + .expect("outgoing-body trailer_sender consumed by a non-owning function"); + + let message = if let Some(ts) = ts { + FinishMessage::Trailers(self.table().get_fields(ts)?.clone().into()) + } else { + FinishMessage::Finished + }; + + // Ignoring failure: receiver died sending body, but we can't report that here. + let _ = sender.send(message.into()); Ok(()) } fn drop_outgoing_body(&mut self, id: OutgoingBody) -> wasmtime::Result<()> { - let _ = self.table().delete_outgoing_body(id)?; + let mut body = self.table().delete_outgoing_body(id)?; + + let sender = body + .finish_sender + .take() + .expect("outgoing-body trailer_sender consumed by a non-owning function"); + + // Ignoring failure: receiver died sending body, but we can't report that here. + let _ = sender.send(FinishMessage::Abort); + Ok(()) } } diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index 821e15f96213..dfcacd8feb73 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -81,7 +81,7 @@ interface types { incoming-request-headers: func(request: /* borrow */ incoming-request) -> /* child */ headers // Will return the input-stream child at most once. If called more than // once, subsequent calls will return error. - incoming-request-consume: func(request: /* borrow */ incoming-request) -> result< /* child */ input-stream> + incoming-request-consume: func(request: /* borrow */ incoming-request) -> result< /* own */ incoming-body> type outgoing-request = u32 drop-outgoing-request: func(request: /* own */ outgoing-request) @@ -140,7 +140,7 @@ interface types { // May be called at most once. returns error if called additional times. // TODO: make incoming-request-consume work the same way, giving a child // incoming-body. - incoming-response-consume: func(response: /* borrow */ incoming-response) -> result + incoming-response-consume: func(response: /* borrow */ incoming-response) -> result type incoming-body = u32 drop-incoming-body: func(this: /* own */ incoming-body) @@ -174,16 +174,18 @@ interface types { /// Will give the child outgoing-response at most once. subsequent calls will /// return an error. - outgoing-response-write: func(this: /* borrow */ outgoing-response) -> result + outgoing-response-write: func(this: /* borrow */ outgoing-response) -> result type outgoing-body = u32 drop-outgoing-body: func(this: /* own */ outgoing-body) /// Will give the child output-stream at most once. subsequent calls will /// return an error. outgoing-body-write: func(this: /* borrow */ outgoing-body) -> result - /// Write trailers as the way to finish an outgoing-body. To finish an - /// outgoing-body without writing trailers, use drop-outgoing-body. - outgoing-body-write-trailers: func(this: /* own */ outgoing-body, trailers: /* own */ trailers) + /// Finalize an outgoing body, optionally providing trailers. This must be + /// called to signal that the response is complete. If the `outgoing-body` is + /// dropped without calling `outgoing-body-finalize`, the implementation + /// should treat the body as corrupted. + outgoing-body-finish: func(this: /* own */ outgoing-body, trailers: /* own */ option) /// The following block defines a special resource type used by the /// `wasi:http/outgoing-handler` interface to emulate diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index 821e15f96213..dfcacd8feb73 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -81,7 +81,7 @@ interface types { incoming-request-headers: func(request: /* borrow */ incoming-request) -> /* child */ headers // Will return the input-stream child at most once. If called more than // once, subsequent calls will return error. - incoming-request-consume: func(request: /* borrow */ incoming-request) -> result< /* child */ input-stream> + incoming-request-consume: func(request: /* borrow */ incoming-request) -> result< /* own */ incoming-body> type outgoing-request = u32 drop-outgoing-request: func(request: /* own */ outgoing-request) @@ -140,7 +140,7 @@ interface types { // May be called at most once. returns error if called additional times. // TODO: make incoming-request-consume work the same way, giving a child // incoming-body. - incoming-response-consume: func(response: /* borrow */ incoming-response) -> result + incoming-response-consume: func(response: /* borrow */ incoming-response) -> result type incoming-body = u32 drop-incoming-body: func(this: /* own */ incoming-body) @@ -174,16 +174,18 @@ interface types { /// Will give the child outgoing-response at most once. subsequent calls will /// return an error. - outgoing-response-write: func(this: /* borrow */ outgoing-response) -> result + outgoing-response-write: func(this: /* borrow */ outgoing-response) -> result type outgoing-body = u32 drop-outgoing-body: func(this: /* own */ outgoing-body) /// Will give the child output-stream at most once. subsequent calls will /// return an error. outgoing-body-write: func(this: /* borrow */ outgoing-body) -> result - /// Write trailers as the way to finish an outgoing-body. To finish an - /// outgoing-body without writing trailers, use drop-outgoing-body. - outgoing-body-write-trailers: func(this: /* own */ outgoing-body, trailers: /* own */ trailers) + /// Finalize an outgoing body, optionally providing trailers. This must be + /// called to signal that the response is complete. If the `outgoing-body` is + /// dropped without calling `outgoing-body-finalize`, the implementation + /// should treat the body as corrupted. + outgoing-body-finish: func(this: /* own */ outgoing-body, trailers: /* own */ option) /// The following block defines a special resource type used by the /// `wasi:http/outgoing-handler` interface to emulate diff --git a/src/bin/wasmtime.rs b/src/bin/wasmtime.rs index d32a0b63f373..46868b3aca05 100644 --- a/src/bin/wasmtime.rs +++ b/src/bin/wasmtime.rs @@ -5,6 +5,8 @@ use anyhow::Result; use clap::Parser; +#[cfg(feature = "serve")] +use wasmtime_cli::commands::ServeCommand; use wasmtime_cli::commands::{ CompileCommand, ConfigCommand, ExploreCommand, RunCommand, SettingsCommand, WastCommand, }; @@ -53,6 +55,9 @@ enum Subcommand { Explore(ExploreCommand), /// Runs a WebAssembly module Run(RunCommand), + /// Serves requests from a wasi-http proxy component. + #[cfg(feature = "serve")] + Serve(ServeCommand), /// Displays available Cranelift settings for a target. Settings(SettingsCommand), /// Runs a WebAssembly test script file @@ -68,6 +73,8 @@ impl Wasmtime { Subcommand::Compile(c) => c.execute(), Subcommand::Explore(c) => c.execute(), Subcommand::Run(c) => c.execute(), + #[cfg(feature = "serve")] + Subcommand::Serve(c) => c.execute(), Subcommand::Settings(c) => c.execute(), Subcommand::Wast(c) => c.execute(), } diff --git a/src/commands.rs b/src/commands.rs index 764f01e936cd..ae46a2264e17 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -7,4 +7,10 @@ mod run; mod settings; mod wast; +#[cfg(feature = "serve")] +mod serve; + pub use self::{compile::*, config::*, explore::*, run::*, settings::*, wast::*}; + +#[cfg(feature = "serve")] +pub use self::serve::*; diff --git a/src/commands/serve.rs b/src/commands/serve.rs new file mode 100644 index 000000000000..5cd1a8225e4c --- /dev/null +++ b/src/commands/serve.rs @@ -0,0 +1,199 @@ +use anyhow::Result; +use clap::Parser; +use std::{path::PathBuf, pin::Pin, sync::Arc}; +use wasmtime::component::{Component, InstancePre, Linker}; +use wasmtime::{Engine, Store}; +use wasmtime_cli_flags::CommonOptions; +use wasmtime_wasi::preview2::{Table, WasiCtx, WasiCtxBuilder, WasiView}; +use wasmtime_wasi_http::{body::HyperOutgoingBody, WasiHttpCtx, WasiHttpView}; + +struct Host { + table: Table, + ctx: WasiCtx, + http: WasiHttpCtx, +} + +impl Host { + fn new() -> Result { + let mut table = Table::new(); + let ctx = WasiCtxBuilder::new().build(&mut table)?; + Ok(Host { + table, + ctx, + http: WasiHttpCtx, + }) + } +} + +impl WasiView for Host { + fn table(&self) -> &Table { + &self.table + } + + fn table_mut(&mut self) -> &mut Table { + &mut self.table + } + + fn ctx(&self) -> &WasiCtx { + &self.ctx + } + + fn ctx_mut(&mut self) -> &mut WasiCtx { + &mut self.ctx + } +} + +impl WasiHttpView for Host { + fn table(&mut self) -> &mut Table { + &mut self.table + } + + fn ctx(&mut self) -> &mut WasiHttpCtx { + &mut self.http + } +} + +/// Runs a WebAssembly module +#[derive(Parser)] +#[structopt(name = "run")] +pub struct ServeCommand { + #[clap(flatten)] + common: CommonOptions, + + /// Socket address for the web server to bind to. Defaults to 0.0.0.0:8080. + #[clap(long = "addr", value_name = "SOCKADDR")] + addr: Option, + + /// The WebAssembly component to run. + #[clap(value_name = "WASM", required = true)] + component: PathBuf, +} + +impl ServeCommand { + fn addr(&self) -> std::net::SocketAddr { + self.addr.unwrap_or("0.0.0.0:8080".parse().unwrap()) + } + + /// Start a server to run the given wasi-http proxy component + pub fn execute(mut self) -> Result<()> { + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_time() + .enable_io() + .build()?; + + runtime.block_on(async move { + tokio::select! { + _ = tokio::signal::ctrl_c() => { + Ok::<_, anyhow::Error>(()) + } + + res = self.serve() => { + res + } + } + })?; + + Ok(()) + } + + fn add_to_linker(&self, linker: &mut Linker) -> Result<()> { + wasmtime_wasi_http::proxy::add_to_linker(linker)?; + Ok(()) + } + + async fn serve(&mut self) -> Result<()> { + use hyper::server::conn::http1; + + let mut config = self.common.config(None)?; + config.wasm_component_model(true); + config.async_support(true); + + let engine = Arc::new(Engine::new(&config)?); + let mut linker = Linker::new(&engine); + + self.add_to_linker(&mut linker)?; + + let component = Component::from_file(&engine, &self.component)?; + + let instance = Arc::new(linker.instantiate_pre(&component)?); + + let listener = tokio::net::TcpListener::bind(self.addr()).await?; + + loop { + let (stream, _) = listener.accept().await?; + let engine = Arc::clone(&engine); + let instance = Arc::clone(&instance); + tokio::task::spawn(async move { + let handler = ProxyHandler::new(engine, instance); + if let Err(e) = http1::Builder::new() + .keep_alive(true) + .serve_connection(stream, handler) + .await + { + eprintln!("error: {e:?}"); + } + }); + } + } +} + +#[derive(Clone)] +struct ProxyHandler { + engine: Arc, + instance_pre: Arc>, +} + +impl ProxyHandler { + fn new(engine: Arc, instance_pre: Arc>) -> Self { + Self { + engine, + instance_pre, + } + } +} + +type Request = hyper::Request; + +impl hyper::service::Service for ProxyHandler { + type Response = hyper::Response; + type Error = anyhow::Error; + type Future = Pin> + Send>>; + + fn call(&mut self, req: Request) -> Self::Future { + use http_body_util::BodyExt; + + let handler = self.clone(); + + let (sender, receiver) = tokio::sync::oneshot::channel(); + + // TODO: need to track the join handle, but don't want to block the response on it + tokio::task::spawn(async move { + let host = Host::new()?; + let mut store = Store::new(&handler.engine, host); + + let req = store.data_mut().new_incoming_request( + req.map(|body| body.map_err(|e| anyhow::anyhow!(e)).boxed()), + )?; + + let out = store.data_mut().new_response_outparam(sender)?; + + let (proxy, _inst) = wasmtime_wasi_http::proxy::Proxy::instantiate_pre( + &mut store, + &handler.instance_pre, + ) + .await?; + + proxy + .wasi_http_incoming_handler() + .call_handle(store, req, out) + .await?; + + Ok::<_, anyhow::Error>(()) + }); + + Box::pin(async move { + let resp = receiver.await.unwrap()?; + Ok(resp) + }) + } +} diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index dae6f3ab9aa0..90803257ae06 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -497,6 +497,14 @@ start = "2023-01-20" end = "2024-06-26" notes = "The Bytecode Alliance is the author of this crate." +[[wildcard-audits.wasmtime-wmemcheck]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2022-11-27" +end = "2024-06-26" +notes = "The Bytecode Alliance is the author of this crate." + [[wildcard-audits.wast]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -1896,6 +1904,11 @@ criteria = "safe-to-deploy" delta = "0.9.9 -> 0.10.2" notes = "This upgrade is mostly a code refactor, as far as I can tell. No new uses of unsafe nor any new ambient capabilities usage." +[[audits.signal-hook-registry]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "1.4.1" + [[audits.slab]] who = "Pat Hickey " criteria = "safe-to-deploy" diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 28b251f7ac1d..429251b49472 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -169,6 +169,9 @@ audit-as-crates-io = true [policy.wasmtime-wit-bindgen] audit-as-crates-io = true +[policy.wasmtime-wmemcheck] +audit-as-crates-io = true + [policy.wiggle] audit-as-crates-io = true diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 80e95f2bf4e4..3908247ffcec 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -353,6 +353,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-wmemcheck]] +version = "14.0.0" +audited_as = "13.0.0" + [[unpublished.wiggle]] version = "13.0.0" audited_as = "11.0.1" @@ -1686,6 +1690,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-wmemcheck]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wast]] version = "62.0.1" when = "2023-07-26" From 3ffb6828ca82921b7746079ee1b8bf0e343f5dd1 Mon Sep 17 00:00:00 2001 From: Tyler Rockwood Date: Fri, 29 Sep 2023 08:40:17 -0500 Subject: [PATCH 024/199] c-api: Correct wasmtime_module_image_range type (#7110) --- crates/c-api/include/wasmtime/module.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/c-api/include/wasmtime/module.h b/crates/c-api/include/wasmtime/module.h index 8eca72a4899e..222239e81f66 100644 --- a/crates/c-api/include/wasmtime/module.h +++ b/crates/c-api/include/wasmtime/module.h @@ -158,7 +158,7 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_module_deserialize_file( * For more details see: https://docs.wasmtime.dev/api/wasmtime/struct.Module.html#method.image_range */ WASM_API_EXTERN void wasmtime_module_image_range( - const wasm_module_t *module, + const wasmtime_module_t *module, size_t *start, size_t *end ); From 90e4daff232ae68e377b81a01fcef5d09f322fb1 Mon Sep 17 00:00:00 2001 From: Silas Groh Date: Fri, 29 Sep 2023 22:08:05 +0800 Subject: [PATCH 025/199] Make C API usable from Rust (#6765) --- crates/c-api/Cargo.toml | 2 +- crates/c-api/src/lib.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml index 26e358424405..454d244d07d3 100644 --- a/crates/c-api/Cargo.toml +++ b/crates/c-api/Cargo.toml @@ -11,7 +11,7 @@ publish = false [lib] name = "wasmtime" -crate-type = ["staticlib", "cdylib"] +crate-type = ["staticlib", "cdylib", "rlib"] doc = false test = false doctest = false diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index 4bc111b5dc1b..3ef450731902 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -11,6 +11,8 @@ #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] +pub use wasmtime::*; + mod config; mod engine; mod error; From 11a66086d42a51583b0f3adc1df872f574e34a58 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 29 Sep 2023 09:41:38 -0500 Subject: [PATCH 026/199] Remove usage of `is-terminal` and `atty` crates (#7104) * Remove usage of `is-terminal` and `atty` crates This functionality is now folded into the standard library itself. * Fix syntax * Fix a unix/windows cfg --- Cargo.lock | 3 --- crates/environ/Cargo.toml | 1 - crates/environ/examples/factc.rs | 4 ++-- crates/test-programs/Cargo.toml | 1 - crates/test-programs/src/lib.rs | 8 ++++---- crates/wasi-common/cap-std-sync/Cargo.toml | 1 - crates/wasi-common/cap-std-sync/src/file.rs | 8 +++++--- crates/wasi-common/cap-std-sync/src/stdio.rs | 9 +++++---- 8 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index edd189ea6a03..22d858a280b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2678,7 +2678,6 @@ dependencies = [ "http-body", "http-body-util", "hyper", - "is-terminal", "lazy_static", "tempfile", "test-log", @@ -3025,7 +3024,6 @@ dependencies = [ "fs-set-times", "io-extras", "io-lifetimes 2.0.2", - "is-terminal", "once_cell", "rustix 0.38.8", "system-interface", @@ -3526,7 +3524,6 @@ name = "wasmtime-environ" version = "14.0.0" dependencies = [ "anyhow", - "atty", "clap", "cranelift-entity", "env_logger 0.10.0", diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index 1fd84a613fdf..b09f3cfd4276 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -28,7 +28,6 @@ wasmprinter = { workspace = true, optional = true } wasmtime-component-util = { workspace = true, optional = true } [dev-dependencies] -atty = "0.2.14" clap = { workspace = true } env_logger = { workspace = true } wat = { workspace = true } diff --git a/crates/environ/examples/factc.rs b/crates/environ/examples/factc.rs index 171fd6cd962d..6dfb7136e485 100644 --- a/crates/environ/examples/factc.rs +++ b/crates/environ/examples/factc.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Context, Result}; use clap::Parser; -use std::io::Write; +use std::io::{IsTerminal, Write}; use std::path::PathBuf; use wasmparser::{Validator, WasmFeatures}; use wasmtime_environ::component::*; @@ -179,7 +179,7 @@ impl Factc { wasmprinter::print_bytes(&wasm) .context("failed to convert binary wasm to text")? .into_bytes() - } else if self.output.is_none() && atty::is(atty::Stream::Stdout) { + } else if self.output.is_none() && std::io::stdout().is_terminal() { bail!("cannot print binary wasm output to a terminal unless `-t` flag is passed") } else { wasm.clone() diff --git a/crates/test-programs/Cargo.toml b/crates/test-programs/Cargo.toml index 12ad3188e2ef..9785dc38d738 100644 --- a/crates/test-programs/Cargo.toml +++ b/crates/test-programs/Cargo.toml @@ -20,7 +20,6 @@ http = { version = "0.2.9" } http-body = "1.0.0-rc.2" http-body-util = "0.1.0-rc.2" hyper = { version = "1.0.0-rc.3", features = ["full"] } -is-terminal = { workspace = true } tokio = { workspace = true, features = ["net", "rt-multi-thread", "macros"] } tracing = { workspace = true } diff --git a/crates/test-programs/src/lib.rs b/crates/test-programs/src/lib.rs index cd3b710703c1..f793242b3048 100644 --- a/crates/test-programs/src/lib.rs +++ b/crates/test-programs/src/lib.rs @@ -1,5 +1,6 @@ ///! This crate exists to build crates for wasm32-wasi in build.rs, and execute ///! these wasm programs in harnesses defined under tests/. +use std::io::IsTerminal; #[cfg(all(feature = "test_programs", not(skip_wasi_http_tests)))] pub mod http_server; @@ -40,8 +41,7 @@ pub fn wasi_tests_environment() -> &'static [(&'static str, &'static str)] { } pub fn stdio_is_terminal() -> bool { - use is_terminal::is_terminal; - is_terminal(&std::io::stdin()) - && is_terminal(&std::io::stdout()) - && is_terminal(&std::io::stderr()) + std::io::stdin().is_terminal() + && std::io::stdout().is_terminal() + && std::io::stderr().is_terminal() } diff --git a/crates/wasi-common/cap-std-sync/Cargo.toml b/crates/wasi-common/cap-std-sync/Cargo.toml index f0fe058eac32..f9811bbb51f6 100644 --- a/crates/wasi-common/cap-std-sync/Cargo.toml +++ b/crates/wasi-common/cap-std-sync/Cargo.toml @@ -23,7 +23,6 @@ fs-set-times = { workspace = true } system-interface = { workspace = true, features = ["cap_std_impls"] } tracing = { workspace = true } io-lifetimes = { workspace = true } -is-terminal = { workspace = true } [target.'cfg(unix)'.dependencies] rustix = { workspace = true, features = ["fs", "event"] } diff --git a/crates/wasi-common/cap-std-sync/src/file.rs b/crates/wasi-common/cap-std-sync/src/file.rs index 20a95e94425a..e986c173404d 100644 --- a/crates/wasi-common/cap-std-sync/src/file.rs +++ b/crates/wasi-common/cap-std-sync/src/file.rs @@ -1,10 +1,9 @@ use cap_fs_ext::MetadataExt; use fs_set_times::{SetTimes, SystemTimeSpec}; use io_lifetimes::AsFilelike; -use is_terminal::IsTerminal; use std::any::Any; use std::convert::TryInto; -use std::io; +use std::io::{self, IsTerminal}; use system_interface::{ fs::{FileIoExt, GetSetFdFlags}, io::{IoExt, ReadReady}, @@ -128,7 +127,10 @@ impl WasiFile for File { Ok(self.0.num_ready_bytes()?) } fn isatty(&self) -> bool { - self.0.is_terminal() + #[cfg(unix)] + return self.0.as_fd().is_terminal(); + #[cfg(windows)] + return self.0.as_handle().is_terminal(); } } diff --git a/crates/wasi-common/cap-std-sync/src/stdio.rs b/crates/wasi-common/cap-std-sync/src/stdio.rs index e60ddc034ebf..b2723d076c98 100644 --- a/crates/wasi-common/cap-std-sync/src/stdio.rs +++ b/crates/wasi-common/cap-std-sync/src/stdio.rs @@ -1,10 +1,8 @@ use crate::file::convert_systimespec; use fs_set_times::SetTimes; -use is_terminal::IsTerminal; use std::any::Any; use std::convert::TryInto; -use std::io; -use std::io::{Read, Write}; +use std::io::{self, IsTerminal, Read, Write}; use system_interface::io::ReadReady; #[cfg(windows)] @@ -77,7 +75,10 @@ impl WasiFile for Stdin { Ok(self.0.num_ready_bytes()?) } fn isatty(&self) -> bool { - self.0.is_terminal() + #[cfg(unix)] + return self.0.as_fd().is_terminal(); + #[cfg(windows)] + return self.0.as_handle().is_terminal(); } } #[cfg(windows)] From 4b288ba88dfe1c2cce720c46bf9d919e54871c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Cabrera?= Date: Fri, 29 Sep 2023 12:59:40 -0400 Subject: [PATCH 027/199] winch(x64): Call indirect (#7100) * winch(x64): Call indirect This change adds support for the `call_indirect` instruction to Winch. Libcalls are a pre-requisite for supporting `call_indirect` in order to lazily initialy funcrefs. This change adds support for libcalls to Winch by introducing a `BuiltinFunctions` struct similar to Cranelift's `BuiltinFunctionSignatures` struct. In general, libcalls are handled like any other function call, with the only difference that given that not all the information to fulfill the function call might be known up-front, control is given to the caller for finalizing the call. The introduction of function references also involves dealing with pointer-sized loads and stores, so this change also adds the required functionality to `FuncEnv` and `MacroAssembler` to be pointer aware, making it straight forward to derive an `OperandSize` or `WasmType` from the target's pointer size. Finally, given the complexity of the call_indirect instrunction, this change bundles an improvement to the register allocator, allowing it to track the allocatable vs non-allocatable registers, this is done to avoid any mistakes when allocating/de-allocating registers that are not alloctable. -- prtest:full * Address review comments * Fix typos * Better documentation for `new_unchecked` * Introduce `max` for `BitSet` * Make allocatable property `u64` * winch(calls): Overhaul `FnCall` This commit simplifies `FnCall`'s interface making its usage more uniform throughout the compiler. In summary, this change: * Avoids side effects in the `FnCall::new` constructor, and also makes it the only constructor. * Exposes `FnCall::save_live_registers` and `FnCall::calculate_call_stack_space` to calculate the stack space consumed by the call and so that the caller can decide which one to use at callsites depending on their use-case. * tests: Fix regset tests --- build.rs | 8 +- crates/winch/src/compiler.rs | 2 +- fuzz/fuzz_targets/differential.rs | 3 +- tests/misc_testsuite/winch/call_indirect.wast | 413 ++++++++++++++++++ winch/codegen/src/abi/mod.rs | 27 +- winch/codegen/src/codegen/builtin.rs | 100 +++++ winch/codegen/src/codegen/call.rs | 171 ++++++-- winch/codegen/src/codegen/context.rs | 33 +- winch/codegen/src/codegen/env.rs | 129 +++++- winch/codegen/src/codegen/mod.rs | 180 +++++--- winch/codegen/src/isa/aarch64/abi.rs | 17 +- winch/codegen/src/isa/aarch64/masm.rs | 28 +- winch/codegen/src/isa/aarch64/mod.rs | 22 +- winch/codegen/src/isa/aarch64/regs.rs | 12 +- winch/codegen/src/isa/mod.rs | 4 +- winch/codegen/src/isa/reg.rs | 4 +- winch/codegen/src/isa/x64/abi.rs | 17 +- winch/codegen/src/isa/x64/address.rs | 3 +- winch/codegen/src/isa/x64/asm.rs | 74 ++-- winch/codegen/src/isa/x64/masm.rs | 111 ++++- winch/codegen/src/isa/x64/mod.rs | 35 +- winch/codegen/src/isa/x64/regs.rs | 15 +- winch/codegen/src/masm.rs | 63 ++- winch/codegen/src/regalloc.rs | 22 +- winch/codegen/src/regset.rs | 124 +++++- winch/codegen/src/stack.rs | 24 +- winch/codegen/src/visitor.rs | 110 ++++- .../x64/call_indirect/call_indirect.wat | 130 ++++++ winch/filetests/src/lib.rs | 9 +- winch/src/compile.rs | 11 +- 30 files changed, 1626 insertions(+), 275 deletions(-) create mode 100644 tests/misc_testsuite/winch/call_indirect.wast create mode 100644 winch/codegen/src/codegen/builtin.rs create mode 100644 winch/filetests/filetests/x64/call_indirect/call_indirect.wat diff --git a/build.rs b/build.rs index b591af39d7cd..041373eba0c3 100644 --- a/build.rs +++ b/build.rs @@ -205,11 +205,17 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool { // We ignore tests that assert for traps on windows, given // that Winch doesn't encode unwind information for Windows, yet. if strategy == "Winch" { + if testsuite == "misc_testsuite" { + // The misc/call_indirect is fully supported by Winch. + if testname == "call_indirect" { + return false; + } + } if testsuite != "winch" { return true; } - let assert_trap = ["i32", "i64"].contains(&testname); + let assert_trap = ["i32", "i64", "call_indirect"].contains(&testname); if assert_trap && env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "windows" { return true; diff --git a/crates/winch/src/compiler.rs b/crates/winch/src/compiler.rs index 73605c2c3761..37ff419335e6 100644 --- a/crates/winch/src/compiler.rs +++ b/crates/winch/src/compiler.rs @@ -68,7 +68,7 @@ impl wasmtime_environ::Compiler for Compiler { let mut validator = validator.into_validator(self.take_allocations()); let buffer = self .isa - .compile_function(ty, &body, &translation, &mut validator) + .compile_function(ty, types, &body, &translation, &mut validator) .map_err(|e| CompileError::Codegen(format!("{e:?}"))); self.save_allocations(validator.into_allocations()); let buffer = buffer?; diff --git a/fuzz/fuzz_targets/differential.rs b/fuzz/fuzz_targets/differential.rs index 9cbf7c608389..34164666ebf0 100644 --- a/fuzz/fuzz_targets/differential.rs +++ b/fuzz/fuzz_targets/differential.rs @@ -374,7 +374,8 @@ fn winch_supports_module(module: &[u8]) -> bool { | F32Abs { .. } | F64Abs { .. } | F32Neg { .. } - | F64Neg { .. } => {} + | F64Neg { .. } + | CallIndirect { .. } => {} _ => { supported = false; break 'main; diff --git a/tests/misc_testsuite/winch/call_indirect.wast b/tests/misc_testsuite/winch/call_indirect.wast new file mode 100644 index 000000000000..a432415aa6c6 --- /dev/null +++ b/tests/misc_testsuite/winch/call_indirect.wast @@ -0,0 +1,413 @@ +;; Test `call_indirect` operator + +(module + ;; Auxiliary definitions + (type $proc (func)) + (type $out-i32 (func (result i32))) + (type $out-i64 (func (result i64))) + (type $out-f32 (func (result f32))) + (type $out-f64 (func (result f64))) + (type $over-i32 (func (param i32) (result i32))) + (type $over-i64 (func (param i64) (result i64))) + (type $over-f32 (func (param f32) (result f32))) + (type $over-f64 (func (param f64) (result f64))) + (type $f32-i32 (func (param f32 i32) (result i32))) + (type $i32-i64 (func (param i32 i64) (result i64))) + (type $f64-f32 (func (param f64 f32) (result f32))) + (type $i64-f64 (func (param i64 f64) (result f64))) + (type $over-i32-duplicate (func (param i32) (result i32))) + (type $over-i64-duplicate (func (param i64) (result i64))) + (type $over-f32-duplicate (func (param f32) (result f32))) + (type $over-f64-duplicate (func (param f64) (result f64))) + + (func $const-i32 (type $out-i32) (i32.const 0x132)) + (func $const-i64 (type $out-i64) (i64.const 0x164)) + (func $const-f32 (type $out-f32) (f32.const 0xf32)) + (func $const-f64 (type $out-f64) (f64.const 0xf64)) + + (func $id-i32 (type $over-i32) (local.get 0)) + (func $id-i64 (type $over-i64) (local.get 0)) + (func $id-f32 (type $over-f32) (local.get 0)) + (func $id-f64 (type $over-f64) (local.get 0)) + + (func $i32-i64 (type $i32-i64) (local.get 1)) + (func $i64-f64 (type $i64-f64) (local.get 1)) + (func $f32-i32 (type $f32-i32) (local.get 1)) + (func $f64-f32 (type $f64-f32) (local.get 1)) + + (func $over-i32-duplicate (type $over-i32-duplicate) (local.get 0)) + (func $over-i64-duplicate (type $over-i64-duplicate) (local.get 0)) + (func $over-f32-duplicate (type $over-f32-duplicate) (local.get 0)) + (func $over-f64-duplicate (type $over-f64-duplicate) (local.get 0)) + + (table funcref + (elem + $const-i32 $const-i64 $const-f32 $const-f64 ;; 0..3 + $id-i32 $id-i64 $id-f32 $id-f64 ;; 4..7 + $f32-i32 $i32-i64 $f64-f32 $i64-f64 ;; 8..11 + $fac-i64 $fib-i64 $even $odd ;; 12..15 + $over-i32-duplicate $over-i64-duplicate ;; 16..17 + $over-f32-duplicate $over-f64-duplicate ;; 18..19 + $fac-i32 $fib-i32 ;; 20..21 + ) + ) + + ;; Typing + + (func (export "type-i32") (result i32) + (call_indirect (type $out-i32) (i32.const 0)) + ) + (func (export "type-i64") (result i64) + (call_indirect (type $out-i64) (i32.const 1)) + ) + (func (export "type-f32") (result f32) + (call_indirect (type $out-f32) (i32.const 2)) + ) + (func (export "type-f64") (result f64) + (call_indirect (type $out-f64) (i32.const 3)) + ) + + (func (export "type-index") (result i64) + (call_indirect (type $over-i64) (i64.const 100) (i32.const 5)) + ) + + (func (export "type-first-i32") (result i32) + (call_indirect (type $over-i32) (i32.const 32) (i32.const 4)) + ) + (func (export "type-first-i64") (result i64) + (call_indirect (type $over-i64) (i64.const 64) (i32.const 5)) + ) + (func (export "type-first-f32") (result f32) + (call_indirect (type $over-f32) (f32.const 1.32) (i32.const 6)) + ) + (func (export "type-first-f64") (result f64) + (call_indirect (type $over-f64) (f64.const 1.64) (i32.const 7)) + ) + + (func (export "type-second-i32") (result i32) + (call_indirect (type $f32-i32) (f32.const 32.1) (i32.const 32) (i32.const 8)) + ) + (func (export "type-second-i64") (result i64) + (call_indirect (type $i32-i64) (i32.const 32) (i64.const 64) (i32.const 9)) + ) + (func (export "type-second-f32") (result f32) + (call_indirect (type $f64-f32) (f64.const 64) (f32.const 32) (i32.const 10)) + ) + (func (export "type-second-f64") (result f64) + (call_indirect (type $i64-f64) (i64.const 64) (f64.const 64.1) (i32.const 11)) + ) + + ;; Dispatch + + (func (export "dispatch") (param i32 i64) (result i64) + (call_indirect (type $over-i64) (local.get 1) (local.get 0)) + ) + + (func (export "dispatch-structural-i64") (param i32) (result i64) + (call_indirect (type $over-i64-duplicate) (i64.const 9) (local.get 0)) + ) + (func (export "dispatch-structural-i32") (param i32) (result i32) + (call_indirect (type $over-i32-duplicate) (i32.const 9) (local.get 0)) + ) + (func (export "dispatch-structural-f32") (param i32) (result f32) + (call_indirect (type $over-f32-duplicate) (f32.const 9.0) (local.get 0)) + ) + (func (export "dispatch-structural-f64") (param i32) (result f64) + (call_indirect (type $over-f64-duplicate) (f64.const 9.0) (local.get 0)) + ) + + ;; Recursion + + (func $fac-i64 (export "fac-i64") (type $over-i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 1)) + (else + (i64.mul + (local.get 0) + (call_indirect (type $over-i64) + (i64.sub (local.get 0) (i64.const 1)) + (i32.const 12) + ) + ) + ) + ) + ) + + (func $fib-i64 (export "fib-i64") (type $over-i64) + (if (result i64) (i64.le_u (local.get 0) (i64.const 1)) + (then (i64.const 1)) + (else + (i64.add + (call_indirect (type $over-i64) + (i64.sub (local.get 0) (i64.const 2)) + (i32.const 13) + ) + (call_indirect (type $over-i64) + (i64.sub (local.get 0) (i64.const 1)) + (i32.const 13) + ) + ) + ) + ) + ) + + (func $fac-i32 (export "fac-i32") (type $over-i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 1)) + (else + (i32.mul + (local.get 0) + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 20) + ) + ) + ) + ) + ) + + (func $fib-i32 (export "fib-i32") (type $over-i32) + (if (result i32) (i32.le_u (local.get 0) (i32.const 1)) + (then (i32.const 1)) + (else + (i32.add + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 2)) + (i32.const 21) + ) + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 21) + ) + ) + ) + ) + ) + + (func $even (export "even") (param i32) (result i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 44)) + (else + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 15) + ) + ) + ) + ) + (func $odd (export "odd") (param i32) (result i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 99)) + (else + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 14) + ) + ) + ) + ) + + ;; As parameter of control constructs and instructions + + (memory 1) + + (func (export "as-select-first") (result i32) + (select (call_indirect (type $out-i32) (i32.const 0)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (result i32) + (select (i32.const 2) (call_indirect (type $out-i32) (i32.const 0)) (i32.const 3)) + ) + (func (export "as-select-last") (result i32) + (select (i32.const 2) (i32.const 3) (call_indirect (type $out-i32) (i32.const 0))) + ) + + (func (export "as-if-condition") (result i32) + (if (result i32) (call_indirect (type $out-i32) (i32.const 0)) (then (i32.const 1)) (else (i32.const 2))) + ) + + (func (export "as-br_if-first") (result i64) + (block (result i64) (br_if 0 (call_indirect (type $out-i64) (i32.const 1)) (i32.const 2))) + ) + (func (export "as-br_if-last") (result i32) + (block (result i32) (br_if 0 (i32.const 2) (call_indirect (type $out-i32) (i32.const 0)))) + ) + + (func (export "as-br_table-first") (result f32) + (block (result f32) (call_indirect (type $out-f32) (i32.const 2)) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (result i32) + (block (result i32) (i32.const 2) (call_indirect (type $out-i32) (i32.const 0)) (br_table 0 0)) + ) + + (func (export "as-return-value") (result i32) + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) (return) + ) + (func (export "as-drop-operand") + (call_indirect (type $over-i64) (i64.const 1) (i32.const 5)) (drop) + ) + (func (export "as-br-value") (result f32) + (block (result f32) (br 0 (call_indirect (type $over-f32) (f32.const 1) (i32.const 6)))) + ) + (func (export "as-local.set-value") (result f64) + (local f64) (local.set 0 (call_indirect (type $over-f64) (f64.const 1) (i32.const 7))) (local.get 0) + ) + (func (export "as-local.tee-value") (result f64) + (local f64) (local.tee 0 (call_indirect (type $over-f64) (f64.const 1) (i32.const 7))) + ) + (global $a (mut f64) (f64.const 10.0)) + (func (export "as-global.set-value") (result f64) + (global.set $a (call_indirect (type $over-f64) (f64.const 1.0) (i32.const 7))) + (global.get $a) + ) + + (func (export "as-binary-left") (result i32) + (block (result i32) + (i32.add + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + (i32.const 10) + ) + ) + ) + (func (export "as-binary-right") (result i32) + (block (result i32) + (i32.sub + (i32.const 10) + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) + + (func (export "as-test-operand") (result i32) + (block (result i32) + (i32.eqz + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) + + (func (export "as-compare-left") (result i32) + (block (result i32) + (i32.le_u + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + (i32.const 10) + ) + ) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) + (i32.ne + (i32.const 10) + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) + +(assert_return (invoke "type-index") (i64.const 100)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "dispatch" (i32.const 5) (i64.const 2)) (i64.const 2)) +(assert_return (invoke "dispatch" (i32.const 5) (i64.const 5)) (i64.const 5)) +(assert_return (invoke "dispatch" (i32.const 12) (i64.const 5)) (i64.const 120)) +(assert_return (invoke "dispatch" (i32.const 13) (i64.const 5)) (i64.const 8)) +(assert_return (invoke "dispatch" (i32.const 17) (i64.const 2)) (i64.const 2)) +(assert_trap (invoke "dispatch" (i32.const 0) (i64.const 2)) "indirect call type mismatch") +(assert_trap (invoke "dispatch" (i32.const 15) (i64.const 2)) "indirect call type mismatch") +(assert_trap (invoke "dispatch" (i32.const 32) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const -1) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const 1213432423) (i64.const 2)) "undefined element") + +(assert_return (invoke "dispatch-structural-i64" (i32.const 5)) (i64.const 9)) +(assert_return (invoke "dispatch-structural-i64" (i32.const 12)) (i64.const 362880)) +(assert_return (invoke "dispatch-structural-i64" (i32.const 13)) (i64.const 55)) +(assert_return (invoke "dispatch-structural-i64" (i32.const 17)) (i64.const 9)) +(assert_trap (invoke "dispatch-structural-i64" (i32.const 11)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-i64" (i32.const 19)) "indirect call type mismatch") + +(assert_return (invoke "dispatch-structural-i32" (i32.const 4)) (i32.const 9)) +(assert_return (invoke "dispatch-structural-i32" (i32.const 20)) (i32.const 362880)) +(assert_return (invoke "dispatch-structural-i32" (i32.const 21)) (i32.const 55)) +(assert_return (invoke "dispatch-structural-i32" (i32.const 16)) (i32.const 9)) +(assert_trap (invoke "dispatch-structural-i32" (i32.const 8)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-i32" (i32.const 18)) "indirect call type mismatch") + +(assert_return (invoke "dispatch-structural-f32" (i32.const 6)) (f32.const 9.0)) +(assert_return (invoke "dispatch-structural-f32" (i32.const 18)) (f32.const 9.0)) +(assert_trap (invoke "dispatch-structural-f32" (i32.const 8)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-f32" (i32.const 19)) "indirect call type mismatch") + +(assert_return (invoke "dispatch-structural-f64" (i32.const 7)) (f64.const 9.0)) +(assert_return (invoke "dispatch-structural-f64" (i32.const 19)) (f64.const 9.0)) +(assert_trap (invoke "dispatch-structural-f64" (i32.const 10)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-f64" (i32.const 18)) "indirect call type mismatch") + +(assert_return (invoke "fac-i64" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fac-i64" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-i64" (i64.const 5)) (i64.const 120)) +(assert_return (invoke "fac-i64" (i64.const 25)) (i64.const 7034535277573963776)) + +(assert_return (invoke "fac-i32" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "fac-i32" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "fac-i32" (i32.const 5)) (i32.const 120)) +(assert_return (invoke "fac-i32" (i32.const 10)) (i32.const 3628800)) + +(assert_return (invoke "fib-i64" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fib-i64" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fib-i64" (i64.const 2)) (i64.const 2)) +(assert_return (invoke "fib-i64" (i64.const 5)) (i64.const 8)) +(assert_return (invoke "fib-i64" (i64.const 20)) (i64.const 10946)) + +(assert_return (invoke "fib-i32" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "fib-i32" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "fib-i32" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "fib-i32" (i32.const 5)) (i32.const 8)) +(assert_return (invoke "fib-i32" (i32.const 20)) (i32.const 10946)) + +(assert_return (invoke "even" (i32.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i32.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 77)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i32.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 77)) (i32.const 44)) + +(assert_return (invoke "as-select-first") (i32.const 0x132)) +(assert_return (invoke "as-select-mid") (i32.const 2)) +(assert_return (invoke "as-select-last") (i32.const 2)) + +(assert_return (invoke "as-if-condition") (i32.const 1)) + +(assert_return (invoke "as-br_if-first") (i64.const 0x164)) +(assert_return (invoke "as-br_if-last") (i32.const 2)) + +(assert_return (invoke "as-br_table-first") (f32.const 0xf32)) +(assert_return (invoke "as-br_table-last") (i32.const 2)) + +(assert_return (invoke "as-return-value") (i32.const 1)) +(assert_return (invoke "as-drop-operand")) +(assert_return (invoke "as-br-value") (f32.const 1)) +(assert_return (invoke "as-local.set-value") (f64.const 1)) +(assert_return (invoke "as-local.tee-value") (f64.const 1)) +(assert_return (invoke "as-global.set-value") (f64.const 1.0)) + +(assert_return (invoke "as-binary-left") (i32.const 11)) +(assert_return (invoke "as-binary-right") (i32.const 9)) +(assert_return (invoke "as-test-operand") (i32.const 0)) +(assert_return (invoke "as-compare-left") (i32.const 1)) +(assert_return (invoke "as-compare-right") (i32.const 1)) + diff --git a/winch/codegen/src/abi/mod.rs b/winch/codegen/src/abi/mod.rs index 5fff6798240a..5362a81cfc78 100644 --- a/winch/codegen/src/abi/mod.rs +++ b/winch/codegen/src/abi/mod.rs @@ -72,6 +72,10 @@ pub(crate) trait ABI { /// function type. fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> ABISig; + /// Construct an ABI signature from WasmType params and returns. + fn sig_from(params: &[WasmType], returns: &[WasmType], call_conv: &CallingConvention) + -> ABISig; + /// Construct the ABI-specific result from a slice of /// [`wasmtime_environ::WasmtType`]. fn result(returns: &[WasmType], call_conv: &CallingConvention) -> ABIResult; @@ -107,7 +111,7 @@ pub(crate) trait ABI { /// ABI-specific representation of a function argument. #[derive(Debug)] -pub(crate) enum ABIArg { +pub enum ABIArg { /// A register argument. Reg { /// Type of the argument. @@ -212,11 +216,20 @@ impl ABIResult { 1 } } + + /// Returns an iterator over the result registers. + /// + /// NOTE: Currently only one or zero registers + /// will be returned until suport for multi-value is introduced. + pub fn regs(&self) -> impl Iterator + '_ { + std::iter::once(self.result_reg()).filter_map(|v| v) + } } pub(crate) type ABIParams = SmallVec<[ABIArg; 6]>; /// An ABI-specific representation of a function signature. +#[derive(Debug)] pub(crate) struct ABISig { /// Function parameters. pub params: ABIParams, @@ -235,6 +248,18 @@ impl ABISig { stack_bytes, } } + + /// Returns an iterator over all the registers used as params. + pub fn param_regs(&self) -> impl Iterator + '_ { + self.params.iter().filter_map(|r| r.get_reg()) + } + + /// Returns an iterator over all the registers used in the signature. + pub fn regs(&self) -> impl Iterator + '_ { + let params_iter = self.param_regs(); + let result_iter = self.result.regs(); + params_iter.chain(result_iter) + } } /// Returns the size in bytes of a given WebAssembly type. diff --git a/winch/codegen/src/codegen/builtin.rs b/winch/codegen/src/codegen/builtin.rs new file mode 100644 index 000000000000..9a5fe34a34d3 --- /dev/null +++ b/winch/codegen/src/codegen/builtin.rs @@ -0,0 +1,100 @@ +//! Builtin function handling. + +use crate::{ + abi::{ABISig, ABI}, + codegen::env::ptr_type_from_ptr_size, + CallingConvention, +}; +use wasmtime_environ::{BuiltinFunctionIndex, PtrSize, WasmType}; + +/// Metadata about a builtin function. +pub(crate) struct BuiltinFunction { + /// The ABI specific signature of the function. + pub sig: ABISig, + /// The offset of the builtin function + pub offset: u32, + /// The builtin function base, relative to the VMContext. + pub base: u32, +} + +macro_rules! declare_function_sig { + ( + $( + $( #[$attr:meta] )* + $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?; + )* + ) => { + /// Provides the ABI signatures for each builtin function + /// signature. + pub struct BuiltinFunctions { + /// The target calling convention. + call_conv: CallingConvention, + /// The target pointer size. + ptr_size: u8, + /// The target pointer type, as a WebAssembly type. + ptr_type: WasmType, + /// The builtin functions base relative to the VMContext. + base: u32, + $( + $name: Option, + )* + } + + // Until all the builtin functions are used. + #[allow(dead_code)] + impl BuiltinFunctions { + pub fn new(ptr: impl PtrSize, call_conv: CallingConvention, base: u32) -> Self { + let size = ptr.size(); + Self { + ptr_size: size, + call_conv, + base, + ptr_type: ptr_type_from_ptr_size(size), + $( + $name: None, + )* + } + } + + fn pointer(&self) -> WasmType { + self.ptr_type + } + + fn vmctx(&self) -> WasmType { + self.pointer() + } + + fn i32(&self) -> WasmType { + WasmType::I32 + } + + fn i64(&self) -> WasmType { + WasmType::I64 + } + + fn reference(&self) -> WasmType { + self.pointer() + } + + $( + pub(crate) fn $name(&mut self) -> &BuiltinFunction { + if self.$name.is_none() { + let params = vec![ $(self.$param() ),* ]; + let result = vec![ $(self.$result() )?]; + let sig = A::sig_from(¶ms, &result, &self.call_conv); + let index = BuiltinFunctionIndex::$name(); + self.$name = Some(BuiltinFunction { + sig, + offset: index.index() * (self.ptr_size as u32), + base: self.base, + }); + } + + self.$name.as_ref().unwrap() + } + )* + } + } +} + +wasmtime_environ::foreach_builtin_function!(declare_function_sig); diff --git a/winch/codegen/src/codegen/call.rs b/winch/codegen/src/codegen/call.rs index aab3e688cf8c..6b311e94848b 100644 --- a/winch/codegen/src/codegen/call.rs +++ b/winch/codegen/src/codegen/call.rs @@ -2,13 +2,14 @@ //! calling convention, see [ABI]. use crate::{ abi::{ABIArg, ABISig, ABI}, - codegen::CodeGenContext, + codegen::{BuiltinFunction, CodeGenContext}, masm::{CalleeKind, MacroAssembler, OperandSize}, reg::Reg, }; use wasmtime_environ::FuncIndex; /// All the information needed to emit a function call. +#[derive(Copy, Clone)] pub(crate) struct FnCall<'a> { /// The stack space consumed by the function call; that is, /// the sum of: @@ -57,37 +58,44 @@ pub(crate) struct FnCall<'a> { /// │ │ /// └──────────────────────────────────────────────────┘ ------> Stack pointer when emitting the call /// - call_stack_space: u32, + call_stack_space: Option, /// The total stack space needed for the callee arguments on the /// stack, including any adjustments to the function's frame and /// aligned to to the required ABI alignment. arg_stack_space: u32, /// The ABI-specific signature of the callee. - abi_sig: &'a ABISig, + pub abi_sig: &'a ABISig, } impl<'a> FnCall<'a> { - /// Allocate and setup a new function call. - /// - /// The setup process, will first save all the live registers in - /// the value stack, tracking down those spilled for the function - /// arguments(see comment below for more details) it will also - /// track all the memory entries consumed by the function - /// call. Then, it will calculate any adjustments needed to ensure - /// the alignment of the caller's frame. It's important to note - /// that the order of operations in the setup is important, as we - /// want to calculate any adjustments to the caller's frame, after - /// having saved any live registers, so that we can account for - /// any pushes generated by register spilling. - pub fn new( - callee_sig: &'a ABISig, + /// Creates a new [`FnCall`] from the callee's [`ABISig`]. + pub fn new(callee_sig: &'a ABISig) -> Self { + Self { + abi_sig: &callee_sig, + arg_stack_space: callee_sig.stack_bytes, + call_stack_space: None, + } + } + + /// Saves any live registers and records the stack space that will be + /// consumed by the function call. The stack space consumed by the call must + /// be known before emitting the call via any of the emission variants: + /// [`FnCall::direct`], [`FnCall::indirect`] or [`FnCall::addr`], which + /// means that the call stack space must be calculated either by invoking + /// [`FnCall::save_live_registers`] or + /// [`FnCall::calculate_call_stack_space`] before invoking any of + /// the emission variants. + pub fn save_live_registers( + &mut self, context: &mut CodeGenContext, masm: &mut M, - ) -> Self { + ) -> &mut Self { + // Invariant: ensure that `call_stack_space` is only set once: either by + // [`FnCall::save_live_registers`] or + // [`FnCall::calculate_call_stack_space`] + debug_assert!(self.call_stack_space.is_none()); + let callee_params = &self.abi_sig.params; let stack = &context.stack; - let arg_stack_space = callee_sig.stack_bytes; - let callee_params = &callee_sig.params; - let call_stack_space = match callee_params.len() { 0 => { let _ = context.save_live_registers_and_calculate_sizeof(masm, ..); @@ -126,45 +134,138 @@ impl<'a> FnCall<'a> { } }; - Self { - abi_sig: &callee_sig, - arg_stack_space, - call_stack_space, - } + self.call_stack_space = Some(call_stack_space); + self + } + + /// Records the stack space that will be needeed by the function call by + /// scanning the value stack and returning the size of the all the memory + /// entries present in callee's argument length range. The stack space + /// consumed by the call must be known before emitting the call via any of + /// the emission variants: [`FnCall::direct`], [`FnCall::indirect`] or + /// [`FnCall::addr`], which means that the call stack space must be + /// calculated either by invoking [`FnCall::save_live_registers`] or + /// [`FnCall::calculate_call_stack_space`] before invoking any of + /// the emission variants. + /// This function is particularly useful when there's no need to save any + /// live registers before emitting the function call. This could happen when + /// emitting calls to libcalls: [`FnCall::with_lib`] will eagerly save all + /// the live registers when invoked and will also ensure that any registers + /// allocated after are non argument registers, in which case if any of + /// those registers need to go on the value stack to be used as function + /// arguments, they don't need to be saved. + pub fn calculate_call_stack_space(&mut self, context: &mut CodeGenContext) -> &mut Self { + // Invariant: ensure that `call_stack_space` is only set once: either by + // [`FnCall::save_live_registers`] or + // [`FnCall::calculate_call_stack_space`] + debug_assert!(self.call_stack_space.is_none()); + let params_len = self.abi_sig.params.len(); + assert!(context.stack.len() >= params_len); + + let stack_len = context.stack.len(); + let call_stack_space = if params_len == 0 { + 0 + } else { + context.stack.sizeof((stack_len - params_len)..) + }; + self.call_stack_space = Some(call_stack_space); + self } /// Emit a direct function call, to a locally defined function. pub fn direct( - &self, + self, masm: &mut M, context: &mut CodeGenContext, callee: FuncIndex, ) { + // Invariant: `call_stack_space` must be known. + debug_assert!(self.call_stack_space.is_some()); let reserved_stack = masm.call(self.arg_stack_space, |masm| { self.assign_args(context, masm, ::scratch_reg()); - CalleeKind::Direct(callee.as_u32()) + CalleeKind::direct(callee.as_u32()) }); self.post_call::(masm, context, reserved_stack); } - /// Emit an indirect function call, using a raw address. - pub fn indirect( - &self, + /// Emit an indirect function call, using a register. + pub fn reg(self, masm: &mut M, context: &mut CodeGenContext, reg: Reg) { + // Invariant: `call_stack_space` must be known. + debug_assert!(self.call_stack_space.is_some()); + let reserved_stack = masm.call(self.arg_stack_space, |masm| { + let scratch = ::scratch_reg(); + self.assign_args(context, masm, scratch); + CalleeKind::indirect(reg) + }); + context.free_reg(reg); + self.post_call::(masm, context, reserved_stack); + } + + /// Emit an indirect function call, using a an address. + /// This function will load the provided address into a unallocatable + /// scratch register. + pub fn addr( + self, masm: &mut M, context: &mut CodeGenContext, - addr: M::Address, + callee: M::Address, ) { + // Invariant: `call_stack_space` must be known. + debug_assert!(self.call_stack_space.is_some()); let reserved_stack = masm.call(self.arg_stack_space, |masm| { let scratch = ::scratch_reg(); self.assign_args(context, masm, scratch); - masm.load(addr, scratch, OperandSize::S64); - CalleeKind::Indirect(scratch) + masm.load(callee, scratch, OperandSize::S64); + CalleeKind::indirect(scratch) }); + self.post_call::(masm, context, reserved_stack); } + /// Prepares the compiler to call a built-in function (libcall). + /// This fuction, saves all the live registers and loads the callee + /// address into a non-argument register which is then passed to the + /// caller through the provided callback. + /// + /// It is the caller's responsibility to finalize the function call + /// by calling `FnCall::reg` once all the information is known. + pub fn with_lib( + &mut self, + masm: &mut M, + context: &mut CodeGenContext, + func: &BuiltinFunction, + mut f: F, + ) where + F: FnMut(&mut CodeGenContext, &mut M, &mut Self, Reg), + { + // When dealing with libcalls, we don't have all the information + // upfront (all necessary arguments in the stack) in order to optimize + // saving the live registers, so we save all the values available in + // the value stack. + context.spill(masm); + let vmctx = ::vmctx_reg(); + let scratch = ::scratch_reg(); + + let builtins_base = masm.address_at_reg(vmctx, func.base); + masm.load(builtins_base, scratch, OperandSize::S64); + let builtin_func_addr = masm.address_at_reg(scratch, func.offset); + context.without::<(), M, _>( + // Do not free the result registers if any as the function call will + // push them onto the stack as a result of the call. + self.abi_sig.regs(), + self.abi_sig.param_regs(), + masm, + |cx, masm| { + let callee = cx.any_gpr(masm); + masm.load_ptr(builtin_func_addr, callee); + f(cx, masm, self, callee); + cx.free_reg(callee); + }, + ); + } + fn post_call(&self, masm: &mut M, context: &mut CodeGenContext, size: u32) { - masm.free_stack(self.call_stack_space + size); + masm.free_stack(self.call_stack_space.unwrap() + size); // Only account for registers given that any memory entries // consumed by the call (assigned to a register or to a stack // slot) were freed by the previous call to diff --git a/winch/codegen/src/codegen/context.rs b/winch/codegen/src/codegen/context.rs index ad4bc40693ee..1f7658611911 100644 --- a/winch/codegen/src/codegen/context.rs +++ b/winch/codegen/src/codegen/context.rs @@ -2,7 +2,7 @@ use wasmtime_environ::WasmType; use super::ControlStackFrame; use crate::{ - abi::ABIResult, + abi::{ABIResult, ABI}, frame::Frame, isa::reg::RegClass, masm::{MacroAssembler, OperandSize, RegImm}, @@ -81,22 +81,32 @@ impl<'a> CodeGenContext<'a> { self.reg_for_class(RegClass::Int, masm) } - /// Executes the provided function, guaranteeing that the - /// specified register, if any, remains unallocatable throughout - /// the function's execution. - pub fn without(&mut self, reg: Option, masm: &mut M, mut f: F) -> T + /// Executes the provided function, guaranteeing that the specified set of + /// registers, if any, remain unallocatable throughout the function's + /// execution. Only the registers in the `free` iterator will be freed. The + /// caller must guarantee that in case the iterators are different, the free + /// iterator must be a subset of the alloc iterator. + pub fn without( + &mut self, + alloc: impl Iterator, + free: impl Iterator, + masm: &mut M, + mut f: F, + ) -> T where M: MacroAssembler, F: FnMut(&mut Self, &mut M) -> T, { - if let Some(reg) = reg { - self.reg(reg, masm); + debug_assert!(free.size_hint().0 <= alloc.size_hint().0); + + for r in alloc { + self.reg(r, masm); } let result = f(self, masm); - if let Some(reg) = reg { - self.free_reg(reg); + for r in free { + self.free_reg(r); } result @@ -403,8 +413,9 @@ impl<'a> CodeGenContext<'a> { Val::Local(local) => { let slot = frame.get_local(local.index).expect("valid local at slot"); let addr = masm.local_address(&slot); - masm.load(addr, regalloc.scratch, slot.ty.into()); - let stack_slot = masm.push(regalloc.scratch, slot.ty.into()); + let scratch = ::scratch_reg(); + masm.load(addr, scratch, slot.ty.into()); + let stack_slot = masm.push(scratch, slot.ty.into()); *v = Val::mem(slot.ty, stack_slot); } _ => {} diff --git a/winch/codegen/src/codegen/env.rs b/winch/codegen/src/codegen/env.rs index d3676ca65399..8e2d36c05f3f 100644 --- a/winch/codegen/src/codegen/env.rs +++ b/winch/codegen/src/codegen/env.rs @@ -1,31 +1,108 @@ +use crate::{codegen::BuiltinFunctions, CallingConvention}; use smallvec::{smallvec, SmallVec}; use wasmparser::BlockType; use wasmtime_environ::{ - FuncIndex, GlobalIndex, ModuleTranslation, PtrSize, TypeConvert, VMOffsets, WasmFuncType, - WasmType, + FuncIndex, GlobalIndex, ModuleTranslation, ModuleTypes, PtrSize, TableIndex, TypeConvert, + TypeIndex, VMOffsets, WasmFuncType, WasmType, }; +/// Table metadata. +pub struct TableData { + /// The offset to the base of the table. + pub offset: u32, + /// The offset to the current elements field. + pub current_elems_offset: u32, + /// If the table is imported, return the base + /// offset of the `from` field in `VMTableImport`. + pub base: Option, + /// The size of the table elements, in bytes. + pub element_size: u8, +} + +/// A function callee. +/// It categorizes how the callee should be treated +/// when performing the call. +pub enum Callee { + /// Locally defined function. + Local(CalleeInfo), + /// Imported function. + Import(CalleeInfo), + /// Function reference. + FuncRef(WasmFuncType), +} + +/// Metadata about a function callee. Used by the code generation to +/// emit function calls to local or imported functions. +pub struct CalleeInfo { + /// The function type. + pub ty: WasmFuncType, + /// The callee index in the WebAssembly function index space. + pub index: FuncIndex, +} + /// The function environment. /// /// Contains all information about the module and runtime that is accessible to /// to a particular function during code generation. -pub struct FuncEnv<'a, P> { +pub struct FuncEnv<'a, P: PtrSize> { /// Offsets to the fields within the `VMContext` ptr. pub vmoffsets: VMOffsets

, /// Metadata about the translation process of a WebAssembly module. pub translation: &'a ModuleTranslation<'a>, + /// Metadata about the builtin functions. + pub builtins: BuiltinFunctions, + /// The module's function types. + pub types: &'a ModuleTypes, +} + +pub fn ptr_type_from_ptr_size(size: u8) -> WasmType { + (size == 8) + .then(|| WasmType::I64) + .unwrap_or_else(|| unimplemented!("Support for non-64-bit architectures")) } impl<'a, P: PtrSize> FuncEnv<'a, P> { /// Create a new function environment. - pub fn new(ptr: P, translation: &'a ModuleTranslation) -> Self { + pub fn new( + ptr: P, + translation: &'a ModuleTranslation, + types: &'a ModuleTypes, + call_conv: CallingConvention, + ) -> Self { let vmoffsets = VMOffsets::new(ptr, &translation.module); + let size = vmoffsets.ptr.size(); + let builtins_base = vmoffsets.vmctx_builtin_functions(); Self { vmoffsets, translation, + builtins: BuiltinFunctions::new(size, call_conv, builtins_base), + types, } } + /// Returns a slice of types representing the caller and callee VMContext types. + pub(crate) fn vmctx_args_type(&self) -> [WasmType; 2] { + let ty = self.ptr_type(); + [ty, ty] + } + + /// Derive the [`WasmType`] from the pointer size. + pub(crate) fn ptr_type(&self) -> WasmType { + ptr_type_from_ptr_size(self.ptr_size()) + } + + /// Returns the pointer size for the target ISA. + fn ptr_size(&self) -> u8 { + self.vmoffsets.ptr.size() + } + + /// Resolves a [`Callee::FuncRef`] from a type index. + pub fn funcref(&self, idx: TypeIndex) -> Callee { + let sig_index = self.translation.module.types[idx].unwrap_function(); + let ty = self.types[sig_index].clone(); + Callee::FuncRef(ty) + } + /// Resolves a function [`Callee`] from an index. pub fn callee_from_index(&self, idx: FuncIndex) -> Callee { let types = &self.translation.get_types(); @@ -33,10 +110,12 @@ impl<'a, P: PtrSize> FuncEnv<'a, P> { let ty = self.translation.module.convert_func_type(ty); let import = self.translation.module.is_imported_function(idx); - Callee { - ty, - import, - index: idx, + let info = CalleeInfo { ty, index: idx }; + + if import { + Callee::Import(info) + } else { + Callee::Local(info) } } @@ -60,15 +139,29 @@ impl<'a, P: PtrSize> FuncEnv<'a, P> { (ty, offset) } -} -/// Metadata about a function callee. Use by the code generation -/// to emit function calls. -pub struct Callee { - /// The function type. - pub ty: WasmFuncType, - /// A flag to determine if the callee is imported. - pub import: bool, - /// The callee index in the WebAssembly function index space. - pub index: FuncIndex, + /// Returns the table information for the given table index. + pub fn resolve_table_data(&self, index: TableIndex) -> TableData { + let (from_offset, base_offset, current_elems_offset) = + match self.translation.module.defined_table_index(index) { + Some(defined) => ( + None, + self.vmoffsets.vmctx_vmtable_definition_base(defined), + self.vmoffsets + .vmctx_vmtable_definition_current_elements(defined), + ), + None => ( + Some(self.vmoffsets.vmctx_vmtable_import_from(index)), + self.vmoffsets.vmtable_definition_base().into(), + self.vmoffsets.vmtable_definition_current_elements().into(), + ), + }; + + TableData { + base: from_offset, + offset: base_offset, + current_elems_offset, + element_size: self.vmoffsets.ptr.size(), + } + } } diff --git a/winch/codegen/src/codegen/mod.rs b/winch/codegen/src/codegen/mod.rs index 1c3813901c6b..6771e15ea668 100644 --- a/winch/codegen/src/codegen/mod.rs +++ b/winch/codegen/src/codegen/mod.rs @@ -1,22 +1,25 @@ use crate::{ abi::{ABISig, ABI}, - masm::{MacroAssembler, OperandSize}, + isa::reg::Reg, + masm::{CmpKind, MacroAssembler, OperandSize, TrapCode}, stack::{TypedReg, Val}, CallingConvention, }; use anyhow::Result; -use call::FnCall; use smallvec::SmallVec; use wasmparser::{BinaryReader, FuncValidator, Operator, ValidatorResources, VisitOperator}; -use wasmtime_environ::{FuncIndex, WasmFuncType, WasmType}; +use wasmtime_environ::{PtrSize, TypeIndex, WasmFuncType, WasmType}; mod context; pub(crate) use context::*; mod env; pub use env::*; -pub mod call; +mod call; +pub(crate) use call::*; mod control; pub(crate) use control::*; +mod builtin; +pub(crate) use builtin::*; /// The code generation abstraction. pub(crate) struct CodeGen<'a, M> @@ -173,8 +176,7 @@ where ) -> Result<()> { self.spill_register_arguments(); let defined_locals_range = &self.context.frame.defined_locals_range; - self.masm - .zero_mem_range(defined_locals_range.as_range(), &mut self.context.regalloc); + self.masm.zero_mem_range(defined_locals_range.as_range()); // Save the vmctx pointer to its local slot in case we need to reload it // at any point. @@ -247,51 +249,131 @@ where } } - /// Emit a direct function call. - pub fn emit_call(&mut self, index: FuncIndex) { - let callee = self.env.callee_from_index(index); - let (sig, callee_addr): (ABISig, Option<::Address>) = if callee.import - { - let mut params = vec![WasmType::I64, WasmType::I64]; - params.extend_from_slice(callee.ty.params()); - let sig = WasmFuncType::new(params.into(), callee.ty.returns().into()); - - let caller_vmctx = ::vmctx_reg(); - let callee_vmctx = self.context.any_gpr(self.masm); - let callee_vmctx_offset = self.env.vmoffsets.vmctx_vmfunction_import_vmctx(index); - let callee_vmctx_addr = self.masm.address_at_reg(caller_vmctx, callee_vmctx_offset); - // FIXME Remove harcoded operand size, this will be needed - // once 32-bit architectures are supported. - self.masm - .load(callee_vmctx_addr, callee_vmctx, OperandSize::S64); - - let callee_body_offset = self.env.vmoffsets.vmctx_vmfunction_import_wasm_call(index); - let callee_addr = self.masm.address_at_reg(caller_vmctx, callee_body_offset); - - // Put the callee / caller vmctx at the start of the - // range of the stack so that they are used as first - // and second arguments. - let stack = &mut self.context.stack; - let location = stack.len() - (sig.params().len() - 2); - stack.insert(location as usize, TypedReg::i64(caller_vmctx).into()); - stack.insert(location as usize, TypedReg::i64(callee_vmctx).into()); - ( - ::sig(&sig, &CallingConvention::Default), - Some(callee_addr), - ) - } else { - ( - ::sig(&callee.ty, &CallingConvention::Default), - None, - ) + /// Emit a function call to: + /// * A locally defined function. + /// * A function import. + /// * A funcref. + pub fn emit_call(&mut self, callee: Callee) { + match callee { + Callee::Import(callee) => { + let mut params = Vec::with_capacity(callee.ty.params().len() + 2); + params.extend_from_slice(&self.env.vmctx_args_type()); + params.extend_from_slice(callee.ty.params()); + let sig = WasmFuncType::new(params.into(), callee.ty.returns().into()); + + let caller_vmctx = ::vmctx_reg(); + let callee_vmctx = self.context.any_gpr(self.masm); + let callee_vmctx_offset = self + .env + .vmoffsets + .vmctx_vmfunction_import_vmctx(callee.index); + let callee_vmctx_addr = self.masm.address_at_vmctx(callee_vmctx_offset); + // FIXME Remove harcoded operand size, this will be needed + // once 32-bit architectures are supported. + self.masm + .load(callee_vmctx_addr, callee_vmctx, OperandSize::S64); + + let callee_body_offset = self + .env + .vmoffsets + .vmctx_vmfunction_import_wasm_call(callee.index); + let callee_addr = self.masm.address_at_vmctx(callee_body_offset); + + // Put the callee / caller vmctx at the start of the + // range of the stack so that they are used as first + // and second arguments. + let stack = &mut self.context.stack; + let location = stack.len() - (sig.params().len() - 2); + stack.insert(location as usize, TypedReg::i64(caller_vmctx).into()); + stack.insert(location as usize, TypedReg::i64(callee_vmctx).into()); + + let abi_sig = ::sig(&sig, &CallingConvention::Default); + FnCall::new(&abi_sig) + .save_live_registers(&mut self.context, self.masm) + .addr(self.masm, &mut self.context, callee_addr); + } + + Callee::Local(callee) => { + let abi_sig = ::sig(&callee.ty, &CallingConvention::Default); + FnCall::new(&abi_sig) + .save_live_registers(&mut self.context, self.masm) + .direct(self.masm, &mut self.context, callee.index); + } + + Callee::FuncRef(ty) => { + // Get type for the caller and callee VMContext. + let ptr_type = self.env.ptr_type(); + let abi_sig = ::sig(&ty, &CallingConvention::Default); + // Pop the funcref pointer to a register and allocate a register to hold the + // address of the funcref. Since the callee is not addressed from a global non + // allocatable register (like the vmctx in the case of an import), we load the + // funcref to a register ensuring that it doesn't get assigned to a non-arg + // register. + let (funcref_ptr, funcref) = self.context.without::<_, M, _>( + abi_sig.param_regs(), + abi_sig.param_regs(), + self.masm, + |cx, masm| (cx.pop_to_reg(masm, None).into(), cx.any_gpr(masm)), + ); + self.masm.load( + self.masm.address_at_reg( + funcref_ptr, + self.env.vmoffsets.ptr.vm_func_ref_wasm_call().into(), + ), + funcref, + ptr_type.into(), + ); + self.context.free_reg(funcref_ptr); + + FnCall::new(&abi_sig) + .save_live_registers(&mut self.context, self.masm) + .reg(self.masm, &mut self.context, funcref); + } }; + } - let fncall = FnCall::new::(&sig, &mut self.context, self.masm); - if let Some(addr) = callee_addr { - fncall.indirect::(self.masm, &mut self.context, addr); - } else { - fncall.direct::(self.masm, &mut self.context, index); - } + /// Emits a a series of instructions that will type check a function reference call. + pub fn emit_typecheck_funcref(&mut self, funcref_ptr: Reg, type_index: TypeIndex) { + let ptr_size: OperandSize = self.env.ptr_type().into(); + let sig_index_bytes = self.env.vmoffsets.size_of_vmshared_signature_index(); + let sig_size = OperandSize::from_bytes(sig_index_bytes); + let sig_index = self.env.translation.module.types[type_index].unwrap_function(); + let sig_offset = sig_index + .as_u32() + .checked_mul(sig_index_bytes.into()) + .unwrap(); + let signatures_base_offset = self.env.vmoffsets.vmctx_signature_ids_array(); + let scratch = ::scratch_reg(); + let funcref_sig_offset = self.env.vmoffsets.ptr.vm_func_ref_type_index(); + + // Load the signatures address into the scratch register. + self.masm.load( + self.masm.address_at_vmctx(signatures_base_offset), + scratch, + ptr_size, + ); + + // Get the caller id. + let caller_id = self.context.any_gpr(self.masm); + self.masm.load( + self.masm.address_at_reg(scratch, sig_offset), + caller_id, + sig_size, + ); + + let callee_id = self.context.any_gpr(self.masm); + self.masm.load( + self.masm + .address_at_reg(funcref_ptr, funcref_sig_offset.into()), + callee_id, + sig_size, + ); + + // Typecheck. + self.masm.cmp(callee_id.into(), caller_id, OperandSize::S32); + self.masm.trapif(CmpKind::Ne, TrapCode::BadSignature); + self.context.free_reg(callee_id); + self.context.free_reg(caller_id); } /// Emit the usual function end instruction sequence. diff --git a/winch/codegen/src/isa/aarch64/abi.rs b/winch/codegen/src/isa/aarch64/abi.rs index 99d6a2560897..8a38a2918d18 100644 --- a/winch/codegen/src/isa/aarch64/abi.rs +++ b/winch/codegen/src/isa/aarch64/abi.rs @@ -65,22 +65,29 @@ impl ABI for Aarch64ABI { } fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> ABISig { + Self::sig_from(wasm_sig.params(), wasm_sig.returns(), call_conv) + } + + fn sig_from( + params: &[WasmType], + returns: &[WasmType], + call_conv: &CallingConvention, + ) -> ABISig { assert!(call_conv.is_apple_aarch64() || call_conv.is_default()); - if wasm_sig.returns().len() > 1 { + if returns.len() > 1 { panic!("multi-value not supported"); } let mut stack_offset = 0; let mut index_env = RegIndexEnv::default(); - let params: SmallVec<[ABIArg; 6]> = wasm_sig - .params() + let params: SmallVec<[ABIArg; 6]> = params .iter() .map(|arg| Self::to_abi_arg(arg, &mut stack_offset, &mut index_env)) .collect(); - let result = Self::result(wasm_sig.returns(), call_conv); + let result = Self::result(returns, call_conv); ABISig::new(params, result, stack_offset) } @@ -101,7 +108,7 @@ impl ABI for Aarch64ABI { } fn scratch_reg() -> Reg { - todo!() + regs::scratch() } fn sp_reg() -> Reg { diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs index 5953d4d231d1..880357d60587 100644 --- a/winch/codegen/src/isa/aarch64/masm.rs +++ b/winch/codegen/src/isa/aarch64/masm.rs @@ -1,11 +1,11 @@ use super::{abi::Aarch64ABI, address::Address, asm::Assembler, regs}; use crate::{ abi::{self, local::LocalSlot}, - codegen::CodeGenContext, + codegen::{CodeGenContext, TableData}, isa::reg::Reg, masm::{ CalleeKind, CmpKind, DivKind, Imm as I, MacroAssembler as Masm, OperandSize, RegImm, - RemKind, RoundingMode, ShiftKind, StackSlot, + RemKind, RoundingMode, ShiftKind, StackSlot, TrapCode, }, }; use cranelift_codegen::{settings, Final, MachBufferFinalized, MachLabel}; @@ -97,6 +97,16 @@ impl Masm for MacroAssembler { Address::offset(reg, offset as i64) } + fn table_elem_address( + &mut self, + _index: Reg, + _size: OperandSize, + _table_data: &TableData, + _context: &mut CodeGenContext, + ) -> Reg { + todo!() + } + fn address_from_sp(&self, _offset: u32) -> Self::Address { todo!() } @@ -105,6 +115,10 @@ impl Masm for MacroAssembler { todo!() } + fn address_at_vmctx(&self, _offset: u32) -> Self::Address { + todo!() + } + fn store(&mut self, src: RegImm, dst: Address, size: OperandSize) { let src = match src { RegImm::Imm(v) => { @@ -135,6 +149,10 @@ impl Masm for MacroAssembler { self.asm.ldr(src, dst, size); } + fn load_ptr(&mut self, _src: Self::Address, _dst: Reg) { + todo!() + } + fn pop(&mut self, _dst: Reg, _size: OperandSize) { todo!() } @@ -313,7 +331,7 @@ impl Masm for MacroAssembler { &mut self, _kind: CmpKind, _lhs: RegImm, - _rhs: RegImm, + _rhs: Reg, _taken: MachLabel, _size: OperandSize, ) { @@ -331,6 +349,10 @@ impl Masm for MacroAssembler { fn jmp_table(&mut self, _targets: &[MachLabel], _index: Reg, _tmp: Reg) { todo!() } + + fn trapif(&mut self, _cc: CmpKind, _code: TrapCode) { + todo!() + } } impl MacroAssembler { diff --git a/winch/codegen/src/isa/aarch64/mod.rs b/winch/codegen/src/isa/aarch64/mod.rs index 8d299eba0a93..70c61ab9ca31 100644 --- a/winch/codegen/src/isa/aarch64/mod.rs +++ b/winch/codegen/src/isa/aarch64/mod.rs @@ -1,4 +1,4 @@ -use self::regs::{scratch, ALL_GPR}; +use self::regs::{ALL_GPR, MAX_FPR, MAX_GPR, NON_ALLOCATABLE_GPR}; use crate::{ abi::ABI, codegen::{CodeGen, CodeGenContext, FuncEnv}, @@ -6,7 +6,7 @@ use crate::{ isa::{Builder, CallingConvention, TargetIsa}, masm::MacroAssembler, regalloc::RegAlloc, - regset::RegSet, + regset::RegBitSet, stack::Stack, TrampolineKind, }; @@ -17,7 +17,7 @@ use cranelift_codegen::{MachTextSectionBuilder, TextSectionBuilder}; use masm::MacroAssembler as Aarch64Masm; use target_lexicon::Triple; use wasmparser::{FuncValidator, FunctionBody, ValidatorResources}; -use wasmtime_environ::{ModuleTranslation, WasmFuncType}; +use wasmtime_environ::{ModuleTranslation, ModuleTypes, WasmFuncType}; mod abi; mod address; @@ -85,6 +85,7 @@ impl TargetIsa for Aarch64 { fn compile_function( &self, sig: &WasmFuncType, + types: &ModuleTypes, body: &FunctionBody, translation: &ModuleTranslation, validator: &mut FuncValidator, @@ -96,10 +97,21 @@ impl TargetIsa for Aarch64 { let defined_locals = DefinedLocals::new(translation, &mut body, validator)?; let frame = Frame::new::(&abi_sig, &defined_locals)?; + let gpr = RegBitSet::int( + ALL_GPR.into(), + NON_ALLOCATABLE_GPR.into(), + usize::try_from(MAX_GPR).unwrap(), + ); // TODO: Add floating point bitmask - let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), scratch()); + let fpr = RegBitSet::float(0, 0, usize::try_from(MAX_FPR).unwrap()); + let regalloc = RegAlloc::from(gpr, fpr); let codegen_context = CodeGenContext::new(regalloc, stack, &frame); - let env = FuncEnv::new(self.pointer_bytes(), translation); + let env = FuncEnv::new( + self.pointer_bytes(), + translation, + types, + self.wasmtime_call_conv(), + ); let mut codegen = CodeGen::new(&mut masm, codegen_context, env, abi_sig); codegen.emit(&mut body, validator)?; diff --git a/winch/codegen/src/isa/aarch64/regs.rs b/winch/codegen/src/isa/aarch64/regs.rs index e11d3a84f94d..ec540b43fe10 100644 --- a/winch/codegen/src/isa/aarch64/regs.rs +++ b/winch/codegen/src/isa/aarch64/regs.rs @@ -4,15 +4,20 @@ use crate::{isa::reg::Reg, masm::OperandSize}; use regalloc2::{PReg, RegClass}; use smallvec::{smallvec, SmallVec}; +/// FPR index bound. +pub(crate) const MAX_FPR: u32 = 32; +/// FPR index bound. +pub(crate) const MAX_GPR: u32 = 32; + /// Construct a X-register from an index. pub(crate) const fn xreg(num: u8) -> Reg { - assert!(num < 32); + assert!((num as u32) < MAX_GPR); Reg::new(PReg::new(num as usize, RegClass::Int)) } /// Construct a V-register from an index. pub(crate) const fn vreg(num: u8) -> Reg { - assert!(num < 32); + assert!((num as u32) < MAX_FPR); Reg::new(PReg::new(num as usize, RegClass::Float)) } @@ -131,7 +136,8 @@ pub(crate) const fn shadow_sp() -> Reg { xreg(28) } -const NON_ALLOCATABLE_GPR: u32 = (1 << ip0().hw_enc()) +/// Bitmask for non-allocatble GPR. +pub(crate) const NON_ALLOCATABLE_GPR: u32 = (1 << ip0().hw_enc()) | (1 << ip1().hw_enc()) | (1 << platform().hw_enc()) | (1 << fp().hw_enc()) diff --git a/winch/codegen/src/isa/mod.rs b/winch/codegen/src/isa/mod.rs index c90d4264c271..771fc556f9ac 100644 --- a/winch/codegen/src/isa/mod.rs +++ b/winch/codegen/src/isa/mod.rs @@ -10,7 +10,7 @@ use std::{ }; use target_lexicon::{Architecture, Triple}; use wasmparser::{FuncValidator, FunctionBody, ValidatorResources}; -use wasmtime_environ::{ModuleTranslation, WasmFuncType}; +use wasmtime_environ::{ModuleTranslation, ModuleTypes, WasmFuncType}; #[cfg(feature = "x64")] pub(crate) mod x64; @@ -77,6 +77,7 @@ pub(crate) enum LookupError { /// This enum is a reduced subset of the calling conventions defined in /// [cranelift_codegen::isa::CallConv]. Introducing this enum makes it easier /// to enforce the invariant of all the calling conventions supported by Winch. +#[derive(Copy, Clone)] pub enum CallingConvention { /// See [cranelift_codegen::isa::CallConv::WasmtimeSystemV] WasmtimeSystemV, @@ -148,6 +149,7 @@ pub trait TargetIsa: Send + Sync { fn compile_function( &self, sig: &WasmFuncType, + types: &ModuleTypes, body: &FunctionBody, translation: &ModuleTranslation, validator: &mut FuncValidator, diff --git a/winch/codegen/src/isa/reg.rs b/winch/codegen/src/isa/reg.rs index fda5f07cfdc0..0a96df3293be 100644 --- a/winch/codegen/src/isa/reg.rs +++ b/winch/codegen/src/isa/reg.rs @@ -34,8 +34,8 @@ impl Reg { } /// Get the encoding of the underlying register. - pub const fn hw_enc(self) -> u8 { - self.0.hw_enc() as u8 + pub const fn hw_enc(self) -> usize { + self.0.hw_enc() } /// Get the physical register representation. diff --git a/winch/codegen/src/isa/x64/abi.rs b/winch/codegen/src/isa/x64/abi.rs index e7194a1acdb1..be52fe9fb455 100644 --- a/winch/codegen/src/isa/x64/abi.rs +++ b/winch/codegen/src/isa/x64/abi.rs @@ -97,10 +97,14 @@ impl ABI for X64ABI { 64 } - fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> ABISig { + fn sig_from( + params: &[WasmType], + returns: &[WasmType], + call_conv: &CallingConvention, + ) -> ABISig { assert!(call_conv.is_fastcall() || call_conv.is_systemv() || call_conv.is_default()); - if wasm_sig.returns().len() > 1 { + if returns.len() > 1 { panic!("multi-value not supported"); } @@ -115,16 +119,19 @@ impl ABI for X64ABI { (0, RegIndexEnv::default()) }; - let params: SmallVec<[ABIArg; 6]> = wasm_sig - .params() + let params: SmallVec<[ABIArg; 6]> = params .iter() .map(|arg| Self::to_abi_arg(arg, &mut stack_offset, &mut index_env, is_fastcall)) .collect(); - let result = Self::result(wasm_sig.returns(), call_conv); + let result = Self::result(returns, call_conv); ABISig::new(params, result, stack_offset) } + fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> ABISig { + Self::sig_from(wasm_sig.params(), wasm_sig.returns(), call_conv) + } + fn result(returns: &[WasmType], _call_conv: &CallingConvention) -> ABIResult { // This invariant will be lifted once support for multi-value is added. assert!(returns.len() <= 1, "multi-value not supported"); diff --git a/winch/codegen/src/isa/x64/address.rs b/winch/codegen/src/isa/x64/address.rs index 970c0fa8b0d2..47229ae37448 100644 --- a/winch/codegen/src/isa/x64/address.rs +++ b/winch/codegen/src/isa/x64/address.rs @@ -1,8 +1,7 @@ //! x64 addressing mode. -use cranelift_codegen::ir::Constant; - use crate::reg::Reg; +use cranelift_codegen::ir::Constant; /// Memory address representation. #[derive(Debug, Copy, Clone)] diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs index 930ba22e9ae1..8377c3a1edda 100644 --- a/winch/codegen/src/isa/x64/asm.rs +++ b/winch/codegen/src/isa/x64/asm.rs @@ -2,7 +2,7 @@ use crate::{ isa::reg::{Reg, RegClass}, - masm::{CalleeKind, CmpKind, DivKind, OperandSize, RemKind, RoundingMode, ShiftKind}, + masm::{CmpKind, DivKind, OperandSize, RemKind, RoundingMode, ShiftKind}, }; use cranelift_codegen::{ entity::EntityRef, @@ -845,41 +845,35 @@ impl Assembler { }) } - /// Emit a function call to a known or unknown location. - /// - /// A known location is a locally defined function index. - /// An unknown location is an address whose value is located - /// ina register. - pub fn call(&mut self, callee: CalleeKind) { - match callee { - CalleeKind::Indirect(reg) => { - self.emit(Inst::CallUnknown { - dest: RegMem::reg(reg.into()), - info: Box::new(CallInfo { - uses: smallvec![], - defs: smallvec![], - clobbers: Default::default(), - opcode: Opcode::Call, - callee_pop_size: 0, - callee_conv: CallConv::SystemV, - }), - }); - } - CalleeKind::Direct(index) => { - let dest = ExternalName::user(UserExternalNameRef::new(index as usize)); - self.emit(Inst::CallKnown { - dest, - info: Box::new(CallInfo { - uses: smallvec![], - defs: smallvec![], - clobbers: Default::default(), - opcode: Opcode::Call, - callee_pop_size: 0, - callee_conv: CallConv::SystemV, - }), - }); - } - } + /// Emit a call to an unknown location through a register. + pub fn call_with_reg(&mut self, callee: Reg) { + self.emit(Inst::CallUnknown { + dest: RegMem::reg(callee.into()), + info: Box::new(CallInfo { + uses: smallvec![], + defs: smallvec![], + clobbers: Default::default(), + opcode: Opcode::Call, + callee_pop_size: 0, + callee_conv: CallConv::SystemV, + }), + }); + } + + /// Emit a call to a locally defined function through an index. + pub fn call_with_index(&mut self, index: u32) { + let dest = ExternalName::user(UserExternalNameRef::new(index as usize)); + self.emit(Inst::CallKnown { + dest, + info: Box::new(CallInfo { + uses: smallvec![], + defs: smallvec![], + clobbers: Default::default(), + opcode: Opcode::Call, + callee_pop_size: 0, + callee_conv: CallConv::SystemV, + }), + }); } /// Emits a conditional jump to the given label. @@ -917,4 +911,12 @@ impl Assembler { pub fn trap(&mut self, code: TrapCode) { self.emit(Inst::Ud2 { trap_code: code }) } + + /// Conditional trap. + pub fn trapif(&mut self, cc: CmpKind, trap_code: TrapCode) { + self.emit(Inst::TrapIf { + cc: cc.into(), + trap_code, + }); + } } diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs index d4c6cb6a0f47..8d89bd435c79 100644 --- a/winch/codegen/src/isa/x64/masm.rs +++ b/winch/codegen/src/isa/x64/masm.rs @@ -7,12 +7,12 @@ use super::{ use crate::masm::{ CmpKind, DivKind, Imm as I, MacroAssembler as Masm, OperandSize, RegImm, RemKind, RoundingMode, - ShiftKind, + ShiftKind, TrapCode, }; use crate::{abi::ABI, masm::StackSlot}; use crate::{ abi::{self, align_to, calculate_frame_adjustment, LocalSlot}, - codegen::CodeGenContext, + codegen::{ptr_type_from_ptr_size, CodeGenContext, TableData}, stack::Val, }; use crate::{ @@ -20,10 +20,11 @@ use crate::{ masm::CalleeKind, }; use cranelift_codegen::{ - ir::TrapCode, isa::x64::settings as x64_settings, settings, Final, MachBufferFinalized, - MachLabel, + isa::x64::settings as x64_settings, settings, Final, MachBufferFinalized, MachLabel, }; +use wasmtime_environ::PtrSize; + /// x64 MacroAssembler. pub(crate) struct MacroAssembler { /// Stack pointer offset. @@ -32,6 +33,10 @@ pub(crate) struct MacroAssembler { asm: Assembler, /// ISA flags. flags: x64_settings::Flags, + /// Shared flags. + shared_flags: settings::Flags, + /// The target pointer size. + ptr_size: OperandSize, } impl Masm for MacroAssembler { @@ -104,6 +109,67 @@ impl Masm for MacroAssembler { Address::offset(reg, offset) } + fn table_elem_address( + &mut self, + index: Reg, + size: OperandSize, + table_data: &TableData, + context: &mut CodeGenContext, + ) -> Reg { + let vmctx = ::vmctx_reg(); + let scratch = regs::scratch(); + let bound = context.any_gpr(self); + let ptr_base = context.any_gpr(self); + let tmp = context.any_gpr(self); + + if let Some(offset) = table_data.base { + // If the table data declares a particular offset base, + // load the address into a register to further use it as + // the table address. + self.asm + .mov_mr(&Address::offset(vmctx, offset), ptr_base, OperandSize::S64); + } else { + // Else, simply move the vmctx register into the addr register as + // the base to calculate the table address. + self.asm.mov_rr(vmctx, ptr_base, OperandSize::S64); + }; + + // OOB check. + let bound_addr = Address::offset(ptr_base, table_data.current_elems_offset); + self.asm.mov_mr(&bound_addr, bound, OperandSize::S64); + self.asm.cmp_rr(bound, index, size); + self.asm.trapif(CmpKind::GeU, TrapCode::TableOutOfBounds); + + // Move the index into the scratch register to calcualte the table + // element address. + // Moving the value of the index register to the scratch register + // also avoids overwriting the context of the index register. + self.asm.mov_rr(index, scratch, OperandSize::S32); + self.asm + .mul_ir(table_data.element_size as i32, scratch, OperandSize::S64); + self.asm.mov_mr( + &Address::offset(ptr_base, table_data.offset), + ptr_base, + OperandSize::S64, + ); + // Copy the value of the table base into a temporary register + // so that we can use it later in case of a misspeculation. + self.asm.mov_rr(ptr_base, tmp, OperandSize::S64); + // Calculate the address of the table element. + self.asm.add_rr(scratch, ptr_base, OperandSize::S64); + if self.shared_flags.enable_table_access_spectre_mitigation() { + // Perform a bounds check and override the value of the + // table element address in case the index is out of bounds. + self.asm.cmp_rr(bound, index, OperandSize::S32); + self.asm.cmov(tmp, ptr_base, CmpKind::GeU, OperandSize::S64); + } + self.asm + .mov_mr(&Address::offset(ptr_base, 0), ptr_base, OperandSize::S64); + context.free_reg(bound); + context.free_reg(tmp); + ptr_base + } + fn address_from_sp(&self, offset: u32) -> Self::Address { Address::offset(regs::rsp(), self.sp_offset - offset) } @@ -112,6 +178,10 @@ impl Masm for MacroAssembler { Address::offset(regs::rsp(), offset) } + fn address_at_vmctx(&self, offset: u32) -> Self::Address { + Address::offset(::vmctx_reg(), offset) + } + fn store(&mut self, src: RegImm, dst: Address, size: OperandSize) { match src { RegImm::Imm(imm) => match imm { @@ -142,7 +212,7 @@ impl Masm for MacroAssembler { self.asm.pop_r(dst); self.decrement_sp(::word_bytes()); } else { - let addr = self.address_at_sp(self.sp_offset); + let addr = self.address_from_sp(self.sp_offset); self.asm.xmm_mov_mr(&addr, dst, size); self.free_stack(size.bytes()); } @@ -160,10 +230,17 @@ impl Masm for MacroAssembler { let total_stack = delta + aligned_args_size; self.reserve_stack(total_stack); let callee = load_callee(self); - self.asm.call(callee); + match callee { + CalleeKind::Indirect(reg) => self.asm.call_with_reg(reg), + CalleeKind::Direct(idx) => self.asm.call_with_index(idx), + }; total_stack } + fn load_ptr(&mut self, src: Self::Address, dst: Reg) { + self.load(src, dst, self.ptr_size); + } + fn load(&mut self, src: Address, dst: Reg, size: OperandSize) { if dst.is_int() { self.asm.mov_mr(&src, dst, size); @@ -531,24 +608,24 @@ impl Masm for MacroAssembler { &mut self, kind: CmpKind, lhs: RegImm, - rhs: RegImm, + rhs: Reg, taken: MachLabel, size: OperandSize, ) { use CmpKind::*; match &(lhs, rhs) { - (RegImm::Reg(rlhs), RegImm::Reg(rrhs)) => { + (RegImm::Reg(rlhs), rrhs) => { // If the comparision kind is zero or not zero and both operands // are the same register, emit a test instruction. Else we emit // a normal comparison. if (kind == Eq || kind == Ne) && (rlhs == rrhs) { self.asm.test_rr(*rrhs, *rlhs, size); } else { - self.cmp(lhs, rhs.get_reg().unwrap(), size); + self.cmp(lhs, rhs, size); } } - _ => self.cmp(lhs, rhs.get_reg().unwrap(), size), + _ => self.cmp(lhs, rhs, size), } self.asm.jmp_if(kind, taken); } @@ -627,6 +704,10 @@ impl Masm for MacroAssembler { self.asm.trap(TrapCode::UnreachableCodeReached) } + fn trapif(&mut self, cc: CmpKind, code: TrapCode) { + self.asm.trapif(cc, code); + } + fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg) { // At least one default target. assert!(targets.len() >= 1); @@ -649,11 +730,17 @@ impl Masm for MacroAssembler { impl MacroAssembler { /// Create an x64 MacroAssembler. - pub fn new(shared_flags: settings::Flags, isa_flags: x64_settings::Flags) -> Self { + pub fn new( + ptr_size: impl PtrSize, + shared_flags: settings::Flags, + isa_flags: x64_settings::Flags, + ) -> Self { Self { sp_offset: 0, - asm: Assembler::new(shared_flags, isa_flags.clone()), + asm: Assembler::new(shared_flags.clone(), isa_flags.clone()), flags: isa_flags, + shared_flags, + ptr_size: ptr_type_from_ptr_size(ptr_size.size()).into(), } } diff --git a/winch/codegen/src/isa/x64/mod.rs b/winch/codegen/src/isa/x64/mod.rs index 0d3359f8ec39..4703f7f20edb 100644 --- a/winch/codegen/src/isa/x64/mod.rs +++ b/winch/codegen/src/isa/x64/mod.rs @@ -11,7 +11,7 @@ use crate::stack::Stack; use crate::trampoline::{Trampoline, TrampolineKind}; use crate::{ isa::{Builder, TargetIsa}, - regset::RegSet, + regset::RegBitSet, }; use anyhow::Result; use cranelift_codegen::settings::{self, Flags}; @@ -19,9 +19,9 @@ use cranelift_codegen::{isa::x64::settings as x64_settings, Final, MachBufferFin use cranelift_codegen::{MachTextSectionBuilder, TextSectionBuilder}; use target_lexicon::Triple; use wasmparser::{FuncValidator, FunctionBody, ValidatorResources}; -use wasmtime_environ::{ModuleTranslation, WasmFuncType}; +use wasmtime_environ::{ModuleTranslation, ModuleTypes, WasmFuncType}; -use self::regs::{ALL_FPR, ALL_GPR}; +use self::regs::{ALL_FPR, ALL_GPR, MAX_FPR, MAX_GPR, NON_ALLOCATABLE_FPR, NON_ALLOCATABLE_GPR}; mod abi; mod address; @@ -89,20 +89,37 @@ impl TargetIsa for X64 { fn compile_function( &self, sig: &WasmFuncType, + types: &ModuleTypes, body: &FunctionBody, translation: &ModuleTranslation, validator: &mut FuncValidator, ) -> Result> { + let pointer_bytes = self.pointer_bytes(); let mut body = body.get_binary_reader(); - let mut masm = X64Masm::new(self.shared_flags.clone(), self.isa_flags.clone()); + let mut masm = X64Masm::new( + pointer_bytes, + self.shared_flags.clone(), + self.isa_flags.clone(), + ); let stack = Stack::new(); let abi_sig = abi::X64ABI::sig(sig, &CallingConvention::Default); let defined_locals = DefinedLocals::new(translation, &mut body, validator)?; let frame = Frame::new::(&abi_sig, &defined_locals)?; - let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, ALL_FPR), regs::scratch()); + let gpr = RegBitSet::int( + ALL_GPR.into(), + NON_ALLOCATABLE_GPR.into(), + usize::try_from(MAX_GPR).unwrap(), + ); + let fpr = RegBitSet::float( + ALL_FPR.into(), + NON_ALLOCATABLE_FPR.into(), + usize::try_from(MAX_FPR).unwrap(), + ); + + let regalloc = RegAlloc::from(gpr, fpr); let codegen_context = CodeGenContext::new(regalloc, stack, &frame); - let env = FuncEnv::new(self.pointer_bytes(), translation); + let env = FuncEnv::new(pointer_bytes, translation, types, self.wasmtime_call_conv()); let mut codegen = CodeGen::new(&mut masm, codegen_context, env, abi_sig); codegen.emit(&mut body, validator)?; @@ -126,7 +143,11 @@ impl TargetIsa for X64 { ) -> Result> { use TrampolineKind::*; - let mut masm = X64Masm::new(self.shared_flags.clone(), self.isa_flags.clone()); + let mut masm = X64Masm::new( + self.pointer_bytes(), + self.shared_flags.clone(), + self.isa_flags.clone(), + ); let call_conv = self.wasmtime_call_conv(); let mut trampoline = Trampoline::new( diff --git a/winch/codegen/src/isa/x64/regs.rs b/winch/codegen/src/isa/x64/regs.rs index 0724b1d67252..238423dac896 100644 --- a/winch/codegen/src/isa/x64/regs.rs +++ b/winch/codegen/src/isa/x64/regs.rs @@ -170,22 +170,29 @@ pub(crate) fn scratch_xmm() -> Reg { xmm15() } +/// GPR count. const GPR: u32 = 16; +/// FPR count. const FPR: u32 = 16; +/// GPR index bound. +pub(crate) const MAX_GPR: u32 = GPR; +/// GPR index bound. +pub(crate) const MAX_FPR: u32 = FPR; const ALLOCATABLE_GPR: u32 = (1 << GPR) - 1; const ALLOCATABLE_FPR: u32 = (1 << FPR) - 1; +/// Bitmask of non-alloctable GPRs. // R11: Is used as the scratch register. // R14: Is a pinned register, used as the instance register. -const NON_ALLOCATABLE_GPR: u32 = (1 << ENC_RBP) | (1 << ENC_RSP) | (1 << ENC_R11) | (1 << ENC_R14); +pub(crate) const NON_ALLOCATABLE_GPR: u32 = + (1 << ENC_RBP) | (1 << ENC_RSP) | (1 << ENC_R11) | (1 << ENC_R14); +/// Bitmask of non-alloctable FPRs. // xmm15: Is used as the scratch register. -const NON_ALLOCATABLE_FPR: u32 = 1 << 15; +pub(crate) const NON_ALLOCATABLE_FPR: u32 = 1 << 15; /// Bitmask to represent the available general purpose registers. pub(crate) const ALL_GPR: u32 = ALLOCATABLE_GPR & !NON_ALLOCATABLE_GPR; /// Bitmask to represent the available floating point registers. -// Note: at the time of writing all floating point registers are allocatable, -// but we might need a scratch register in the future. pub(crate) const ALL_FPR: u32 = ALLOCATABLE_FPR & !NON_ALLOCATABLE_FPR; /// Returns the callee-saved registers according to a particular calling diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs index 2e7da9e208b5..ec258b7b39af 100644 --- a/winch/codegen/src/masm.rs +++ b/winch/codegen/src/masm.rs @@ -1,11 +1,12 @@ use crate::abi::{self, align_to, LocalSlot}; -use crate::codegen::CodeGenContext; +use crate::codegen::{CodeGenContext, TableData}; use crate::isa::reg::Reg; -use crate::regalloc::RegAlloc; use cranelift_codegen::{Final, MachBufferFinalized, MachLabel}; use std::{fmt::Debug, ops::Range}; use wasmtime_environ::PtrSize; +pub(crate) use cranelift_codegen::ir::TrapCode; + #[derive(Eq, PartialEq)] pub(crate) enum DivKind { /// Signed division. @@ -112,6 +113,17 @@ impl OperandSize { OperandSize::S128 => 7, } } + + /// Create an [`OperandSize`] from the given number of bytes. + pub fn from_bytes(bytes: u8) -> Self { + use OperandSize::*; + match bytes { + 4 => S32, + 8 => S64, + 16 => S128, + _ => panic!("Invalid bytes {} for OperandSize", bytes), + } + } } /// An abstraction over a register or immediate. @@ -179,6 +191,18 @@ pub(crate) enum CalleeKind { Direct(u32), } +impl CalleeKind { + /// Creates a callee kind from a register. + pub fn indirect(reg: Reg) -> Self { + Self::Indirect(reg) + } + + /// Creates a direct callee kind from a function index. + pub fn direct(index: u32) -> Self { + Self::Direct(index) + } +} + impl RegImm { /// Register constructor. pub fn reg(r: Reg) -> Self { @@ -281,6 +305,16 @@ pub(crate) trait MacroAssembler { /// Get the address of a local slot. fn local_address(&mut self, local: &LocalSlot) -> Self::Address; + /// Loads the address of the table element at a given index. + /// Returns a register that contains address of the table element. + fn table_elem_address( + &mut self, + index: Reg, + size: OperandSize, + table_data: &TableData, + context: &mut CodeGenContext, + ) -> Reg; + /// Constructs an address with an offset that is relative to the /// current position of the stack pointer (e.g. [sp + (sp_offset - /// offset)]. @@ -290,6 +324,11 @@ pub(crate) trait MacroAssembler { /// current position of the stack pointer (e.g. [sp + offset]. fn address_at_sp(&self, offset: u32) -> Self::Address; + /// Alias for [`Self::address_at_reg`] using the VMContext register as + /// a base. The VMContext register is derived from the ABI type that is + /// associated to the MacroAssembler. + fn address_at_vmctx(&self, offset: u32) -> Self::Address; + /// Construct an address that is absolute to the current position /// of the given register. fn address_at_reg(&self, reg: Reg, offset: u32) -> Self::Address; @@ -306,6 +345,10 @@ pub(crate) trait MacroAssembler { /// Perform a stack load. fn load(&mut self, src: Self::Address, dst: Reg, size: OperandSize); + /// Alias for `MacroAssembler::load` with the operand size corresponding + /// to the pointer size of the target. + fn load_ptr(&mut self, src: Self::Address, dst: Reg); + /// Pop a value from the machine stack into the given register. fn pop(&mut self, dst: Reg, size: OperandSize); @@ -406,7 +449,7 @@ pub(crate) trait MacroAssembler { /// The default implementation divides the given memory range /// into word-sized slots. Then it unrolls a series of store /// instructions, effectively assigning zero to each slot. - fn zero_mem_range(&mut self, mem: &Range, regalloc: &mut RegAlloc) { + fn zero_mem_range(&mut self, mem: &Range) { let word_size = ::word_bytes(); if mem.is_empty() { return; @@ -437,7 +480,7 @@ pub(crate) trait MacroAssembler { // Add an upper bound to this generation; // given a considerably large amount of slots // this will be inefficient. - let zero = regalloc.scratch; + let zero = ::scratch_reg(); self.zero(zero); let zero = RegImm::reg(zero); @@ -460,14 +503,7 @@ pub(crate) trait MacroAssembler { /// Performs a comparison between the two operands, /// and immediately after emits a jump to the given /// label destination if the condition is met. - fn branch( - &mut self, - kind: CmpKind, - lhs: RegImm, - rhs: RegImm, - taken: MachLabel, - size: OperandSize, - ); + fn branch(&mut self, kind: CmpKind, lhs: RegImm, rhs: Reg, taken: MachLabel, size: OperandSize); /// Emits and unconditional jump to the given label. fn jmp(&mut self, target: MachLabel); @@ -478,4 +514,7 @@ pub(crate) trait MacroAssembler { /// Emit an unreachable code trap. fn unreachable(&mut self); + + /// Traps if the condition code is met. + fn trapif(&mut self, cc: CmpKind, code: TrapCode); } diff --git a/winch/codegen/src/regalloc.rs b/winch/codegen/src/regalloc.rs index c63036ff2cf8..2bcb8db3063a 100644 --- a/winch/codegen/src/regalloc.rs +++ b/winch/codegen/src/regalloc.rs @@ -1,6 +1,6 @@ use crate::{ isa::reg::{Reg, RegClass}, - regset::RegSet, + regset::{RegBitSet, RegSet}, }; /// The register allocator. @@ -15,15 +15,15 @@ use crate::{ /// This processs ensures that whenever a register is requested, /// it is going to be available. pub(crate) struct RegAlloc { - pub scratch: Reg, + /// The register set. regset: RegSet, } impl RegAlloc { - /// Create a new register allocator - /// from a register set. - pub fn new(regset: RegSet, scratch: Reg) -> Self { - Self { regset, scratch } + /// Create a register allocator from a bit set for each register class. + pub fn from(gpr: RegBitSet, fpr: RegBitSet) -> Self { + let rs = RegSet::new(gpr, fpr); + Self { regset: rs } } /// Allocate the next available register for the given class, @@ -50,12 +50,6 @@ impl RegAlloc { where F: FnMut(&mut RegAlloc), { - // If the scratch register is explicitly requested - // just return it, it's usage should never cause spills. - if named == self.scratch { - return named; - } - self.regset.reg(named).unwrap_or_else(|| { spill(self); self.regset @@ -66,8 +60,6 @@ impl RegAlloc { /// Free the given register. pub fn free(&mut self, reg: Reg) { - if reg != self.scratch { - self.regset.free(reg); - } + self.regset.free(reg); } } diff --git a/winch/codegen/src/regset.rs b/winch/codegen/src/regset.rs index 655a10dc10ee..0f51dc2babed 100644 --- a/winch/codegen/src/regset.rs +++ b/winch/codegen/src/regset.rs @@ -3,15 +3,15 @@ use crate::isa::reg::{Reg, RegClass}; /// A bit set to track regiter availability. pub(crate) struct RegSet { /// Bitset to track general purpose register availability. - gpr: u32, + gpr: RegBitSet, /// Bitset to track floating-point register availability. - fpr: u32, + fpr: RegBitSet, } use std::ops::{Index, IndexMut}; impl Index for RegSet { - type Output = u32; + type Output = RegBitSet; fn index(&self, class: RegClass) -> &Self::Output { match class { @@ -32,9 +32,53 @@ impl IndexMut for RegSet { } } +/// Bitset for a particular register class. +pub struct RegBitSet { + /// The register class. + class: RegClass, + /// The set of allocatable + allocatable: u64, + /// The set of non-alloctable registers. + non_allocatable: u64, + /// The max number of registers. + /// Invariant: + /// When allocating or freeing a register the encoding (index) of the + /// register must be less than the max property. + max: usize, +} + +impl RegBitSet { + /// Creates an integer register class bitset. + pub fn int(allocatable: u64, non_allocatable: u64, max: usize) -> Self { + // Assert that one set is the complement of the other. + debug_assert!(allocatable & non_allocatable == 0); + Self { + class: RegClass::Int, + allocatable, + non_allocatable, + max, + } + } + + /// Creates a float register class bitset. + pub fn float(allocatable: u64, non_allocatable: u64, max: usize) -> Self { + // Assert that one set is the complement of the other. + debug_assert!(allocatable & non_allocatable == 0); + Self { + class: RegClass::Float, + allocatable, + non_allocatable, + max, + } + } +} + impl RegSet { /// Create a new register set. - pub fn new(gpr: u32, fpr: u32) -> Self { + pub fn new(gpr: RegBitSet, fpr: RegBitSet) -> Self { + debug_assert!(gpr.class == RegClass::Int); + debug_assert!(fpr.class == RegClass::Float); + Self { gpr, fpr } } @@ -42,9 +86,9 @@ impl RegSet { /// returning `None` if there are no more registers available. pub fn reg_for_class(&mut self, class: RegClass) -> Option { self.available(class).then(|| { - let bitset = self[class]; - let index = bitset.trailing_zeros(); - self.allocate(class, index); + let bitset = &self[class]; + let index = bitset.allocatable.trailing_zeros(); + self.allocate(class, index.into()); Reg::from(class, index as usize) }) } @@ -53,7 +97,7 @@ impl RegSet { pub fn reg(&mut self, reg: Reg) -> Option { let index = reg.hw_enc(); self.named_reg_available(reg).then(|| { - self.allocate(reg.class(), index.into()); + self.allocate(reg.class(), index.try_into().unwrap()); reg }) } @@ -61,36 +105,55 @@ impl RegSet { /// Marks the specified register as available, utilizing the /// register class to determine the bitset that requires updating. pub fn free(&mut self, reg: Reg) { - let index = reg.hw_enc() as u32; - self[reg.class()] |= 1 << index; + let bitset = &self[reg.class()]; + let index = reg.hw_enc(); + assert!(index < bitset.max); + let index = u64::try_from(index).unwrap(); + if !self.is_non_allocatable(reg.class(), index) { + self[reg.class()].allocatable |= 1 << index; + } } /// Returns true if the specified register is allocatable. pub fn named_reg_available(&self, reg: Reg) -> bool { - let bitset = self[reg.class()]; + let bitset = &self[reg.class()]; + assert!(reg.hw_enc() < bitset.max); let index = 1 << reg.hw_enc(); - (!bitset & index) == 0 + + (!bitset.allocatable & index) == 0 + || self.is_non_allocatable(reg.class(), reg.hw_enc().try_into().unwrap()) } fn available(&self, class: RegClass) -> bool { - let bitset = self[class]; - bitset != 0 + let bitset = &self[class]; + bitset.allocatable != 0 + } + + fn allocate(&mut self, class: RegClass, index: u64) { + if !self.is_non_allocatable(class, index) { + self[class].allocatable &= !(1 << index); + } } - fn allocate(&mut self, class: RegClass, index: u32) { - self[class] &= !(1 << index); + fn is_non_allocatable(&self, class: RegClass, index: u64) -> bool { + let bitset = &self[class]; + let non_allocatable = bitset.non_allocatable; + non_allocatable != 0 && !non_allocatable & (1 << index) == 0 } } #[cfg(test)] mod tests { - use super::{Reg, RegClass, RegSet}; + use super::{Reg, RegBitSet, RegClass, RegSet}; - const UNIVERSE: u32 = (1 << 16) - 1; + const UNIVERSE: u64 = (1 << 16) - 1; + const MAX: usize = 16; #[test] fn test_any_gpr() { - let mut set = RegSet::new(UNIVERSE, 0); + let bitset = RegBitSet::int(UNIVERSE, !UNIVERSE, MAX); + let zero = RegBitSet::float(0, 0, MAX); + let mut set = RegSet::new(bitset, zero); for _ in 0..16 { let gpr = set.reg_for_class(RegClass::Int); assert!(gpr.is_some()) @@ -102,15 +165,28 @@ mod tests { #[test] fn test_gpr() { - let all = UNIVERSE & !(1 << 5); - let target = Reg::int(5); - let mut set = RegSet::new(all, 0); - assert!(set.reg(target).is_none()); + let non_allocatable: u64 = 1 << 5; + let all = UNIVERSE & !non_allocatable; + let non_alloc = Reg::int(5); + let alloc = Reg::int(2); + let bitset = RegBitSet::int(all, non_allocatable, MAX); + let zero = RegBitSet::float(0, 0, MAX); + let mut set = RegSet::new(bitset, zero); + // Requesting a non alloctable register returns the register + // and doesn't allocate it. + assert!(set.reg(non_alloc).is_some()); + assert!(set.reg(non_alloc).is_some()); + // Requesting an allocatable register twice returns none the + // second time. + assert!(set.reg(alloc).is_some()); + assert!(set.reg(alloc).is_none()); } #[test] fn test_free_reg() { - let mut set = RegSet::new(UNIVERSE, 0); + let set = RegBitSet::int(UNIVERSE, !UNIVERSE, MAX); + let zero = RegBitSet::float(0, 0, MAX); + let mut set = RegSet::new(set, zero); let gpr = set.reg_for_class(RegClass::Int).unwrap(); set.free(gpr); assert!(set.reg(gpr).is_some()); diff --git a/winch/codegen/src/stack.rs b/winch/codegen/src/stack.rs index d176b8d4d04c..203eeab01742 100644 --- a/winch/codegen/src/stack.rs +++ b/winch/codegen/src/stack.rs @@ -1,5 +1,6 @@ use crate::{isa::reg::Reg, masm::StackSlot}; use std::collections::VecDeque; +use std::ops::RangeBounds; use wasmparser::{Ieee32, Ieee64}; use wasmtime_environ::WasmType; @@ -14,12 +15,12 @@ pub struct TypedReg { } impl TypedReg { - /// Create a new TypedReg. + /// Create a new [`TypedReg`]. pub fn new(ty: WasmType, reg: Reg) -> Self { Self { ty, reg } } - /// Create an i64 TypedReg. + /// Create an i64 [`TypedReg`]. pub fn i64(reg: Reg) -> Self { Self { ty: WasmType::I64, @@ -89,6 +90,13 @@ impl From for Val { } } +impl TryFrom for Val { + type Error = anyhow::Error; + fn try_from(value: u32) -> Result { + i32::try_from(value).map(Val::i32).map_err(Into::into) + } +} + impl Val { /// Create a new I32 constant value. pub fn i32(v: i32) -> Self { @@ -294,6 +302,18 @@ impl Stack { pub fn inner_mut(&mut self) -> &mut VecDeque { &mut self.inner } + + /// Calculates size in bytes of memory entries within the specified range of + /// the stack. + pub fn sizeof(&self, range: R) -> u32 + where + R: RangeBounds, + { + self.inner.range(range).fold(0, |acc, v| match v { + Val::Memory(m) => acc + m.slot.size, + _ => acc, + }) + } } #[cfg(test)] diff --git a/winch/codegen/src/visitor.rs b/winch/codegen/src/visitor.rs index 101df49ff5d9..fc96b7d00d72 100644 --- a/winch/codegen/src/visitor.rs +++ b/winch/codegen/src/visitor.rs @@ -5,8 +5,7 @@ //! machine code emitter. use crate::abi::ABI; -use crate::codegen::ControlStackFrame; -use crate::codegen::{control_index, CodeGen}; +use crate::codegen::{control_index, CodeGen, ControlStackFrame, FnCall}; use crate::masm::{ CmpKind, DivKind, MacroAssembler, OperandSize, RegImm, RemKind, RoundingMode, ShiftKind, }; @@ -14,7 +13,9 @@ use crate::stack::{TypedReg, Val}; use smallvec::SmallVec; use wasmparser::BrTable; use wasmparser::{BlockType, Ieee32, Ieee64, VisitOperator}; -use wasmtime_environ::{FuncIndex, GlobalIndex, WasmType}; +use wasmtime_environ::{ + FuncIndex, GlobalIndex, TableIndex, TableStyle, TypeIndex, WasmType, FUNCREF_MASK, +}; /// A macro to define unsupported WebAssembly operators. /// @@ -130,6 +131,8 @@ macro_rules! def_unsupported { (emit Select $($rest:tt)*) => {}; (emit Drop $($rest:tt)*) => {}; (emit BrTable $($rest:tt)*) => {}; + (emit CallIndirect $($rest:tt)*) => {}; + (emit $unsupported:tt $($rest:tt)*) => {$($rest)*}; } @@ -609,14 +612,97 @@ where } } - // TODO verify the case where the target local is on the stack. + // TODO: verify the case where the target local is on the stack. fn visit_local_set(&mut self, index: u32) { let src = self.context.set_local(self.masm, index); self.context.free_reg(src); } fn visit_call(&mut self, index: u32) { - self.emit_call(FuncIndex::from_u32(index)); + let callee = self.env.callee_from_index(FuncIndex::from_u32(index)); + self.emit_call(callee); + } + + fn visit_call_indirect(&mut self, type_index: u32, table_index: u32, _: u8) { + let type_index = TypeIndex::from_u32(type_index); + let table_index = TableIndex::from_u32(table_index); + let table_data = self.env.resolve_table_data(table_index); + let ptr_type = self.env.ptr_type(); + + let builtin = self + .env + .builtins + .table_get_lazy_init_func_ref::(); + + FnCall::new(&builtin.sig).with_lib( + self.masm, + &mut self.context, + &builtin, + |cx, masm, call, callee| { + // Calculate the table element address. + let index = cx.pop_to_reg(masm, None); + let elem_addr = + masm.table_elem_address(index.into(), index.ty.into(), &table_data, cx); + + let defined = masm.get_label(); + let cont = masm.get_label(); + + // Preemptively move the table element address to the + // result register, to avoid conflicts at the control flow merge. + let result = call.abi_sig.result.result_reg().unwrap(); + masm.mov(elem_addr.into(), result, ptr_type.into()); + cx.free_reg(result); + + // Push the builtin function arguments to the stack. + cx.stack + .push(TypedReg::new(ptr_type, ::vmctx_reg()).into()); + cx.stack.push(table_index.as_u32().try_into().unwrap()); + cx.stack.push(index.into()); + + masm.branch( + CmpKind::Ne, + elem_addr.into(), + elem_addr, + defined, + ptr_type.into(), + ); + + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + // We know the signature of the libcall in this case, so we assert that there's + // one element in the stack and that it's the ABI signature's result register. + let top = cx.stack.peek().unwrap(); + let top = top.get_reg(); + debug_assert!(top.reg == result); + masm.jmp(cont); + + // In the defined case, mask the funcref address in place, by peeking into the + // last element of the value stack, which was pushed by the `indirect` function + // call above. + masm.bind(defined); + let imm = RegImm::i64(FUNCREF_MASK as i64); + let dst = top.into(); + masm.and(dst, dst, imm, top.ty.into()); + + masm.bind(cont); + // The indirect call above, will take care of freeing the registers used as + // params. + // So we only free the params used to lazily initialize the func ref. + cx.free_reg(elem_addr); + }, + ); + + // Perform the indirect call. + match self.env.translation.module.table_plans[table_index].style { + TableStyle::CallerChecksSignature => { + let funcref_ptr = self.context.stack.peek().map(|v| v.get_reg()).unwrap(); + self.emit_typecheck_funcref(funcref_ptr.into(), type_index); + } + } + + // Perform call indirect. + // `emit_call` expects the callee to be on the stack. Delaying the + // computation of the callee address reduces register pressure. + self.emit_call(self.env.funcref(type_index)); } fn visit_nop(&mut self) {} @@ -671,11 +757,12 @@ where let frame = &mut self.control_frames[index]; frame.set_as_target(); let result = frame.as_target_result(); - let top = - self.context - .without::(result.result_reg(), self.masm, |ctx, masm| { - ctx.pop_to_reg(masm, None) - }); + let top = self.context.without::( + result.regs(), + result.regs(), + self.masm, + |ctx, masm| ctx.pop_to_reg(masm, None), + ); self.context.pop_abi_results(&result, self.masm); self.context.push_abi_results(&result, self.masm); self.masm.branch( @@ -699,7 +786,8 @@ where let default_index = control_index(targets.default(), self.control_frames.len()); let default_result = self.control_frames[default_index].as_target_result(); let (index, tmp) = self.context.without::<(TypedReg, _), M, _>( - default_result.result_reg(), + default_result.regs(), + default_result.regs(), self.masm, |cx, masm| (cx.pop_to_reg(masm, None), cx.any_gpr(masm)), ); diff --git a/winch/filetests/filetests/x64/call_indirect/call_indirect.wat b/winch/filetests/filetests/x64/call_indirect/call_indirect.wat new file mode 100644 index 000000000000..b2dd60224111 --- /dev/null +++ b/winch/filetests/filetests/x64/call_indirect/call_indirect.wat @@ -0,0 +1,130 @@ +;;! target="x86_64" + +(module + (type $over-i32 (func (param i32) (result i32))) + + (table funcref + (elem + $fib-i32 + ) + ) + + (func $fib-i32 (export "fib-i32") (type $over-i32) + (if (result i32) (i32.le_u (local.get 0) (i32.const 1)) + (then (i32.const 1)) + (else + (i32.add + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 2)) + (i32.const 0) + ) + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 0) + ) + ) + ) + ) + ) +) + + +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec10 sub rsp, 0x10 +;; 8: 897c240c mov dword ptr [rsp + 0xc], edi +;; c: 4c89742404 mov qword ptr [rsp + 4], r14 +;; 11: 8b44240c mov eax, dword ptr [rsp + 0xc] +;; 15: 83f801 cmp eax, 1 +;; 18: b800000000 mov eax, 0 +;; 1d: 400f96c0 setbe al +;; 21: 85c0 test eax, eax +;; 23: 0f840a000000 je 0x33 +;; 29: b801000000 mov eax, 1 +;; 2e: e913010000 jmp 0x146 +;; 33: 8b44240c mov eax, dword ptr [rsp + 0xc] +;; 37: 83e802 sub eax, 2 +;; 3a: 50 push rax +;; 3b: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 3f: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; 43: bb00000000 mov ebx, 0 +;; 48: 4d89f1 mov r9, r14 +;; 4b: 4d8b4150 mov r8, qword ptr [r9 + 0x50] +;; 4f: 4439c3 cmp ebx, r8d +;; 52: 0f83f4000000 jae 0x14c +;; 58: 4189db mov r11d, ebx +;; 5b: 4d6bdb08 imul r11, r11, 8 +;; 5f: 4d8b4948 mov r9, qword ptr [r9 + 0x48] +;; 63: 4d89ca mov r10, r9 +;; 66: 4d01d9 add r9, r11 +;; 69: 4439c3 cmp ebx, r8d +;; 6c: 4d0f43ca cmovae r9, r10 +;; 70: 4d8b09 mov r9, qword ptr [r9] +;; 73: 4c89c8 mov rax, r9 +;; 76: 4d85c9 test r9, r9 +;; 79: 0f8519000000 jne 0x98 +;; 7f: 4883ec08 sub rsp, 8 +;; 83: 4c89f7 mov rdi, r14 +;; 86: be00000000 mov esi, 0 +;; 8b: 89da mov edx, ebx +;; 8d: ffd1 call rcx +;; 8f: 4883c408 add rsp, 8 +;; 93: e904000000 jmp 0x9c +;; 98: 4883e0fe and rax, 0xfffffffffffffffe +;; 9c: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] +;; a0: 418b0b mov ecx, dword ptr [r11] +;; a3: 8b5018 mov edx, dword ptr [rax + 0x18] +;; a6: 39d1 cmp ecx, edx +;; a8: 0f85a0000000 jne 0x14e +;; ae: 488b4810 mov rcx, qword ptr [rax + 0x10] +;; b2: 4883ec08 sub rsp, 8 +;; b6: 8b7c2408 mov edi, dword ptr [rsp + 8] +;; ba: ffd1 call rcx +;; bc: 4883c410 add rsp, 0x10 +;; c0: 8b4c240c mov ecx, dword ptr [rsp + 0xc] +;; c4: 83e901 sub ecx, 1 +;; c7: 50 push rax +;; c8: 51 push rcx +;; c9: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; cd: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; d1: bb00000000 mov ebx, 0 +;; d6: 4d89f1 mov r9, r14 +;; d9: 4d8b4150 mov r8, qword ptr [r9 + 0x50] +;; dd: 4439c3 cmp ebx, r8d +;; e0: 0f836a000000 jae 0x150 +;; e6: 4189db mov r11d, ebx +;; e9: 4d6bdb08 imul r11, r11, 8 +;; ed: 4d8b4948 mov r9, qword ptr [r9 + 0x48] +;; f1: 4d89ca mov r10, r9 +;; f4: 4d01d9 add r9, r11 +;; f7: 4439c3 cmp ebx, r8d +;; fa: 4d0f43ca cmovae r9, r10 +;; fe: 4d8b09 mov r9, qword ptr [r9] +;; 101: 4c89c8 mov rax, r9 +;; 104: 4d85c9 test r9, r9 +;; 107: 0f8511000000 jne 0x11e +;; 10d: 4c89f7 mov rdi, r14 +;; 110: be00000000 mov esi, 0 +;; 115: 89da mov edx, ebx +;; 117: ffd1 call rcx +;; 119: e904000000 jmp 0x122 +;; 11e: 4883e0fe and rax, 0xfffffffffffffffe +;; 122: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] +;; 126: 418b0b mov ecx, dword ptr [r11] +;; 129: 8b5018 mov edx, dword ptr [rax + 0x18] +;; 12c: 39d1 cmp ecx, edx +;; 12e: 0f851e000000 jne 0x152 +;; 134: 488b4810 mov rcx, qword ptr [rax + 0x10] +;; 138: 8b3c24 mov edi, dword ptr [rsp] +;; 13b: ffd1 call rcx +;; 13d: 4883c408 add rsp, 8 +;; 141: 59 pop rcx +;; 142: 01c1 add ecx, eax +;; 144: 89c8 mov eax, ecx +;; 146: 4883c410 add rsp, 0x10 +;; 14a: 5d pop rbp +;; 14b: c3 ret +;; 14c: 0f0b ud2 +;; 14e: 0f0b ud2 +;; 150: 0f0b ud2 +;; 152: 0f0b ud2 diff --git a/winch/filetests/src/lib.rs b/winch/filetests/src/lib.rs index b1f5daa86c6d..6b435b701a8f 100644 --- a/winch/filetests/src/lib.rs +++ b/winch/filetests/src/lib.rs @@ -12,7 +12,7 @@ mod test { use wasmtime_environ::ModuleTranslation; use wasmtime_environ::{ wasmparser::{Parser as WasmParser, Validator}, - DefinedFuncIndex, FunctionBodyData, ModuleEnvironment, Tunables, TypeConvert, + DefinedFuncIndex, FunctionBodyData, ModuleEnvironment, ModuleTypes, Tunables, TypeConvert, }; use winch_codegen::{lookup, TargetIsa}; use winch_test_macros::generate_file_tests; @@ -108,13 +108,13 @@ mod test { .translate(parser, &wasm) .context("Failed to translate WebAssembly module") .unwrap(); - let _ = types.finish(); + let types = types.finish(); let body_inputs = std::mem::take(&mut translation.function_body_inputs); let binding = body_inputs .into_iter() - .map(|func| compile(&isa, &translation, func).join("\n")) + .map(|func| compile(&isa, &types, &translation, func).join("\n")) .collect::>() .join("\n\n"); let actual = binding.as_str(); @@ -147,6 +147,7 @@ mod test { fn compile( isa: &Box, + module_types: &ModuleTypes, translation: &ModuleTranslation, f: (DefinedFuncIndex, FunctionBodyData<'_>), ) -> Vec { @@ -160,7 +161,7 @@ mod test { let FunctionBodyData { body, validator } = f.1; let mut validator = validator.into_validator(Default::default()); let buffer = isa - .compile_function(&sig, &body, &translation, &mut validator) + .compile_function(&sig, module_types, &body, &translation, &mut validator) .expect("Couldn't compile function"); disasm(buffer.data(), isa).unwrap() diff --git a/winch/src/compile.rs b/winch/src/compile.rs index 723f3a6ac4fd..2d8b929022d6 100644 --- a/winch/src/compile.rs +++ b/winch/src/compile.rs @@ -5,8 +5,8 @@ use std::{fs, path::PathBuf, str::FromStr}; use target_lexicon::Triple; use wasmtime_environ::{ wasmparser::{Parser as WasmParser, Validator}, - DefinedFuncIndex, FunctionBodyData, ModuleEnvironment, ModuleTranslation, Tunables, - TypeConvert, + DefinedFuncIndex, FunctionBodyData, ModuleEnvironment, ModuleTranslation, ModuleTypes, + Tunables, TypeConvert, }; use winch_codegen::{lookup, TargetIsa}; use winch_filetests::disasm::disasm; @@ -36,12 +36,12 @@ pub fn run(opt: &Options) -> Result<()> { let mut translation = ModuleEnvironment::new(&tunables, &mut validator, &mut types) .translate(parser, &bytes) .context("Failed to translate WebAssembly module")?; - let _ = types.finish(); + let types = types.finish(); let body_inputs = std::mem::take(&mut translation.function_body_inputs); body_inputs .into_iter() - .try_for_each(|func| compile(&isa, &translation, func))?; + .try_for_each(|func| compile(&isa, &translation, &types, func))?; Ok(()) } @@ -49,6 +49,7 @@ pub fn run(opt: &Options) -> Result<()> { fn compile( isa: &Box, translation: &ModuleTranslation, + module_types: &ModuleTypes, f: (DefinedFuncIndex, FunctionBodyData<'_>), ) -> Result<()> { let index = translation.module.func_index(f.0); @@ -58,7 +59,7 @@ fn compile( let FunctionBodyData { body, validator } = f.1; let mut validator = validator.into_validator(Default::default()); let buffer = isa - .compile_function(&sig, &body, &translation, &mut validator) + .compile_function(&sig, module_types, &body, &translation, &mut validator) .expect("Couldn't compile function"); println!("Disassembly for function: {}", index.as_u32()); From 3e5b30bd9606460682c5eff7b3237d42d1460b30 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 29 Sep 2023 12:23:54 -0700 Subject: [PATCH 028/199] Start to port Wasmtime to the new wasi-io API with resources. (#7029) * Rename `Host*` things to avoid name conflicts with bindings. * Update to the latest resource-enabled wit files. * Adapting the code to the new bindings. * Update wasi-http to the resource-enabled wit deps. * Start adapting the wasi-http code to the new bindings. * Make `get_directories` always return new owned handles. * Simplify the `poll_one` implementation. * Update the wasi-preview1-component-adapter. FIXME: temporarily disable wasi-http tests. Add logging to the cli world, since stderr is now a reseource that can only be claimed once. * Work around a bug hit by poll-list, fix a bug in poll-one. * Comment out `test_fd_readwrite_invalid_fd`, which panics now. * Fix a few FIXMEs. * Use `.as_ref().trapping_unwrap()` instead of `TrappingUnwrapRef`. * Use `drop_in_place`. * Remove `State::with_mut`. * Remove the `RefCell` around the `State`. * Update to wit-bindgen 0.12. * Update wasi-http to use resources for poll and I/O. This required making incoming-body and outgoing-body resourrces too, to work with `push_input_stream_child` and `push_output_stream_child`. * Re-enable disabled tests, remove logging from the worlds. * Remove the `poll_list` workarounds that are no longer needed. * Remove logging from the adapter. That said, there is no replacement yet, so add a FIXME comment. * Reenable a test that now passes. * Remove `.descriptors_mut` and use `with_descriptors_mut` instead. Replace `.descriptors()` and `.descriptors_mut()` with functions that take closures, which limits their scope, to prevent them from invalid aliasing. * Implement dynamic borrow checking for descriptors. * Add a cargo-vet audit for wasmtime-wmemcheck. * Update cargo vet for wit-bindgen 0.12. * Cut down on duplicate sync/async resource types (#1) * Allow calling `get-directories` more than once (#2) For now `Clone` the directories into new descriptor slots as needed. * Start to lift restriction of stdio only once (#3) * Start to lift restriction of stdio only once This commit adds new `{Stdin,Stdout}Stream` traits which take over the job of the stdio streams in `WasiCtxBuilder` and `WasiCtx`. These traits bake in the ability to create a stream at any time to satisfy the API of `wasi:cli`. The TTY functionality is folded into them as while I was at it. The implementation for stdin is relatively trivial since the stdin implementation already handles multiple streams reading it. Built-in impls of the `StdinStream` trait are also provided for helper types in `preview2::pipe` which resulted in the implementation of `MemoryInputPipe` being updated to support `Clone` where all clones read the same original data. * Get tests building * Un-ignore now-passing test * Remove unneeded argument from `WasiCtxBuilder::build` * Fix tests * Remove some workarounds Stdio functions can now be called multiple times. * If `poll_oneoff` fails part-way through, clean up properly. Fix the `Drop` implementation for pollables to only drop the pollables that have been successfully added to the list. This fixes the poll_oneoff_files failure and removes a FIXME. --------- Co-authored-by: Alex Crichton --- Cargo.lock | 21 +- Cargo.toml | 2 +- .../src/bin/stream_pollable_lifetimes.rs | 20 +- crates/test-programs/reactor-tests/src/lib.rs | 29 +- crates/test-programs/tests/command.rs | 104 +- crates/test-programs/tests/reactor.rs | 18 +- .../tests/wasi-http-components-sync.rs | 11 +- .../tests/wasi-http-components.rs | 10 +- crates/test-programs/tests/wasi-http-proxy.rs | 10 +- .../tests/wasi-preview1-host-in-preview2.rs | 10 +- .../tests/wasi-preview2-components-sync.rs | 10 +- .../tests/wasi-preview2-components.rs | 10 +- crates/test-programs/tests/wasi-sockets.rs | 4 +- .../wasi-http-proxy-tests/src/lib.rs | 4 +- .../test-programs/wasi-http-tests/src/lib.rs | 35 +- .../wasi-sockets-tests/src/bin/tcp_v4.rs | 13 +- .../wasi-sockets-tests/src/bin/tcp_v6.rs | 13 +- .../wasi-sockets-tests/src/lib.rs | 113 +- .../test-programs/wasi-tests/src/bin/sleep.rs | 14 +- crates/wasi-http/src/lib.rs | 2 +- crates/wasi-http/src/proxy.rs | 2 +- crates/wasi-http/src/types.rs | 49 +- crates/wasi-http/src/types_impl.rs | 75 +- crates/wasi-http/wit/command-extended.wit | 2 +- crates/wasi-http/wit/deps/cli/reactor.wit | 2 +- crates/wasi-http/wit/deps/cli/terminal.wit | 16 +- .../wit/deps/clocks/monotonic-clock.wit | 4 +- crates/wasi-http/wit/deps/clocks/timezone.wit | 19 +- .../wasi-http/wit/deps/clocks/wall-clock.wit | 2 - crates/wasi-http/wit/deps/clocks/world.wit | 7 + .../wasi-http/wit/deps/filesystem/types.wit | 1018 ++++++------ .../wasi-http/wit/deps/filesystem/world.wit | 2 +- crates/wasi-http/wit/deps/http/types.wit | 31 +- crates/wasi-http/wit/deps/io/poll.wit | 34 + crates/wasi-http/wit/deps/io/streams.wit | 465 +++--- crates/wasi-http/wit/deps/io/world.wit | 6 + crates/wasi-http/wit/deps/logging/world.wit | 5 + crates/wasi-http/wit/deps/poll/poll.wit | 39 - crates/wasi-http/wit/deps/random/random.wit | 26 +- crates/wasi-http/wit/deps/random/world.wit | 7 + .../wit/deps/sockets/ip-name-lookup.wit | 58 +- crates/wasi-http/wit/deps/sockets/network.wit | 13 +- crates/wasi-http/wit/deps/sockets/tcp.wit | 477 +++--- crates/wasi-http/wit/deps/sockets/udp.wit | 394 +++-- crates/wasi-http/wit/deps/sockets/world.wit | 11 + crates/wasi-http/wit/main.wit | 2 +- crates/wasi-http/wit/test.wit | 5 +- .../wasi-preview1-component-adapter/build.rs | 49 +- .../src/descriptors.rs | 181 +- .../src/lib.rs | 1452 +++++++++-------- .../src/macros.rs | 3 +- crates/wasi/Cargo.toml | 2 - crates/wasi/src/preview2/command.rs | 8 +- crates/wasi/src/preview2/ctx.rs | 102 +- crates/wasi/src/preview2/filesystem.rs | 51 +- crates/wasi/src/preview2/host/clocks.rs | 15 +- crates/wasi/src/preview2/host/filesystem.rs | 324 ++-- .../wasi/src/preview2/host/filesystem/sync.rs | 228 +-- .../src/preview2/host/instance_network.rs | 7 +- crates/wasi/src/preview2/host/io.rs | 553 ++++--- crates/wasi/src/preview2/host/network.rs | 7 +- crates/wasi/src/preview2/host/tcp.rs | 188 ++- .../src/preview2/host/tcp_create_socket.rs | 7 +- crates/wasi/src/preview2/mod.rs | 98 +- crates/wasi/src/preview2/network.rs | 30 +- crates/wasi/src/preview2/pipe.rs | 28 +- crates/wasi/src/preview2/poll.rs | 109 +- crates/wasi/src/preview2/preview1.rs | 328 ++-- crates/wasi/src/preview2/stdio.rs | 232 ++- .../src/preview2/stdio/worker_thread_stdin.rs | 11 +- crates/wasi/src/preview2/stream.rs | 160 +- crates/wasi/src/preview2/table.rs | 12 +- crates/wasi/src/preview2/tcp.rs | 58 +- crates/wasi/wit/command-extended.wit | 2 +- crates/wasi/wit/deps/cli/reactor.wit | 2 +- crates/wasi/wit/deps/cli/terminal.wit | 16 +- .../wasi/wit/deps/clocks/monotonic-clock.wit | 4 +- crates/wasi/wit/deps/clocks/timezone.wit | 19 +- crates/wasi/wit/deps/clocks/wall-clock.wit | 2 - crates/wasi/wit/deps/clocks/world.wit | 7 + crates/wasi/wit/deps/filesystem/types.wit | 1018 ++++++------ crates/wasi/wit/deps/filesystem/world.wit | 2 +- crates/wasi/wit/deps/http/types.wit | 31 +- crates/wasi/wit/deps/io/poll.wit | 34 + crates/wasi/wit/deps/io/streams.wit | 465 +++--- crates/wasi/wit/deps/io/world.wit | 6 + crates/wasi/wit/deps/logging/world.wit | 5 + crates/wasi/wit/deps/poll/poll.wit | 39 - crates/wasi/wit/deps/random/random.wit | 26 +- crates/wasi/wit/deps/random/world.wit | 7 + .../wasi/wit/deps/sockets/ip-name-lookup.wit | 58 +- crates/wasi/wit/deps/sockets/network.wit | 13 +- crates/wasi/wit/deps/sockets/tcp.wit | 477 +++--- crates/wasi/wit/deps/sockets/udp.wit | 394 +++-- crates/wasi/wit/deps/sockets/world.wit | 11 + crates/wasi/wit/main.wit | 2 +- crates/wasi/wit/test.wit | 5 +- crates/wasmtime/src/component/resources.rs | 13 +- src/commands/run.rs | 7 +- supply-chain/audits.toml | 6 + supply-chain/imports.lock | 35 + tests/all/cli_tests.rs | 4 +- 102 files changed, 5212 insertions(+), 4965 deletions(-) create mode 100644 crates/wasi-http/wit/deps/clocks/world.wit create mode 100644 crates/wasi-http/wit/deps/io/poll.wit create mode 100644 crates/wasi-http/wit/deps/io/world.wit create mode 100644 crates/wasi-http/wit/deps/logging/world.wit delete mode 100644 crates/wasi-http/wit/deps/poll/poll.wit create mode 100644 crates/wasi-http/wit/deps/random/world.wit create mode 100644 crates/wasi-http/wit/deps/sockets/world.wit create mode 100644 crates/wasi/wit/deps/clocks/world.wit create mode 100644 crates/wasi/wit/deps/io/poll.wit create mode 100644 crates/wasi/wit/deps/io/world.wit create mode 100644 crates/wasi/wit/deps/logging/world.wit delete mode 100644 crates/wasi/wit/deps/poll/poll.wit create mode 100644 crates/wasi/wit/deps/random/world.wit create mode 100644 crates/wasi/wit/deps/sockets/world.wit diff --git a/Cargo.lock b/Cargo.lock index 22d858a280b8..69d27ff848a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3750,7 +3750,6 @@ dependencies = [ "futures", "io-extras", "io-lifetimes 2.0.2", - "is-terminal", "libc", "once_cell", "rustix 0.38.8", @@ -4136,9 +4135,9 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" +checksum = "b4f7c5d6f59ae013fc4c013c76eab667844a46e86b51987acb71b1e32953211a" dependencies = [ "bitflags 2.3.3", "wit-bindgen-rust-macro", @@ -4146,9 +4145,9 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" +checksum = "7f0371c47784e7559efb422f74473e395b49f7101725584e2673657e0b4fc104" dependencies = [ "anyhow", "wit-component", @@ -4157,9 +4156,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" +checksum = "eeab5a09a85b1641690922ce05d79d868a2f2e78e9415a5302f58b9846fab8f1" dependencies = [ "anyhow", "heck", @@ -4171,9 +4170,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-lib" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" +checksum = "a13c89c9c1a93e164318745841026f63f889376f38664f86a7f678930280e728" dependencies = [ "heck", "wit-bindgen-core", @@ -4181,9 +4180,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" +checksum = "a70c97e09751a9a95a592bd8ef84e953e5cdce6ebbfdb35ceefa5cc511da3b71" dependencies = [ "anyhow", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index db8b4ff5836c..ddaffc7d5889 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -213,7 +213,7 @@ io-extras = "0.18.0" rustix = "0.38.8" is-terminal = "0.4.0" # wit-bindgen: -wit-bindgen = { version = "0.11.0", default-features = false } +wit-bindgen = { version = "0.12.0", default-features = false } # wasm-tools family: wasmparser = "0.113.2" diff --git a/crates/test-programs/command-tests/src/bin/stream_pollable_lifetimes.rs b/crates/test-programs/command-tests/src/bin/stream_pollable_lifetimes.rs index ebd8071957d1..8693e1f373d1 100644 --- a/crates/test-programs/command-tests/src/bin/stream_pollable_lifetimes.rs +++ b/crates/test-programs/command-tests/src/bin/stream_pollable_lifetimes.rs @@ -1,24 +1,24 @@ use command_tests::wasi::cli::environment; use command_tests::wasi::cli::stdin; +use command_tests::wasi::io::poll; use command_tests::wasi::io::streams; -use command_tests::wasi::poll::poll; fn main() { let args = environment::get_arguments(); if args == &["correct"] { let stdin: streams::InputStream = stdin::get_stdin(); - let stdin_pollable = streams::subscribe_to_input_stream(stdin); - let ready = poll::poll_oneoff(&[stdin_pollable]); - assert_eq!(ready, &[true]); - poll::drop_pollable(stdin_pollable); - streams::drop_input_stream(stdin); + let stdin_pollable = stdin.subscribe(); + let ready = poll::poll_list(&[&stdin_pollable]); + assert_eq!(ready, &[0]); + drop(stdin_pollable); + drop(stdin); } else if args == &["trap"] { let stdin: streams::InputStream = stdin::get_stdin(); - let stdin_pollable = streams::subscribe_to_input_stream(stdin); - let ready = poll::poll_oneoff(&[stdin_pollable]); - assert_eq!(ready, &[true]); - streams::drop_input_stream(stdin); + let stdin_pollable = stdin.subscribe(); + let ready = poll::poll_list(&[&stdin_pollable]); + assert_eq!(ready, &[0]); + drop(stdin); unreachable!( "execution should have trapped in line above when stream dropped before pollable" ); diff --git a/crates/test-programs/reactor-tests/src/lib.rs b/crates/test-programs/reactor-tests/src/lib.rs index c3219d37fc4c..c5514d6a1505 100644 --- a/crates/test-programs/reactor-tests/src/lib.rs +++ b/crates/test-programs/reactor-tests/src/lib.rs @@ -7,21 +7,10 @@ wit_bindgen::generate!({ }); struct T; -use wasi::io::streams; -use wasi::poll::poll; +use wasi::io::poll; static mut STATE: Vec = Vec::new(); -struct DropPollable { - pollable: poll::Pollable, -} - -impl Drop for DropPollable { - fn drop(&mut self) { - poll::drop_pollable(self.pollable); - } -} - impl Guest for T { fn add_strings(ss: Vec) -> u32 { for s in ss { @@ -40,34 +29,32 @@ impl Guest for T { } fn write_strings_to(o: OutputStream) -> Result<(), ()> { - let sub = DropPollable { - pollable: streams::subscribe_to_output_stream(o), - }; + let pollable = o.subscribe(); unsafe { for s in STATE.iter() { let mut out = s.as_bytes(); while !out.is_empty() { - poll::poll_oneoff(&[sub.pollable]); - let n = match streams::check_write(o) { + poll::poll_list(&[&pollable]); + let n = match o.check_write() { Ok(n) => n, Err(_) => return Err(()), }; let len = (n as usize).min(out.len()); - match streams::write(o, &out[..len]) { + match o.write(&out[..len]) { Ok(_) => out = &out[len..], Err(_) => return Err(()), } } } - match streams::flush(o) { + match o.flush() { Ok(_) => {} Err(_) => return Err(()), } - poll::poll_oneoff(&[sub.pollable]); - match streams::check_write(o) { + poll::poll_list(&[&pollable]); + match o.check_write() { Ok(_) => {} Err(_) => return Err(()), } diff --git a/crates/test-programs/tests/command.rs b/crates/test-programs/tests/command.rs index 574380420348..bdba29ff3839 100644 --- a/crates/test-programs/tests/command.rs +++ b/crates/test-programs/tests/command.rs @@ -8,7 +8,7 @@ use wasmtime::{ use wasmtime_wasi::preview2::{ command::{add_to_linker, Command}, pipe::MemoryInputPipe, - DirPerms, FilePerms, HostMonotonicClock, HostWallClock, IsATTY, Table, WasiCtx, WasiCtxBuilder, + DirPerms, FilePerms, HostMonotonicClock, HostWallClock, Table, WasiCtx, WasiCtxBuilder, WasiView, }; @@ -61,10 +61,10 @@ async fn instantiate( #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn hello_stdout() -> Result<()> { - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .args(&["gussie", "sparky", "willa"]) - .build(&mut table)?; + .build(); let (mut store, command) = instantiate(get_component("hello_stdout"), CommandCtx { table, wasi }).await?; command @@ -76,7 +76,7 @@ async fn hello_stdout() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn panic() -> Result<()> { - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .args(&[ "diesel", @@ -88,7 +88,7 @@ async fn panic() -> Result<()> { "good", "yesterday", ]) - .build(&mut table)?; + .build(); let (mut store, command) = instantiate(get_component("panic"), CommandCtx { table, wasi }).await?; let r = command.wasi_cli_run().call_run(&mut store).await; @@ -99,10 +99,10 @@ async fn panic() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn args() -> Result<()> { - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .args(&["hello", "this", "", "is an argument", "with 🚩 emoji"]) - .build(&mut table)?; + .build(); let (mut store, command) = instantiate(get_component("args"), CommandCtx { table, wasi }).await?; command @@ -114,8 +114,8 @@ async fn args() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn random() -> Result<()> { - let mut table = Table::new(); - let wasi = WasiCtxBuilder::new().build(&mut table)?; + let table = Table::new(); + let wasi = WasiCtxBuilder::new().build(); let (mut store, command) = instantiate(get_component("random"), CommandCtx { table, wasi }).await?; @@ -157,11 +157,11 @@ async fn time() -> Result<()> { } } - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .monotonic_clock(FakeMonotonicClock { now: Mutex::new(0) }) .wall_clock(FakeWallClock) - .build(&mut table)?; + .build(); let (mut store, command) = instantiate(get_component("time"), CommandCtx { table, wasi }).await?; @@ -175,13 +175,12 @@ async fn time() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn stdin() -> Result<()> { - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() - .stdin( - MemoryInputPipe::new("So rested he by the Tumtum tree".into()), - IsATTY::No, - ) - .build(&mut table)?; + .stdin(MemoryInputPipe::new( + "So rested he by the Tumtum tree".into(), + )) + .build(); let (mut store, command) = instantiate(get_component("stdin"), CommandCtx { table, wasi }).await?; @@ -195,13 +194,12 @@ async fn stdin() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn poll_stdin() -> Result<()> { - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() - .stdin( - MemoryInputPipe::new("So rested he by the Tumtum tree".into()), - IsATTY::No, - ) - .build(&mut table)?; + .stdin(MemoryInputPipe::new( + "So rested he by the Tumtum tree".into(), + )) + .build(); let (mut store, command) = instantiate(get_component("poll_stdin"), CommandCtx { table, wasi }).await?; @@ -215,11 +213,11 @@ async fn poll_stdin() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn env() -> Result<()> { - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .env("frabjous", "day") .env("callooh", "callay") - .build(&mut table)?; + .build(); let (mut store, command) = instantiate(get_component("env"), CommandCtx { table, wasi }).await?; @@ -239,10 +237,10 @@ async fn file_read() -> Result<()> { let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .preopened_dir(open_dir, DirPerms::all(), FilePerms::all(), "/") - .build(&mut table)?; + .build(); let (mut store, command) = instantiate(get_component("file_read"), CommandCtx { table, wasi }).await?; @@ -263,10 +261,10 @@ async fn file_append() -> Result<()> { let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .preopened_dir(open_dir, DirPerms::all(), FilePerms::all(), "/") - .build(&mut table)?; + .build(); let (mut store, command) = instantiate(get_component("file_append"), CommandCtx { table, wasi }).await?; @@ -296,10 +294,10 @@ async fn file_dir_sync() -> Result<()> { let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .preopened_dir(open_dir, DirPerms::all(), FilePerms::all(), "/") - .build(&mut table)?; + .build(); let (mut store, command) = instantiate(get_component("file_dir_sync"), CommandCtx { table, wasi }).await?; @@ -313,8 +311,8 @@ async fn file_dir_sync() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn exit_success() -> Result<()> { - let mut table = Table::new(); - let wasi = WasiCtxBuilder::new().build(&mut table)?; + let table = Table::new(); + let wasi = WasiCtxBuilder::new().build(); let (mut store, command) = instantiate(get_component("exit_success"), CommandCtx { table, wasi }).await?; @@ -330,8 +328,8 @@ async fn exit_success() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn exit_default() -> Result<()> { - let mut table = Table::new(); - let wasi = WasiCtxBuilder::new().build(&mut table)?; + let table = Table::new(); + let wasi = WasiCtxBuilder::new().build(); let (mut store, command) = instantiate(get_component("exit_default"), CommandCtx { table, wasi }).await?; @@ -343,8 +341,8 @@ async fn exit_default() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn exit_failure() -> Result<()> { - let mut table = Table::new(); - let wasi = WasiCtxBuilder::new().build(&mut table)?; + let table = Table::new(); + let wasi = WasiCtxBuilder::new().build(); let (mut store, command) = instantiate(get_component("exit_failure"), CommandCtx { table, wasi }).await?; @@ -360,8 +358,8 @@ async fn exit_failure() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn exit_panic() -> Result<()> { - let mut table = Table::new(); - let wasi = WasiCtxBuilder::new().build(&mut table)?; + let table = Table::new(); + let wasi = WasiCtxBuilder::new().build(); let (mut store, command) = instantiate(get_component("exit_panic"), CommandCtx { table, wasi }).await?; @@ -388,12 +386,12 @@ async fn directory_list() -> Result<()> { let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .inherit_stdout() .inherit_stderr() .preopened_dir(open_dir, DirPerms::all(), FilePerms::all(), "/") - .build(&mut table)?; + .build(); let (mut store, command) = instantiate(get_component("directory_list"), CommandCtx { table, wasi }).await?; @@ -407,8 +405,8 @@ async fn directory_list() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn default_clocks() -> Result<()> { - let mut table = Table::new(); - let wasi = WasiCtxBuilder::new().build(&mut table)?; + let table = Table::new(); + let wasi = WasiCtxBuilder::new().build(); let (mut store, command) = instantiate(get_component("default_clocks"), CommandCtx { table, wasi }).await?; @@ -422,8 +420,8 @@ async fn default_clocks() -> Result<()> { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn export_cabi_realloc() -> Result<()> { - let mut table = Table::new(); - let wasi = WasiCtxBuilder::new().build(&mut table)?; + let table = Table::new(); + let wasi = WasiCtxBuilder::new().build(); let (mut store, command) = instantiate( get_component("export_cabi_realloc"), CommandCtx { table, wasi }, @@ -444,11 +442,11 @@ async fn read_only() -> Result<()> { std::fs::File::create(dir.path().join("bar.txt"))?.write_all(b"And stood awhile in thought")?; std::fs::create_dir(dir.path().join("sub"))?; - let mut table = Table::new(); + let table = Table::new(); let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; let wasi = WasiCtxBuilder::new() .preopened_dir(open_dir, DirPerms::READ, FilePerms::READ, "/") - .build(&mut table)?; + .build(); let (mut store, command) = instantiate(get_component("read_only"), CommandCtx { table, wasi }).await?; @@ -465,11 +463,11 @@ async fn stream_pollable_lifetimes() -> Result<()> { // Test program has two modes, dispatching based on argument. { // Correct execution: should succeed - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .args(&["correct"]) - .stdin(MemoryInputPipe::new(" ".into()), IsATTY::No) - .build(&mut table)?; + .stdin(MemoryInputPipe::new(" ".into())) + .build(); let (mut store, command) = instantiate( get_component("stream_pollable_lifetimes"), @@ -485,11 +483,11 @@ async fn stream_pollable_lifetimes() -> Result<()> { } { // Incorrect execution: should trap with a TableError::HasChildren - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .args(&["trap"]) - .stdin(MemoryInputPipe::new(" ".into()), IsATTY::No) - .build(&mut table)?; + .stdin(MemoryInputPipe::new(" ".into())) + .build(); let (mut store, command) = instantiate( get_component("stream_pollable_lifetimes"), diff --git a/crates/test-programs/tests/reactor.rs b/crates/test-programs/tests/reactor.rs index 555c756d086d..a5cfed2beedd 100644 --- a/crates/test-programs/tests/reactor.rs +++ b/crates/test-programs/tests/reactor.rs @@ -81,10 +81,8 @@ async fn instantiate( #[test_log::test(tokio::test)] async fn reactor_tests() -> Result<()> { - let mut table = Table::new(); - let wasi = WasiCtxBuilder::new() - .env("GOOD_DOG", "gussie") - .build(&mut table)?; + let table = Table::new(); + let wasi = WasiCtxBuilder::new().env("GOOD_DOG", "gussie").build(); let (mut store, reactor) = instantiate(get_component("reactor_tests"), ReactorCtx { table, wasi }).await?; @@ -116,20 +114,20 @@ async fn reactor_tests() -> Result<()> { // Show that the `with` invocation in the macro means we get to re-use the // type definitions from inside the `host` crate for these structures: let ds = filesystem::DescriptorStat { - data_access_timestamp: wall_clock::Datetime { + data_access_timestamp: Some(wall_clock::Datetime { nanoseconds: 123, seconds: 45, - }, - data_modification_timestamp: wall_clock::Datetime { + }), + data_modification_timestamp: Some(wall_clock::Datetime { nanoseconds: 789, seconds: 10, - }, + }), link_count: 0, size: 0, - status_change_timestamp: wall_clock::Datetime { + status_change_timestamp: Some(wall_clock::Datetime { nanoseconds: 0, seconds: 1, - }, + }), type_: filesystem::DescriptorType::Unknown, }; let expected = format!("{ds:?}"); diff --git a/crates/test-programs/tests/wasi-http-components-sync.rs b/crates/test-programs/tests/wasi-http-components-sync.rs index 222fc800edce..b27d1c2eec6d 100644 --- a/crates/test-programs/tests/wasi-http-components-sync.rs +++ b/crates/test-programs/tests/wasi-http-components-sync.rs @@ -4,8 +4,7 @@ use wasmtime::{ Config, Engine, Store, }; use wasmtime_wasi::preview2::{ - command::sync::Command, pipe::MemoryOutputPipe, IsATTY, Table, WasiCtx, WasiCtxBuilder, - WasiView, + command::sync::Command, pipe::MemoryOutputPipe, Table, WasiCtx, WasiCtxBuilder, WasiView, }; use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; @@ -71,18 +70,18 @@ fn run(name: &str) -> anyhow::Result<()> { let stdout = MemoryOutputPipe::new(4096); let stderr = MemoryOutputPipe::new(4096); let r = { - let mut table = Table::new(); + let table = Table::new(); let component = get_component(name); // Create our wasi context. let mut builder = WasiCtxBuilder::new(); - builder.stdout(stdout.clone(), IsATTY::No); - builder.stderr(stderr.clone(), IsATTY::No); + builder.stdout(stdout.clone()); + builder.stderr(stderr.clone()); builder.arg(name); for (var, val) in test_programs::wasi_tests_environment() { builder.env(var, val); } - let wasi = builder.build(&mut table)?; + let wasi = builder.build(); let http = WasiHttpCtx {}; let (mut store, command) = instantiate_component(component, Ctx { table, wasi, http })?; diff --git a/crates/test-programs/tests/wasi-http-components.rs b/crates/test-programs/tests/wasi-http-components.rs index 11fb6bd498ca..51f194800854 100644 --- a/crates/test-programs/tests/wasi-http-components.rs +++ b/crates/test-programs/tests/wasi-http-components.rs @@ -4,7 +4,7 @@ use wasmtime::{ Config, Engine, Store, }; use wasmtime_wasi::preview2::{ - command::Command, pipe::MemoryOutputPipe, IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView, + command::Command, pipe::MemoryOutputPipe, Table, WasiCtx, WasiCtxBuilder, WasiView, }; use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; @@ -70,18 +70,18 @@ async fn run(name: &str) -> anyhow::Result<()> { let stdout = MemoryOutputPipe::new(4096); let stderr = MemoryOutputPipe::new(4096); let r = { - let mut table = Table::new(); + let table = Table::new(); let component = get_component(name); // Create our wasi context. let mut builder = WasiCtxBuilder::new(); - builder.stdout(stdout.clone(), IsATTY::No); - builder.stderr(stderr.clone(), IsATTY::No); + builder.stdout(stdout.clone()); + builder.stderr(stderr.clone()); builder.arg(name); for (var, val) in test_programs::wasi_tests_environment() { builder.env(var, val); } - let wasi = builder.build(&mut table)?; + let wasi = builder.build(); let http = WasiHttpCtx; let (mut store, command) = diff --git a/crates/test-programs/tests/wasi-http-proxy.rs b/crates/test-programs/tests/wasi-http-proxy.rs index f344dc15071f..65c07872de52 100644 --- a/crates/test-programs/tests/wasi-http-proxy.rs +++ b/crates/test-programs/tests/wasi-http-proxy.rs @@ -5,7 +5,7 @@ use wasmtime::{ Config, Engine, Store, }; use wasmtime_wasi::preview2::{ - self, pipe::MemoryOutputPipe, IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView, + self, pipe::MemoryOutputPipe, Table, WasiCtx, WasiCtxBuilder, WasiView, }; use wasmtime_wasi_http::{proxy::Proxy, WasiHttpCtx, WasiHttpView}; @@ -70,17 +70,17 @@ async fn wasi_http_proxy_tests() -> anyhow::Result<()> { let stdout = MemoryOutputPipe::new(4096); let stderr = MemoryOutputPipe::new(4096); - let mut table = Table::new(); + let table = Table::new(); let component = get_component("wasi_http_proxy_tests"); // Create our wasi context. let mut builder = WasiCtxBuilder::new(); - builder.stdout(stdout.clone(), IsATTY::No); - builder.stderr(stderr.clone(), IsATTY::No); + builder.stdout(stdout.clone()); + builder.stderr(stderr.clone()); for (var, val) in test_programs::wasi_tests_environment() { builder.env(var, val); } - let wasi = builder.build(&mut table)?; + let wasi = builder.build(); let http = WasiHttpCtx; let ctx = Ctx { table, wasi, http }; diff --git a/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs b/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs index 836d473f75e6..79f619a8a3eb 100644 --- a/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs +++ b/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs @@ -5,7 +5,7 @@ use wasmtime::{Config, Engine, Linker, Store}; use wasmtime_wasi::preview2::{ pipe::MemoryOutputPipe, preview1::{add_to_linker_async, WasiPreview1Adapter, WasiPreview1View}, - DirPerms, FilePerms, IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView, + DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView, }; lazy_static::lazy_static! { @@ -43,9 +43,7 @@ async fn run(name: &str, inherit_stdio: bool) -> Result<()> { if inherit_stdio { builder.inherit_stdio(); } else { - builder - .stdout(stdout.clone(), IsATTY::No) - .stderr(stderr.clone(), IsATTY::No); + builder.stdout(stdout.clone()).stderr(stderr.clone()); } builder.args(&[name, "."]); println!("preopen: {:?}", workspace); @@ -56,8 +54,8 @@ async fn run(name: &str, inherit_stdio: bool) -> Result<()> { builder.env(var, val); } - let mut table = Table::new(); - let wasi = builder.build(&mut table)?; + let table = Table::new(); + let wasi = builder.build(); struct Ctx { wasi: WasiCtx, table: Table, diff --git a/crates/test-programs/tests/wasi-preview2-components-sync.rs b/crates/test-programs/tests/wasi-preview2-components-sync.rs index 5e095923e163..9772f9c8473f 100644 --- a/crates/test-programs/tests/wasi-preview2-components-sync.rs +++ b/crates/test-programs/tests/wasi-preview2-components-sync.rs @@ -5,7 +5,7 @@ use wasmtime::{component::Linker, Config, Engine, Store}; use wasmtime_wasi::preview2::{ command::sync::{add_to_linker, Command}, pipe::MemoryOutputPipe, - DirPerms, FilePerms, IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView, + DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView, }; lazy_static::lazy_static! { @@ -43,9 +43,7 @@ fn run(name: &str, inherit_stdio: bool) -> Result<()> { if inherit_stdio { builder.inherit_stdio(); } else { - builder - .stdout(stdout.clone(), IsATTY::No) - .stderr(stderr.clone(), IsATTY::No); + builder.stdout(stdout.clone()).stderr(stderr.clone()); } builder.args(&[name, "."]); println!("preopen: {:?}", workspace); @@ -56,8 +54,8 @@ fn run(name: &str, inherit_stdio: bool) -> Result<()> { builder.env(var, val); } - let mut table = Table::new(); - let wasi = builder.build(&mut table)?; + let table = Table::new(); + let wasi = builder.build(); struct Ctx { wasi: WasiCtx, table: Table, diff --git a/crates/test-programs/tests/wasi-preview2-components.rs b/crates/test-programs/tests/wasi-preview2-components.rs index 7834562b687b..a2b30ed0d346 100644 --- a/crates/test-programs/tests/wasi-preview2-components.rs +++ b/crates/test-programs/tests/wasi-preview2-components.rs @@ -5,7 +5,7 @@ use wasmtime::{component::Linker, Config, Engine, Store}; use wasmtime_wasi::preview2::{ command::{add_to_linker, Command}, pipe::MemoryOutputPipe, - DirPerms, FilePerms, IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView, + DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView, }; lazy_static::lazy_static! { @@ -43,9 +43,7 @@ async fn run(name: &str, inherit_stdio: bool) -> Result<()> { if inherit_stdio { builder.inherit_stdio(); } else { - builder - .stdout(stdout.clone(), IsATTY::No) - .stderr(stderr.clone(), IsATTY::No); + builder.stdout(stdout.clone()).stderr(stderr.clone()); } builder.args(&[name, "."]); println!("preopen: {:?}", workspace); @@ -56,8 +54,8 @@ async fn run(name: &str, inherit_stdio: bool) -> Result<()> { builder.env(var, val); } - let mut table = Table::new(); - let wasi = builder.build(&mut table)?; + let table = Table::new(); + let wasi = builder.build(); struct Ctx { wasi: WasiCtx, table: Table, diff --git a/crates/test-programs/tests/wasi-sockets.rs b/crates/test-programs/tests/wasi-sockets.rs index c16e9d4fadf7..71669817bce6 100644 --- a/crates/test-programs/tests/wasi-sockets.rs +++ b/crates/test-programs/tests/wasi-sockets.rs @@ -48,12 +48,12 @@ async fn run(name: &str) -> anyhow::Result<()> { preview2::command::add_to_linker(&mut linker)?; // Create our wasi context. - let mut table = Table::new(); + let table = Table::new(); let wasi = WasiCtxBuilder::new() .inherit_stdio() .inherit_network(ambient_authority()) .arg(name) - .build(&mut table)?; + .build(); let mut store = Store::new(&ENGINE, SocketsCtx { table, wasi }); diff --git a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs b/crates/test-programs/wasi-http-proxy-tests/src/lib.rs index 239f1754327d..c51be36d7f38 100644 --- a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-proxy-tests/src/lib.rs @@ -24,10 +24,10 @@ impl bindings::exports::wasi::http::incoming_handler::Guest for T { bindings::wasi::http::types::set_response_outparam(outparam, Ok(resp)); let out = bindings::wasi::http::types::outgoing_body_write(body).expect("outgoing stream"); - bindings::wasi::io::streams::blocking_write_and_flush(out, b"hello, world!") + out.blocking_write_and_flush(b"hello, world!") .expect("writing response"); - bindings::wasi::io::streams::drop_output_stream(out); + drop(out); bindings::wasi::http::types::outgoing_body_finish(body, None); } } diff --git a/crates/test-programs/wasi-http-tests/src/lib.rs b/crates/test-programs/wasi-http-tests/src/lib.rs index e2ae84de6998..5aabc976d6b3 100644 --- a/crates/test-programs/wasi-http-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-tests/src/lib.rs @@ -12,8 +12,8 @@ use std::fmt; use std::sync::OnceLock; use bindings::wasi::http::{outgoing_handler, types as http_types}; +use bindings::wasi::io::poll; use bindings::wasi::io::streams; -use bindings::wasi::poll::poll; pub struct Response { pub status: http_types::StatusCode, @@ -79,11 +79,11 @@ pub async fn request( let request_body = http_types::outgoing_body_write(outgoing_body) .map_err(|_| anyhow!("outgoing request write failed"))?; - let pollable = streams::subscribe_to_output_stream(request_body); + let pollable = request_body.subscribe(); while !buf.is_empty() { - poll::poll_oneoff(&[pollable]); + poll::poll_list(&[&pollable]); - let permit = match streams::check_write(request_body) { + let permit = match request_body.check_write() { Ok(n) => n, Err(_) => anyhow::bail!("output stream error"), }; @@ -92,26 +92,23 @@ pub async fn request( let (chunk, rest) = buf.split_at(len); buf = rest; - match streams::write(request_body, chunk) { + match request_body.write(chunk) { Err(_) => anyhow::bail!("output stream error"), _ => {} } } - match streams::flush(request_body) { + match request_body.flush() { Err(_) => anyhow::bail!("output stream error"), _ => {} } - poll::poll_oneoff(&[pollable]); - poll::drop_pollable(pollable); + poll::poll_list(&[&pollable]); - match streams::check_write(request_body) { + match request_body.check_write() { Ok(_) => {} Err(_) => anyhow::bail!("output stream error"), }; - - streams::drop_output_stream(request_body); } let future_response = outgoing_handler::handle(request, None)?; @@ -125,8 +122,7 @@ pub async fn request( Some(result) => result.map_err(|_| anyhow!("incoming response errored"))?, None => { let pollable = http_types::listen_to_future_incoming_response(future_response); - let _ = poll::poll_oneoff(&[pollable]); - poll::drop_pollable(pollable); + let _ = poll::poll_list(&[&pollable]); http_types::future_incoming_response_get(future_response) .expect("incoming response available") .map_err(|_| anyhow!("incoming response errored"))? @@ -149,15 +145,16 @@ pub async fn request( http_types::drop_incoming_response(incoming_response); - let input_stream = http_types::incoming_body_stream(incoming_body).unwrap(); - let input_stream_pollable = streams::subscribe_to_input_stream(input_stream); + let input_stream = incoming_body.stream().unwrap(); + let input_stream_pollable = input_stream.subscribe(); let mut body = Vec::new(); let mut eof = streams::StreamStatus::Open; while eof != streams::StreamStatus::Ended { - poll::poll_oneoff(&[input_stream_pollable]); + poll::poll_list(&[&input_stream_pollable]); - let (mut body_chunk, stream_status) = streams::read(input_stream, 1024 * 1024) + let (mut body_chunk, stream_status) = input_stream + .read(1024 * 1024) .map_err(|_| anyhow!("input_stream read failed"))?; eof = stream_status; @@ -167,10 +164,6 @@ pub async fn request( } } - poll::drop_pollable(input_stream_pollable); - streams::drop_input_stream(input_stream); - http_types::drop_incoming_body(incoming_body); - Ok(Response { status, headers, diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v4.rs b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v4.rs index 2388cb074afe..cf02fdd79663 100644 --- a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v4.rs +++ b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v4.rs @@ -1,7 +1,8 @@ //! A simple TCP testcase, using IPv4. +use wasi::io::poll; use wasi::sockets::network::{IpAddressFamily, IpSocketAddress, Ipv4SocketAddress}; -use wasi::sockets::{instance_network, tcp, tcp_create_socket}; +use wasi::sockets::{instance_network, tcp_create_socket}; use wasi_sockets_tests::*; fn main() { @@ -14,14 +15,14 @@ fn main() { address: (127, 0, 0, 1), // localhost }); - let sub = tcp::subscribe(sock); + let sub = sock.subscribe(); - tcp::start_bind(sock, net, addr).unwrap(); + sock.start_bind(&net, addr).unwrap(); - wait(sub); - wasi::poll::poll::drop_pollable(sub); + poll::poll_one(&sub); + drop(sub); - tcp::finish_bind(sock).unwrap(); + sock.finish_bind().unwrap(); example_body(net, sock, IpAddressFamily::Ipv4) } diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v6.rs b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v6.rs index b5ff8358cc09..807db9825f1e 100644 --- a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v6.rs +++ b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v6.rs @@ -1,7 +1,8 @@ //! Like v4.rs, but with IPv6. +use wasi::io::poll; use wasi::sockets::network::{IpAddressFamily, IpSocketAddress, Ipv6SocketAddress}; -use wasi::sockets::{instance_network, tcp, tcp_create_socket}; +use wasi::sockets::{instance_network, tcp_create_socket}; use wasi_sockets_tests::*; fn main() { @@ -16,14 +17,14 @@ fn main() { scope_id: 0, }); - let sub = tcp::subscribe(sock); + let sub = sock.subscribe(); - tcp::start_bind(sock, net, addr).unwrap(); + sock.start_bind(&net, addr).unwrap(); - wait(sub); - wasi::poll::poll::drop_pollable(sub); + poll::poll_one(&sub); + drop(sub); - tcp::finish_bind(sock).unwrap(); + sock.finish_bind().unwrap(); example_body(net, sock, IpAddressFamily::Ipv6) } diff --git a/crates/test-programs/wasi-sockets-tests/src/lib.rs b/crates/test-programs/wasi-sockets-tests/src/lib.rs index fb06654c313a..56c32d3e87b7 100644 --- a/crates/test-programs/wasi-sockets-tests/src/lib.rs +++ b/crates/test-programs/wasi-sockets-tests/src/lib.rs @@ -1,40 +1,19 @@ wit_bindgen::generate!("test-command-with-sockets" in "../../wasi/wit"); +use wasi::io::poll; use wasi::io::streams; -use wasi::poll::poll; use wasi::sockets::{network, tcp, tcp_create_socket}; -pub fn wait(sub: poll::Pollable) { - loop { - let wait = poll::poll_oneoff(&[sub]); - if wait[0] { - break; - } - } -} - -pub struct DropPollable { - pub pollable: poll::Pollable, -} - -impl Drop for DropPollable { - fn drop(&mut self) { - poll::drop_pollable(self.pollable); - } -} - -pub fn write(output: streams::OutputStream, mut bytes: &[u8]) -> (usize, streams::StreamStatus) { +pub fn write(output: &streams::OutputStream, mut bytes: &[u8]) -> (usize, streams::StreamStatus) { let total = bytes.len(); let mut written = 0; - let s = DropPollable { - pollable: streams::subscribe_to_output_stream(output), - }; + let pollable = output.subscribe(); while !bytes.is_empty() { - poll::poll_oneoff(&[s.pollable]); + poll::poll_list(&[&pollable]); - let permit = match streams::check_write(output) { + let permit = match output.check_write() { Ok(n) => n, Err(_) => return (written, streams::StreamStatus::Ended), }; @@ -42,12 +21,12 @@ pub fn write(output: streams::OutputStream, mut bytes: &[u8]) -> (usize, streams let len = bytes.len().min(permit as usize); let (chunk, rest) = bytes.split_at(len); - match streams::write(output, chunk) { + match output.write(chunk) { Ok(()) => {} Err(_) => return (written, streams::StreamStatus::Ended), } - match streams::blocking_flush(output) { + match output.blocking_flush() { Ok(()) => {} Err(_) => return (written, streams::StreamStatus::Ended), } @@ -63,81 +42,77 @@ pub fn example_body(net: tcp::Network, sock: tcp::TcpSocket, family: network::Ip let first_message = b"Hello, world!"; let second_message = b"Greetings, planet!"; - let sub = tcp::subscribe(sock); + let sub = sock.subscribe(); - tcp::start_listen(sock).unwrap(); - wait(sub); - tcp::finish_listen(sock).unwrap(); + sock.start_listen().unwrap(); + poll::poll_one(&sub); + sock.finish_listen().unwrap(); - let addr = tcp::local_address(sock).unwrap(); + let addr = sock.local_address().unwrap(); let client = tcp_create_socket::create_tcp_socket(family).unwrap(); - let client_sub = tcp::subscribe(client); + let client_sub = client.subscribe(); - tcp::start_connect(client, net, addr).unwrap(); - wait(client_sub); - let (client_input, client_output) = tcp::finish_connect(client).unwrap(); + client.start_connect(&net, addr).unwrap(); + poll::poll_one(&client_sub); + let (client_input, client_output) = client.finish_connect().unwrap(); - let (n, status) = write(client_output, &[]); + let (n, status) = write(&client_output, &[]); assert_eq!(n, 0); assert_eq!(status, streams::StreamStatus::Open); - let (n, status) = write(client_output, first_message); + let (n, status) = write(&client_output, first_message); assert_eq!(n, first_message.len()); assert_eq!(status, streams::StreamStatus::Open); - streams::drop_input_stream(client_input); - streams::drop_output_stream(client_output); - poll::drop_pollable(client_sub); - tcp::drop_tcp_socket(client); + drop(client_input); + drop(client_output); + drop(client_sub); + drop(client); - wait(sub); - let (accepted, input, output) = tcp::accept(sock).unwrap(); + poll::poll_one(&sub); + let (accepted, input, output) = sock.accept().unwrap(); - let (empty_data, status) = streams::read(input, 0).unwrap(); + let (empty_data, status) = input.read(0).unwrap(); assert!(empty_data.is_empty()); assert_eq!(status, streams::StreamStatus::Open); - let (data, status) = streams::blocking_read(input, first_message.len() as u64).unwrap(); + let (data, status) = input.blocking_read(first_message.len() as u64).unwrap(); assert_eq!(status, streams::StreamStatus::Open); - streams::drop_input_stream(input); - streams::drop_output_stream(output); - tcp::drop_tcp_socket(accepted); + drop(input); + drop(output); + drop(accepted); // Check that we sent and recieved our message! assert_eq!(data, first_message); // Not guaranteed to work but should work in practice. // Another client let client = tcp_create_socket::create_tcp_socket(family).unwrap(); - let client_sub = tcp::subscribe(client); + let client_sub = client.subscribe(); - tcp::start_connect(client, net, addr).unwrap(); - wait(client_sub); - let (client_input, client_output) = tcp::finish_connect(client).unwrap(); + client.start_connect(&net, addr).unwrap(); + poll::poll_one(&client_sub); + let (client_input, client_output) = client.finish_connect().unwrap(); - let (n, status) = write(client_output, second_message); + let (n, status) = write(&client_output, second_message); assert_eq!(n, second_message.len()); assert_eq!(status, streams::StreamStatus::Open); - streams::drop_input_stream(client_input); - streams::drop_output_stream(client_output); - poll::drop_pollable(client_sub); - tcp::drop_tcp_socket(client); + drop(client_input); + drop(client_output); + drop(client_sub); + drop(client); - wait(sub); - let (accepted, input, output) = tcp::accept(sock).unwrap(); - let (data, status) = streams::blocking_read(input, second_message.len() as u64).unwrap(); + poll::poll_one(&sub); + let (accepted, input, output) = sock.accept().unwrap(); + let (data, status) = input.blocking_read(second_message.len() as u64).unwrap(); assert_eq!(status, streams::StreamStatus::Open); - streams::drop_input_stream(input); - streams::drop_output_stream(output); - tcp::drop_tcp_socket(accepted); + drop(input); + drop(output); + drop(accepted); // Check that we sent and recieved our message! assert_eq!(data, second_message); // Not guaranteed to work but should work in practice. - - poll::drop_pollable(sub); - tcp::drop_tcp_socket(sock); - network::drop_network(net); } diff --git a/crates/test-programs/wasi-tests/src/bin/sleep.rs b/crates/test-programs/wasi-tests/src/bin/sleep.rs index a80f11bbff6a..d382a32e1559 100644 --- a/crates/test-programs/wasi-tests/src/bin/sleep.rs +++ b/crates/test-programs/wasi-tests/src/bin/sleep.rs @@ -1,4 +1,4 @@ -use crate::wasi::{clocks::monotonic_clock, poll::poll}; +use crate::wasi::{clocks::monotonic_clock, io::poll}; wit_bindgen::generate!({ path: "../../wasi/wit", @@ -9,8 +9,12 @@ fn main() { // Sleep ten milliseconds. Note that we call the relevant host functions directly rather than go through // libstd, since we want to ensure we're calling `monotonic_clock::subscribe` with an `absolute` parameter of // `true`, which libstd won't necessarily do (but which e.g. CPython _will_ do). - poll::poll_oneoff(&[monotonic_clock::subscribe( - monotonic_clock::now() + 10_000_000, - true, - )]); + eprintln!("calling subscribe"); + let p = monotonic_clock::subscribe(monotonic_clock::now() + 10_000_000, true); + dbg!(&p as *const _); + let list = &[&p]; + dbg!(list.as_ptr()); + eprintln!("calling poll"); + poll::poll_list(list); + eprintln!("done"); } diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs index 8501a0cabb81..1fc7bc3736d7 100644 --- a/crates/wasi-http/src/lib.rs +++ b/crates/wasi-http/src/lib.rs @@ -18,7 +18,7 @@ pub mod bindings { async: false, with: { "wasi:io/streams": wasmtime_wasi::preview2::bindings::io::streams, - "wasi:poll/poll": wasmtime_wasi::preview2::bindings::poll::poll, + "wasi:io/poll": wasmtime_wasi::preview2::bindings::io::poll, } }); diff --git a/crates/wasi-http/src/proxy.rs b/crates/wasi-http/src/proxy.rs index b276a6545265..1a7bca511c29 100644 --- a/crates/wasi-http/src/proxy.rs +++ b/crates/wasi-http/src/proxy.rs @@ -16,7 +16,7 @@ wasmtime::component::bindgen!({ "wasi:http/outgoing-handler": bindings::http::outgoing_handler, "wasi:http/types": bindings::http::types, "wasi:io/streams": preview2::bindings::io::streams, - "wasi:poll/poll": preview2::bindings::poll::poll, + "wasi:io/poll": preview2::bindings::io::poll, "wasi:random/random": preview2::bindings::random::random, }, }); diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs index 49baba25aac1..391c5d91851e 100644 --- a/crates/wasi-http/src/types.rs +++ b/crates/wasi-http/src/types.rs @@ -14,6 +14,7 @@ use crate::{ use std::any::Any; use std::pin::Pin; use std::task; +use wasmtime::component::Resource; use wasmtime_wasi::preview2::{AbortOnDropJoinHandle, Table, TableError}; /// Capture the state necessary for use in the wasi-http API implementation. @@ -262,13 +263,22 @@ pub trait TableHttpExt { id: u32, ) -> Result; - fn push_incoming_body(&mut self, body: HostIncomingBody) -> Result; - fn get_incoming_body(&mut self, id: IncomingBody) -> Result<&mut HostIncomingBody, TableError>; - fn delete_incoming_body(&mut self, id: IncomingBody) -> Result; + fn push_incoming_body( + &mut self, + body: HostIncomingBody, + ) -> Result, TableError>; + fn get_incoming_body( + &mut self, + id: &Resource, + ) -> Result<&mut HostIncomingBody, TableError>; + fn delete_incoming_body( + &mut self, + id: Resource, + ) -> Result; - fn push_outgoing_body(&mut self, body: HostOutgoingBody) -> Result; - fn get_outgoing_body(&mut self, id: OutgoingBody) -> Result<&mut HostOutgoingBody, TableError>; - fn delete_outgoing_body(&mut self, id: OutgoingBody) -> Result; + fn push_outgoing_body(&mut self, body: HostOutgoingBody) -> Result; + fn get_outgoing_body(&mut self, id: u32) -> Result<&mut HostOutgoingBody, TableError>; + fn delete_outgoing_body(&mut self, id: u32) -> Result; fn push_future_trailers( &mut self, @@ -377,27 +387,36 @@ impl TableHttpExt for Table { self.delete(id) } - fn push_incoming_body(&mut self, body: HostIncomingBody) -> Result { - self.push(Box::new(body)) + fn push_incoming_body( + &mut self, + body: HostIncomingBody, + ) -> Result, TableError> { + Ok(Resource::new_own(self.push(Box::new(body))?)) } - fn get_incoming_body(&mut self, id: IncomingBody) -> Result<&mut HostIncomingBody, TableError> { - self.get_mut(id) + fn get_incoming_body( + &mut self, + id: &Resource, + ) -> Result<&mut HostIncomingBody, TableError> { + self.get_mut(id.rep()) } - fn delete_incoming_body(&mut self, id: IncomingBody) -> Result { - self.delete(id) + fn delete_incoming_body( + &mut self, + id: Resource, + ) -> Result { + self.delete(id.rep()) } fn push_outgoing_body(&mut self, body: HostOutgoingBody) -> Result { - self.push(Box::new(body)) + Ok(self.push(Box::new(body))?) } - fn get_outgoing_body(&mut self, id: OutgoingBody) -> Result<&mut HostOutgoingBody, TableError> { + fn get_outgoing_body(&mut self, id: u32) -> Result<&mut HostOutgoingBody, TableError> { self.get_mut(id) } - fn delete_outgoing_body(&mut self, id: OutgoingBody) -> Result { + fn delete_outgoing_body(&mut self, id: u32) -> Result { self.delete(id) } diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index 4baea8f364bf..a2abad080ebd 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -15,9 +15,10 @@ use crate::{ }; use anyhow::Context; use std::any::Any; +use wasmtime::component::Resource; use wasmtime_wasi::preview2::{ + bindings::io::poll::Pollable, bindings::io::streams::{InputStream, OutputStream}, - bindings::poll::poll::Pollable, HostPollable, PollableFuture, TablePollableExt, TableStreamExt, }; @@ -207,7 +208,7 @@ impl crate::bindings::http::types::Host for T { fn incoming_request_consume( &mut self, id: IncomingRequest, - ) -> wasmtime::Result> { + ) -> wasmtime::Result, ()>> { let req = types::IncomingRequestLens::from(id).get_mut(self.table())?; match req.body.take() { Some(builder) => { @@ -326,7 +327,7 @@ impl crate::bindings::http::types::Host for T { fn incoming_response_consume( &mut self, response: IncomingResponse, - ) -> wasmtime::Result> { + ) -> wasmtime::Result, ()>> { let table = self.table(); let r = table .get_incoming_response_mut(response) @@ -348,7 +349,10 @@ impl crate::bindings::http::types::Host for T { Ok(()) } - fn future_trailers_subscribe(&mut self, index: FutureTrailers) -> wasmtime::Result { + fn future_trailers_subscribe( + &mut self, + index: FutureTrailers, + ) -> wasmtime::Result> { // Eagerly force errors about the validity of the index. let _ = self.table().get_future_trailers(index)?; @@ -475,7 +479,7 @@ impl crate::bindings::http::types::Host for T { fn listen_to_future_incoming_response( &mut self, id: FutureIncomingResponse, - ) -> wasmtime::Result { + ) -> wasmtime::Result> { let _ = self.table().get_future_incoming_response(id)?; fn make_future<'a>(elem: &'a mut dyn Any) -> PollableFuture<'a> { @@ -493,40 +497,14 @@ impl crate::bindings::http::types::Host for T { Ok(pollable) } - fn incoming_body_stream( - &mut self, - id: IncomingBody, - ) -> wasmtime::Result> { - let body = self.table().get_incoming_body(id)?; - - if let Some(stream) = body.stream.take() { - let stream = self.table().push_input_stream_child(Box::new(stream), id)?; - return Ok(Ok(stream)); - } - - Ok(Err(())) - } - - fn incoming_body_finish(&mut self, id: IncomingBody) -> wasmtime::Result { - let body = self.table().delete_incoming_body(id)?; - let trailers = self - .table() - .push_future_trailers(body.into_future_trailers())?; - Ok(trailers) - } - - fn drop_incoming_body(&mut self, id: IncomingBody) -> wasmtime::Result<()> { - let _ = self.table().delete_incoming_body(id)?; - Ok(()) - } - fn outgoing_body_write( &mut self, id: OutgoingBody, - ) -> wasmtime::Result> { + ) -> wasmtime::Result, ()>> { let body = self.table().get_outgoing_body(id)?; if let Some(stream) = body.body_output_stream.take() { - let id = self.table().push_output_stream_child(stream, id)?; + let dummy = Resource::::new_own(id); + let id = self.table().push_output_stream_child(stream, dummy)?; Ok(Ok(id)) } else { Ok(Err(())) @@ -571,3 +549,32 @@ impl crate::bindings::http::types::Host for T { Ok(()) } } + +impl crate::bindings::http::types::HostIncomingBody for T { + fn stream( + &mut self, + id: Resource, + ) -> wasmtime::Result, ()>> { + let body = self.table().get_incoming_body(&id)?; + + if let Some(stream) = body.stream.take() { + let stream = self.table().push_input_stream_child(Box::new(stream), id)?; + return Ok(Ok(stream)); + } + + Ok(Err(())) + } + + fn finish(&mut self, id: Resource) -> wasmtime::Result { + let body = self.table().delete_incoming_body(id)?; + let trailers = self + .table() + .push_future_trailers(body.into_future_trailers())?; + Ok(trailers) + } + + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_incoming_body(id)?; + Ok(()) + } +} diff --git a/crates/wasi-http/wit/command-extended.wit b/crates/wasi-http/wit/command-extended.wit index 314e26dfce1b..3c56808e4abe 100644 --- a/crates/wasi-http/wit/command-extended.wit +++ b/crates/wasi-http/wit/command-extended.wit @@ -16,7 +16,7 @@ world command-extended { import wasi:random/random import wasi:random/insecure import wasi:random/insecure-seed - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/exit diff --git a/crates/wasi-http/wit/deps/cli/reactor.wit b/crates/wasi-http/wit/deps/cli/reactor.wit index 46e3186e5636..274d0644dc41 100644 --- a/crates/wasi-http/wit/deps/cli/reactor.wit +++ b/crates/wasi-http/wit/deps/cli/reactor.wit @@ -16,7 +16,7 @@ world reactor { import wasi:random/random import wasi:random/insecure import wasi:random/insecure-seed - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import environment diff --git a/crates/wasi-http/wit/deps/cli/terminal.wit b/crates/wasi-http/wit/deps/cli/terminal.wit index f32e74437484..b0a5bec2a2c7 100644 --- a/crates/wasi-http/wit/deps/cli/terminal.wit +++ b/crates/wasi-http/wit/deps/cli/terminal.wit @@ -1,31 +1,19 @@ interface terminal-input { /// The input side of a terminal. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type terminal-input = u32 + resource terminal-input // In the future, this may include functions for disabling echoing, // disabling input buffering so that keyboard events are sent through // immediately, querying supported features, and so on. - - /// Dispose of the specified terminal-input after which it may no longer - /// be used. - drop-terminal-input: func(this: terminal-input) } interface terminal-output { /// The output side of a terminal. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type terminal-output = u32 + resource terminal-output // In the future, this may include functions for querying the terminal // size, being notified of terminal size changes, querying supported // features, and so on. - - /// Dispose of the specified terminal-output, after which it may no longer - /// be used. - drop-terminal-output: func(this: terminal-output) } /// An interface providing an optional `terminal-input` for stdin as a diff --git a/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit b/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit index 50eb4de111af..703a5fb7a503 100644 --- a/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit +++ b/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit @@ -1,5 +1,3 @@ -package wasi:clocks - /// WASI Monotonic Clock is a clock API intended to let users measure elapsed /// time. /// @@ -11,7 +9,7 @@ package wasi:clocks /// /// It is intended for measuring elapsed time. interface monotonic-clock { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} /// A timestamp in nanoseconds. type instant = u64 diff --git a/crates/wasi-http/wit/deps/clocks/timezone.wit b/crates/wasi-http/wit/deps/clocks/timezone.wit index 2b6855668e1d..a872bffc7414 100644 --- a/crates/wasi-http/wit/deps/clocks/timezone.wit +++ b/crates/wasi-http/wit/deps/clocks/timezone.wit @@ -1,17 +1,6 @@ -package wasi:clocks - interface timezone { use wall-clock.{datetime} - /// A timezone. - /// - /// In timezones that recognize daylight saving time, also known as daylight - /// time and summer time, the information returned from the functions varies - /// over time to reflect these adjustments. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type timezone = u32 - /// Return information needed to display the given `datetime`. This includes /// the UTC offset, the time zone name, and a flag indicating whether /// daylight saving time is active. @@ -19,14 +8,10 @@ interface timezone { /// If the timezone cannot be determined for the given `datetime`, return a /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight /// saving time. - display: func(this: timezone, when: datetime) -> timezone-display + display: func(when: datetime) -> timezone-display /// The same as `display`, but only return the UTC offset. - utc-offset: func(this: timezone, when: datetime) -> s32 - - /// Dispose of the specified input-stream, after which it may no longer - /// be used. - drop-timezone: func(this: timezone) + utc-offset: func(when: datetime) -> s32 /// Information useful for displaying the timezone of a specific `datetime`. /// diff --git a/crates/wasi-http/wit/deps/clocks/wall-clock.wit b/crates/wasi-http/wit/deps/clocks/wall-clock.wit index 6137724f60b1..dae44a7308cd 100644 --- a/crates/wasi-http/wit/deps/clocks/wall-clock.wit +++ b/crates/wasi-http/wit/deps/clocks/wall-clock.wit @@ -1,5 +1,3 @@ -package wasi:clocks - /// WASI Wall Clock is a clock API intended to let users query the current /// time. The name "wall" makes an analogy to a "clock on the wall", which /// is not necessarily monotonic as it may be reset. diff --git a/crates/wasi-http/wit/deps/clocks/world.wit b/crates/wasi-http/wit/deps/clocks/world.wit new file mode 100644 index 000000000000..5c2dd411d2d0 --- /dev/null +++ b/crates/wasi-http/wit/deps/clocks/world.wit @@ -0,0 +1,7 @@ +package wasi:clocks + +world imports { + import monotonic-clock + import wall-clock + import timezone +} diff --git a/crates/wasi-http/wit/deps/filesystem/types.wit b/crates/wasi-http/wit/deps/filesystem/types.wit index e72a742de6c8..3f69bf997a29 100644 --- a/crates/wasi-http/wit/deps/filesystem/types.wit +++ b/crates/wasi-http/wit/deps/filesystem/types.wit @@ -17,6 +17,11 @@ /// `..` and symbolic link steps, reaches a directory outside of the base /// directory, or reaches a symlink to an absolute or rooted path in the /// underlying filesystem, the function fails with `error-code::not-permitted`. +/// +/// For more information about WASI path resolution and sandboxing, see +/// [WASI filesystem path resolution]. +/// +/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md interface types { use wasi:io/streams.{input-stream, output-stream} use wasi:clocks/wall-clock.{datetime} @@ -102,11 +107,20 @@ interface types { /// length in bytes of the pathname contained in the symbolic link. size: filesize, /// Last data access timestamp. - data-access-timestamp: datetime, + /// + /// If the `option` is none, the platform doesn't maintain an access + /// timestamp for this file. + data-access-timestamp: option, /// Last data modification timestamp. - data-modification-timestamp: datetime, - /// Last file status change timestamp. - status-change-timestamp: datetime, + /// + /// If the `option` is none, the platform doesn't maintain a + /// modification timestamp for this file. + data-modification-timestamp: option, + /// Last file status-change timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// status-change timestamp for this file. + status-change-timestamp: option, } /// Flags determining the method of how paths are resolved. @@ -277,13 +291,6 @@ interface types { no-reuse, } - /// A descriptor is a reference to a filesystem object, which may be a file, - /// directory, named pipe, special file, or other object on which filesystem - /// calls may be made. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type descriptor = u32 - /// A 128-bit hash value, split into parts because wasm doesn't have a /// 128-bit integer type. record metadata-hash-value { @@ -293,532 +300,499 @@ interface types { upper: u64, } - /// Return a stream for reading from a file, if available. - /// - /// May fail with an error-code describing why the file cannot be read. - /// - /// Multiple read, write, and append streams may be active on the same open - /// file and they do not interfere with each other. - /// - /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. - read-via-stream: func( - this: descriptor, - /// The offset within the file at which to start reading. - offset: filesize, - ) -> result - - /// Return a stream for writing to a file, if available. - /// - /// May fail with an error-code describing why the file cannot be written. - /// - /// Note: This allows using `write-stream`, which is similar to `write` in - /// POSIX. - write-via-stream: func( - this: descriptor, - /// The offset within the file at which to start writing. - offset: filesize, - ) -> result - - /// Return a stream for appending to a file, if available. - /// - /// May fail with an error-code describing why the file cannot be appended. - /// - /// Note: This allows using `write-stream`, which is similar to `write` with - /// `O_APPEND` in in POSIX. - append-via-stream: func( - this: descriptor, - ) -> result + /// A descriptor is a reference to a filesystem object, which may be a file, + /// directory, named pipe, special file, or other object on which filesystem + /// calls may be made. + resource descriptor { + /// Return a stream for reading from a file, if available. + /// + /// May fail with an error-code describing why the file cannot be read. + /// + /// Multiple read, write, and append streams may be active on the same open + /// file and they do not interfere with each other. + /// + /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. + read-via-stream: func( + /// The offset within the file at which to start reading. + offset: filesize, + ) -> result - /// Provide file advisory information on a descriptor. - /// - /// This is similar to `posix_fadvise` in POSIX. - advise: func( - this: descriptor, - /// The offset within the file to which the advisory applies. - offset: filesize, - /// The length of the region to which the advisory applies. - length: filesize, - /// The advice. - advice: advice - ) -> result<_, error-code> - - /// Synchronize the data of a file to disk. - /// - /// This function succeeds with no effect if the file descriptor is not - /// opened for writing. - /// - /// Note: This is similar to `fdatasync` in POSIX. - sync-data: func(this: descriptor) -> result<_, error-code> + /// Return a stream for writing to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be written. + /// + /// Note: This allows using `write-stream`, which is similar to `write` in + /// POSIX. + write-via-stream: func( + /// The offset within the file at which to start writing. + offset: filesize, + ) -> result + + /// Return a stream for appending to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be appended. + /// + /// Note: This allows using `write-stream`, which is similar to `write` with + /// `O_APPEND` in in POSIX. + append-via-stream: func() -> result - /// Get flags associated with a descriptor. - /// - /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. - /// - /// Note: This returns the value that was the `fs_flags` value returned - /// from `fdstat_get` in earlier versions of WASI. - get-flags: func(this: descriptor) -> result + /// Provide file advisory information on a descriptor. + /// + /// This is similar to `posix_fadvise` in POSIX. + advise: func( + /// The offset within the file to which the advisory applies. + offset: filesize, + /// The length of the region to which the advisory applies. + length: filesize, + /// The advice. + advice: advice + ) -> result<_, error-code> + + /// Synchronize the data of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fdatasync` in POSIX. + sync-data: func() -> result<_, error-code> - /// Get the dynamic type of a descriptor. - /// - /// Note: This returns the same value as the `type` field of the `fd-stat` - /// returned by `stat`, `stat-at` and similar. - /// - /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided - /// by `fstat` in POSIX. - /// - /// Note: This returns the value that was the `fs_filetype` value returned - /// from `fdstat_get` in earlier versions of WASI. - get-type: func(this: descriptor) -> result + /// Get flags associated with a descriptor. + /// + /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + /// + /// Note: This returns the value that was the `fs_flags` value returned + /// from `fdstat_get` in earlier versions of WASI. + get-flags: func() -> result - /// Adjust the size of an open file. If this increases the file's size, the - /// extra bytes are filled with zeros. - /// - /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. - set-size: func(this: descriptor, size: filesize) -> result<_, error-code> + /// Get the dynamic type of a descriptor. + /// + /// Note: This returns the same value as the `type` field of the `fd-stat` + /// returned by `stat`, `stat-at` and similar. + /// + /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided + /// by `fstat` in POSIX. + /// + /// Note: This returns the value that was the `fs_filetype` value returned + /// from `fdstat_get` in earlier versions of WASI. + get-type: func() -> result - /// Adjust the timestamps of an open file or directory. - /// - /// Note: This is similar to `futimens` in POSIX. - /// - /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. - set-times: func( - this: descriptor, - /// The desired values of the data access timestamp. - data-access-timestamp: new-timestamp, - /// The desired values of the data modification timestamp. - data-modification-timestamp: new-timestamp, - ) -> result<_, error-code> - - /// Read from a descriptor, without using and updating the descriptor's offset. - /// - /// This function returns a list of bytes containing the data that was - /// read, along with a bool which, when true, indicates that the end of the - /// file was reached. The returned list will contain up to `length` bytes; it - /// may return fewer than requested, if the end of the file is reached or - /// if the I/O operation is interrupted. - /// - /// In the future, this may change to return a `stream`. - /// - /// Note: This is similar to `pread` in POSIX. - read: func( - this: descriptor, - /// The maximum number of bytes to read. - length: filesize, - /// The offset within the file at which to read. - offset: filesize, - ) -> result, bool>, error-code> - - /// Write to a descriptor, without using and updating the descriptor's offset. - /// - /// It is valid to write past the end of a file; the file is extended to the - /// extent of the write, with bytes between the previous end and the start of - /// the write set to zero. - /// - /// In the future, this may change to take a `stream`. - /// - /// Note: This is similar to `pwrite` in POSIX. - write: func( - this: descriptor, - /// Data to write - buffer: list, - /// The offset within the file at which to write. - offset: filesize, - ) -> result - - /// Read directory entries from a directory. - /// - /// On filesystems where directories contain entries referring to themselves - /// and their parents, often named `.` and `..` respectively, these entries - /// are omitted. - /// - /// This always returns a new stream which starts at the beginning of the - /// directory. Multiple streams may be active on the same directory, and they - /// do not interfere with each other. - read-directory: func( - this: descriptor - ) -> result - - /// Synchronize the data and metadata of a file to disk. - /// - /// This function succeeds with no effect if the file descriptor is not - /// opened for writing. - /// - /// Note: This is similar to `fsync` in POSIX. - sync: func(this: descriptor) -> result<_, error-code> + /// Adjust the size of an open file. If this increases the file's size, the + /// extra bytes are filled with zeros. + /// + /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + set-size: func(size: filesize) -> result<_, error-code> - /// Create a directory. - /// - /// Note: This is similar to `mkdirat` in POSIX. - create-directory-at: func( - this: descriptor, - /// The relative path at which to create the directory. - path: string, - ) -> result<_, error-code> - - /// Return the attributes of an open file or directory. - /// - /// Note: This is similar to `fstat` in POSIX, except that it does not return - /// device and inode information. For testing whether two descriptors refer to - /// the same underlying filesystem object, use `is-same-object`. To obtain - /// additional data that can be used do determine whether a file has been - /// modified, use `metadata-hash`. - /// - /// Note: This was called `fd_filestat_get` in earlier versions of WASI. - stat: func(this: descriptor) -> result + /// Adjust the timestamps of an open file or directory. + /// + /// Note: This is similar to `futimens` in POSIX. + /// + /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + set-times: func( + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + /// The desired values of the data modification timestamp. + data-modification-timestamp: new-timestamp, + ) -> result<_, error-code> + + /// Read from a descriptor, without using and updating the descriptor's offset. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// file was reached. The returned list will contain up to `length` bytes; it + /// may return fewer than requested, if the end of the file is reached or + /// if the I/O operation is interrupted. + /// + /// In the future, this may change to return a `stream`. + /// + /// Note: This is similar to `pread` in POSIX. + read: func( + /// The maximum number of bytes to read. + length: filesize, + /// The offset within the file at which to read. + offset: filesize, + ) -> result, bool>, error-code> + + /// Write to a descriptor, without using and updating the descriptor's offset. + /// + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// In the future, this may change to take a `stream`. + /// + /// Note: This is similar to `pwrite` in POSIX. + write: func( + /// Data to write + buffer: list, + /// The offset within the file at which to write. + offset: filesize, + ) -> result + + /// Read directory entries from a directory. + /// + /// On filesystems where directories contain entries referring to themselves + /// and their parents, often named `.` and `..` respectively, these entries + /// are omitted. + /// + /// This always returns a new stream which starts at the beginning of the + /// directory. Multiple streams may be active on the same directory, and they + /// do not interfere with each other. + read-directory: func() -> result - /// Return the attributes of a file or directory. - /// - /// Note: This is similar to `fstatat` in POSIX, except that it does not - /// return device and inode information. See the `stat` description for a - /// discussion of alternatives. - /// - /// Note: This was called `path_filestat_get` in earlier versions of WASI. - stat-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path of the file or directory to inspect. - path: string, - ) -> result - - /// Adjust the timestamps of a file or directory. - /// - /// Note: This is similar to `utimensat` in POSIX. - /// - /// Note: This was called `path_filestat_set_times` in earlier versions of - /// WASI. - set-times-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path of the file or directory to operate on. - path: string, - /// The desired values of the data access timestamp. - data-access-timestamp: new-timestamp, - /// The desired values of the data modification timestamp. - data-modification-timestamp: new-timestamp, - ) -> result<_, error-code> - - /// Create a hard link. - /// - /// Note: This is similar to `linkat` in POSIX. - link-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - old-path-flags: path-flags, - /// The relative source path from which to link. - old-path: string, - /// The base directory for `new-path`. - new-descriptor: descriptor, - /// The relative destination path at which to create the hard link. - new-path: string, - ) -> result<_, error-code> - - /// Open a file or directory. - /// - /// The returned descriptor is not guaranteed to be the lowest-numbered - /// descriptor not currently open/ it is randomized to prevent applications - /// from depending on making assumptions about indexes, since this is - /// error-prone in multi-threaded contexts. The returned descriptor is - /// guaranteed to be less than 2**31. - /// - /// If `flags` contains `descriptor-flags::mutate-directory`, and the base - /// descriptor doesn't have `descriptor-flags::mutate-directory` set, - /// `open-at` fails with `error-code::read-only`. - /// - /// If `flags` contains `write` or `mutate-directory`, or `open-flags` - /// contains `truncate` or `create`, and the base descriptor doesn't have - /// `descriptor-flags::mutate-directory` set, `open-at` fails with - /// `error-code::read-only`. - /// - /// Note: This is similar to `openat` in POSIX. - open-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path of the object to open. - path: string, - /// The method by which to open the file. - open-flags: open-flags, - /// Flags to use for the resulting descriptor. - %flags: descriptor-flags, - /// Permissions to use when creating a new file. - modes: modes - ) -> result - - /// Read the contents of a symbolic link. - /// - /// If the contents contain an absolute or rooted path in the underlying - /// filesystem, this function fails with `error-code::not-permitted`. - /// - /// Note: This is similar to `readlinkat` in POSIX. - readlink-at: func( - this: descriptor, - /// The relative path of the symbolic link from which to read. - path: string, - ) -> result - - /// Remove a directory. - /// - /// Return `error-code::not-empty` if the directory is not empty. - /// - /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - remove-directory-at: func( - this: descriptor, - /// The relative path to a directory to remove. - path: string, - ) -> result<_, error-code> - - /// Rename a filesystem object. - /// - /// Note: This is similar to `renameat` in POSIX. - rename-at: func( - this: descriptor, - /// The relative source path of the file or directory to rename. - old-path: string, - /// The base directory for `new-path`. - new-descriptor: descriptor, - /// The relative destination path to which to rename the file or directory. - new-path: string, - ) -> result<_, error-code> - - /// Create a symbolic link (also known as a "symlink"). - /// - /// If `old-path` starts with `/`, the function fails with - /// `error-code::not-permitted`. - /// - /// Note: This is similar to `symlinkat` in POSIX. - symlink-at: func( - this: descriptor, - /// The contents of the symbolic link. - old-path: string, - /// The relative destination path at which to create the symbolic link. - new-path: string, - ) -> result<_, error-code> - - /// Check accessibility of a filesystem path. - /// - /// Check whether the given filesystem path names an object which is - /// readable, writable, or executable, or whether it exists. - /// - /// This does not a guarantee that subsequent accesses will succeed, as - /// filesystem permissions may be modified asynchronously by external - /// entities. - /// - /// Note: This is similar to `faccessat` with the `AT_EACCESS` flag in POSIX. - access-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path to check. - path: string, - /// The type of check to perform. - %type: access-type - ) -> result<_, error-code> - - /// Unlink a filesystem object that is not a directory. - /// - /// Return `error-code::is-directory` if the path refers to a directory. - /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - unlink-file-at: func( - this: descriptor, - /// The relative path to a file to unlink. - path: string, - ) -> result<_, error-code> - - /// Change the permissions of a filesystem object that is not a directory. - /// - /// Note that the ultimate meanings of these permissions is - /// filesystem-specific. - /// - /// Note: This is similar to `fchmodat` in POSIX. - change-file-permissions-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path to operate on. - path: string, - /// The new permissions for the filesystem object. - modes: modes, - ) -> result<_, error-code> - - /// Change the permissions of a directory. - /// - /// Note that the ultimate meanings of these permissions is - /// filesystem-specific. - /// - /// Unlike in POSIX, the `executable` flag is not reinterpreted as a "search" - /// flag. `read` on a directory implies readability and searchability, and - /// `execute` is not valid for directories. - /// - /// Note: This is similar to `fchmodat` in POSIX. - change-directory-permissions-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path to operate on. - path: string, - /// The new permissions for the directory. - modes: modes, - ) -> result<_, error-code> - - /// Request a shared advisory lock for an open file. - /// - /// This requests a *shared* lock; more than one shared lock can be held for - /// a file at the same time. - /// - /// If the open file has an exclusive lock, this function downgrades the lock - /// to a shared lock. If it has a shared lock, this function has no effect. - /// - /// This requests an *advisory* lock, meaning that the file could be accessed - /// by other programs that don't hold the lock. - /// - /// It is unspecified how shared locks interact with locks acquired by - /// non-WASI programs. - /// - /// This function blocks until the lock can be acquired. - /// - /// Not all filesystems support locking; on filesystems which don't support - /// locking, this function returns `error-code::unsupported`. - /// - /// Note: This is similar to `flock(fd, LOCK_SH)` in Unix. - lock-shared: func(this: descriptor) -> result<_, error-code> + /// Synchronize the data and metadata of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fsync` in POSIX. + sync: func() -> result<_, error-code> - /// Request an exclusive advisory lock for an open file. - /// - /// This requests an *exclusive* lock; no other locks may be held for the - /// file while an exclusive lock is held. - /// - /// If the open file has a shared lock and there are no exclusive locks held - /// for the file, this function upgrades the lock to an exclusive lock. If the - /// open file already has an exclusive lock, this function has no effect. - /// - /// This requests an *advisory* lock, meaning that the file could be accessed - /// by other programs that don't hold the lock. - /// - /// It is unspecified whether this function succeeds if the file descriptor - /// is not opened for writing. It is unspecified how exclusive locks interact - /// with locks acquired by non-WASI programs. - /// - /// This function blocks until the lock can be acquired. - /// - /// Not all filesystems support locking; on filesystems which don't support - /// locking, this function returns `error-code::unsupported`. - /// - /// Note: This is similar to `flock(fd, LOCK_EX)` in Unix. - lock-exclusive: func(this: descriptor) -> result<_, error-code> + /// Create a directory. + /// + /// Note: This is similar to `mkdirat` in POSIX. + create-directory-at: func( + /// The relative path at which to create the directory. + path: string, + ) -> result<_, error-code> - /// Request a shared advisory lock for an open file. - /// - /// This requests a *shared* lock; more than one shared lock can be held for - /// a file at the same time. - /// - /// If the open file has an exclusive lock, this function downgrades the lock - /// to a shared lock. If it has a shared lock, this function has no effect. - /// - /// This requests an *advisory* lock, meaning that the file could be accessed - /// by other programs that don't hold the lock. - /// - /// It is unspecified how shared locks interact with locks acquired by - /// non-WASI programs. - /// - /// This function returns `error-code::would-block` if the lock cannot be - /// acquired. - /// - /// Not all filesystems support locking; on filesystems which don't support - /// locking, this function returns `error-code::unsupported`. - /// - /// Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. - try-lock-shared: func(this: descriptor) -> result<_, error-code> + /// Return the attributes of an open file or directory. + /// + /// Note: This is similar to `fstat` in POSIX, except that it does not return + /// device and inode information. For testing whether two descriptors refer to + /// the same underlying filesystem object, use `is-same-object`. To obtain + /// additional data that can be used do determine whether a file has been + /// modified, use `metadata-hash`. + /// + /// Note: This was called `fd_filestat_get` in earlier versions of WASI. + stat: func() -> result - /// Request an exclusive advisory lock for an open file. - /// - /// This requests an *exclusive* lock; no other locks may be held for the - /// file while an exclusive lock is held. - /// - /// If the open file has a shared lock and there are no exclusive locks held - /// for the file, this function upgrades the lock to an exclusive lock. If the - /// open file already has an exclusive lock, this function has no effect. - /// - /// This requests an *advisory* lock, meaning that the file could be accessed - /// by other programs that don't hold the lock. - /// - /// It is unspecified whether this function succeeds if the file descriptor - /// is not opened for writing. It is unspecified how exclusive locks interact - /// with locks acquired by non-WASI programs. - /// - /// This function returns `error-code::would-block` if the lock cannot be - /// acquired. - /// - /// Not all filesystems support locking; on filesystems which don't support - /// locking, this function returns `error-code::unsupported`. - /// - /// Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. - try-lock-exclusive: func(this: descriptor) -> result<_, error-code> + /// Return the attributes of a file or directory. + /// + /// Note: This is similar to `fstatat` in POSIX, except that it does not + /// return device and inode information. See the `stat` description for a + /// discussion of alternatives. + /// + /// Note: This was called `path_filestat_get` in earlier versions of WASI. + stat-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to inspect. + path: string, + ) -> result + + /// Adjust the timestamps of a file or directory. + /// + /// Note: This is similar to `utimensat` in POSIX. + /// + /// Note: This was called `path_filestat_set_times` in earlier versions of + /// WASI. + set-times-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to operate on. + path: string, + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + /// The desired values of the data modification timestamp. + data-modification-timestamp: new-timestamp, + ) -> result<_, error-code> + + /// Create a hard link. + /// + /// Note: This is similar to `linkat` in POSIX. + link-at: func( + /// Flags determining the method of how the path is resolved. + old-path-flags: path-flags, + /// The relative source path from which to link. + old-path: string, + /// The base directory for `new-path`. + new-descriptor: borrow, + /// The relative destination path at which to create the hard link. + new-path: string, + ) -> result<_, error-code> + + /// Open a file or directory. + /// + /// The returned descriptor is not guaranteed to be the lowest-numbered + /// descriptor not currently open/ it is randomized to prevent applications + /// from depending on making assumptions about indexes, since this is + /// error-prone in multi-threaded contexts. The returned descriptor is + /// guaranteed to be less than 2**31. + /// + /// If `flags` contains `descriptor-flags::mutate-directory`, and the base + /// descriptor doesn't have `descriptor-flags::mutate-directory` set, + /// `open-at` fails with `error-code::read-only`. + /// + /// If `flags` contains `write` or `mutate-directory`, or `open-flags` + /// contains `truncate` or `create`, and the base descriptor doesn't have + /// `descriptor-flags::mutate-directory` set, `open-at` fails with + /// `error-code::read-only`. + /// + /// Note: This is similar to `openat` in POSIX. + open-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the object to open. + path: string, + /// The method by which to open the file. + open-flags: open-flags, + /// Flags to use for the resulting descriptor. + %flags: descriptor-flags, + /// Permissions to use when creating a new file. + modes: modes + ) -> result + + /// Read the contents of a symbolic link. + /// + /// If the contents contain an absolute or rooted path in the underlying + /// filesystem, this function fails with `error-code::not-permitted`. + /// + /// Note: This is similar to `readlinkat` in POSIX. + readlink-at: func( + /// The relative path of the symbolic link from which to read. + path: string, + ) -> result - /// Release a shared or exclusive lock on an open file. - /// - /// Note: This is similar to `flock(fd, LOCK_UN)` in Unix. - unlock: func(this: descriptor) -> result<_, error-code> + /// Remove a directory. + /// + /// Return `error-code::not-empty` if the directory is not empty. + /// + /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + remove-directory-at: func( + /// The relative path to a directory to remove. + path: string, + ) -> result<_, error-code> - /// Dispose of the specified `descriptor`, after which it may no longer - /// be used. - drop-descriptor: func(this: descriptor) + /// Rename a filesystem object. + /// + /// Note: This is similar to `renameat` in POSIX. + rename-at: func( + /// The relative source path of the file or directory to rename. + old-path: string, + /// The base directory for `new-path`. + new-descriptor: borrow, + /// The relative destination path to which to rename the file or directory. + new-path: string, + ) -> result<_, error-code> + + /// Create a symbolic link (also known as a "symlink"). + /// + /// If `old-path` starts with `/`, the function fails with + /// `error-code::not-permitted`. + /// + /// Note: This is similar to `symlinkat` in POSIX. + symlink-at: func( + /// The contents of the symbolic link. + old-path: string, + /// The relative destination path at which to create the symbolic link. + new-path: string, + ) -> result<_, error-code> + + /// Check accessibility of a filesystem path. + /// + /// Check whether the given filesystem path names an object which is + /// readable, writable, or executable, or whether it exists. + /// + /// This does not a guarantee that subsequent accesses will succeed, as + /// filesystem permissions may be modified asynchronously by external + /// entities. + /// + /// Note: This is similar to `faccessat` with the `AT_EACCESS` flag in POSIX. + access-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to check. + path: string, + /// The type of check to perform. + %type: access-type + ) -> result<_, error-code> + + /// Unlink a filesystem object that is not a directory. + /// + /// Return `error-code::is-directory` if the path refers to a directory. + /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + unlink-file-at: func( + /// The relative path to a file to unlink. + path: string, + ) -> result<_, error-code> + + /// Change the permissions of a filesystem object that is not a directory. + /// + /// Note that the ultimate meanings of these permissions is + /// filesystem-specific. + /// + /// Note: This is similar to `fchmodat` in POSIX. + change-file-permissions-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to operate on. + path: string, + /// The new permissions for the filesystem object. + modes: modes, + ) -> result<_, error-code> + + /// Change the permissions of a directory. + /// + /// Note that the ultimate meanings of these permissions is + /// filesystem-specific. + /// + /// Unlike in POSIX, the `executable` flag is not reinterpreted as a "search" + /// flag. `read` on a directory implies readability and searchability, and + /// `execute` is not valid for directories. + /// + /// Note: This is similar to `fchmodat` in POSIX. + change-directory-permissions-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to operate on. + path: string, + /// The new permissions for the directory. + modes: modes, + ) -> result<_, error-code> + + /// Request a shared advisory lock for an open file. + /// + /// This requests a *shared* lock; more than one shared lock can be held for + /// a file at the same time. + /// + /// If the open file has an exclusive lock, this function downgrades the lock + /// to a shared lock. If it has a shared lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified how shared locks interact with locks acquired by + /// non-WASI programs. + /// + /// This function blocks until the lock can be acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_SH)` in Unix. + lock-shared: func() -> result<_, error-code> - /// A stream of directory entries. - /// - /// This [represents a stream of `dir-entry`](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Streams). - type directory-entry-stream = u32 + /// Request an exclusive advisory lock for an open file. + /// + /// This requests an *exclusive* lock; no other locks may be held for the + /// file while an exclusive lock is held. + /// + /// If the open file has a shared lock and there are no exclusive locks held + /// for the file, this function upgrades the lock to an exclusive lock. If the + /// open file already has an exclusive lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified whether this function succeeds if the file descriptor + /// is not opened for writing. It is unspecified how exclusive locks interact + /// with locks acquired by non-WASI programs. + /// + /// This function blocks until the lock can be acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_EX)` in Unix. + lock-exclusive: func() -> result<_, error-code> + + /// Request a shared advisory lock for an open file. + /// + /// This requests a *shared* lock; more than one shared lock can be held for + /// a file at the same time. + /// + /// If the open file has an exclusive lock, this function downgrades the lock + /// to a shared lock. If it has a shared lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified how shared locks interact with locks acquired by + /// non-WASI programs. + /// + /// This function returns `error-code::would-block` if the lock cannot be + /// acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. + try-lock-shared: func() -> result<_, error-code> - /// Read a single directory entry from a `directory-entry-stream`. - read-directory-entry: func( - this: directory-entry-stream - ) -> result, error-code> + /// Request an exclusive advisory lock for an open file. + /// + /// This requests an *exclusive* lock; no other locks may be held for the + /// file while an exclusive lock is held. + /// + /// If the open file has a shared lock and there are no exclusive locks held + /// for the file, this function upgrades the lock to an exclusive lock. If the + /// open file already has an exclusive lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified whether this function succeeds if the file descriptor + /// is not opened for writing. It is unspecified how exclusive locks interact + /// with locks acquired by non-WASI programs. + /// + /// This function returns `error-code::would-block` if the lock cannot be + /// acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. + try-lock-exclusive: func() -> result<_, error-code> - /// Dispose of the specified `directory-entry-stream`, after which it may no longer - /// be used. - drop-directory-entry-stream: func(this: directory-entry-stream) + /// Release a shared or exclusive lock on an open file. + /// + /// Note: This is similar to `flock(fd, LOCK_UN)` in Unix. + unlock: func() -> result<_, error-code> - /// Test whether two descriptors refer to the same filesystem object. - /// - /// In POSIX, this corresponds to testing whether the two descriptors have the - /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. - /// wasi-filesystem does not expose device and inode numbers, so this function - /// may be used instead. - is-same-object: func(this: descriptor, other: descriptor) -> bool - - /// Return a hash of the metadata associated with a filesystem object referred - /// to by a descriptor. - /// - /// This returns a hash of the last-modification timestamp and file size, and - /// may also include the inode number, device number, birth timestamp, and - /// other metadata fields that may change when the file is modified or - /// replaced. It may also include a secret value chosen by the - /// implementation and not otherwise exposed. - /// - /// Implementations are encourated to provide the following properties: - /// - /// - If the file is not modified or replaced, the computed hash value should - /// usually not change. - /// - If the object is modified or replaced, the computed hash value should - /// usually change. - /// - The inputs to the hash should not be easily computable from the - /// computed hash. - /// - /// However, none of these is required. - metadata-hash: func( - this: descriptor, - ) -> result + /// Test whether two descriptors refer to the same filesystem object. + /// + /// In POSIX, this corresponds to testing whether the two descriptors have the + /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. + /// wasi-filesystem does not expose device and inode numbers, so this function + /// may be used instead. + is-same-object: func(other: borrow) -> bool + + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a descriptor. + /// + /// This returns a hash of the last-modification timestamp and file size, and + /// may also include the inode number, device number, birth timestamp, and + /// other metadata fields that may change when the file is modified or + /// replaced. It may also include a secret value chosen by the + /// implementation and not otherwise exposed. + /// + /// Implementations are encourated to provide the following properties: + /// + /// - If the file is not modified or replaced, the computed hash value should + /// usually not change. + /// - If the object is modified or replaced, the computed hash value should + /// usually change. + /// - The inputs to the hash should not be easily computable from the + /// computed hash. + /// + /// However, none of these is required. + metadata-hash: func() -> result - /// Return a hash of the metadata associated with a filesystem object referred - /// to by a directory descriptor and a relative path. - /// - /// This performs the same hash computation as `metadata-hash`. - metadata-hash-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path of the file or directory to inspect. - path: string, - ) -> result + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a directory descriptor and a relative path. + /// + /// This performs the same hash computation as `metadata-hash`. + metadata-hash-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to inspect. + path: string, + ) -> result + } + + /// A stream of directory entries. + resource directory-entry-stream { + /// Read a single directory entry from a `directory-entry-stream`. + read-directory-entry: func() -> result, error-code> + } } diff --git a/crates/wasi-http/wit/deps/filesystem/world.wit b/crates/wasi-http/wit/deps/filesystem/world.wit index b51f484f8383..5fa7eafdb850 100644 --- a/crates/wasi-http/wit/deps/filesystem/world.wit +++ b/crates/wasi-http/wit/deps/filesystem/world.wit @@ -1,6 +1,6 @@ package wasi:filesystem -world example-world { +world imports { import types import preopens } diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index dfcacd8feb73..1abc7a1ff2c4 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -3,7 +3,7 @@ // imported and exported interfaces. interface types { use wasi:io/streams.{input-stream, output-stream} - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} // This type corresponds to HTTP standard Methods. variant method { @@ -100,7 +100,7 @@ interface types { // Additional optional parameters that can be set when making a request. record request-options { // The following timeouts are specific to the HTTP protocol and work - // independently of the overall timeouts passed to `io.poll.poll-oneoff`. + // independently of the overall timeouts passed to `io.poll.poll-list`. // The timeout for the initial connect. connect-timeout-ms: option, @@ -142,19 +142,18 @@ interface types { // incoming-body. incoming-response-consume: func(response: /* borrow */ incoming-response) -> result - type incoming-body = u32 - drop-incoming-body: func(this: /* own */ incoming-body) - - // returned input-stream is a child - the implementation may trap if - // incoming-body is dropped (or consumed by call to - // incoming-body-finish) before the input-stream is dropped. - // May be called at most once. returns error if called additional times. - incoming-body-stream: func(this: /* borrow */ incoming-body) -> - result - // takes ownership of incoming-body. this will trap if the - // incoming-body-stream child is still alive! - incoming-body-finish: func(this: /* own */ incoming-body) -> - /* transitive child of the incoming-response of incoming-body */ future-trailers + resource incoming-body { + // returned input-stream is a child - the implementation may trap if + // incoming-body is dropped (or consumed by call to + // incoming-body-finish) before the input-stream is dropped. + // May be called at most once. returns error if called additional times. + %stream: func() -> + result + // takes ownership of incoming-body. this will trap if the + // incoming-body-stream child is still alive! + finish: func() -> + /* transitive child of the incoming-response of incoming-body */ future-trailers + } type future-trailers = u32 drop-future-trailers: func(this: /* own */ future-trailers) @@ -193,7 +192,7 @@ interface types { /// `future-incoming-response`, the client can call the non-blocking `get` /// method to get the result if it is available. If the result is not available, /// the client can call `listen` to get a `pollable` that can be passed to - /// `io.poll.poll-oneoff`. + /// `wasi:io/poll.poll-list`. type future-incoming-response = u32 drop-future-incoming-response: func(f: /* own */ future-incoming-response) /// option indicates readiness. diff --git a/crates/wasi-http/wit/deps/io/poll.wit b/crates/wasi-http/wit/deps/io/poll.wit new file mode 100644 index 000000000000..e95762b915db --- /dev/null +++ b/crates/wasi-http/wit/deps/io/poll.wit @@ -0,0 +1,34 @@ +package wasi:io + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +interface poll { + /// A "pollable" handle. + resource pollable + + /// Poll for completion on a set of pollables. + /// + /// This function takes a list of pollables, which identify I/O sources of + /// interest, and waits until one or more of the events is ready for I/O. + /// + /// The result `list` contains one or more indices of handles in the + /// argument list that is ready for I/O. + /// + /// If the list contains more elements than can be indexed with a `u32` + /// value, this function traps. + /// + /// A timeout can be implemented by adding a pollable from the + /// wasi-clocks API to the list. + /// + /// This function does not return a `result`; polling in itself does not + /// do any I/O so it doesn't fail. If any of the I/O sources identified by + /// the pollables has an error, it is indicated by marking the source as + /// being reaedy for I/O. + poll-list: func(in: list>) -> list + + /// Poll for completion on a single pollable. + /// + /// This function is similar to `poll-list`, but operates on only a single + /// pollable. When it returns, the handle is ready for I/O. + poll-one: func(in: borrow) +} diff --git a/crates/wasi-http/wit/deps/io/streams.wit b/crates/wasi-http/wit/deps/io/streams.wit index e2631f66a569..eeeff505890a 100644 --- a/crates/wasi-http/wit/deps/io/streams.wit +++ b/crates/wasi-http/wit/deps/io/streams.wit @@ -6,7 +6,7 @@ package wasi:io /// In the future, the component model is expected to add built-in stream types; /// when it does, they are expected to subsume this API. interface streams { - use wasi:poll/poll.{pollable} + use poll.{pollable} /// Streams provide a sequence of data and then end; once they end, they /// no longer provide any further data. @@ -24,115 +24,81 @@ interface streams { ended, } - /// An input bytestream. In the future, this will be replaced by handle - /// types. + /// An input bytestream. /// /// `input-stream`s are *non-blocking* to the extent practical on underlying /// platforms. I/O operations always return promptly; if fewer bytes are /// promptly available than requested, they return the number of bytes promptly /// available, which could even be zero. To wait for data to be available, - /// use the `subscribe-to-input-stream` function to obtain a `pollable` which - /// can be polled for using `wasi:poll/poll.poll_oneoff`. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type input-stream = u32 - - /// Perform a non-blocking read from the stream. - /// - /// This function returns a list of bytes containing the data that was - /// read, along with a `stream-status` which, indicates whether further - /// reads are expected to produce data. The returned list will contain up to - /// `len` bytes; it may return fewer than requested, but not more. An - /// empty list and `stream-status:open` indicates no more data is - /// available at this time, and that the pollable given by - /// `subscribe-to-input-stream` will be ready when more data is available. - /// - /// Once a stream has reached the end, subsequent calls to `read` or - /// `skip` will always report `stream-status:ended` rather than producing more - /// data. - /// - /// When the caller gives a `len` of 0, it represents a request to read 0 - /// bytes. This read should always succeed and return an empty list and - /// the current `stream-status`. - /// - /// The `len` parameter is a `u64`, which could represent a list of u8 which - /// is not possible to allocate in wasm32, or not desirable to allocate as - /// as a return value by the callee. The callee may return a list of bytes - /// less than `len` in size while more bytes are available for reading. - read: func( - this: input-stream, - /// The maximum number of bytes to read - len: u64 - ) -> result, stream-status>> - - /// Read bytes from a stream, after blocking until at least one byte can - /// be read. Except for blocking, identical to `read`. - blocking-read: func( - this: input-stream, - /// The maximum number of bytes to read - len: u64 - ) -> result, stream-status>> - - /// Skip bytes from a stream. - /// - /// This is similar to the `read` function, but avoids copying the - /// bytes into the instance. - /// - /// Once a stream has reached the end, subsequent calls to read or - /// `skip` will always report end-of-stream rather than producing more - /// data. - /// - /// This function returns the number of bytes skipped, along with a - /// `stream-status` indicating whether the end of the stream was - /// reached. The returned value will be at most `len`; it may be less. - skip: func( - this: input-stream, - /// The maximum number of bytes to skip. - len: u64, - ) -> result> + /// use the `subscribe` function to obtain a `pollable` which can be polled + /// for using `wasi:io/poll`. + resource input-stream { + /// Perform a non-blocking read from the stream. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a `stream-status` which, indicates whether further + /// reads are expected to produce data. The returned list will contain up to + /// `len` bytes; it may return fewer than requested, but not more. An + /// empty list and `stream-status:open` indicates no more data is + /// available at this time, and that the pollable given by `subscribe` + /// will be ready when more data is available. + /// + /// Once a stream has reached the end, subsequent calls to `read` or + /// `skip` will always report `stream-status:ended` rather than producing more + /// data. + /// + /// When the caller gives a `len` of 0, it represents a request to read 0 + /// bytes. This read should always succeed and return an empty list and + /// the current `stream-status`. + /// + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. + read: func( + /// The maximum number of bytes to read + len: u64 + ) -> result, stream-status>> - /// Skip bytes from a stream, after blocking until at least one byte - /// can be skipped. Except for blocking behavior, identical to `skip`. - blocking-skip: func( - this: input-stream, - /// The maximum number of bytes to skip. - len: u64, - ) -> result> + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, identical to `read`. + blocking-read: func( + /// The maximum number of bytes to read + len: u64 + ) -> result, stream-status>> - /// Create a `pollable` which will resolve once either the specified stream - /// has bytes available to read or the other end of the stream has been - /// closed. - /// The created `pollable` is a child resource of the `input-stream`. - /// Implementations may trap if the `input-stream` is dropped before - /// all derived `pollable`s created with this function are dropped. - subscribe-to-input-stream: func(this: input-stream) -> pollable + /// Skip bytes from a stream. + /// + /// This is similar to the `read` function, but avoids copying the + /// bytes into the instance. + /// + /// Once a stream has reached the end, subsequent calls to read or + /// `skip` will always report end-of-stream rather than producing more + /// data. + /// + /// This function returns the number of bytes skipped, along with a + /// `stream-status` indicating whether the end of the stream was + /// reached. The returned value will be at most `len`; it may be less. + skip: func( + /// The maximum number of bytes to skip. + len: u64, + ) -> result> - /// Dispose of the specified `input-stream`, after which it may no longer - /// be used. - /// Implementations may trap if this `input-stream` is dropped while child - /// `pollable` resources are still alive. - /// After this `input-stream` is dropped, implementations may report any - /// corresponding `output-stream` has `stream-state.closed`. - drop-input-stream: func(this: input-stream) + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. + blocking-skip: func( + /// The maximum number of bytes to skip. + len: u64, + ) -> result> - /// An output bytestream. In the future, this will be replaced by handle - /// types. - /// - /// `output-stream`s are *non-blocking* to the extent practical on - /// underlying platforms. Except where specified otherwise, I/O operations also - /// always return promptly, after the number of bytes that can be written - /// promptly, which could even be zero. To wait for the stream to be ready to - /// accept data, the `subscribe-to-output-stream` function to obtain a - /// `pollable` which can be polled for using `wasi:poll`. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type output-stream = u32 + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + subscribe: func() -> pollable + } /// An error for output-stream operations. /// @@ -146,155 +112,174 @@ interface streams { /// future operations. closed } - /// Check readiness for writing. This function never blocks. - /// - /// Returns the number of bytes permitted for the next call to `write`, - /// or an error. Calling `write` with more bytes than this function has - /// permitted will trap. - /// - /// When this function returns 0 bytes, the `subscribe-to-output-stream` - /// pollable will become ready when this function will report at least - /// 1 byte, or an error. - check-write: func( - this: output-stream - ) -> result - /// Perform a write. This function never blocks. - /// - /// Precondition: check-write gave permit of Ok(n) and contents has a - /// length of less than or equal to n. Otherwise, this function will trap. + /// An output bytestream. /// - /// returns Err(closed) without writing if the stream has closed since - /// the last call to check-write provided a permit. - write: func( - this: output-stream, - contents: list - ) -> result<_, write-error> + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe` function to obtain a `pollable` which can be + /// polled for using `wasi:io/poll`. + resource output-stream { + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe` pollable will + /// become ready when this function will report at least 1 byte, or an + /// error. + check-write: func() -> result - /// Perform a write of up to 4096 bytes, and then flush the stream. Block - /// until all of these operations are complete, or an error occurs. - /// - /// This is a convenience wrapper around the use of `check-write`, - /// `subscribe-to-output-stream`, `write`, and `flush`, and is implemented - /// with the following pseudo-code: - /// - /// ```text - /// let pollable = subscribe-to-output-stream(this); - /// while !contents.is_empty() { - /// // Wait for the stream to become writable - /// poll-oneoff(pollable); - /// let Ok(n) = check-write(this); // eliding error handling - /// let len = min(n, contents.len()); - /// let (chunk, rest) = contents.split_at(len); - /// write(this, chunk); // eliding error handling - /// contents = rest; - /// } - /// flush(this); - /// // Wait for completion of `flush` - /// poll-oneoff(pollable); - /// // Check for any errors that arose during `flush` - /// let _ = check-write(this); // eliding error handling - /// ``` - blocking-write-and-flush: func( - this: output-stream, - contents: list - ) -> result<_, write-error> + /// Perform a write. This function never blocks. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. + /// + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. + write: func( + contents: list + ) -> result<_, write-error> - /// Request to flush buffered output. This function never blocks. - /// - /// This tells the output-stream that the caller intends any buffered - /// output to be flushed. the output which is expected to be flushed - /// is all that has been passed to `write` prior to this call. - /// - /// Upon calling this function, the `output-stream` will not accept any - /// writes (`check-write` will return `ok(0)`) until the flush has - /// completed. The `subscribe-to-output-stream` pollable will become ready - /// when the flush has completed and the stream can accept more writes. - flush: func( - this: output-stream, - ) -> result<_, write-error> + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write`, and `flush`, and is implemented with the + /// following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while !contents.is_empty() { + /// // Wait for the stream to become writable + /// poll-one(pollable); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, contents.len()); + /// let (chunk, rest) = contents.split_at(len); + /// this.write(chunk ); // eliding error handling + /// contents = rest; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// poll-one(pollable); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + blocking-write-and-flush: func( + contents: list + ) -> result<_, write-error> - /// Request to flush buffered output, and block until flush completes - /// and stream is ready for writing again. - blocking-flush: func( - this: output-stream, - ) -> result<_, write-error> + /// Request to flush buffered output. This function never blocks. + /// + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe` pollable will become ready when the + /// flush has completed and the stream can accept more writes. + flush: func() -> result<_, write-error> - /// Create a `pollable` which will resolve once the output-stream - /// is ready for more writing, or an error has occured. When this - /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an - /// error. - /// - /// If the stream is closed, this pollable is always ready immediately. - /// - /// The created `pollable` is a child resource of the `output-stream`. - /// Implementations may trap if the `output-stream` is dropped before - /// all derived `pollable`s created with this function are dropped. - subscribe-to-output-stream: func(this: output-stream) -> pollable + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + blocking-flush: func() -> result<_, write-error> - /// Write zeroes to a stream. - /// - /// this should be used precisely like `write` with the exact same - /// preconditions (must use check-write first), but instead of - /// passing a list of bytes, you simply pass the number of zero-bytes - /// that should be written. - write-zeroes: func( - this: output-stream, - /// The number of zero-bytes to write - len: u64 - ) -> result<_, write-error> + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occured. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + subscribe: func() -> pollable - /// Read from one stream and write to another. - /// - /// This function returns the number of bytes transferred; it may be less - /// than `len`. - /// - /// Unlike other I/O functions, this function blocks until all the data - /// read from the input stream has been written to the output stream. - splice: func( - this: output-stream, - /// The stream to read from - src: input-stream, - /// The number of bytes to splice - len: u64, - ) -> result> + /// Write zeroes to a stream. + /// + /// this should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + write-zeroes: func( + /// The number of zero-bytes to write + len: u64 + ) -> result<_, write-error> - /// Read from one stream and write to another, with blocking. - /// - /// This is similar to `splice`, except that it blocks until at least - /// one byte can be read. - blocking-splice: func( - this: output-stream, - /// The stream to read from - src: input-stream, - /// The number of bytes to splice - len: u64, - ) -> result> + /// Perform a write of up to 4096 zeroes, and then flush the stream. + /// Block until all of these operations are complete, or an error + /// occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with + /// the following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while num_zeroes != 0 { + /// // Wait for the stream to become writable + /// poll-one(pollable); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, num_zeroes); + /// this.write-zeroes(len); // eliding error handling + /// num_zeroes -= len; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// poll-one(pollable); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + blocking-write-zeroes-and-flush: func( + /// The number of zero-bytes to write + len: u64 + ) -> result<_, write-error> - /// Forward the entire contents of an input stream to an output stream. - /// - /// This function repeatedly reads from the input stream and writes - /// the data to the output stream, until the end of the input stream - /// is reached, or an error is encountered. - /// - /// Unlike other I/O functions, this function blocks until the end - /// of the input stream is seen and all the data has been written to - /// the output stream. - /// - /// This function returns the number of bytes transferred, and the status of - /// the output stream. - forward: func( - this: output-stream, - /// The stream to read from - src: input-stream - ) -> result> + /// Read from one stream and write to another. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + /// + /// Unlike other I/O functions, this function blocks until all the data + /// read from the input stream has been written to the output stream. + splice: func( + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result> + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until at least + /// one byte can be read. + blocking-splice: func( + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result> - /// Dispose of the specified `output-stream`, after which it may no longer - /// be used. - /// Implementations may trap if this `output-stream` is dropped while - /// child `pollable` resources are still alive. - /// After this `output-stream` is dropped, implementations may report any - /// corresponding `input-stream` has `stream-state.closed`. - drop-output-stream: func(this: output-stream) + /// Forward the entire contents of an input stream to an output stream. + /// + /// This function repeatedly reads from the input stream and writes + /// the data to the output stream, until the end of the input stream + /// is reached, or an error is encountered. + /// + /// Unlike other I/O functions, this function blocks until the end + /// of the input stream is seen and all the data has been written to + /// the output stream. + /// + /// This function returns the number of bytes transferred, and the status of + /// the output stream. + forward: func( + /// The stream to read from + src: input-stream + ) -> result> + } } diff --git a/crates/wasi-http/wit/deps/io/world.wit b/crates/wasi-http/wit/deps/io/world.wit new file mode 100644 index 000000000000..8738dba756d9 --- /dev/null +++ b/crates/wasi-http/wit/deps/io/world.wit @@ -0,0 +1,6 @@ +package wasi:io + +world imports { + import streams + import poll +} diff --git a/crates/wasi-http/wit/deps/logging/world.wit b/crates/wasi-http/wit/deps/logging/world.wit new file mode 100644 index 000000000000..7d49acfaddaa --- /dev/null +++ b/crates/wasi-http/wit/deps/logging/world.wit @@ -0,0 +1,5 @@ +package wasi:logging + +world imports { + import logging +} diff --git a/crates/wasi-http/wit/deps/poll/poll.wit b/crates/wasi-http/wit/deps/poll/poll.wit deleted file mode 100644 index a6334c5570fc..000000000000 --- a/crates/wasi-http/wit/deps/poll/poll.wit +++ /dev/null @@ -1,39 +0,0 @@ -package wasi:poll - -/// A poll API intended to let users wait for I/O events on multiple handles -/// at once. -interface poll { - /// A "pollable" handle. - /// - /// This is conceptually represents a `stream<_, _>`, or in other words, - /// a stream that one can wait on, repeatedly, but which does not itself - /// produce any data. It's temporary scaffolding until component-model's - /// async features are ready. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// `pollable` lifetimes are not automatically managed. Users must ensure - /// that they do not outlive the resource they reference. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type pollable = u32 - - /// Dispose of the specified `pollable`, after which it may no longer - /// be used. - drop-pollable: func(this: pollable) - - /// Poll for completion on a set of pollables. - /// - /// The "oneoff" in the name refers to the fact that this function must do a - /// linear scan through the entire list of subscriptions, which may be - /// inefficient if the number is large and the same subscriptions are used - /// many times. In the future, this is expected to be obsoleted by the - /// component model async proposal, which will include a scalable waiting - /// facility. - /// - /// The result list is the same length as the argument - /// list, and indicates the readiness of each corresponding - /// element in that / list, with true indicating ready. - poll-oneoff: func(in: list) -> list -} diff --git a/crates/wasi-http/wit/deps/random/random.wit b/crates/wasi-http/wit/deps/random/random.wit index f2bd6358c139..2a282dab7eb9 100644 --- a/crates/wasi-http/wit/deps/random/random.wit +++ b/crates/wasi-http/wit/deps/random/random.wit @@ -1,25 +1,25 @@ -package wasi:random - /// WASI Random is a random data API. /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. interface random { - /// Return `len` cryptographically-secure pseudo-random bytes. + /// Return `len` cryptographically-secure random or pseudo-random bytes. /// - /// This function must produce data from an adequately seeded - /// cryptographically-secure pseudo-random number generator (CSPRNG), so it - /// must not block, from the perspective of the calling program, and the - /// returned data is always unpredictable. + /// This function must produce data at least as cryptographically secure and + /// fast as an adequately seeded cryptographically-secure pseudo-random + /// number generator (CSPRNG). It must not block, from the perspective of + /// the calling program, under any circumstances, including on the first + /// request and on requests for numbers of bytes. The returned data must + /// always be unpredictable. /// - /// This function must always return fresh pseudo-random data. Deterministic - /// environments must omit this function, rather than implementing it with - /// deterministic data. + /// This function must always return fresh data. Deterministic environments + /// must omit this function, rather than implementing it with deterministic + /// data. get-random-bytes: func(len: u64) -> list - /// Return a cryptographically-secure pseudo-random `u64` value. + /// Return a cryptographically-secure random or pseudo-random `u64` value. /// - /// This function returns the same type of pseudo-random data as - /// `get-random-bytes`, represented as a `u64`. + /// This function returns the same type of data as `get-random-bytes`, + /// represented as a `u64`. get-random-u64: func() -> u64 } diff --git a/crates/wasi-http/wit/deps/random/world.wit b/crates/wasi-http/wit/deps/random/world.wit new file mode 100644 index 000000000000..41dc9ed10353 --- /dev/null +++ b/crates/wasi-http/wit/deps/random/world.wit @@ -0,0 +1,7 @@ +package wasi:random + +world imports { + import random + import insecure + import insecure-seed +} diff --git a/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit index f15d19d037da..f998aae140ab 100644 --- a/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit +++ b/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit @@ -1,6 +1,6 @@ interface ip-name-lookup { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-address, ip-address-family} @@ -34,36 +34,28 @@ interface ip-name-lookup { /// - /// - /// - - resolve-addresses: func(network: network, name: string, address-family: option, include-unavailable: bool) -> result - - - - type resolve-address-stream = u32 - - /// Returns the next address from the resolver. - /// - /// This function should be called multiple times. On each call, it will - /// return the next address in connection order preference. If all - /// addresses have been exhausted, this function returns `none`. - /// After which, you should release the stream with `drop-resolve-address-stream`. - /// - /// This function never returns IPv4-mapped IPv6 addresses. - /// - /// # Typical errors - /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) - /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) - /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) - /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) - resolve-next-address: func(this: resolve-address-stream) -> result, error-code> - - /// Dispose of the specified `resolve-address-stream`, after which it may no longer be used. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-resolve-address-stream: func(this: resolve-address-stream) - - /// Create a `pollable` which will resolve once the stream is ready for I/O. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func(this: resolve-address-stream) -> pollable + resolve-addresses: func(network: borrow, name: string, address-family: option, include-unavailable: bool) -> result + + resource resolve-address-stream { + /// Returns the next address from the resolver. + /// + /// This function should be called multiple times. On each call, it will + /// return the next address in connection order preference. If all + /// addresses have been exhausted, this function returns `none`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + resolve-next-address: func() -> result, error-code> + + /// Create a `pollable` which will resolve once the stream is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable + } } diff --git a/crates/wasi-http/wit/deps/sockets/network.wit b/crates/wasi-http/wit/deps/sockets/network.wit index a198ea8017de..8214eaaf7211 100644 --- a/crates/wasi-http/wit/deps/sockets/network.wit +++ b/crates/wasi-http/wit/deps/sockets/network.wit @@ -1,18 +1,9 @@ -package wasi:sockets interface network { - /// An opaque resource that represents access to (a subset of) the network. + /// An opaque resource that represents access to (a subset of) the network. /// This enables context-based security for networking. /// There is no need for this to map 1:1 to a physical network interface. - /// - /// FYI, In the future this will be replaced by handle types. - type network = u32 - - /// Dispose of the specified `network`, after which it may no longer be used. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-network: func(this: network) - + resource network /// Error codes. /// diff --git a/crates/wasi-http/wit/deps/sockets/tcp.wit b/crates/wasi-http/wit/deps/sockets/tcp.wit index 3922769b308e..175626cc7620 100644 --- a/crates/wasi-http/wit/deps/sockets/tcp.wit +++ b/crates/wasi-http/wit/deps/sockets/tcp.wit @@ -1,13 +1,9 @@ interface tcp { use wasi:io/streams.{input-stream, output-stream} - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-socket-address, ip-address-family} - /// A TCP socket handle. - type tcp-socket = u32 - - enum shutdown-type { /// Similar to `SHUT_RD` in POSIX. receive, @@ -20,249 +16,234 @@ interface tcp { } - /// Bind the socket to a specific network on the provided IP address and port. - /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// - /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will - /// implicitly bind the socket. - /// - /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - /// # Typical `start` errors - /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) - /// - `already-bound`: The socket is already bound. (EINVAL) - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - /// - /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) - /// - `address-in-use`: Address is already in use. (EADDRINUSE) - /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) - /// - `not-in-progress`: A `bind` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-bind: func(this: tcp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> - finish-bind: func(this: tcp-socket) -> result<_, error-code> - - /// Connect to a remote endpoint. - /// - /// On success: - /// - the socket is transitioned into the Connection state - /// - a pair of streams is returned that can be used to read & write to the connection - /// - /// # Typical `start` errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) - /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. - /// - `already-connected`: The socket is already in the Connection state. (EISCONN) - /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - /// - /// # Typical `finish` errors - /// - `timeout`: Connection timed out. (ETIMEDOUT) - /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) - /// - `connection-reset`: The connection was reset. (ECONNRESET) - /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) - /// - `not-in-progress`: A `connect` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-connect: func(this: tcp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> - /// Note: the returned `input-stream` and `output-stream` are child - /// resources of the `tcp-socket`. Implementations may trap if the - /// `tcp-socket` is dropped before both of these streams are dropped. - finish-connect: func(this: tcp-socket) -> result, error-code> - - /// Start listening for new connections. - /// - /// Transitions the socket into the Listener state. - /// - /// Unlike POSIX: - /// - this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - the socket must already be explicitly bound. - /// - /// # Typical `start` errors - /// - `not-bound`: The socket is not bound to any local address. (EDESTADDRREQ) - /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) - /// - `already-listening`: The socket is already in the Listener state. - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) - /// - /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) - /// - `not-in-progress`: A `listen` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-listen: func(this: tcp-socket) -> result<_, error-code> - finish-listen: func(this: tcp-socket) -> result<_, error-code> - - /// Accept a new client socket. - /// - /// The returned socket is bound and in the Connection state. - /// - /// On success, this function returns the newly accepted client socket along with - /// a pair of streams that can be used to read & write to the connection. - /// - /// Note: the returned `input-stream` and `output-stream` are child - /// resources of the returned `tcp-socket`. Implementations may trap if the - /// `tcp-socket` is dropped before its child streams are dropped. - /// - /// # Typical errors - /// - `not-listening`: Socket is not in the Listener state. (EINVAL) - /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) - /// - /// Host implementations must skip over transient errors returned by the native accept syscall. - /// - /// # References - /// - - /// - - /// - - /// - - accept: func(this: tcp-socket) -> result, error-code> - - /// Get the bound local address. - /// - /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. - /// - /// # References - /// - - /// - - /// - - /// - - local-address: func(this: tcp-socket) -> result - - /// Get the bound remote address. - /// - /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - remote-address: func(this: tcp-socket) -> result - - /// Whether this is a IPv4 or IPv6 socket. - /// - /// Equivalent to the SO_DOMAIN socket option. - address-family: func(this: tcp-socket) -> ip-address-family + /// A TCP socket handle. + resource tcp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will + /// implicitly bind the socket. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + /// - `already-bound`: The socket is already bound. (EINVAL) + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code> + finish-bind: func() -> result<_, error-code> + + /// Connect to a remote endpoint. + /// + /// On success: + /// - the socket is transitioned into the Connection state + /// - a pair of streams is returned that can be used to read & write to the connection + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `already-connected`: The socket is already in the Connection state. (EISCONN) + /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A `connect` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code> + finish-connect: func() -> result, error-code> + + /// Start listening for new connections. + /// + /// Transitions the socket into the Listener state. + /// + /// Unlike POSIX: + /// - this function is async. This enables interactive WASI hosts to inject permission prompts. + /// - the socket must already be explicitly bound. + /// + /// # Typical `start` errors + /// - `not-bound`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) + /// - `already-listening`: The socket is already in the Listener state. + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A `listen` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-listen: func() -> result<_, error-code> + finish-listen: func() -> result<_, error-code> + + /// Accept a new client socket. + /// + /// The returned socket is bound and in the Connection state. + /// + /// On success, this function returns the newly accepted client socket along with + /// a pair of streams that can be used to read & write to the connection. + /// + /// # Typical errors + /// - `not-listening`: Socket is not in the Listener state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// + /// Host implementations must skip over transient errors returned by the native accept syscall. + /// + /// # References + /// - + /// - + /// - + /// - + accept: func() -> result, error-code> + + /// Get the bound local address. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func() -> result + + /// Get the bound remote address. + /// + /// # Typical errors + /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func() -> result + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func() -> ip-address-family - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. - /// - `already-bound`: (set) The socket is already bound. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - ipv6-only: func(this: tcp-socket) -> result - set-ipv6-only: func(this: tcp-socket, value: bool) -> result<_, error-code> - - /// Hints the desired listen queue size. Implementations are free to ignore this. - /// - /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - set-listen-backlog-size: func(this: tcp-socket, value: u64) -> result<_, error-code> - - /// Equivalent to the SO_KEEPALIVE socket option. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - keep-alive: func(this: tcp-socket) -> result - set-keep-alive: func(this: tcp-socket, value: bool) -> result<_, error-code> - - /// Equivalent to the TCP_NODELAY socket option. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - no-delay: func(this: tcp-socket) -> result - set-no-delay: func(this: tcp-socket, value: bool) -> result<_, error-code> + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// + /// Equivalent to the IPV6_V6ONLY socket option. + /// + /// # Typical errors + /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + ipv6-only: func() -> result + set-ipv6-only: func(value: bool) -> result<_, error-code> + + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + set-listen-backlog-size: func(value: u64) -> result<_, error-code> + + /// Equivalent to the SO_KEEPALIVE socket option. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + keep-alive: func() -> result + set-keep-alive: func(value: bool) -> result<_, error-code> + + /// Equivalent to the TCP_NODELAY socket option. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + no-delay: func() -> result + set-no-delay: func(value: bool) -> result<_, error-code> - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// - /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `already-listening`: (set) The socket is already in the Listener state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - unicast-hop-limit: func(this: tcp-socket) -> result - set-unicast-hop-limit: func(this: tcp-socket, value: u8) -> result<_, error-code> - - /// The kernel buffer space reserved for sends/receives on this socket. - /// - /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. - /// In other words, after setting a value, reading the same setting back may return a different value. - /// - /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of - /// actual data to be sent/received by the application, because the kernel might also use the buffer space - /// for internal metadata structures. - /// - /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// - /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `already-listening`: (set) The socket is already in the Listener state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - receive-buffer-size: func(this: tcp-socket) -> result - set-receive-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> - send-buffer-size: func(this: tcp-socket) -> result - set-send-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> - - /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// - /// The created `pollable` is a child resource of the `tcp-socket`. - /// Implementations may trap if the `tcp-socket` is dropped before all - /// derived `pollable`s created with this function are dropped. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func(this: tcp-socket) -> pollable - - /// Initiate a graceful shutdown. - /// - /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read - /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. - /// Any data still in the receive queue at time of calling `shutdown` will be discarded. - /// - send: the socket is not expecting to send any more data to the peer. All subsequent write - /// operations on the `output-stream` associated with this socket will return an error. - /// - both: same effect as receive & send combined. - /// - /// The shutdown function does not close (drop) the socket. - /// - /// # Typical errors - /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - shutdown: func(this: tcp-socket, shutdown-type: shutdown-type) -> result<_, error-code> - - /// Dispose of the specified `tcp-socket`, after which it may no longer be used. - /// - /// Similar to the POSIX `close` function. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-tcp-socket: func(this: tcp-socket) + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `already-listening`: (set) The socket is already in the Listener state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + unicast-hop-limit: func() -> result + set-unicast-hop-limit: func(value: u8) -> result<_, error-code> + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. + /// In other words, after setting a value, reading the same setting back may return a different value. + /// + /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of + /// actual data to be sent/received by the application, because the kernel might also use the buffer space + /// for internal metadata structures. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `already-listening`: (set) The socket is already in the Listener state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + receive-buffer-size: func() -> result + set-receive-buffer-size: func(value: u64) -> result<_, error-code> + send-buffer-size: func() -> result + set-send-buffer-size: func(value: u64) -> result<_, error-code> + + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable + + /// Initiate a graceful shutdown. + /// + /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read + /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. + /// Any data still in the receive queue at time of calling `shutdown` will be discarded. + /// - send: the socket is not expecting to send any more data to the peer. All subsequent write + /// operations on the `output-stream` associated with this socket will return an error. + /// - both: same effect as receive & send combined. + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code> + } } diff --git a/crates/wasi-http/wit/deps/sockets/udp.wit b/crates/wasi-http/wit/deps/sockets/udp.wit index 700b9e247692..01e5b95b97b7 100644 --- a/crates/wasi-http/wit/deps/sockets/udp.wit +++ b/crates/wasi-http/wit/deps/sockets/udp.wit @@ -1,13 +1,9 @@ interface udp { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-socket-address, ip-address-family} - /// A UDP socket handle. - type udp-socket = u32 - - record datagram { data: list, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. remote-address: ip-socket-address, @@ -22,199 +18,197 @@ interface udp { - /// Bind the socket to a specific network on the provided IP address and port. - /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// - /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. - /// - /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - /// # Typical `start` errors - /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) - /// - `already-bound`: The socket is already bound. (EINVAL) - /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) - /// - /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) - /// - `address-in-use`: Address is already in use. (EADDRINUSE) - /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) - /// - `not-in-progress`: A `bind` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-bind: func(this: udp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> - finish-bind: func(this: udp-socket) -> result<_, error-code> - - /// Set the destination address. - /// - /// The local-address is updated based on the best network path to `remote-address`. - /// - /// When a destination address is set: - /// - all receive operations will only return datagrams sent from the provided `remote-address`. - /// - the `send` function can only be used to send to this destination. - /// - /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". - /// - /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - /// # Typical `start` errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. - /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) - /// - /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) - /// - `not-in-progress`: A `connect` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-connect: func(this: udp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> - finish-connect: func(this: udp-socket) -> result<_, error-code> - - /// Receive messages on the socket. - /// - /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. - /// The returned list may contain fewer elements than requested, but never more. - /// If `max-results` is 0, this function returns successfully with an empty list. - /// - /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. (EINVAL) - /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - /// - - /// - - /// - - /// - - receive: func(this: udp-socket, max-results: u64) -> result, error-code> - - /// Send messages on the socket. - /// - /// This function attempts to send all provided `datagrams` on the socket without blocking and - /// returns how many messages were actually sent (or queued for sending). - /// - /// This function semantically behaves the same as iterating the `datagrams` list and sequentially - /// sending each individual datagram until either the end of the list has been reached or the first error occurred. - /// If at least one datagram has been sent successfully, this function never returns an error. - /// - /// If the input list is empty, the function returns `ok(0)`. - /// - /// The remote address option is required. To send a message to the "connected" peer, - /// call `remote-address` to get their address. - /// - /// # Typical errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) - /// - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. - /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) - /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - /// - - /// - - /// - - /// - - send: func(this: udp-socket, datagrams: list) -> result - - /// Get the current bound address. - /// - /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. - /// - /// # References - /// - - /// - - /// - - /// - - local-address: func(this: udp-socket) -> result - - /// Get the address set with `connect`. - /// - /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - remote-address: func(this: udp-socket) -> result - - /// Whether this is a IPv4 or IPv6 socket. - /// - /// Equivalent to the SO_DOMAIN socket option. - address-family: func(this: udp-socket) -> ip-address-family - - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. - /// - `already-bound`: (set) The socket is already bound. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - ipv6-only: func(this: udp-socket) -> result - set-ipv6-only: func(this: udp-socket, value: bool) -> result<_, error-code> - - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - unicast-hop-limit: func(this: udp-socket) -> result - set-unicast-hop-limit: func(this: udp-socket, value: u8) -> result<_, error-code> - - /// The kernel buffer space reserved for sends/receives on this socket. - /// - /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. - /// In other words, after setting a value, reading the same setting back may return a different value. - /// - /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of - /// actual data to be sent/received by the application, because the kernel might also use the buffer space - /// for internal metadata structures. - /// - /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - receive-buffer-size: func(this: udp-socket) -> result - set-receive-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> - send-buffer-size: func(this: udp-socket) -> result - set-send-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> - - /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func(this: udp-socket) -> pollable - - /// Dispose of the specified `udp-socket`, after which it may no longer be used. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-udp-socket: func(this: udp-socket) + /// A UDP socket handle. + resource udp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + /// - `already-bound`: The socket is already bound. (EINVAL) + /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code> + finish-bind: func() -> result<_, error-code> + + /// Set the destination address. + /// + /// The local-address is updated based on the best network path to `remote-address`. + /// + /// When a destination address is set: + /// - all receive operations will only return datagrams sent from the provided `remote-address`. + /// - the `send` function can only be used to send to this destination. + /// + /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A `connect` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code> + finish-connect: func() -> result<_, error-code> + + /// Receive messages on the socket. + /// + /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. + /// The returned list may contain fewer elements than requested, but never more. + /// If `max-results` is 0, this function returns successfully with an empty list. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. (EINVAL) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + receive: func(max-results: u64) -> result, error-code> + + /// Send messages on the socket. + /// + /// This function attempts to send all provided `datagrams` on the socket without blocking and + /// returns how many messages were actually sent (or queued for sending). + /// + /// This function semantically behaves the same as iterating the `datagrams` list and sequentially + /// sending each individual datagram until either the end of the list has been reached or the first error occurred. + /// If at least one datagram has been sent successfully, this function never returns an error. + /// + /// If the input list is empty, the function returns `ok(0)`. + /// + /// The remote address option is required. To send a message to the "connected" peer, + /// call `remote-address` to get their address. + /// + /// # Typical errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) + /// - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. + /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + send: func(datagrams: list) -> result + + /// Get the current bound address. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func() -> result + + /// Get the address set with `connect`. + /// + /// # Typical errors + /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func() -> result + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func() -> ip-address-family + + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// + /// Equivalent to the IPV6_V6ONLY socket option. + /// + /// # Typical errors + /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + ipv6-only: func() -> result + set-ipv6-only: func(value: bool) -> result<_, error-code> + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + unicast-hop-limit: func() -> result + set-unicast-hop-limit: func(value: u8) -> result<_, error-code> + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. + /// In other words, after setting a value, reading the same setting back may return a different value. + /// + /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of + /// actual data to be sent/received by the application, because the kernel might also use the buffer space + /// for internal metadata structures. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + receive-buffer-size: func() -> result + set-receive-buffer-size: func(value: u64) -> result<_, error-code> + send-buffer-size: func() -> result + set-send-buffer-size: func(value: u64) -> result<_, error-code> + + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable + } } diff --git a/crates/wasi-http/wit/deps/sockets/world.wit b/crates/wasi-http/wit/deps/sockets/world.wit new file mode 100644 index 000000000000..12f3c2868177 --- /dev/null +++ b/crates/wasi-http/wit/deps/sockets/world.wit @@ -0,0 +1,11 @@ +package wasi:sockets + +world imports { + import instance-network + import network + import udp + import udp-create-socket + import tcp + import tcp-create-socket + import ip-name-lookup +} diff --git a/crates/wasi-http/wit/main.wit b/crates/wasi-http/wit/main.wit index 753770ad22ab..739e1bd4ac48 100644 --- a/crates/wasi-http/wit/main.wit +++ b/crates/wasi-http/wit/main.wit @@ -18,7 +18,7 @@ world preview1-adapter-reactor { import wasi:random/random import wasi:random/insecure import wasi:random/insecure-seed - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/exit diff --git a/crates/wasi-http/wit/test.wit b/crates/wasi-http/wit/test.wit index 4543cb194af1..0b6bd28e997d 100644 --- a/crates/wasi-http/wit/test.wit +++ b/crates/wasi-http/wit/test.wit @@ -2,6 +2,7 @@ world test-reactor { import wasi:cli/environment + import wasi:io/poll import wasi:io/streams import wasi:filesystem/types import wasi:filesystem/preopens @@ -19,7 +20,7 @@ world test-reactor { } world test-command { - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/stdin @@ -28,7 +29,7 @@ world test-command { } world test-command-with-sockets { - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/stdin diff --git a/crates/wasi-preview1-component-adapter/build.rs b/crates/wasi-preview1-component-adapter/build.rs index a901cc7037d6..09f56df1526c 100644 --- a/crates/wasi-preview1-component-adapter/build.rs +++ b/crates/wasi-preview1-component-adapter/build.rs @@ -84,8 +84,6 @@ fn build_raw_intrinsics() -> Vec { funcs.function(1); funcs.function(0); funcs.function(1); - funcs.function(0); - funcs.function(1); module.section(&funcs); // Declare the globals. @@ -106,14 +104,6 @@ fn build_raw_intrinsics() -> Vec { }, &ConstExpr::i32_const(0), ); - // stderr_stream - globals.global( - GlobalType { - val_type: ValType::I32, - mutable: true, - }, - &ConstExpr::i32_const(123), - ); module.section(&globals); // Here the `code` section is defined. This is tricky because an offset is @@ -125,7 +115,7 @@ fn build_raw_intrinsics() -> Vec { // code section. let mut code = Vec::new(); - 6u32.encode(&mut code); // number of functions + 4u32.encode(&mut code); // number of functions let global_get = 0x23; let global_set = 0x24; @@ -152,8 +142,6 @@ fn build_raw_intrinsics() -> Vec { let internal_state_ptr_ref2 = encode(&mut code, 0, global_set); // set_state_ptr let allocation_state_ref1 = encode(&mut code, 1, global_get); // get_allocation_state let allocation_state_ref2 = encode(&mut code, 1, global_set); // set_allocation_state - let stderr_stream_ref1 = encode(&mut code, 2, global_get); // get_stderr_stream - let stderr_stream_ref2 = encode(&mut code, 2, global_set); // set_stderr_stream module.section(&RawSection { id: SectionId::Code as u8, @@ -171,7 +159,7 @@ fn build_raw_intrinsics() -> Vec { linking.push(0x08); // `WASM_SYMBOL_TABLE` let mut subsection = Vec::new(); - 9u32.encode(&mut subsection); // 9 symbols (6 functions + 3 globals) + 6u32.encode(&mut subsection); // 6 symbols (4 functions + 2 globals) subsection.push(0x00); // SYMTAB_FUNCTION 0x00.encode(&mut subsection); // flags @@ -193,16 +181,6 @@ fn build_raw_intrinsics() -> Vec { 3u32.encode(&mut subsection); // function index "set_allocation_state".encode(&mut subsection); // symbol name - subsection.push(0x00); // SYMTAB_FUNCTION - 0x00.encode(&mut subsection); // flags - 4u32.encode(&mut subsection); // function index - "get_stderr_stream".encode(&mut subsection); // symbol name - - subsection.push(0x00); // SYMTAB_FUNCTION - 0x00.encode(&mut subsection); // flags - 5u32.encode(&mut subsection); // function index - "set_stderr_stream".encode(&mut subsection); // symbol name - subsection.push(0x02); // SYMTAB_GLOBAL 0x02.encode(&mut subsection); // flags (WASM_SYM_BINDING_LOCAL) 0u32.encode(&mut subsection); // global index @@ -213,11 +191,6 @@ fn build_raw_intrinsics() -> Vec { 1u32.encode(&mut subsection); // global index "allocation_state".encode(&mut subsection); // symbol name - subsection.push(0x02); // SYMTAB_GLOBAL - 0x00.encode(&mut subsection); // flags - 2u32.encode(&mut subsection); // global index - "stderr_stream".encode(&mut subsection); // symbol name - subsection.encode(&mut linking); module.section(&CustomSection { name: "linking".into(), @@ -230,31 +203,23 @@ fn build_raw_intrinsics() -> Vec { { let mut reloc = Vec::new(); 3u32.encode(&mut reloc); // target section (code is the 4th section, 3 when 0-indexed) - 6u32.encode(&mut reloc); // 6 relocations + 4u32.encode(&mut reloc); // 4 relocations reloc.push(0x07); // R_WASM_GLOBAL_INDEX_LEB internal_state_ptr_ref1.encode(&mut reloc); // offset - 6u32.encode(&mut reloc); // symbol index + 4u32.encode(&mut reloc); // symbol index reloc.push(0x07); // R_WASM_GLOBAL_INDEX_LEB internal_state_ptr_ref2.encode(&mut reloc); // offset - 6u32.encode(&mut reloc); // symbol index + 4u32.encode(&mut reloc); // symbol index reloc.push(0x07); // R_WASM_GLOBAL_INDEX_LEB allocation_state_ref1.encode(&mut reloc); // offset - 7u32.encode(&mut reloc); // symbol index + 5u32.encode(&mut reloc); // symbol index reloc.push(0x07); // R_WASM_GLOBAL_INDEX_LEB allocation_state_ref2.encode(&mut reloc); // offset - 7u32.encode(&mut reloc); // symbol index - - reloc.push(0x07); // R_WASM_GLOBAL_INDEX_LEB - stderr_stream_ref1.encode(&mut reloc); // offset - 8u32.encode(&mut reloc); // symbol index - - reloc.push(0x07); // R_WASM_GLOBAL_INDEX_LEB - stderr_stream_ref2.encode(&mut reloc); // offset - 8u32.encode(&mut reloc); // symbol index + 5u32.encode(&mut reloc); // symbol index module.section(&CustomSection { name: "reloc.CODE".into(), diff --git a/crates/wasi-preview1-component-adapter/src/descriptors.rs b/crates/wasi-preview1-component-adapter/src/descriptors.rs index 6045358bde36..354de6f96801 100644 --- a/crates/wasi-preview1-component-adapter/src/descriptors.rs +++ b/crates/wasi-preview1-component-adapter/src/descriptors.rs @@ -1,13 +1,10 @@ use crate::bindings::wasi::cli::{ - stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin, - terminal_stdout, + stderr, stdin, stdout, terminal_stderr, terminal_stdin, terminal_stdout, }; use crate::bindings::wasi::filesystem::types as filesystem; -use crate::bindings::wasi::io::streams::{self, InputStream, OutputStream}; +use crate::bindings::wasi::io::streams::{InputStream, OutputStream}; use crate::bindings::wasi::sockets::tcp; -use crate::{ - set_stderr_stream, BlockingMode, BumpArena, File, ImportAlloc, TrappingUnwrap, WasmStr, -}; +use crate::{BlockingMode, BumpArena, File, ImportAlloc, TrappingUnwrap, WasmStr}; use core::cell::{Cell, UnsafeCell}; use core::mem::MaybeUninit; use wasi::{Errno, Fd}; @@ -24,36 +21,15 @@ pub enum Descriptor { Streams(Streams), } -impl Drop for Descriptor { - fn drop(&mut self) { - match self { - Descriptor::Streams(stream) => { - if let Some(input) = stream.input.get() { - streams::drop_input_stream(input); - } - if let Some(output) = stream.output.get() { - streams::drop_output_stream(output); - } - match &stream.type_ { - StreamType::File(file) => filesystem::drop_descriptor(file.fd), - StreamType::Socket(_) => unreachable!(), - StreamType::Stdio(_) => {} - } - } - Descriptor::Closed(_) => {} - } - } -} - /// Input and/or output wasi-streams, along with a stream type that /// identifies what kind of stream they are and possibly supporting /// type-specific operations like seeking. pub struct Streams { /// The input stream, if present. - pub input: Cell>, + pub input: UnsafeCell>, /// The output stream, if present. - pub output: Cell>, + pub output: UnsafeCell>, /// Information about the source of the stream. pub type_: StreamType, @@ -61,59 +37,69 @@ pub struct Streams { impl Streams { /// Return the input stream, initializing it on the fly if needed. - pub fn get_read_stream(&self) -> Result { - match &self.input.get() { - Some(wasi_stream) => Ok(*wasi_stream), - None => match &self.type_ { - // For directories, preview 1 behavior was to return ERRNO_BADF on attempts to read - // or write. - StreamType::File(File { - descriptor_type: filesystem::DescriptorType::Directory, - .. - }) => Err(wasi::ERRNO_BADF), - // For files, we may have adjusted the position for seeking, so - // create a new stream. - StreamType::File(file) => { - let input = filesystem::read_via_stream(file.fd, file.position.get())?; - self.input.set(Some(input)); - Ok(input) + pub fn get_read_stream(&self) -> Result<&InputStream, Errno> { + match unsafe { &*self.input.get() } { + Some(wasi_stream) => Ok(wasi_stream), + None => { + let input = match &self.type_ { + // For directories, preview 1 behavior was to return ERRNO_BADF on attempts to read + // or write. + StreamType::File(File { + descriptor_type: filesystem::DescriptorType::Directory, + .. + }) => return Err(wasi::ERRNO_BADF), + // For files, we may have adjusted the position for seeking, so + // create a new stream. + StreamType::File(file) => { + let input = file.fd.read_via_stream(file.position.get())?; + input + } + _ => return Err(wasi::ERRNO_BADF), + }; + unsafe { + *self.input.get() = Some(input); + Ok((*self.input.get()).as_ref().trapping_unwrap()) } - _ => Err(wasi::ERRNO_BADF), - }, + } } } /// Return the output stream, initializing it on the fly if needed. - pub fn get_write_stream(&self) -> Result { - match &self.output.get() { - Some(wasi_stream) => Ok(*wasi_stream), - None => match &self.type_ { - // For directories, preview 1 behavior was to return ERRNO_BADF on attempts to read - // or write. - StreamType::File(File { - descriptor_type: filesystem::DescriptorType::Directory, - .. - }) => Err(wasi::ERRNO_BADF), - // For files, we may have adjusted the position for seeking, so - // create a new stream. - StreamType::File(file) => { - let output = if file.append { - filesystem::append_via_stream(file.fd)? - } else { - filesystem::write_via_stream(file.fd, file.position.get())? - }; - self.output.set(Some(output)); - Ok(output) + pub fn get_write_stream(&self) -> Result<&OutputStream, Errno> { + match unsafe { &*self.output.get() } { + Some(wasi_stream) => Ok(wasi_stream), + None => { + let output = match &self.type_ { + // For directories, preview 1 behavior was to return ERRNO_BADF on attempts to read + // or write. + StreamType::File(File { + descriptor_type: filesystem::DescriptorType::Directory, + .. + }) => return Err(wasi::ERRNO_BADF), + // For files, we may have adjusted the position for seeking, so + // create a new stream. + StreamType::File(file) => { + let output = if file.append { + file.fd.append_via_stream()? + } else { + file.fd.write_via_stream(file.position.get())? + }; + output + } + _ => return Err(wasi::ERRNO_BADF), + }; + unsafe { + *self.output.get() = Some(output); + Ok((*self.output.get()).as_ref().trapping_unwrap()) } - _ => Err(wasi::ERRNO_BADF), - }, + } } } } #[allow(dead_code)] // until Socket is implemented pub enum StreamType { - /// Stream is used for implementing stdio. + /// Streams for implementing stdio. Stdio(IsATTY), /// Streaming data with a file. @@ -161,47 +147,34 @@ impl Descriptors { preopens: Cell::new(None), }; - let stdin = stdin::get_stdin(); let stdin_isatty = match terminal_stdin::get_terminal_stdin() { - Some(t) => { - terminal_input::drop_terminal_input(t); - IsATTY::Yes - } + Some(t) => IsATTY::Yes, None => IsATTY::No, }; - let stdout = stdout::get_stdout(); let stdout_isatty = match terminal_stdout::get_terminal_stdout() { - Some(t) => { - terminal_output::drop_terminal_output(t); - IsATTY::Yes - } + Some(t) => IsATTY::Yes, None => IsATTY::No, }; - let stderr = stderr::get_stderr(); - unsafe { set_stderr_stream(stderr) }; let stderr_isatty = match terminal_stderr::get_terminal_stderr() { - Some(t) => { - terminal_output::drop_terminal_output(t); - IsATTY::Yes - } + Some(t) => IsATTY::Yes, None => IsATTY::No, }; d.push(Descriptor::Streams(Streams { - input: Cell::new(Some(stdin)), - output: Cell::new(None), + input: UnsafeCell::new(Some(stdin::get_stdin())), + output: UnsafeCell::new(None), type_: StreamType::Stdio(stdin_isatty), })) .trapping_unwrap(); d.push(Descriptor::Streams(Streams { - input: Cell::new(None), - output: Cell::new(Some(stdout)), + input: UnsafeCell::new(None), + output: UnsafeCell::new(Some(stdout::get_stdout())), type_: StreamType::Stdio(stdout_isatty), })) .trapping_unwrap(); d.push(Descriptor::Streams(Streams { - input: Cell::new(None), - output: Cell::new(Some(stderr)), + input: UnsafeCell::new(None), + output: UnsafeCell::new(Some(stderr::get_stderr())), type_: StreamType::Stdio(stderr_isatty), })) .trapping_unwrap(); @@ -224,14 +197,18 @@ impl Descriptors { std::slice::from_raw_parts(list.base, list.len) }; for preopen in preopens { + // Acquire ownership of the descriptor, leaving the rest of the + // `Preopen` struct in place. + let descriptor = unsafe { preopen.descriptor.assume_init_read() }; // Expectation is that the descriptor index is initialized with // stdio (0,1,2) and no others, so that preopens are 3.. + let descriptor_type = descriptor.get_type().trapping_unwrap(); d.push(Descriptor::Streams(Streams { - input: Cell::new(None), - output: Cell::new(None), + input: UnsafeCell::new(None), + output: UnsafeCell::new(None), type_: StreamType::File(File { - fd: preopen.descriptor, - descriptor_type: filesystem::get_type(preopen.descriptor).trapping_unwrap(), + fd: descriptor, + descriptor_type, position: Cell::new(0), append: false, blocking_mode: BlockingMode::Blocking, @@ -387,12 +364,12 @@ impl Descriptors { } #[allow(dead_code)] // until Socket is implemented - pub fn get_socket(&self, fd: Fd) -> Result { + pub fn get_socket(&self, fd: Fd) -> Result<&tcp::TcpSocket, Errno> { match self.get(fd)? { Descriptor::Streams(Streams { type_: StreamType::Socket(socket), .. - }) => Ok(*socket), + }) => Ok(&*socket), Descriptor::Closed(_) => Err(wasi::ERRNO_BADF), _ => Err(wasi::ERRNO_INVAL), } @@ -435,14 +412,14 @@ impl Descriptors { self.get_stream_with_error(fd, wasi::ERRNO_SPIPE) } - pub fn get_read_stream(&self, fd: Fd) -> Result { + pub fn get_read_stream(&self, fd: Fd) -> Result<&InputStream, Errno> { match self.get(fd)? { Descriptor::Streams(streams) => streams.get_read_stream(), Descriptor::Closed(_) => Err(wasi::ERRNO_BADF), } } - pub fn get_write_stream(&self, fd: Fd) -> Result { + pub fn get_write_stream(&self, fd: Fd) -> Result<&OutputStream, Errno> { match self.get(fd)? { Descriptor::Streams(streams) => streams.get_write_stream(), Descriptor::Closed(_) => Err(wasi::ERRNO_BADF), @@ -452,7 +429,9 @@ impl Descriptors { #[repr(C)] pub struct Preopen { - pub descriptor: u32, + /// This is `MaybeUninit` because we take ownership of the `Descriptor` to + /// put it in our own table. + pub descriptor: MaybeUninit, pub path: WasmStr, } diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs index d75e8caa5ef0..e5096ce21633 100644 --- a/crates/wasi-preview1-component-adapter/src/lib.rs +++ b/crates/wasi-preview1-component-adapter/src/lib.rs @@ -1,18 +1,15 @@ -#![allow(unused_variables)] // TODO: remove this when more things are implemented - use crate::bindings::wasi::cli::exit; use crate::bindings::wasi::clocks::{monotonic_clock, wall_clock}; use crate::bindings::wasi::filesystem::types as filesystem; +use crate::bindings::wasi::io::poll; use crate::bindings::wasi::io::streams; -use crate::bindings::wasi::poll::poll; use crate::bindings::wasi::random::random; use crate::bindings::wasi::sockets::network; -use core::cell::{Cell, RefCell, RefMut, UnsafeCell}; +use core::cell::{Cell, UnsafeCell}; use core::cmp::min; use core::ffi::c_void; use core::hint::black_box; use core::mem::{self, align_of, forget, size_of, ManuallyDrop, MaybeUninit}; -use core::ops::{Deref, DerefMut}; use core::ptr::{self, null_mut}; use core::slice; use poll::Pollable; @@ -40,7 +37,7 @@ pub mod bindings { // can't support in these special core-wasm adapters. // Instead, we manually define the bindings for these functions in // terms of raw pointers. - skip: ["run", "get-environment", "poll-oneoff"], + skip: ["run", "get-environment", "poll-list"], }); #[cfg(feature = "reactor")] @@ -55,7 +52,7 @@ pub mod bindings { // can't support in these special core-wasm adapters. // Instead, we manually define the bindings for these functions in // terms of raw pointers. - skip: ["get-environment", "poll-oneoff"], + skip: ["get-environment", "poll-list"], }); } @@ -420,10 +417,11 @@ pub unsafe extern "C" fn fd_advise( _ => return ERRNO_INVAL, }; State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_seekable_file(fd)?; - filesystem::advise(file.fd, offset, len, advice)?; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_seekable_file(fd)?; + file.fd.advise(offset, len, advice)?; + Ok(()) + }) }) } @@ -432,11 +430,12 @@ pub unsafe extern "C" fn fd_advise( #[no_mangle] pub unsafe extern "C" fn fd_allocate(fd: Fd, offset: Filesize, len: Filesize) -> Errno { State::with(|state| { - let ds = state.descriptors(); - // For not-files, fail with BADF - let file = ds.get_file(fd)?; - // For all files, fail with NOTSUP, because this call does not exist in preview 2. - Err(wasi::ERRNO_NOTSUP) + state.with_descriptors(|ds| { + // For not-files, fail with BADF + let file = ds.get_file(fd)?; + // For all files, fail with NOTSUP, because this call does not exist in preview 2. + Err(wasi::ERRNO_NOTSUP) + }) }) } @@ -452,7 +451,7 @@ pub unsafe extern "C" fn fd_close(fd: Fd) -> Errno { drop(state.dirent_cache.stream.replace(None)); } - let desc = state.descriptors_mut().close(fd)?; + let _ = state.with_descriptors_mut(|ds: &mut Descriptors| ds.close(fd))?; Ok(()) }) } @@ -462,10 +461,11 @@ pub unsafe extern "C" fn fd_close(fd: Fd) -> Errno { #[no_mangle] pub unsafe extern "C" fn fd_datasync(fd: Fd) -> Errno { State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_file(fd)?; - filesystem::sync_data(file.fd)?; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_file(fd)?; + file.fd.sync_data()?; + Ok(()) + }) }) } @@ -473,122 +473,126 @@ pub unsafe extern "C" fn fd_datasync(fd: Fd) -> Errno { /// Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. #[no_mangle] pub unsafe extern "C" fn fd_fdstat_get(fd: Fd, stat: *mut Fdstat) -> Errno { - State::with(|state| match state.descriptors().get(fd)? { - Descriptor::Streams(Streams { - type_: StreamType::File(file), - .. - }) => { - let flags = filesystem::get_flags(file.fd)?; - let type_ = filesystem::get_type(file.fd)?; - match type_ { - filesystem::DescriptorType::Directory => { - // Hard-coded set of rights expected by many userlands: - let fs_rights_base = wasi::RIGHTS_PATH_CREATE_DIRECTORY - | wasi::RIGHTS_PATH_CREATE_FILE - | wasi::RIGHTS_PATH_LINK_SOURCE - | wasi::RIGHTS_PATH_LINK_TARGET - | wasi::RIGHTS_PATH_OPEN - | wasi::RIGHTS_FD_READDIR - | wasi::RIGHTS_PATH_READLINK - | wasi::RIGHTS_PATH_RENAME_SOURCE - | wasi::RIGHTS_PATH_RENAME_TARGET - | wasi::RIGHTS_PATH_SYMLINK - | wasi::RIGHTS_PATH_REMOVE_DIRECTORY - | wasi::RIGHTS_PATH_UNLINK_FILE - | wasi::RIGHTS_PATH_FILESTAT_GET - | wasi::RIGHTS_PATH_FILESTAT_SET_TIMES - | wasi::RIGHTS_FD_FILESTAT_GET - | wasi::RIGHTS_FD_FILESTAT_SET_TIMES; - - let fs_rights_inheriting = fs_rights_base - | wasi::RIGHTS_FD_DATASYNC - | wasi::RIGHTS_FD_READ - | wasi::RIGHTS_FD_SEEK - | wasi::RIGHTS_FD_FDSTAT_SET_FLAGS - | wasi::RIGHTS_FD_SYNC - | wasi::RIGHTS_FD_TELL - | wasi::RIGHTS_FD_WRITE - | wasi::RIGHTS_FD_ADVISE - | wasi::RIGHTS_FD_ALLOCATE - | wasi::RIGHTS_FD_FILESTAT_GET - | wasi::RIGHTS_FD_FILESTAT_SET_SIZE - | wasi::RIGHTS_FD_FILESTAT_SET_TIMES - | wasi::RIGHTS_POLL_FD_READWRITE; - - stat.write(Fdstat { - fs_filetype: wasi::FILETYPE_DIRECTORY, - fs_flags: 0, - fs_rights_base, - fs_rights_inheriting, - }); - Ok(()) - } - _ => { - let fs_filetype = type_.into(); + State::with(|state| { + state.with_descriptors(|ds| { + match ds.get(fd)? { + Descriptor::Streams(Streams { + type_: StreamType::File(file), + .. + }) => { + let flags = file.fd.get_flags()?; + let type_ = file.fd.get_type()?; + match type_ { + filesystem::DescriptorType::Directory => { + // Hard-coded set of rights expected by many userlands: + let fs_rights_base = wasi::RIGHTS_PATH_CREATE_DIRECTORY + | wasi::RIGHTS_PATH_CREATE_FILE + | wasi::RIGHTS_PATH_LINK_SOURCE + | wasi::RIGHTS_PATH_LINK_TARGET + | wasi::RIGHTS_PATH_OPEN + | wasi::RIGHTS_FD_READDIR + | wasi::RIGHTS_PATH_READLINK + | wasi::RIGHTS_PATH_RENAME_SOURCE + | wasi::RIGHTS_PATH_RENAME_TARGET + | wasi::RIGHTS_PATH_SYMLINK + | wasi::RIGHTS_PATH_REMOVE_DIRECTORY + | wasi::RIGHTS_PATH_UNLINK_FILE + | wasi::RIGHTS_PATH_FILESTAT_GET + | wasi::RIGHTS_PATH_FILESTAT_SET_TIMES + | wasi::RIGHTS_FD_FILESTAT_GET + | wasi::RIGHTS_FD_FILESTAT_SET_TIMES; + + let fs_rights_inheriting = fs_rights_base + | wasi::RIGHTS_FD_DATASYNC + | wasi::RIGHTS_FD_READ + | wasi::RIGHTS_FD_SEEK + | wasi::RIGHTS_FD_FDSTAT_SET_FLAGS + | wasi::RIGHTS_FD_SYNC + | wasi::RIGHTS_FD_TELL + | wasi::RIGHTS_FD_WRITE + | wasi::RIGHTS_FD_ADVISE + | wasi::RIGHTS_FD_ALLOCATE + | wasi::RIGHTS_FD_FILESTAT_GET + | wasi::RIGHTS_FD_FILESTAT_SET_SIZE + | wasi::RIGHTS_FD_FILESTAT_SET_TIMES + | wasi::RIGHTS_POLL_FD_READWRITE; + + stat.write(Fdstat { + fs_filetype: wasi::FILETYPE_DIRECTORY, + fs_flags: 0, + fs_rights_base, + fs_rights_inheriting, + }); + Ok(()) + } + _ => { + let fs_filetype = type_.into(); - let mut fs_flags = 0; - let mut fs_rights_base = !0; - if !flags.contains(filesystem::DescriptorFlags::READ) { - fs_rights_base &= !RIGHTS_FD_READ; - } - if !flags.contains(filesystem::DescriptorFlags::WRITE) { - fs_rights_base &= !RIGHTS_FD_WRITE; - } - if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) { - fs_flags |= FDFLAGS_DSYNC; - } - if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) { - fs_flags |= FDFLAGS_RSYNC; - } - if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) { - fs_flags |= FDFLAGS_SYNC; + let mut fs_flags = 0; + let mut fs_rights_base = !0; + if !flags.contains(filesystem::DescriptorFlags::READ) { + fs_rights_base &= !RIGHTS_FD_READ; + } + if !flags.contains(filesystem::DescriptorFlags::WRITE) { + fs_rights_base &= !RIGHTS_FD_WRITE; + } + if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) { + fs_flags |= FDFLAGS_DSYNC; + } + if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) { + fs_flags |= FDFLAGS_RSYNC; + } + if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) { + fs_flags |= FDFLAGS_SYNC; + } + if file.append { + fs_flags |= FDFLAGS_APPEND; + } + if matches!(file.blocking_mode, BlockingMode::NonBlocking) { + fs_flags |= FDFLAGS_NONBLOCK; + } + let fs_rights_inheriting = fs_rights_base; + + stat.write(Fdstat { + fs_filetype, + fs_flags, + fs_rights_base, + fs_rights_inheriting, + }); + Ok(()) + } } - if file.append { - fs_flags |= FDFLAGS_APPEND; + } + Descriptor::Streams(Streams { + input, + output, + type_: StreamType::Stdio(isatty), + }) => { + let fs_flags = 0; + let mut fs_rights_base = 0; + if (*input.get()).is_some() { + fs_rights_base |= RIGHTS_FD_READ; } - if matches!(file.blocking_mode, BlockingMode::NonBlocking) { - fs_flags |= FDFLAGS_NONBLOCK; + if (*output.get()).is_some() { + fs_rights_base |= RIGHTS_FD_WRITE; } let fs_rights_inheriting = fs_rights_base; - stat.write(Fdstat { - fs_filetype, + fs_filetype: isatty.filetype(), fs_flags, fs_rights_base, fs_rights_inheriting, }); Ok(()) } + Descriptor::Closed(_) => Err(ERRNO_BADF), + Descriptor::Streams(Streams { + input: _, + output: _, + type_: StreamType::Socket(_), + }) => unreachable!(), } - } - Descriptor::Streams(Streams { - input, - output, - type_: StreamType::Stdio(isatty), - }) => { - let fs_flags = 0; - let mut fs_rights_base = 0; - if input.get().is_some() { - fs_rights_base |= RIGHTS_FD_READ; - } - if output.get().is_some() { - fs_rights_base |= RIGHTS_FD_WRITE; - } - let fs_rights_inheriting = fs_rights_base; - stat.write(Fdstat { - fs_filetype: isatty.filetype(), - fs_flags, - fs_rights_base, - fs_rights_inheriting, - }); - Ok(()) - } - Descriptor::Closed(_) => Err(ERRNO_BADF), - Descriptor::Streams(Streams { - input, - output, - type_: StreamType::Socket(_), - }) => unreachable!(), + }) }) } @@ -602,21 +606,22 @@ pub unsafe extern "C" fn fd_fdstat_set_flags(fd: Fd, flags: Fdflags) -> Errno { } State::with(|state| { - let mut ds = state.descriptors_mut(); - let file = match ds.get_mut(fd)? { - Descriptor::Streams(Streams { - type_: StreamType::File(file), - .. - }) if !file.is_dir() => file, - _ => Err(wasi::ERRNO_BADF)?, - }; - file.append = flags & FDFLAGS_APPEND == FDFLAGS_APPEND; - file.blocking_mode = if flags & FDFLAGS_NONBLOCK == FDFLAGS_NONBLOCK { - BlockingMode::NonBlocking - } else { - BlockingMode::Blocking - }; - Ok(()) + state.with_descriptors_mut(|ds: &mut Descriptors| { + let file = match ds.get_mut(fd)? { + Descriptor::Streams(Streams { + type_: StreamType::File(file), + .. + }) if !file.is_dir() => file, + _ => Err(wasi::ERRNO_BADF)?, + }; + file.append = flags & FDFLAGS_APPEND == FDFLAGS_APPEND; + file.blocking_mode = if flags & FDFLAGS_NONBLOCK == FDFLAGS_NONBLOCK { + BlockingMode::NonBlocking + } else { + BlockingMode::Blocking + }; + Ok(()) + }) }) } @@ -627,9 +632,11 @@ pub unsafe extern "C" fn fd_fdstat_set_rights( fs_rights_base: Rights, fs_rights_inheriting: Rights, ) -> Errno { - State::with(|state| match state.descriptors().get(fd)? { - Descriptor::Streams(..) => Ok(()), - Descriptor::Closed(..) => Err(wasi::ERRNO_BADF), + State::with(|state| { + state.with_descriptors(|ds| match ds.get(fd)? { + Descriptor::Streams(..) => Ok(()), + Descriptor::Closed(..) => Err(wasi::ERRNO_BADF), + }) }) } @@ -637,46 +644,47 @@ pub unsafe extern "C" fn fd_fdstat_set_rights( #[no_mangle] pub unsafe extern "C" fn fd_filestat_get(fd: Fd, buf: *mut Filestat) -> Errno { State::with(|state| { - let ds = state.descriptors(); - match ds.get(fd)? { - Descriptor::Streams(Streams { - type_: StreamType::File(file), - .. - }) => { - let stat = filesystem::stat(file.fd)?; - let metadata_hash = filesystem::metadata_hash(file.fd)?; - let filetype = stat.type_.into(); - *buf = Filestat { - dev: 1, - ino: metadata_hash.lower, - filetype, - nlink: stat.link_count, - size: stat.size, - atim: datetime_to_timestamp(stat.data_access_timestamp), - mtim: datetime_to_timestamp(stat.data_modification_timestamp), - ctim: datetime_to_timestamp(stat.status_change_timestamp), - }; - Ok(()) - } - // Stdio is all zero fields, except for filetype character device - Descriptor::Streams(Streams { - type_: StreamType::Stdio(isatty), - .. - }) => { - *buf = Filestat { - dev: 0, - ino: 0, - filetype: isatty.filetype(), - nlink: 0, - size: 0, - atim: 0, - mtim: 0, - ctim: 0, - }; - Ok(()) + state.with_descriptors(|ds| { + match ds.get(fd)? { + Descriptor::Streams(Streams { + type_: StreamType::File(file), + .. + }) => { + let stat = file.fd.stat()?; + let metadata_hash = file.fd.metadata_hash()?; + let filetype = stat.type_.into(); + *buf = Filestat { + dev: 1, + ino: metadata_hash.lower, + filetype, + nlink: stat.link_count, + size: stat.size, + atim: datetime_to_timestamp(stat.data_access_timestamp), + mtim: datetime_to_timestamp(stat.data_modification_timestamp), + ctim: datetime_to_timestamp(stat.status_change_timestamp), + }; + Ok(()) + } + // Stdio is all zero fields, except for filetype character device + Descriptor::Streams(Streams { + type_: StreamType::Stdio(isatty), + .. + }) => { + *buf = Filestat { + dev: 0, + ino: 0, + filetype: isatty.filetype(), + nlink: 0, + size: 0, + atim: 0, + mtim: 0, + ctim: 0, + }; + Ok(()) + } + _ => Err(wasi::ERRNO_BADF), } - _ => Err(wasi::ERRNO_BADF), - } + }) }) } @@ -685,10 +693,11 @@ pub unsafe extern "C" fn fd_filestat_get(fd: Fd, buf: *mut Filestat) -> Errno { #[no_mangle] pub unsafe extern "C" fn fd_filestat_set_size(fd: Fd, size: Filesize) -> Errno { State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_file(fd)?; - filesystem::set_size(file.fd, size)?; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_file(fd)?; + file.fd.set_size(size)?; + Ok(()) + }) }) } @@ -727,10 +736,11 @@ pub unsafe extern "C" fn fd_filestat_set_times( mtim, fst_flags & FSTFLAGS_MTIM_NOW == FSTFLAGS_MTIM_NOW, )?; - let ds = state.descriptors(); - let file = ds.get_file(fd)?; - filesystem::set_times(file.fd, atim, mtim)?; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_file(fd)?; + file.fd.set_times(atim, mtim)?; + Ok(()) + }) }) } @@ -758,22 +768,23 @@ pub unsafe extern "C" fn fd_pread( let ptr = (*iovs_ptr).buf; let len = (*iovs_ptr).buf_len; - let ds = state.descriptors(); - let file = ds.get_file(fd)?; - let (data, end) = state - .import_alloc - .with_buffer(ptr, len, || filesystem::read(file.fd, len as u64, offset))?; - assert_eq!(data.as_ptr(), ptr); - assert!(data.len() <= len); - - let len = data.len(); - forget(data); - if !end && len == 0 { - Err(ERRNO_INTR) - } else { - *nread = len; - Ok(()) - } + state.with_descriptors(|ds| { + let file = ds.get_file(fd)?; + let (data, end) = state + .import_alloc + .with_buffer(ptr, len, || file.fd.read(len as u64, offset))?; + assert_eq!(data.as_ptr(), ptr); + assert!(data.len() <= len); + + let len = data.len(); + forget(data); + if !end && len == 0 { + Err(ERRNO_INTR) + } else { + *nread = len; + Ok(()) + } + }) }) } @@ -785,20 +796,22 @@ pub unsafe extern "C" fn fd_prestat_get(fd: Fd, buf: *mut Prestat) -> Errno { AllocationState::StackAllocated | AllocationState::StateAllocated ) { State::with(|state| { - if let Some(preopen) = state.descriptors().get_preopen(fd) { - buf.write(Prestat { - tag: 0, - u: PrestatU { - dir: PrestatDir { - pr_name_len: preopen.path.len, + state.with_descriptors(|ds| { + if let Some(preopen) = ds.get_preopen(fd) { + buf.write(Prestat { + tag: 0, + u: PrestatU { + dir: PrestatDir { + pr_name_len: preopen.path.len, + }, }, - }, - }); + }); - Ok(()) - } else { - Err(ERRNO_BADF) - } + Ok(()) + } else { + Err(ERRNO_BADF) + } + }) }) } else { ERRNO_BADF @@ -809,16 +822,18 @@ pub unsafe extern "C" fn fd_prestat_get(fd: Fd, buf: *mut Prestat) -> Errno { #[no_mangle] pub unsafe extern "C" fn fd_prestat_dir_name(fd: Fd, path: *mut u8, path_max_len: Size) -> Errno { State::with(|state| { - if let Some(preopen) = state.descriptors().get_preopen(fd) { - if preopen.path.len > path_max_len as usize { - Err(ERRNO_NAMETOOLONG) + state.with_descriptors(|ds| { + if let Some(preopen) = ds.get_preopen(fd) { + if preopen.path.len > path_max_len as usize { + Err(ERRNO_NAMETOOLONG) + } else { + ptr::copy_nonoverlapping(preopen.path.ptr, path, preopen.path.len); + Ok(()) + } } else { - ptr::copy_nonoverlapping(preopen.path.ptr, path, preopen.path.len); - Ok(()) + Err(ERRNO_NOTDIR) } - } else { - Err(ERRNO_NOTDIR) - } + }) }) } @@ -846,11 +861,12 @@ pub unsafe extern "C" fn fd_pwrite( let len = (*iovs_ptr).buf_len; State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_seekable_file(fd)?; - let bytes = filesystem::write(file.fd, slice::from_raw_parts(ptr, len), offset)?; - *nwritten = bytes as usize; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_seekable_file(fd)?; + let bytes = file.fd.write(slice::from_raw_parts(ptr, len), offset)?; + *nwritten = bytes as usize; + Ok(()) + }) }) } @@ -877,41 +893,43 @@ pub unsafe extern "C" fn fd_read( let len = (*iovs_ptr).buf_len; State::with(|state| { - match state.descriptors().get(fd)? { - Descriptor::Streams(streams) => { - let blocking_mode = if let StreamType::File(file) = &streams.type_ { - file.blocking_mode - } else { - BlockingMode::Blocking - }; + state.with_descriptors(|ds| { + match ds.get(fd)? { + Descriptor::Streams(streams) => { + let blocking_mode = if let StreamType::File(file) = &streams.type_ { + file.blocking_mode + } else { + BlockingMode::Blocking + }; - let read_len = u64::try_from(len).trapping_unwrap(); - let wasi_stream = streams.get_read_stream()?; - let (data, stream_stat) = state - .import_alloc - .with_buffer(ptr, len, || blocking_mode.read(wasi_stream, read_len)) - .map_err(|_| ERRNO_IO)?; + let read_len = u64::try_from(len).trapping_unwrap(); + let wasi_stream = streams.get_read_stream()?; + let (data, stream_stat) = state + .import_alloc + .with_buffer(ptr, len, || blocking_mode.read(wasi_stream, read_len)) + .map_err(|_| ERRNO_IO)?; - assert_eq!(data.as_ptr(), ptr); - assert!(data.len() <= len); + assert_eq!(data.as_ptr(), ptr); + assert!(data.len() <= len); - // If this is a file, keep the current-position pointer up to date. - if let StreamType::File(file) = &streams.type_ { - file.position - .set(file.position.get() + data.len() as filesystem::Filesize); - } + // If this is a file, keep the current-position pointer up to date. + if let StreamType::File(file) = &streams.type_ { + file.position + .set(file.position.get() + data.len() as filesystem::Filesize); + } - let len = data.len(); - forget(data); - if stream_stat == crate::streams::StreamStatus::Open && len == 0 { - Err(ERRNO_INTR) - } else { - *nread = len; - Ok(()) + let len = data.len(); + forget(data); + if stream_stat == crate::streams::StreamStatus::Open && len == 0 { + Err(ERRNO_INTR) + } else { + *nread = len; + Ok(()) + } } + Descriptor::Closed(_) => Err(ERRNO_BADF), } - Descriptor::Closed(_) => Err(ERRNO_BADF), - } + }) }) } @@ -955,108 +973,113 @@ pub unsafe extern "C" fn fd_readdir( // Compute the inode of `.` so that the iterator can produce an entry // for it. - let ds = state.descriptors(); - let dir = ds.get_dir(fd)?; - - let mut iter; - match stream { - // All our checks passed and a dirent cache was available with a - // prior stream. Construct an iterator which will yield its first - // entry from cache and is additionally resuming at the `cookie` - // specified. - Some(stream) => { - iter = DirectoryEntryIterator { - stream, - state, - cookie, - use_cache: true, - dir_descriptor: dir.fd, + state.with_descriptors(|ds| { + let dir = ds.get_dir(fd)?; + + let mut iter; + match stream { + // All our checks passed and a dirent cache was available with a + // prior stream. Construct an iterator which will yield its first + // entry from cache and is additionally resuming at the `cookie` + // specified. + Some(stream) => { + iter = DirectoryEntryIterator { + stream, + state, + cookie, + use_cache: true, + dir_descriptor: &dir.fd, + } } - } - // Either a dirent stream wasn't previously available, a different - // cookie was requested, or a brand new directory is now being read. - // In these situations fall back to resuming reading the directory - // from scratch, and the `cookie` value indicates how many items - // need skipping. - None => { - iter = DirectoryEntryIterator { - state, - cookie: wasi::DIRCOOKIE_START, - use_cache: false, - stream: DirectoryEntryStream(filesystem::read_directory(dir.fd)?), - dir_descriptor: dir.fd, - }; + // Either a dirent stream wasn't previously available, a different + // cookie was requested, or a brand new directory is now being read. + // In these situations fall back to resuming reading the directory + // from scratch, and the `cookie` value indicates how many items + // need skipping. + None => { + iter = DirectoryEntryIterator { + state, + cookie: wasi::DIRCOOKIE_START, + use_cache: false, + stream: DirectoryEntryStream(dir.fd.read_directory()?), + dir_descriptor: &dir.fd, + }; - // Skip to the entry that is requested by the `cookie` - // parameter. - for _ in wasi::DIRCOOKIE_START..cookie { - match iter.next() { - Some(Ok(_)) => {} - Some(Err(e)) => return Err(e), - None => return Ok(()), + // Skip to the entry that is requested by the `cookie` + // parameter. + for _ in wasi::DIRCOOKIE_START..cookie { + match iter.next() { + Some(Ok(_)) => {} + Some(Err(e)) => return Err(e), + None => return Ok(()), + } } } - } - }; - - while buf.len() > 0 { - let (dirent, name) = match iter.next() { - Some(Ok(pair)) => pair, - Some(Err(e)) => return Err(e), - None => break, }; - // Copy a `dirent` describing this entry into the destination `buf`, - // truncating it if it doesn't fit entirely. - let bytes = slice::from_raw_parts( - (&dirent as *const wasi::Dirent).cast::(), - size_of::(), - ); - let dirent_bytes_to_copy = buf.len().min(bytes.len()); - buf[..dirent_bytes_to_copy].copy_from_slice(&bytes[..dirent_bytes_to_copy]); - buf = &mut buf[dirent_bytes_to_copy..]; - - // Copy the name bytes into the output `buf`, truncating it if it - // doesn't fit. - // - // Note that this might be a 0-byte copy if the `dirent` was - // truncated or fit entirely into the destination. - let name_bytes_to_copy = buf.len().min(name.len()); - ptr::copy_nonoverlapping(name.as_ptr().cast(), buf.as_mut_ptr(), name_bytes_to_copy); - - buf = &mut buf[name_bytes_to_copy..]; - - // If the buffer is empty then that means the value may be - // truncated, so save the state of the iterator in our dirent cache - // and return. - // - // Note that `cookie - 1` is stored here since `iter.cookie` stores - // the address of the next item, and we're rewinding one item since - // the current item is truncated and will want to resume from that - // in the future. - // - // Additionally note that this caching step is skipped if the name - // to store doesn't actually fit in the dirent cache's path storage. - // In that case there's not much we can do and let the next call to - // `fd_readdir` start from scratch. - if buf.len() == 0 && name.len() <= DIRENT_CACHE { - let DirectoryEntryIterator { stream, cookie, .. } = iter; - state.dirent_cache.stream.set(Some(stream)); - state.dirent_cache.for_fd.set(fd); - state.dirent_cache.cookie.set(cookie - 1); - state.dirent_cache.cached_dirent.set(dirent); - ptr::copy( - name.as_ptr().cast::(), - (*state.dirent_cache.path_data.get()).as_mut_ptr() as *mut u8, - name.len(), + while buf.len() > 0 { + let (dirent, name) = match iter.next() { + Some(Ok(pair)) => pair, + Some(Err(e)) => return Err(e), + None => break, + }; + + // Copy a `dirent` describing this entry into the destination `buf`, + // truncating it if it doesn't fit entirely. + let bytes = slice::from_raw_parts( + (&dirent as *const wasi::Dirent).cast::(), + size_of::(), ); - break; + let dirent_bytes_to_copy = buf.len().min(bytes.len()); + buf[..dirent_bytes_to_copy].copy_from_slice(&bytes[..dirent_bytes_to_copy]); + buf = &mut buf[dirent_bytes_to_copy..]; + + // Copy the name bytes into the output `buf`, truncating it if it + // doesn't fit. + // + // Note that this might be a 0-byte copy if the `dirent` was + // truncated or fit entirely into the destination. + let name_bytes_to_copy = buf.len().min(name.len()); + ptr::copy_nonoverlapping( + name.as_ptr().cast(), + buf.as_mut_ptr(), + name_bytes_to_copy, + ); + + buf = &mut buf[name_bytes_to_copy..]; + + // If the buffer is empty then that means the value may be + // truncated, so save the state of the iterator in our dirent cache + // and return. + // + // Note that `cookie - 1` is stored here since `iter.cookie` stores + // the address of the next item, and we're rewinding one item since + // the current item is truncated and will want to resume from that + // in the future. + // + // Additionally note that this caching step is skipped if the name + // to store doesn't actually fit in the dirent cache's path storage. + // In that case there's not much we can do and let the next call to + // `fd_readdir` start from scratch. + if buf.len() == 0 && name.len() <= DIRENT_CACHE { + let DirectoryEntryIterator { stream, cookie, .. } = iter; + state.dirent_cache.stream.set(Some(stream)); + state.dirent_cache.for_fd.set(fd); + state.dirent_cache.cookie.set(cookie - 1); + state.dirent_cache.cached_dirent.set(dirent); + ptr::copy( + name.as_ptr().cast::(), + (*state.dirent_cache.path_data.get()).as_mut_ptr() as *mut u8, + name.len(), + ); + break; + } } - } - *bufused = buf_len - buf.len(); - Ok(()) + *bufused = buf_len - buf.len(); + Ok(()) + }) }); struct DirectoryEntryIterator<'a> { @@ -1064,7 +1087,7 @@ pub unsafe extern "C" fn fd_readdir( use_cache: bool, cookie: Dircookie, stream: DirectoryEntryStream, - dir_descriptor: filesystem::Descriptor, + dir_descriptor: &'a filesystem::Descriptor, } impl<'a> Iterator for DirectoryEntryIterator<'a> { @@ -1081,7 +1104,7 @@ pub unsafe extern "C" fn fd_readdir( // Preview2 excludes them, so re-add them. match current_cookie { 0 => { - let metadata_hash = match filesystem::metadata_hash(self.dir_descriptor) { + let metadata_hash = match self.dir_descriptor.metadata_hash() { Ok(h) => h, Err(e) => return Some(Err(e.into())), }; @@ -1119,7 +1142,7 @@ pub unsafe extern "C" fn fd_readdir( let entry = self.state.import_alloc.with_buffer( self.state.path_buf.get().cast(), PATH_MAX, - || filesystem::read_directory_entry(self.stream.0), + || self.stream.0.read_directory_entry(), ); let entry = match entry { Ok(Some(entry)) => entry, @@ -1128,13 +1151,11 @@ pub unsafe extern "C" fn fd_readdir( }; let filesystem::DirectoryEntry { type_, name } = entry; - let d_ino = filesystem::metadata_hash_at( - self.dir_descriptor, - filesystem::PathFlags::empty(), - &name, - ) - .map(|h| h.lower) - .unwrap_or(0); + let d_ino = self + .dir_descriptor + .metadata_hash_at(filesystem::PathFlags::empty(), &name) + .map(|h| h.lower) + .unwrap_or(0); let name = ManuallyDrop::new(name); let dirent = wasi::Dirent { d_next: self.cookie, @@ -1163,7 +1184,7 @@ pub unsafe extern "C" fn fd_readdir( /// would disappear if `dup2()` were to be removed entirely. #[no_mangle] pub unsafe extern "C" fn fd_renumber(fd: Fd, to: Fd) -> Errno { - State::with(|state| state.descriptors_mut().renumber(fd, to)) + State::with(|state| state.with_descriptors_mut(|ds| ds.renumber(fd, to))) } /// Move the offset of a file descriptor. @@ -1176,36 +1197,37 @@ pub unsafe extern "C" fn fd_seek( newoffset: *mut Filesize, ) -> Errno { State::with(|state| { - let ds = state.descriptors(); - let stream = ds.get_seekable_stream(fd)?; - - // Seeking only works on files. - if let StreamType::File(file) = &stream.type_ { - if let filesystem::DescriptorType::Directory = file.descriptor_type { - // This isn't really the "right" errno, but it is consistient with wasmtime's - // preview 1 tests. - return Err(ERRNO_BADF); - } - let from = match whence { - WHENCE_SET if offset >= 0 => offset, - WHENCE_CUR => match (file.position.get() as i64).checked_add(offset) { - Some(pos) if pos >= 0 => pos, - _ => return Err(ERRNO_INVAL), - }, - WHENCE_END => match (filesystem::stat(file.fd)?.size as i64).checked_add(offset) { - Some(pos) if pos >= 0 => pos, + state.with_descriptors(|ds| { + let stream = ds.get_seekable_stream(fd)?; + + // Seeking only works on files. + if let StreamType::File(file) = &stream.type_ { + if let filesystem::DescriptorType::Directory = file.descriptor_type { + // This isn't really the "right" errno, but it is consistient with wasmtime's + // preview 1 tests. + return Err(ERRNO_BADF); + } + let from = match whence { + WHENCE_SET if offset >= 0 => offset, + WHENCE_CUR => match (file.position.get() as i64).checked_add(offset) { + Some(pos) if pos >= 0 => pos, + _ => return Err(ERRNO_INVAL), + }, + WHENCE_END => match (file.fd.stat()?.size as i64).checked_add(offset) { + Some(pos) if pos >= 0 => pos, + _ => return Err(ERRNO_INVAL), + }, _ => return Err(ERRNO_INVAL), - }, - _ => return Err(ERRNO_INVAL), - }; - stream.input.set(None); - stream.output.set(None); - file.position.set(from as filesystem::Filesize); - *newoffset = from as filesystem::Filesize; - Ok(()) - } else { - Err(ERRNO_SPIPE) - } + }; + *stream.input.get() = None; + *stream.output.get() = None; + file.position.set(from as filesystem::Filesize); + *newoffset = from as filesystem::Filesize; + Ok(()) + } else { + Err(ERRNO_SPIPE) + } + }) }) } @@ -1214,10 +1236,11 @@ pub unsafe extern "C" fn fd_seek( #[no_mangle] pub unsafe extern "C" fn fd_sync(fd: Fd) -> Errno { State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_file(fd)?; - filesystem::sync(file.fd)?; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_file(fd)?; + file.fd.sync()?; + Ok(()) + }) }) } @@ -1226,10 +1249,11 @@ pub unsafe extern "C" fn fd_sync(fd: Fd) -> Errno { #[no_mangle] pub unsafe extern "C" fn fd_tell(fd: Fd, offset: *mut Filesize) -> Errno { State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_seekable_file(fd)?; - *offset = file.position.get() as Filesize; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_seekable_file(fd)?; + *offset = file.position.get() as Filesize; + Ok(()) + }) }) } @@ -1261,34 +1285,35 @@ pub unsafe extern "C" fn fd_write( let bytes = slice::from_raw_parts(ptr, len); State::with(|state| { - let ds = state.descriptors(); - match ds.get(fd)? { - Descriptor::Streams(streams) => { - let wasi_stream = streams.get_write_stream()?; - - let nbytes = if let StreamType::File(file) = &streams.type_ { - file.blocking_mode.write(wasi_stream, bytes)? - } else { - // Use blocking writes on non-file streams (stdout, stderr, as sockets - // aren't currently used). - BlockingMode::Blocking.write(wasi_stream, bytes)? - }; - - // If this is a file, keep the current-position pointer up to date. - if let StreamType::File(file) = &streams.type_ { - // But don't update if we're in append mode. Strictly speaking, - // we should set the position to the new end of the file, but - // we don't have an API to do that atomically. - if !file.append { - file.position.set(file.position.get() + nbytes as u64); + state.with_descriptors(|ds| { + match ds.get(fd)? { + Descriptor::Streams(streams) => { + let wasi_stream = streams.get_write_stream()?; + + let nbytes = if let StreamType::File(file) = &streams.type_ { + file.blocking_mode.write(wasi_stream, bytes)? + } else { + // Use blocking writes on non-file streams (stdout, stderr, as sockets + // aren't currently used). + BlockingMode::Blocking.write(wasi_stream, bytes)? + }; + + // If this is a file, keep the current-position pointer up to date. + if let StreamType::File(file) = &streams.type_ { + // But don't update if we're in append mode. Strictly speaking, + // we should set the position to the new end of the file, but + // we don't have an API to do that atomically. + if !file.append { + file.position.set(file.position.get() + nbytes as u64); + } } - } - *nwritten = nbytes; - Ok(()) + *nwritten = nbytes; + Ok(()) + } + Descriptor::Closed(_) => Err(ERRNO_BADF), } - Descriptor::Closed(_) => Err(ERRNO_BADF), - } + }) }) } else { *nwritten = 0; @@ -1307,10 +1332,11 @@ pub unsafe extern "C" fn path_create_directory( let path = slice::from_raw_parts(path_ptr, path_len); State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_dir(fd)?; - filesystem::create_directory_at(file.fd, path)?; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_dir(fd)?; + file.fd.create_directory_at(path)?; + Ok(()) + }) }) } @@ -1328,22 +1354,23 @@ pub unsafe extern "C" fn path_filestat_get( let at_flags = at_flags_from_lookupflags(flags); State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_dir(fd)?; - let stat = filesystem::stat_at(file.fd, at_flags, path)?; - let metadata_hash = filesystem::metadata_hash_at(file.fd, at_flags, path)?; - let filetype = stat.type_.into(); - *buf = Filestat { - dev: 1, - ino: metadata_hash.lower, - filetype, - nlink: stat.link_count, - size: stat.size, - atim: datetime_to_timestamp(stat.data_access_timestamp), - mtim: datetime_to_timestamp(stat.data_modification_timestamp), - ctim: datetime_to_timestamp(stat.status_change_timestamp), - }; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_dir(fd)?; + let stat = file.fd.stat_at(at_flags, path)?; + let metadata_hash = file.fd.metadata_hash_at(at_flags, path)?; + let filetype = stat.type_.into(); + *buf = Filestat { + dev: 1, + ino: metadata_hash.lower, + filetype, + nlink: stat.link_count, + size: stat.size, + atim: datetime_to_timestamp(stat.data_access_timestamp), + mtim: datetime_to_timestamp(stat.data_modification_timestamp), + ctim: datetime_to_timestamp(stat.status_change_timestamp), + }; + Ok(()) + }) }) } @@ -1374,10 +1401,11 @@ pub unsafe extern "C" fn path_filestat_set_times( fst_flags & FSTFLAGS_MTIM_NOW == FSTFLAGS_MTIM_NOW, )?; - let ds = state.descriptors(); - let file = ds.get_dir(fd)?; - filesystem::set_times_at(file.fd, at_flags, path, atim, mtim)?; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_dir(fd)?; + file.fd.set_times_at(at_flags, path, atim, mtim)?; + Ok(()) + }) }) } @@ -1398,10 +1426,12 @@ pub unsafe extern "C" fn path_link( let at_flags = at_flags_from_lookupflags(old_flags); State::with(|state| { - let old = state.descriptors().get_dir(old_fd)?.fd; - let new = state.descriptors().get_dir(new_fd)?.fd; - filesystem::link_at(old, at_flags, old_path, new, new_path)?; - Ok(()) + state.with_descriptors(|ds| { + let old = &ds.get_dir(old_fd)?.fd; + let new = &ds.get_dir(new_fd)?.fd; + old.link_at(at_flags, old_path, new, new_path)?; + Ok(()) + }) }) } @@ -1434,29 +1464,30 @@ pub unsafe extern "C" fn path_open( let append = fdflags & wasi::FDFLAGS_APPEND == wasi::FDFLAGS_APPEND; State::with(|state| { - let mut ds = state.descriptors_mut(); - let file = ds.get_dir(fd)?; - let result = filesystem::open_at(file.fd, at_flags, path, o_flags, flags, mode)?; - let descriptor_type = filesystem::get_type(result)?; - let desc = Descriptor::Streams(Streams { - input: Cell::new(None), - output: Cell::new(None), - type_: StreamType::File(File { - fd: result, - descriptor_type, - position: Cell::new(0), - append, - blocking_mode: if fdflags & wasi::FDFLAGS_NONBLOCK == 0 { - BlockingMode::Blocking - } else { - BlockingMode::NonBlocking - }, - }), - }); + state.with_descriptors_mut(|ds: &mut Descriptors| { + let file = ds.get_dir(fd)?; + let result = file.fd.open_at(at_flags, path, o_flags, flags, mode)?; + let descriptor_type = result.get_type()?; + let desc = Descriptor::Streams(Streams { + input: UnsafeCell::new(None), + output: UnsafeCell::new(None), + type_: StreamType::File(File { + fd: result, + descriptor_type, + position: Cell::new(0), + append, + blocking_mode: if fdflags & wasi::FDFLAGS_NONBLOCK == 0 { + BlockingMode::Blocking + } else { + BlockingMode::NonBlocking + }, + }), + }); - let fd = ds.open(desc)?; - *opened_fd = fd; - Ok(()) + let fd = ds.open(desc)?; + *opened_fd = fd; + Ok(()) + }) }) } @@ -1479,35 +1510,36 @@ pub unsafe extern "C" fn path_readlink( // so instead we handle this case specially. let use_state_buf = buf_len < PATH_MAX; - let ds = state.descriptors(); - let file = ds.get_dir(fd)?; - let path = if use_state_buf { - state - .import_alloc - .with_buffer(state.path_buf.get().cast(), PATH_MAX, || { - filesystem::readlink_at(file.fd, path) - })? - } else { - state - .import_alloc - .with_buffer(buf, buf_len, || filesystem::readlink_at(file.fd, path))? - }; + state.with_descriptors(|ds| { + let file = ds.get_dir(fd)?; + let path = if use_state_buf { + state + .import_alloc + .with_buffer(state.path_buf.get().cast(), PATH_MAX, || { + file.fd.readlink_at(path) + })? + } else { + state + .import_alloc + .with_buffer(buf, buf_len, || file.fd.readlink_at(path))? + }; - if use_state_buf { - // Preview1 follows POSIX in truncating the returned path if it - // doesn't fit. - let len = min(path.len(), buf_len); - ptr::copy_nonoverlapping(path.as_ptr().cast(), buf, len); - *bufused = len; - } else { - *bufused = path.len(); - } + if use_state_buf { + // Preview1 follows POSIX in truncating the returned path if it + // doesn't fit. + let len = min(path.len(), buf_len); + ptr::copy_nonoverlapping(path.as_ptr().cast(), buf, len); + *bufused = len; + } else { + *bufused = path.len(); + } - // The returned string's memory was allocated in `buf`, so don't separately - // free it. - forget(path); + // The returned string's memory was allocated in `buf`, so don't separately + // free it. + forget(path); - Ok(()) + Ok(()) + }) }) } @@ -1523,10 +1555,11 @@ pub unsafe extern "C" fn path_remove_directory( let path = slice::from_raw_parts(path_ptr, path_len); State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_dir(fd)?; - filesystem::remove_directory_at(file.fd, path)?; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_dir(fd)?; + file.fd.remove_directory_at(path)?; + Ok(()) + }) }) } @@ -1545,11 +1578,12 @@ pub unsafe extern "C" fn path_rename( let new_path = slice::from_raw_parts(new_path_ptr, new_path_len); State::with(|state| { - let ds = state.descriptors(); - let old = ds.get_dir(old_fd)?.fd; - let new = ds.get_dir(new_fd)?.fd; - filesystem::rename_at(old, old_path, new, new_path)?; - Ok(()) + state.with_descriptors(|ds| { + let old = &ds.get_dir(old_fd)?.fd; + let new = &ds.get_dir(new_fd)?.fd; + old.rename_at(old_path, new, new_path)?; + Ok(()) + }) }) } @@ -1567,10 +1601,11 @@ pub unsafe extern "C" fn path_symlink( let new_path = slice::from_raw_parts(new_path_ptr, new_path_len); State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_dir(fd)?; - filesystem::symlink_at(file.fd, old_path, new_path)?; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_dir(fd)?; + file.fd.symlink_at(old_path, new_path)?; + Ok(()) + }) }) } @@ -1582,10 +1617,11 @@ pub unsafe extern "C" fn path_unlink_file(fd: Fd, path_ptr: *const u8, path_len: let path = slice::from_raw_parts(path_ptr, path_len); State::with(|state| { - let ds = state.descriptors(); - let file = ds.get_dir(fd)?; - filesystem::unlink_file_at(file.fd, path)?; - Ok(()) + state.with_descriptors(|ds| { + let file = ds.get_dir(fd)?; + file.fd.unlink_file_at(path)?; + Ok(()) + }) }) } @@ -1598,15 +1634,22 @@ struct Pollables { impl Pollables { unsafe fn push(&mut self, pollable: Pollable) { assert!(self.index < self.length); - *self.pointer.add(self.index) = pollable; + // Use `ptr::write` instead of `*... = pollable` because `ptr::write` + // doesn't call drop on the old memory. + self.pointer.add(self.index).write(pollable); self.index += 1; } } +// We create new pollable handles for each `poll_oneoff` call, so drop them all +// after the call. impl Drop for Pollables { fn drop(&mut self) { - for i in 0..self.index { - poll::drop_pollable(unsafe { *self.pointer.add(i) }) + while self.index != 0 { + self.index -= 1; + unsafe { + core::ptr::drop_in_place(self.pointer.add(self.index)); + } } } } @@ -1649,7 +1692,7 @@ pub unsafe extern "C" fn poll_oneoff( // // First, we assert that this is possible: assert!(align_of::() >= align_of::()); - assert!(align_of::() >= align_of::()); + assert!(align_of::() >= align_of::()); assert!( nsubscriptions .checked_mul(size_of::()) @@ -1659,7 +1702,7 @@ pub unsafe extern "C" fn poll_oneoff( .trapping_unwrap() .checked_add( nsubscriptions - .checked_mul(size_of::()) + .checked_mul(size_of::()) .trapping_unwrap() ) .trapping_unwrap() @@ -1667,7 +1710,7 @@ pub unsafe extern "C" fn poll_oneoff( // Store the pollable handles at the beginning, and the bool results at the // end, so that we don't clobber the bool results when writting the events. let pollables = out as *mut c_void as *mut Pollable; - let results = out.add(nsubscriptions).cast::().sub(nsubscriptions); + let results = out.add(nsubscriptions).cast::().sub(nsubscriptions); // Indefinite sleeping is not supported in preview1. if nsubscriptions == 0 { @@ -1724,175 +1767,157 @@ pub unsafe extern "C" fn poll_oneoff( monotonic_clock::subscribe(timeout, false) } - CLOCKID_MONOTONIC => monotonic_clock::subscribe(clock.timeout, absolute), + CLOCKID_MONOTONIC => { + let s = monotonic_clock::subscribe(clock.timeout, absolute); + s + } _ => return Err(ERRNO_INVAL), } } - EVENTTYPE_FD_READ => { - let stream = state - .descriptors() - .get_read_stream(subscription.u.u.fd_read.file_descriptor)?; - streams::subscribe_to_input_stream(stream) - } + EVENTTYPE_FD_READ => state.with_descriptors(|ds| { + ds.get_read_stream(subscription.u.u.fd_read.file_descriptor) + .map(|stream| stream.subscribe()) + })?, - EVENTTYPE_FD_WRITE => { - let stream = state - .descriptors() - .get_write_stream(subscription.u.u.fd_write.file_descriptor)?; - streams::subscribe_to_output_stream(stream) - } + EVENTTYPE_FD_WRITE => state.with_descriptors(|ds| { + ds.get_write_stream(subscription.u.u.fd_write.file_descriptor) + .map(|stream| stream.subscribe()) + })?, _ => return Err(ERRNO_INVAL), }); } - #[link(wasm_import_module = "wasi:poll/poll")] + #[link(wasm_import_module = "wasi:io/poll")] extern "C" { - #[link_name = "poll-oneoff"] - fn poll_oneoff_import(pollables: *const Pollable, len: usize, rval: *mut BoolList); + #[link_name = "poll-list"] + fn poll_list_import(pollables: *const Pollable, len: usize, rval: *mut ReadyList); } - let mut ready_list = BoolList { + let mut ready_list = ReadyList { base: std::ptr::null(), len: 0, }; state.import_alloc.with_buffer( - results, + results.cast(), nsubscriptions - .checked_mul(size_of::()) + .checked_mul(size_of::()) .trapping_unwrap(), || { - poll_oneoff_import( + poll_list_import( pollables.pointer, pollables.length, &mut ready_list as *mut _, - ) + ); }, ); - assert_eq!(ready_list.len, nsubscriptions); - assert_eq!(ready_list.base, results as *const bool); + assert!(ready_list.len <= nsubscriptions); + assert_eq!(ready_list.base, results as *const u32); drop(pollables); - let ready = subscriptions - .iter() - .enumerate() - .filter_map(|(i, s)| (*ready_list.base.add(i)).then_some(s)); + let ready = std::slice::from_raw_parts(ready_list.base, ready_list.len); let mut count = 0; for subscription in ready { - let error; + let subscription = *subscriptions.as_ptr().add(*subscription as usize); + let type_; - let nbytes; - let flags; - match subscription.u.tag { + let (error, nbytes, flags) = match subscription.u.tag { EVENTTYPE_CLOCK => { - error = ERRNO_SUCCESS; type_ = wasi::EVENTTYPE_CLOCK; - nbytes = 0; - flags = 0; + (ERRNO_SUCCESS, 0, 0) } EVENTTYPE_FD_READ => { type_ = wasi::EVENTTYPE_FD_READ; - let ds = state.descriptors(); - let desc = ds - .get(subscription.u.u.fd_read.file_descriptor) - .trapping_unwrap(); - match desc { - Descriptor::Streams(streams) => match &streams.type_ { - StreamType::File(file) => match filesystem::stat(file.fd) { - Ok(stat) => { - error = ERRNO_SUCCESS; - nbytes = stat.size.saturating_sub(file.position.get()); - flags = if nbytes == 0 { - EVENTRWFLAGS_FD_READWRITE_HANGUP - } else { - 0 - }; - } - Err(e) => { - error = e.into(); - nbytes = 1; - flags = 0; + state.with_descriptors(|ds| { + let desc = ds + .get(subscription.u.u.fd_read.file_descriptor) + .trapping_unwrap(); + match desc { + Descriptor::Streams(streams) => match &streams.type_ { + StreamType::File(file) => match file.fd.stat() { + Ok(stat) => { + let nbytes = stat.size.saturating_sub(file.position.get()); + ( + ERRNO_SUCCESS, + nbytes, + if nbytes == 0 { + EVENTRWFLAGS_FD_READWRITE_HANGUP + } else { + 0 + }, + ) + } + Err(e) => (e.into(), 1, 0), + }, + StreamType::Socket(connection) => { + unreachable!() // TODO + /* + match tcp::bytes_readable(*connection) { + Ok(result) => ( + ERRNO_SUCCESS, + result.0, + if result.1 { + EVENTRWFLAGS_FD_READWRITE_HANGUP + } else { + 0 + } + ) + Err(e) => { + (e.into(), 1, 0) + } + } + */ } + StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0), }, - StreamType::Socket(connection) => { - unreachable!() // TODO - /* - match tcp::bytes_readable(*connection) { - Ok(result) => { - error = ERRNO_SUCCESS; - nbytes = result.0; - flags = if result.1 { - EVENTRWFLAGS_FD_READWRITE_HANGUP - } else { - 0 - }; - } - Err(e) => { - error = e.into(); - nbytes = 0; - flags = 0; - } - } - */ - } - StreamType::Stdio(_) => { - error = ERRNO_SUCCESS; - nbytes = 1; - flags = 0; - } - }, - _ => unreachable!(), - } + _ => unreachable!(), + } + }) } EVENTTYPE_FD_WRITE => { type_ = wasi::EVENTTYPE_FD_WRITE; - let ds = state.descriptors(); - let desc = ds - .get(subscription.u.u.fd_write.file_descriptor) - .trapping_unwrap(); - match desc { - Descriptor::Streams(streams) => match streams.type_ { - StreamType::File(_) | StreamType::Stdio(_) => { - error = ERRNO_SUCCESS; - nbytes = 1; - flags = 0; - } - StreamType::Socket(connection) => { - unreachable!() // TODO - /* - match tcp::bytes_writable(connection) { - Ok(result) => { - error = ERRNO_SUCCESS; - nbytes = result.0; - flags = if result.1 { - EVENTRWFLAGS_FD_READWRITE_HANGUP - } else { - 0 - }; - } - Err(e) => { - error = e.into(); - nbytes = 0; - flags = 0; + state.with_descriptors(|ds| { + let desc = ds + .get(subscription.u.u.fd_write.file_descriptor) + .trapping_unwrap(); + match desc { + Descriptor::Streams(streams) => match &streams.type_ { + StreamType::File(_) | StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0), + StreamType::Socket(connection) => { + unreachable!() // TODO + /* + match tcp::bytes_writable(connection) { + Ok(result) => ( + ERRNO_SUCCESS, + result.0, + if result.1 { + EVENTRWFLAGS_FD_READWRITE_HANGUP + } else { + 0 + } + ) + Err(e) => { + (e.into(), 0, 0) + } } - } - */ - } - }, - _ => unreachable!(), - } + */ + } + }, + _ => unreachable!(), + } + }) } _ => unreachable!(), - } + }; *out.add(count) = Event { userdata: subscription.userdata, @@ -2009,8 +2034,12 @@ pub unsafe extern "C" fn sock_shutdown(fd: Fd, how: Sdflags) -> Errno { unreachable!() } -fn datetime_to_timestamp(datetime: filesystem::Datetime) -> Timestamp { - u64::from(datetime.nanoseconds).saturating_add(datetime.seconds.saturating_mul(1_000_000_000)) +fn datetime_to_timestamp(datetime: Option) -> Timestamp { + match datetime { + Some(datetime) => u64::from(datetime.nanoseconds) + .saturating_add(datetime.seconds.saturating_mul(1_000_000_000)), + None => 0, + } } fn at_flags_from_lookupflags(flags: Lookupflags) -> filesystem::PathFlags { @@ -2135,15 +2164,19 @@ impl BlockingMode { // breaking our fragile linking scheme fn read( self, - input_stream: streams::InputStream, + input_stream: &streams::InputStream, read_len: u64, ) -> Result<(Vec, streams::StreamStatus), ()> { match self { - BlockingMode::NonBlocking => streams::read(input_stream, read_len), - BlockingMode::Blocking => streams::blocking_read(input_stream, read_len), + BlockingMode::NonBlocking => input_stream.read(read_len), + BlockingMode::Blocking => input_stream.blocking_read(read_len), } } - fn write(self, output_stream: streams::OutputStream, mut bytes: &[u8]) -> Result { + fn write( + self, + output_stream: &streams::OutputStream, + mut bytes: &[u8], + ) -> Result { match self { BlockingMode::Blocking => { let total = bytes.len(); @@ -2151,7 +2184,7 @@ impl BlockingMode { let len = bytes.len().min(4096); let (chunk, rest) = bytes.split_at(len); bytes = rest; - match streams::blocking_write_and_flush(output_stream, chunk) { + match output_stream.blocking_write_and_flush(chunk) { Ok(()) => {} Err(_) => return Err(ERRNO_IO), } @@ -2160,7 +2193,7 @@ impl BlockingMode { } BlockingMode::NonBlocking => { - let permit = match streams::check_write(output_stream) { + let permit = match output_stream.check_write() { Ok(n) => n, Err(streams::WriteError::Closed) => 0, Err(streams::WriteError::LastOperationFailed) => return Err(ERRNO_IO), @@ -2171,13 +2204,13 @@ impl BlockingMode { return Ok(0); } - match streams::write(output_stream, &bytes[..len]) { + match output_stream.write(&bytes[..len]) { Ok(_) => {} Err(streams::WriteError::Closed) => return Ok(0), Err(streams::WriteError::LastOperationFailed) => return Err(ERRNO_IO), } - match streams::blocking_flush(output_stream) { + match output_stream.blocking_flush() { Ok(_) => {} Err(streams::WriteError::Closed) => return Ok(0), Err(streams::WriteError::LastOperationFailed) => return Err(ERRNO_IO), @@ -2245,7 +2278,13 @@ struct State { /// /// Do not use this member directly - use State::descriptors() to ensure /// lazy initialization happens. - descriptors: RefCell>, + descriptors: UnsafeCell>, + + /// Borrow state of `descriptors`. + /// + /// If it looks like we're kind re-implementing `RefCell`, it's because we + /// basically are; `RefCell` itself pulls in static initializers. + descriptors_borrowed: UnsafeCell, /// Auxiliary storage to handle the `path_readlink` function. path_buf: UnsafeCell>, @@ -2288,12 +2327,6 @@ struct DirentCache { struct DirectoryEntryStream(filesystem::DirectoryEntryStream); -impl Drop for DirectoryEntryStream { - fn drop(&mut self) { - filesystem::drop_directory_entry_stream(self.0); - } -} - #[repr(C)] pub struct WasmStr { ptr: *const u8, @@ -2321,8 +2354,8 @@ pub struct StrTupleList { #[derive(Copy, Clone)] #[repr(C)] -pub struct BoolList { - base: *const bool, +pub struct ReadyList { + base: *const u32, len: usize, } @@ -2337,7 +2370,7 @@ const fn bump_arena_size() -> usize { start -= size_of::(); // Remove miscellaneous metadata also stored in state. - start -= 16 * size_of::(); + start -= 12 * size_of::(); // Everything else is the `command_data` allocation. start @@ -2348,7 +2381,7 @@ const fn bump_arena_size() -> usize { // below. #[cfg(target_arch = "wasm32")] const _: () = { - let _size_assert: [(); PAGE_SIZE] = [(); size_of::>()]; + let _size_assert: [(); PAGE_SIZE] = [(); size_of::>()]; }; #[allow(unused)] @@ -2363,28 +2396,25 @@ enum AllocationState { #[allow(improper_ctypes)] extern "C" { - fn get_state_ptr() -> *const RefCell; - fn set_state_ptr(state: *const RefCell); + fn get_state_ptr() -> *const State; + fn set_state_ptr(state: *const State); fn get_allocation_state() -> AllocationState; fn set_allocation_state(state: AllocationState); - fn get_stderr_stream() -> Fd; - fn set_stderr_stream(fd: Fd); } impl State { fn with(f: impl FnOnce(&State) -> Result<(), Errno>) -> Errno { - let ptr = State::ptr(); - let ptr = ptr.try_borrow().unwrap_or_else(|_| unreachable!()); - assert_eq!(ptr.magic1, MAGIC); - assert_eq!(ptr.magic2, MAGIC); - let ret = f(&*ptr); + let state_ref = State::ptr(); + assert_eq!(state_ref.magic1, MAGIC); + assert_eq!(state_ref.magic2, MAGIC); + let ret = f(state_ref); match ret { Ok(()) => ERRNO_SUCCESS, Err(err) => err, } } - fn ptr() -> &'static RefCell { + fn ptr() -> &'static State { unsafe { let mut ptr = get_state_ptr(); if ptr.is_null() { @@ -2396,7 +2426,7 @@ impl State { } #[cold] - fn new() -> &'static RefCell { + fn new() -> &'static State { #[link(wasm_import_module = "__main_module__")] extern "C" { fn cabi_realloc( @@ -2418,19 +2448,20 @@ impl State { cabi_realloc( ptr::null_mut(), 0, - mem::align_of::>(), - mem::size_of::>(), - ) as *mut RefCell + mem::align_of::>(), + mem::size_of::>(), + ) as *mut State }; unsafe { set_allocation_state(AllocationState::StateAllocated) }; unsafe { - ret.write(RefCell::new(State { + ret.write(State { magic1: MAGIC, magic2: MAGIC, import_alloc: ImportAlloc::new(), - descriptors: RefCell::new(None), + descriptors: UnsafeCell::new(None), + descriptors_borrowed: UnsafeCell::new(false), path_buf: UnsafeCell::new(MaybeUninit::uninit()), long_lived_arena: BumpArena::new(), args: Cell::new(None), @@ -2448,33 +2479,62 @@ impl State { path_data: UnsafeCell::new(MaybeUninit::uninit()), }, dotdot: [UnsafeCell::new(b'.'), UnsafeCell::new(b'.')], - })); + }); &*ret } } /// Accessor for the descriptors member that ensures it is properly initialized - fn descriptors<'a>(&'a self) -> impl Deref + 'a { - let mut d = self - .descriptors - .try_borrow_mut() - .unwrap_or_else(|_| unreachable!()); - if d.is_none() { - *d = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena)); + fn with_descriptors T>(&self, fn_: F) -> T { + unsafe { + if core::mem::replace(&mut *self.descriptors_borrowed.get(), true) { + unreachable!(); // Don't borrow descriptors while they're already borrowed. + } } - RefMut::map(d, |d| d.as_mut().unwrap_or_else(|| unreachable!())) + + let descriptors: &mut Option = unsafe { &mut *self.descriptors.get() }; + match descriptors { + None => { + *descriptors = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena)); + } + Some(_descriptors) => {} + } + let result = match descriptors { + Some(descriptors) => fn_(descriptors), + None => unreachable!(), + }; + + unsafe { + *self.descriptors_borrowed.get() = false; + } + + result } - /// Mut accessor for the descriptors member that ensures it is properly initialized - fn descriptors_mut<'a>(&'a self) -> impl DerefMut + Deref + 'a { - let mut d = self - .descriptors - .try_borrow_mut() - .unwrap_or_else(|_| unreachable!()); - if d.is_none() { - *d = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena)); + fn with_descriptors_mut T>(&self, fn_: F) -> T { + unsafe { + if core::mem::replace(&mut *self.descriptors_borrowed.get(), true) { + unreachable!(); // Don't borrow descriptors while they're already borrowed. + } } - RefMut::map(d, |d| d.as_mut().unwrap_or_else(|| unreachable!())) + + let descriptors: &mut Option = unsafe { &mut *self.descriptors.get() }; + match descriptors { + None => { + *descriptors = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena)); + } + Some(_descriptors) => {} + } + let result = match descriptors { + Some(descriptors) => fn_(descriptors), + None => unreachable!(), + }; + + unsafe { + *self.descriptors_borrowed.get() = false; + } + + result } fn get_environment(&self) -> &[StrTuple] { diff --git a/crates/wasi-preview1-component-adapter/src/macros.rs b/crates/wasi-preview1-component-adapter/src/macros.rs index ca46aacacf1c..c4eef95763c9 100644 --- a/crates/wasi-preview1-component-adapter/src/macros.rs +++ b/crates/wasi-preview1-component-adapter/src/macros.rs @@ -3,12 +3,13 @@ //! We're avoiding static initializers, so we can't have things like string //! literals. Replace the standard assert macros with simpler implementations. +use crate::bindings::wasi::cli::stderr::get_stderr; use crate::bindings::wasi::io::streams; #[allow(dead_code)] #[doc(hidden)] pub fn print(message: &[u8]) { - let _ = unsafe { streams::write(crate::get_stderr_stream(), message) }; + let _ = unsafe { get_stderr().blocking_write_and_flush(message) }; } /// A minimal `eprint` for debugging. diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 5bc3b63cf8b0..2ef0c8d67c30 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -33,7 +33,6 @@ cap-net-ext = { workspace = true, optional = true } cap-time-ext = { workspace = true, optional = true } io-lifetimes = { workspace = true, optional = true } fs-set-times = { workspace = true, optional = true } -is-terminal = { workspace = true, optional = true } bitflags = { workspace = true, optional = true } async-trait = { workspace = true, optional = true } system-interface = { workspace = true, optional = true} @@ -72,7 +71,6 @@ preview2 = [ 'dep:cap-time-ext', 'dep:io-lifetimes', 'dep:fs-set-times', - 'dep:is-terminal', 'dep:bitflags', 'dep:async-trait', 'dep:system-interface', diff --git a/crates/wasi/src/preview2/command.rs b/crates/wasi/src/preview2/command.rs index 1ba80b93923c..f83dce6b20cf 100644 --- a/crates/wasi/src/preview2/command.rs +++ b/crates/wasi/src/preview2/command.rs @@ -13,7 +13,7 @@ wasmtime::component::bindgen!({ "wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens, "wasi:sockets/tcp": crate::preview2::bindings::sockets::tcp, "wasi:clocks/monotonic_clock": crate::preview2::bindings::clocks::monotonic_clock, - "wasi:poll/poll": crate::preview2::bindings::poll::poll, + "wasi:io/poll": crate::preview2::bindings::io::poll, "wasi:io/streams": crate::preview2::bindings::io::streams, "wasi:clocks/timezone": crate::preview2::bindings::clocks::timezone, "wasi:clocks/wall_clock": crate::preview2::bindings::clocks::wall_clock, @@ -37,7 +37,7 @@ pub fn add_to_linker(l: &mut wasmtime::component::Linker) -> any crate::preview2::bindings::clocks::timezone::add_to_linker(l, |t| t)?; crate::preview2::bindings::filesystem::types::add_to_linker(l, |t| t)?; crate::preview2::bindings::filesystem::preopens::add_to_linker(l, |t| t)?; - crate::preview2::bindings::poll::poll::add_to_linker(l, |t| t)?; + crate::preview2::bindings::io::poll::add_to_linker(l, |t| t)?; crate::preview2::bindings::io::streams::add_to_linker(l, |t| t)?; crate::preview2::bindings::random::random::add_to_linker(l, |t| t)?; crate::preview2::bindings::cli::exit::add_to_linker(l, |t| t)?; @@ -73,7 +73,7 @@ pub mod sync { "wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens, "wasi:sockets/tcp": crate::preview2::bindings::sockets::tcp, "wasi:clocks/monotonic_clock": crate::preview2::bindings::clocks::monotonic_clock, - "wasi:poll/poll": crate::preview2::bindings::sync_io::poll::poll, + "wasi:io/poll": crate::preview2::bindings::sync_io::io::poll, "wasi:io/streams": crate::preview2::bindings::sync_io::io::streams, "wasi:clocks/timezone": crate::preview2::bindings::clocks::timezone, "wasi:clocks/wall_clock": crate::preview2::bindings::clocks::wall_clock, @@ -99,7 +99,7 @@ pub mod sync { crate::preview2::bindings::clocks::timezone::add_to_linker(l, |t| t)?; crate::preview2::bindings::sync_io::filesystem::types::add_to_linker(l, |t| t)?; crate::preview2::bindings::filesystem::preopens::add_to_linker(l, |t| t)?; - crate::preview2::bindings::sync_io::poll::poll::add_to_linker(l, |t| t)?; + crate::preview2::bindings::sync_io::io::poll::add_to_linker(l, |t| t)?; crate::preview2::bindings::sync_io::io::streams::add_to_linker(l, |t| t)?; crate::preview2::bindings::random::random::add_to_linker(l, |t| t)?; crate::preview2::bindings::cli::exit::add_to_linker(l, |t| t)?; diff --git a/crates/wasi/src/preview2/ctx.rs b/crates/wasi/src/preview2/ctx.rs index 785ce43be2c1..95c0f2eae41d 100644 --- a/crates/wasi/src/preview2/ctx.rs +++ b/crates/wasi/src/preview2/ctx.rs @@ -1,11 +1,10 @@ use super::clocks::host::{monotonic_clock, wall_clock}; use crate::preview2::{ clocks::{self, HostMonotonicClock, HostWallClock}, - filesystem::{Dir, TableFsExt}, + filesystem::Dir, pipe, random, stdio, - stdio::{StdioInput, StdioOutput}, - stream::{HostInputStream, HostOutputStream, TableStreamExt}, - DirPerms, FilePerms, IsATTY, Table, + stdio::{StdinStream, StdoutStream}, + DirPerms, FilePerms, Table, }; use cap_rand::{Rng, RngCore, SeedableRng}; use cap_std::ipnet::{self, IpNet}; @@ -15,9 +14,9 @@ use std::mem; use std::net::{Ipv4Addr, Ipv6Addr}; pub struct WasiCtxBuilder { - stdin: (Box, IsATTY), - stdout: (Box, IsATTY), - stderr: (Box, IsATTY), + stdin: Box, + stdout: Box, + stderr: Box, env: Vec<(String, String)>, args: Vec, preopens: Vec<(Dir, String)>, @@ -65,9 +64,9 @@ impl WasiCtxBuilder { let insecure_random_seed = cap_rand::thread_rng(cap_rand::ambient_authority()).gen::(); Self { - stdin: (Box::new(pipe::ClosedInputStream), IsATTY::No), - stdout: (Box::new(pipe::SinkOutputStream), IsATTY::No), - stderr: (Box::new(pipe::SinkOutputStream), IsATTY::No), + stdin: Box::new(pipe::ClosedInputStream), + stdout: Box::new(pipe::SinkOutputStream), + stderr: Box::new(pipe::SinkOutputStream), env: Vec::new(), args: Vec::new(), preopens: Vec::new(), @@ -81,52 +80,31 @@ impl WasiCtxBuilder { } } - pub fn stdin(&mut self, stdin: impl HostInputStream + 'static, isatty: IsATTY) -> &mut Self { - self.stdin = (Box::new(stdin), isatty); + pub fn stdin(&mut self, stdin: impl StdinStream + 'static) -> &mut Self { + self.stdin = Box::new(stdin); self } - pub fn stdout(&mut self, stdout: impl HostOutputStream + 'static, isatty: IsATTY) -> &mut Self { - self.stdout = (Box::new(stdout), isatty); + pub fn stdout(&mut self, stdout: impl StdoutStream + 'static) -> &mut Self { + self.stdout = Box::new(stdout); self } - pub fn stderr(&mut self, stderr: impl HostOutputStream + 'static, isatty: IsATTY) -> &mut Self { - self.stderr = (Box::new(stderr), isatty); + pub fn stderr(&mut self, stderr: impl StdoutStream + 'static) -> &mut Self { + self.stderr = Box::new(stderr); self } pub fn inherit_stdin(&mut self) -> &mut Self { - use is_terminal::IsTerminal; - let inherited = stdio::stdin(); - let isatty = if inherited.is_terminal() { - IsATTY::Yes - } else { - IsATTY::No - }; - self.stdin(inherited, isatty) + self.stdin(stdio::stdin()) } pub fn inherit_stdout(&mut self) -> &mut Self { - use is_terminal::IsTerminal; - let inherited = stdio::stdout(); - let isatty = if inherited.is_terminal() { - IsATTY::Yes - } else { - IsATTY::No - }; - self.stdout(inherited, isatty) + self.stdout(stdio::stdout()) } pub fn inherit_stderr(&mut self) -> &mut Self { - use is_terminal::IsTerminal; - let inherited = stdio::stderr(); - let isatty = if inherited.is_terminal() { - IsATTY::Yes - } else { - IsATTY::No - }; - self.stderr(inherited, isatty) + self.stderr(stdio::stderr()) } pub fn inherit_stdio(&mut self) -> &mut Self { @@ -270,10 +248,9 @@ impl WasiCtxBuilder { /// # Panics /// /// Panics if this method is called twice. - pub fn build(&mut self, table: &mut Table) -> Result { + pub fn build(&mut self) -> WasiCtx { assert!(!self.built); - use anyhow::Context; let Self { stdin, stdout, @@ -291,33 +268,10 @@ impl WasiCtxBuilder { } = mem::replace(self, Self::new()); self.built = true; - let stdin_ix = table.push_input_stream(stdin.0).context("stdin")?; - let stdout_ix = table.push_output_stream(stdout.0).context("stdout")?; - let stderr_ix = table.push_output_stream(stderr.0).context("stderr")?; - - let preopens = preopens - .into_iter() - .map(|(dir, path)| { - let dirfd = table - .push_dir(dir) - .with_context(|| format!("preopen {path:?}"))?; - Ok((dirfd, path)) - }) - .collect::>>()?; - - Ok(WasiCtx { - stdin: StdioInput { - input_stream: stdin_ix, - isatty: stdin.1, - }, - stdout: StdioOutput { - output_stream: stdout_ix, - isatty: stdout.1, - }, - stderr: StdioOutput { - output_stream: stderr_ix, - isatty: stderr.1, - }, + WasiCtx { + stdin, + stdout, + stderr, env, args, preopens, @@ -327,7 +281,7 @@ impl WasiCtxBuilder { insecure_random_seed, wall_clock, monotonic_clock, - }) + } } } @@ -346,9 +300,9 @@ pub struct WasiCtx { pub(crate) monotonic_clock: Box, pub(crate) env: Vec<(String, String)>, pub(crate) args: Vec, - pub(crate) preopens: Vec<(u32, String)>, - pub(crate) stdin: StdioInput, - pub(crate) stdout: StdioOutput, - pub(crate) stderr: StdioOutput, + pub(crate) preopens: Vec<(Dir, String)>, + pub(crate) stdin: Box, + pub(crate) stdout: Box, + pub(crate) stderr: Box, pub(crate) pool: Pool, } diff --git a/crates/wasi/src/preview2/filesystem.rs b/crates/wasi/src/preview2/filesystem.rs index f99b6ab2d9af..3f3cfb1b4329 100644 --- a/crates/wasi/src/preview2/filesystem.rs +++ b/crates/wasi/src/preview2/filesystem.rs @@ -1,3 +1,4 @@ +use crate::preview2::bindings::filesystem::types::Descriptor; use crate::preview2::{ AbortOnDropJoinHandle, HostOutputStream, OutputStreamError, StreamRuntimeError, StreamState, Table, TableError, @@ -6,6 +7,7 @@ use anyhow::anyhow; use bytes::{Bytes, BytesMut}; use futures::future::{maybe_done, MaybeDone}; use std::sync::Arc; +use wasmtime::component::Resource; bitflags::bitflags! { #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -44,42 +46,42 @@ impl File { } } pub(crate) trait TableFsExt { - fn push_file(&mut self, file: File) -> Result; - fn delete_file(&mut self, fd: u32) -> Result; - fn is_file(&self, fd: u32) -> bool; - fn get_file(&self, fd: u32) -> Result<&File, TableError>; + fn push_file(&mut self, file: File) -> Result, TableError>; + fn delete_file(&mut self, fd: Resource) -> Result; + fn is_file(&self, fd: &Resource) -> bool; + fn get_file(&self, fd: &Resource) -> Result<&File, TableError>; - fn push_dir(&mut self, dir: Dir) -> Result; - fn delete_dir(&mut self, fd: u32) -> Result; - fn is_dir(&self, fd: u32) -> bool; - fn get_dir(&self, fd: u32) -> Result<&Dir, TableError>; + fn push_dir(&mut self, dir: Dir) -> Result, TableError>; + fn delete_dir(&mut self, fd: Resource) -> Result; + fn is_dir(&self, fd: &Resource) -> bool; + fn get_dir(&self, fd: &Resource) -> Result<&Dir, TableError>; } impl TableFsExt for Table { - fn push_file(&mut self, file: File) -> Result { - self.push(Box::new(file)) + fn push_file(&mut self, file: File) -> Result, TableError> { + Ok(Resource::new_own(self.push(Box::new(file))?)) } - fn delete_file(&mut self, fd: u32) -> Result { - self.delete(fd) + fn delete_file(&mut self, fd: Resource) -> Result { + self.delete(fd.rep()) } - fn is_file(&self, fd: u32) -> bool { - self.is::(fd) + fn is_file(&self, fd: &Resource) -> bool { + self.is::(fd.rep()) } - fn get_file(&self, fd: u32) -> Result<&File, TableError> { - self.get(fd) + fn get_file(&self, fd: &Resource) -> Result<&File, TableError> { + self.get(fd.rep()) } - fn push_dir(&mut self, dir: Dir) -> Result { - self.push(Box::new(dir)) + fn push_dir(&mut self, dir: Dir) -> Result, TableError> { + Ok(Resource::new_own(self.push(Box::new(dir))?)) } - fn delete_dir(&mut self, fd: u32) -> Result { - self.delete(fd) + fn delete_dir(&mut self, fd: Resource) -> Result { + self.delete(fd.rep()) } - fn is_dir(&self, fd: u32) -> bool { - self.is::

(fd) + fn is_dir(&self, fd: &Resource) -> bool { + self.is::(fd.rep()) } - fn get_dir(&self, fd: u32) -> Result<&Dir, TableError> { - self.get(fd) + fn get_dir(&self, fd: &Resource) -> Result<&Dir, TableError> { + self.get(fd.rep()) } } @@ -91,6 +93,7 @@ bitflags::bitflags! { } } +#[derive(Clone)] pub(crate) struct Dir { pub dir: Arc, pub perms: DirPerms, diff --git a/crates/wasi/src/preview2/host/clocks.rs b/crates/wasi/src/preview2/host/clocks.rs index 2c461bd804c8..3c9240af5608 100644 --- a/crates/wasi/src/preview2/host/clocks.rs +++ b/crates/wasi/src/preview2/host/clocks.rs @@ -2,12 +2,13 @@ use crate::preview2::bindings::{ clocks::monotonic_clock::{self, Instant}, - clocks::timezone::{self, Timezone, TimezoneDisplay}, + clocks::timezone::{self, TimezoneDisplay}, clocks::wall_clock::{self, Datetime}, - poll::poll::Pollable, + io::poll::Pollable, }; use crate::preview2::{HostPollable, TablePollableExt, WasiView}; use cap_std::time::SystemTime; +use wasmtime::component::Resource; impl TryFrom for Datetime { type Error = anyhow::Error; @@ -50,7 +51,7 @@ impl monotonic_clock::Host for T { Ok(self.ctx().monotonic_clock.resolution()) } - fn subscribe(&mut self, when: Instant, absolute: bool) -> anyhow::Result { + fn subscribe(&mut self, when: Instant, absolute: bool) -> anyhow::Result> { use std::time::Duration; // Calculate time relative to clock object, which may not have the same zero // point as tokio Inst::now() @@ -93,15 +94,11 @@ impl monotonic_clock::Host for T { } impl timezone::Host for T { - fn display(&mut self, timezone: Timezone, when: Datetime) -> anyhow::Result { + fn display(&mut self, when: Datetime) -> anyhow::Result { todo!("timezone display is not implemented") } - fn utc_offset(&mut self, timezone: Timezone, when: Datetime) -> anyhow::Result { + fn utc_offset(&mut self, when: Datetime) -> anyhow::Result { todo!("timezone utc_offset is not implemented") } - - fn drop_timezone(&mut self, timezone: Timezone) -> anyhow::Result<()> { - todo!("timezone drop is not implemented") - } } diff --git a/crates/wasi/src/preview2/host/filesystem.rs b/crates/wasi/src/preview2/host/filesystem.rs index c9f2105fdb44..374d4a455fa5 100644 --- a/crates/wasi/src/preview2/host/filesystem.rs +++ b/crates/wasi/src/preview2/host/filesystem.rs @@ -1,8 +1,13 @@ use crate::preview2::bindings::clocks::wall_clock; +use crate::preview2::bindings::filesystem::types::{ + DirectoryEntryStream, HostDescriptor, HostDirectoryEntryStream, +}; use crate::preview2::bindings::filesystem::{preopens, types}; use crate::preview2::bindings::io::streams; use crate::preview2::filesystem::{Dir, File, TableFsExt}; use crate::preview2::{DirPerms, FilePerms, Table, TableError, WasiView}; +use anyhow::Context; +use wasmtime::component::Resource; use types::ErrorCode; @@ -15,16 +20,29 @@ impl From for types::Error { } impl preopens::Host for T { - fn get_directories(&mut self) -> Result, anyhow::Error> { - Ok(self.ctx().preopens.clone()) + fn get_directories( + &mut self, + ) -> Result, String)>, anyhow::Error> { + let mut results = Vec::new(); + for (dir, name) in self.ctx().preopens.clone() { + let fd = self + .table_mut() + .push_dir(dir) + .with_context(|| format!("failed to push preopen {name}"))?; + results.push((fd, name)); + } + Ok(results) } } #[async_trait::async_trait] -impl types::Host for T { +impl types::Host for T {} + +#[async_trait::async_trait] +impl HostDescriptor for T { async fn advise( &mut self, - fd: types::Descriptor, + fd: Resource, offset: types::Filesize, len: types::Filesize, advice: types::Advice, @@ -41,16 +59,16 @@ impl types::Host for T { Advice::NoReuse => A::NoReuse, }; - let f = self.table().get_file(fd)?; + let f = self.table().get_file(&fd)?; f.spawn_blocking(move |f| f.advise(offset, len, advice)) .await?; Ok(()) } - async fn sync_data(&mut self, fd: types::Descriptor) -> Result<(), types::Error> { + async fn sync_data(&mut self, fd: Resource) -> Result<(), types::Error> { let table = self.table(); - if table.is_file(fd) { - let f = table.get_file(fd)?; + if table.is_file(&fd) { + let f = table.get_file(&fd)?; match f.spawn_blocking(|f| f.sync_data()).await { Ok(()) => Ok(()), // On windows, `sync_data` uses `FileFlushBuffers` which fails with @@ -65,8 +83,8 @@ impl types::Host for T { } Err(e) => Err(e.into()), } - } else if table.is_dir(fd) { - let d = table.get_dir(fd)?; + } else if table.is_dir(&fd) { + let d = table.get_dir(&fd)?; d.spawn_blocking(|d| Ok(d.open(std::path::Component::CurDir)?.sync_data()?)) .await } else { @@ -76,7 +94,7 @@ impl types::Host for T { async fn get_flags( &mut self, - fd: types::Descriptor, + fd: Resource, ) -> Result { use system_interface::fs::{FdFlags, GetSetFdFlags}; use types::DescriptorFlags; @@ -96,8 +114,8 @@ impl types::Host for T { } let table = self.table(); - if table.is_file(fd) { - let f = table.get_file(fd)?; + if table.is_file(&fd) { + let f = table.get_file(&fd)?; let flags = f.spawn_blocking(|f| f.get_fd_flags()).await?; let mut flags = get_from_fdflags(flags); if f.perms.contains(FilePerms::READ) { @@ -107,8 +125,8 @@ impl types::Host for T { flags |= DescriptorFlags::WRITE; } Ok(flags) - } else if table.is_dir(fd) { - let d = table.get_dir(fd)?; + } else if table.is_dir(&fd) { + let d = table.get_dir(&fd)?; let flags = d.spawn_blocking(|d| d.get_fd_flags()).await?; let mut flags = get_from_fdflags(flags); if d.perms.contains(DirPerms::READ) { @@ -125,15 +143,15 @@ impl types::Host for T { async fn get_type( &mut self, - fd: types::Descriptor, + fd: Resource, ) -> Result { let table = self.table(); - if table.is_file(fd) { - let f = table.get_file(fd)?; + if table.is_file(&fd) { + let f = table.get_file(&fd)?; let meta = f.spawn_blocking(|f| f.metadata()).await?; Ok(descriptortype_from(meta.file_type())) - } else if table.is_dir(fd) { + } else if table.is_dir(&fd) { Ok(types::DescriptorType::Directory) } else { Err(ErrorCode::BadDescriptor.into()) @@ -142,10 +160,10 @@ impl types::Host for T { async fn set_size( &mut self, - fd: types::Descriptor, + fd: Resource, size: types::Filesize, ) -> Result<(), types::Error> { - let f = self.table().get_file(fd)?; + let f = self.table().get_file(&fd)?; if !f.perms.contains(FilePerms::WRITE) { Err(ErrorCode::NotPermitted)?; } @@ -155,15 +173,15 @@ impl types::Host for T { async fn set_times( &mut self, - fd: types::Descriptor, + fd: Resource, atim: types::NewTimestamp, mtim: types::NewTimestamp, ) -> Result<(), types::Error> { use fs_set_times::SetTimes; let table = self.table(); - if table.is_file(fd) { - let f = table.get_file(fd)?; + if table.is_file(&fd) { + let f = table.get_file(&fd)?; if !f.perms.contains(FilePerms::WRITE) { return Err(ErrorCode::NotPermitted.into()); } @@ -171,8 +189,8 @@ impl types::Host for T { let mtim = systemtimespec_from(mtim)?; f.spawn_blocking(|f| f.set_times(atim, mtim)).await?; Ok(()) - } else if table.is_dir(fd) { - let d = table.get_dir(fd)?; + } else if table.is_dir(&fd) { + let d = table.get_dir(&fd)?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -187,7 +205,7 @@ impl types::Host for T { async fn read( &mut self, - fd: types::Descriptor, + fd: Resource, len: types::Filesize, offset: types::Filesize, ) -> Result<(Vec, bool), types::Error> { @@ -196,7 +214,7 @@ impl types::Host for T { let table = self.table(); - let f = table.get_file(fd)?; + let f = table.get_file(&fd)?; if !f.perms.contains(FilePerms::READ) { return Err(ErrorCode::NotPermitted.into()); } @@ -225,7 +243,7 @@ impl types::Host for T { async fn write( &mut self, - fd: types::Descriptor, + fd: Resource, buf: Vec, offset: types::Filesize, ) -> Result { @@ -233,7 +251,7 @@ impl types::Host for T { use system_interface::fs::FileIoExt; let table = self.table(); - let f = table.get_file(fd)?; + let f = table.get_file(&fd)?; if !f.perms.contains(FilePerms::WRITE) { return Err(ErrorCode::NotPermitted.into()); } @@ -247,10 +265,10 @@ impl types::Host for T { async fn read_directory( &mut self, - fd: types::Descriptor, - ) -> Result { + fd: Resource, + ) -> Result, types::Error> { let table = self.table_mut(); - let d = table.get_dir(fd)?; + let d = table.get_dir(&fd)?; if !d.perms.contains(DirPerms::READ) { return Err(ErrorCode::NotPermitted.into()); } @@ -310,27 +328,10 @@ impl types::Host for T { Ok(table.push_readdir(ReaddirIterator::new(entries))?) } - async fn read_directory_entry( - &mut self, - stream: types::DirectoryEntryStream, - ) -> Result, types::Error> { + async fn sync(&mut self, fd: Resource) -> Result<(), types::Error> { let table = self.table(); - let readdir = table.get_readdir(stream)?; - readdir.next() - } - - fn drop_directory_entry_stream( - &mut self, - stream: types::DirectoryEntryStream, - ) -> anyhow::Result<()> { - self.table_mut().delete_readdir(stream)?; - Ok(()) - } - - async fn sync(&mut self, fd: types::Descriptor) -> Result<(), types::Error> { - let table = self.table(); - if table.is_file(fd) { - let f = table.get_file(fd)?; + if table.is_file(&fd) { + let f = table.get_file(&fd)?; match f.spawn_blocking(|f| f.sync_all()).await { Ok(()) => Ok(()), // On windows, `sync_data` uses `FileFlushBuffers` which fails with @@ -345,8 +346,8 @@ impl types::Host for T { } Err(e) => Err(e.into()), } - } else if table.is_dir(fd) { - let d = table.get_dir(fd)?; + } else if table.is_dir(&fd) { + let d = table.get_dir(&fd)?; d.spawn_blocking(|d| Ok(d.open(std::path::Component::CurDir)?.sync_all()?)) .await } else { @@ -356,11 +357,11 @@ impl types::Host for T { async fn create_directory_at( &mut self, - fd: types::Descriptor, + fd: Resource, path: String, ) -> Result<(), types::Error> { let table = self.table(); - let d = table.get_dir(fd)?; + let d = table.get_dir(&fd)?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -368,15 +369,18 @@ impl types::Host for T { Ok(()) } - async fn stat(&mut self, fd: types::Descriptor) -> Result { + async fn stat( + &mut self, + fd: Resource, + ) -> Result { let table = self.table(); - if table.is_file(fd) { - let f = table.get_file(fd)?; + if table.is_file(&fd) { + let f = table.get_file(&fd)?; // No permissions check on stat: if opened, allowed to stat it let meta = f.spawn_blocking(|f| f.metadata()).await?; Ok(descriptorstat_from(meta)) - } else if table.is_dir(fd) { - let d = table.get_dir(fd)?; + } else if table.is_dir(&fd) { + let d = table.get_dir(&fd)?; // No permissions check on stat: if opened, allowed to stat it let meta = d.spawn_blocking(|d| d.dir_metadata()).await?; Ok(descriptorstat_from(meta)) @@ -387,12 +391,12 @@ impl types::Host for T { async fn stat_at( &mut self, - fd: types::Descriptor, + fd: Resource, path_flags: types::PathFlags, path: String, ) -> Result { let table = self.table(); - let d = table.get_dir(fd)?; + let d = table.get_dir(&fd)?; if !d.perms.contains(DirPerms::READ) { return Err(ErrorCode::NotPermitted.into()); } @@ -407,7 +411,7 @@ impl types::Host for T { async fn set_times_at( &mut self, - fd: types::Descriptor, + fd: Resource, path_flags: types::PathFlags, path: String, atim: types::NewTimestamp, @@ -416,7 +420,7 @@ impl types::Host for T { use cap_fs_ext::DirExt; let table = self.table(); - let d = table.get_dir(fd)?; + let d = table.get_dir(&fd)?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -446,19 +450,19 @@ impl types::Host for T { async fn link_at( &mut self, - fd: types::Descriptor, + fd: Resource, // TODO delete the path flags from this function old_path_flags: types::PathFlags, old_path: String, - new_descriptor: types::Descriptor, + new_descriptor: Resource, new_path: String, ) -> Result<(), types::Error> { let table = self.table(); - let old_dir = table.get_dir(fd)?; + let old_dir = table.get_dir(&fd)?; if !old_dir.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } - let new_dir = table.get_dir(new_descriptor)?; + let new_dir = table.get_dir(&new_descriptor)?; if !new_dir.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -474,7 +478,7 @@ impl types::Host for T { async fn open_at( &mut self, - fd: types::Descriptor, + fd: Resource, path_flags: types::PathFlags, path: String, oflags: types::OpenFlags, @@ -482,16 +486,16 @@ impl types::Host for T { // TODO: These are the permissions to use when creating a new file. // Not implemented yet. _mode: types::Modes, - ) -> Result { + ) -> Result, types::Error> { use cap_fs_ext::{FollowSymlinks, OpenOptionsFollowExt, OpenOptionsMaybeDirExt}; use system_interface::fs::{FdFlags, GetSetFdFlags}; use types::{DescriptorFlags, OpenFlags}; let table = self.table_mut(); - if table.is_file(fd) { + if table.is_file(&fd) { Err(ErrorCode::NotDirectory)?; } - let d = table.get_dir(fd)?; + let d = table.get_dir(&fd)?; if !d.perms.contains(DirPerms::READ) { Err(ErrorCode::NotPermitted)?; } @@ -590,7 +594,7 @@ impl types::Host for T { } } - fn drop_descriptor(&mut self, fd: types::Descriptor) -> anyhow::Result<()> { + fn drop(&mut self, fd: Resource) -> anyhow::Result<()> { let table = self.table_mut(); // The Drop will close the file/dir, but if the close syscall @@ -598,7 +602,7 @@ impl types::Host for T { // tokio::fs::File just uses std::fs::File's Drop impl to close, so // it doesn't appear anyone else has found this to be a problem. // (Not that they could solve it without async drop...) - if table.delete_file(fd).is_err() { + if table.delete_file(Resource::new_own(fd.rep())).is_err() { table.delete_dir(fd)?; } @@ -607,11 +611,11 @@ impl types::Host for T { async fn readlink_at( &mut self, - fd: types::Descriptor, + fd: Resource, path: String, ) -> Result { let table = self.table(); - let d = table.get_dir(fd)?; + let d = table.get_dir(&fd)?; if !d.perms.contains(DirPerms::READ) { return Err(ErrorCode::NotPermitted.into()); } @@ -624,11 +628,11 @@ impl types::Host for T { async fn remove_directory_at( &mut self, - fd: types::Descriptor, + fd: Resource, path: String, ) -> Result<(), types::Error> { let table = self.table(); - let d = table.get_dir(fd)?; + let d = table.get_dir(&fd)?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -637,17 +641,17 @@ impl types::Host for T { async fn rename_at( &mut self, - fd: types::Descriptor, + fd: Resource, old_path: String, - new_fd: types::Descriptor, + new_fd: Resource, new_path: String, ) -> Result<(), types::Error> { let table = self.table(); - let old_dir = table.get_dir(fd)?; + let old_dir = table.get_dir(&fd)?; if !old_dir.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } - let new_dir = table.get_dir(new_fd)?; + let new_dir = table.get_dir(&new_fd)?; if !new_dir.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -659,7 +663,7 @@ impl types::Host for T { async fn symlink_at( &mut self, - fd: types::Descriptor, + fd: Resource, src_path: String, dest_path: String, ) -> Result<(), types::Error> { @@ -668,7 +672,7 @@ impl types::Host for T { use cap_fs_ext::DirExt; let table = self.table(); - let d = table.get_dir(fd)?; + let d = table.get_dir(&fd)?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -678,13 +682,13 @@ impl types::Host for T { async fn unlink_file_at( &mut self, - fd: types::Descriptor, + fd: Resource, path: String, ) -> Result<(), types::Error> { use cap_fs_ext::DirExt; let table = self.table(); - let d = table.get_dir(fd)?; + let d = table.get_dir(&fd)?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -694,7 +698,7 @@ impl types::Host for T { async fn access_at( &mut self, - _fd: types::Descriptor, + _fd: Resource, _path_flags: types::PathFlags, _path: String, _access: types::AccessType, @@ -704,7 +708,7 @@ impl types::Host for T { async fn change_file_permissions_at( &mut self, - _fd: types::Descriptor, + _fd: Resource, _path_flags: types::PathFlags, _path: String, _mode: types::Modes, @@ -714,7 +718,7 @@ impl types::Host for T { async fn change_directory_permissions_at( &mut self, - _fd: types::Descriptor, + _fd: Resource, _path_flags: types::PathFlags, _path: String, _mode: types::Modes, @@ -722,38 +726,47 @@ impl types::Host for T { todo!("filesystem change_directory_permissions_at is not implemented") } - async fn lock_shared(&mut self, _fd: types::Descriptor) -> Result<(), types::Error> { + async fn lock_shared(&mut self, _fd: Resource) -> Result<(), types::Error> { todo!("filesystem lock_shared is not implemented") } - async fn lock_exclusive(&mut self, _fd: types::Descriptor) -> Result<(), types::Error> { + async fn lock_exclusive( + &mut self, + _fd: Resource, + ) -> Result<(), types::Error> { todo!("filesystem lock_exclusive is not implemented") } - async fn try_lock_shared(&mut self, _fd: types::Descriptor) -> Result<(), types::Error> { + async fn try_lock_shared( + &mut self, + _fd: Resource, + ) -> Result<(), types::Error> { todo!("filesystem try_lock_shared is not implemented") } - async fn try_lock_exclusive(&mut self, _fd: types::Descriptor) -> Result<(), types::Error> { + async fn try_lock_exclusive( + &mut self, + _fd: Resource, + ) -> Result<(), types::Error> { todo!("filesystem try_lock_exclusive is not implemented") } - async fn unlock(&mut self, _fd: types::Descriptor) -> Result<(), types::Error> { + async fn unlock(&mut self, _fd: Resource) -> Result<(), types::Error> { todo!("filesystem unlock is not implemented") } fn read_via_stream( &mut self, - fd: types::Descriptor, + fd: Resource, offset: types::Filesize, - ) -> Result { + ) -> Result, types::Error> { use crate::preview2::{ filesystem::FileInputStream, stream::{InternalInputStream, InternalTableStreamExt}, }; // Trap if fd lookup fails: - let f = self.table().get_file(fd)?; + let f = self.table().get_file(&fd)?; if !f.perms.contains(FilePerms::READ) { Err(types::ErrorCode::BadDescriptor)?; @@ -774,13 +787,13 @@ impl types::Host for T { fn write_via_stream( &mut self, - fd: types::Descriptor, + fd: Resource, offset: types::Filesize, - ) -> Result { + ) -> Result, types::Error> { use crate::preview2::{filesystem::FileOutputStream, TableStreamExt}; // Trap if fd lookup fails: - let f = self.table().get_file(fd)?; + let f = self.table().get_file(&fd)?; if !f.perms.contains(FilePerms::WRITE) { Err(types::ErrorCode::BadDescriptor)?; @@ -800,12 +813,12 @@ impl types::Host for T { fn append_via_stream( &mut self, - fd: types::Descriptor, - ) -> Result { + fd: Resource, + ) -> Result, types::Error> { use crate::preview2::{filesystem::FileOutputStream, TableStreamExt}; // Trap if fd lookup fails: - let f = self.table().get_file(fd)?; + let f = self.table().get_file(&fd)?; if !f.perms.contains(FilePerms::WRITE) { Err(types::ErrorCode::BadDescriptor)?; @@ -824,8 +837,8 @@ impl types::Host for T { async fn is_same_object( &mut self, - a: types::Descriptor, - b: types::Descriptor, + a: Resource, + b: Resource, ) -> anyhow::Result { use cap_fs_ext::MetadataExt; let table = self.table(); @@ -850,7 +863,7 @@ impl types::Host for T { } async fn metadata_hash( &mut self, - fd: types::Descriptor, + fd: Resource, ) -> Result { let table = self.table(); let meta = get_descriptor_metadata(table, fd).await?; @@ -858,12 +871,12 @@ impl types::Host for T { } async fn metadata_hash_at( &mut self, - fd: types::Descriptor, + fd: Resource, path_flags: types::PathFlags, path: String, ) -> Result { let table = self.table(); - let d = table.get_dir(fd)?; + let d = table.get_dir(&fd)?; // No permissions check on metadata: if dir opened, allowed to stat it let meta = d .spawn_blocking(move |d| { @@ -878,16 +891,33 @@ impl types::Host for T { } } +#[async_trait::async_trait] +impl HostDirectoryEntryStream for T { + async fn read_directory_entry( + &mut self, + stream: Resource, + ) -> Result, types::Error> { + let table = self.table(); + let readdir = table.get_readdir(&stream)?; + readdir.next() + } + + fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { + self.table_mut().delete_readdir(stream)?; + Ok(()) + } +} + async fn get_descriptor_metadata( table: &Table, - fd: types::Descriptor, + fd: Resource, ) -> Result { - if table.is_file(fd) { - let f = table.get_file(fd)?; + if table.is_file(&fd) { + let f = table.get_file(&fd)?; // No permissions check on metadata: if opened, allowed to stat it Ok(f.spawn_blocking(|f| f.metadata()).await?) - } else if table.is_dir(fd) { - let d = table.get_dir(fd)?; + } else if table.is_dir(&fd) { + let d = table.get_dir(&fd)?; // No permissions check on metadata: if opened, allowed to stat it Ok(d.spawn_blocking(|d| d.dir_metadata()).await?) } else { @@ -1065,28 +1095,9 @@ fn descriptorstat_from(meta: cap_std::fs::Metadata) -> types::DescriptorStat { type_: descriptortype_from(meta.file_type()), link_count: meta.nlink(), size: meta.len(), - // FIXME change the wit to make these timestamps optional - data_access_timestamp: meta - .accessed() - .map(|t| datetime_from(t.into_std())) - .unwrap_or(wall_clock::Datetime { - seconds: 0, - nanoseconds: 0, - }), - data_modification_timestamp: meta - .modified() - .map(|t| datetime_from(t.into_std())) - .unwrap_or(wall_clock::Datetime { - seconds: 0, - nanoseconds: 0, - }), - status_change_timestamp: meta - .created() - .map(|t| datetime_from(t.into_std())) - .unwrap_or(wall_clock::Datetime { - seconds: 0, - nanoseconds: 0, - }), + data_access_timestamp: meta.accessed().map(|t| datetime_from(t.into_std())).ok(), + data_modification_timestamp: meta.modified().map(|t| datetime_from(t.into_std())).ok(), + status_change_timestamp: meta.created().map(|t| datetime_from(t.into_std())).ok(), } } @@ -1121,21 +1132,39 @@ impl IntoIterator for ReaddirIterator { } pub(crate) trait TableReaddirExt { - fn push_readdir(&mut self, readdir: ReaddirIterator) -> Result; - fn delete_readdir(&mut self, fd: u32) -> Result; - fn get_readdir(&self, fd: u32) -> Result<&ReaddirIterator, TableError>; + fn push_readdir( + &mut self, + readdir: ReaddirIterator, + ) -> Result, TableError>; + fn delete_readdir( + &mut self, + fd: Resource, + ) -> Result; + fn get_readdir( + &self, + fd: &Resource, + ) -> Result<&ReaddirIterator, TableError>; } impl TableReaddirExt for Table { - fn push_readdir(&mut self, readdir: ReaddirIterator) -> Result { - self.push(Box::new(readdir)) + fn push_readdir( + &mut self, + readdir: ReaddirIterator, + ) -> Result, TableError> { + Ok(Resource::new_own(self.push(Box::new(readdir))?)) } - fn delete_readdir(&mut self, fd: u32) -> Result { - self.delete(fd) + fn delete_readdir( + &mut self, + fd: Resource, + ) -> Result { + self.delete(fd.rep()) } - fn get_readdir(&self, fd: u32) -> Result<&ReaddirIterator, TableError> { - self.get(fd) + fn get_readdir( + &self, + fd: &Resource, + ) -> Result<&ReaddirIterator, TableError> { + self.get(fd.rep()) } } @@ -1160,8 +1189,7 @@ mod test { let ix = table .push_readdir(ReaddirIterator::new(std::iter::empty())) .unwrap(); - let _ = table.get_readdir(ix).unwrap(); + let _ = table.get_readdir(&ix).unwrap(); table.delete_readdir(ix).unwrap(); - let _ = table.get_readdir(ix).err().unwrap(); } } diff --git a/crates/wasi/src/preview2/host/filesystem/sync.rs b/crates/wasi/src/preview2/host/filesystem/sync.rs index 55ad761d8cfc..36bb244b5719 100644 --- a/crates/wasi/src/preview2/host/filesystem/sync.rs +++ b/crates/wasi/src/preview2/host/filesystem/sync.rs @@ -2,154 +2,146 @@ use crate::preview2::bindings::filesystem::types as async_filesystem; use crate::preview2::bindings::sync_io::filesystem::types as sync_filesystem; use crate::preview2::bindings::sync_io::io::streams; use crate::preview2::in_tokio; +use wasmtime::component::Resource; -impl sync_filesystem::Host for T { +impl sync_filesystem::Host for T {} + +impl sync_filesystem::HostDescriptor for T { fn advise( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, offset: sync_filesystem::Filesize, len: sync_filesystem::Filesize, advice: sync_filesystem::Advice, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::advise(self, fd, offset, len, advice.into()).await + async_filesystem::HostDescriptor::advise(self, fd, offset, len, advice.into()).await })?) } - fn sync_data(&mut self, fd: sync_filesystem::Descriptor) -> Result<(), sync_filesystem::Error> { + fn sync_data( + &mut self, + fd: Resource, + ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::sync_data(self, fd).await + async_filesystem::HostDescriptor::sync_data(self, fd).await })?) } fn get_flags( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, ) -> Result { - Ok(in_tokio(async { async_filesystem::Host::get_flags(self, fd).await })?.into()) + Ok(in_tokio(async { async_filesystem::HostDescriptor::get_flags(self, fd).await })?.into()) } fn get_type( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, ) -> Result { - Ok(in_tokio(async { async_filesystem::Host::get_type(self, fd).await })?.into()) + Ok(in_tokio(async { async_filesystem::HostDescriptor::get_type(self, fd).await })?.into()) } fn set_size( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, size: sync_filesystem::Filesize, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::set_size(self, fd, size).await + async_filesystem::HostDescriptor::set_size(self, fd, size).await })?) } fn set_times( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, atim: sync_filesystem::NewTimestamp, mtim: sync_filesystem::NewTimestamp, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::set_times(self, fd, atim.into(), mtim.into()).await + async_filesystem::HostDescriptor::set_times(self, fd, atim.into(), mtim.into()).await })?) } fn read( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, len: sync_filesystem::Filesize, offset: sync_filesystem::Filesize, ) -> Result<(Vec, bool), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::read(self, fd, len, offset).await + async_filesystem::HostDescriptor::read(self, fd, len, offset).await })?) } fn write( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, buf: Vec, offset: sync_filesystem::Filesize, ) -> Result { Ok(in_tokio(async { - async_filesystem::Host::write(self, fd, buf, offset).await + async_filesystem::HostDescriptor::write(self, fd, buf, offset).await })?) } fn read_directory( &mut self, - fd: sync_filesystem::Descriptor, - ) -> Result { + fd: Resource, + ) -> Result, sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::read_directory(self, fd).await + async_filesystem::HostDescriptor::read_directory(self, fd).await })?) } - fn read_directory_entry( - &mut self, - stream: sync_filesystem::DirectoryEntryStream, - ) -> Result, sync_filesystem::Error> { - Ok( - in_tokio(async { async_filesystem::Host::read_directory_entry(self, stream).await })? - .map(|e| e.into()), - ) - } - - fn drop_directory_entry_stream( + fn sync( &mut self, - stream: sync_filesystem::DirectoryEntryStream, - ) -> anyhow::Result<()> { - async_filesystem::Host::drop_directory_entry_stream(self, stream) - } - - fn sync(&mut self, fd: sync_filesystem::Descriptor) -> Result<(), sync_filesystem::Error> { + fd: Resource, + ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::sync(self, fd).await + async_filesystem::HostDescriptor::sync(self, fd).await })?) } fn create_directory_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path: String, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::create_directory_at(self, fd, path).await + async_filesystem::HostDescriptor::create_directory_at(self, fd, path).await })?) } fn stat( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, ) -> Result { - Ok(in_tokio(async { async_filesystem::Host::stat(self, fd).await })?.into()) + Ok(in_tokio(async { async_filesystem::HostDescriptor::stat(self, fd).await })?.into()) } fn stat_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path_flags: sync_filesystem::PathFlags, path: String, ) -> Result { Ok(in_tokio(async { - async_filesystem::Host::stat_at(self, fd, path_flags.into(), path).await + async_filesystem::HostDescriptor::stat_at(self, fd, path_flags.into(), path).await })? .into()) } fn set_times_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path_flags: sync_filesystem::PathFlags, path: String, atim: sync_filesystem::NewTimestamp, mtim: sync_filesystem::NewTimestamp, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::set_times_at( + async_filesystem::HostDescriptor::set_times_at( self, fd, path_flags.into(), @@ -163,15 +155,15 @@ impl sync_filesystem::Host for T { fn link_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, // TODO delete the path flags from this function old_path_flags: sync_filesystem::PathFlags, old_path: String, - new_descriptor: sync_filesystem::Descriptor, + new_descriptor: Resource, new_path: String, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::link_at( + async_filesystem::HostDescriptor::link_at( self, fd, old_path_flags.into(), @@ -185,15 +177,15 @@ impl sync_filesystem::Host for T { fn open_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path_flags: sync_filesystem::PathFlags, path: String, oflags: sync_filesystem::OpenFlags, flags: sync_filesystem::DescriptorFlags, mode: sync_filesystem::Modes, - ) -> Result { + ) -> Result, sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::open_at( + async_filesystem::HostDescriptor::open_at( self, fd, path_flags.into(), @@ -206,85 +198,91 @@ impl sync_filesystem::Host for T { })?) } - fn drop_descriptor(&mut self, fd: sync_filesystem::Descriptor) -> anyhow::Result<()> { - async_filesystem::Host::drop_descriptor(self, fd) + fn drop(&mut self, fd: Resource) -> anyhow::Result<()> { + async_filesystem::HostDescriptor::drop(self, fd) } fn readlink_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path: String, ) -> Result { Ok(in_tokio(async { - async_filesystem::Host::readlink_at(self, fd, path).await + async_filesystem::HostDescriptor::readlink_at(self, fd, path).await })?) } fn remove_directory_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path: String, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::remove_directory_at(self, fd, path).await + async_filesystem::HostDescriptor::remove_directory_at(self, fd, path).await })?) } fn rename_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, old_path: String, - new_fd: sync_filesystem::Descriptor, + new_fd: Resource, new_path: String, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::rename_at(self, fd, old_path, new_fd, new_path).await + async_filesystem::HostDescriptor::rename_at(self, fd, old_path, new_fd, new_path).await })?) } fn symlink_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, src_path: String, dest_path: String, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::symlink_at(self, fd, src_path, dest_path).await + async_filesystem::HostDescriptor::symlink_at(self, fd, src_path, dest_path).await })?) } fn unlink_file_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path: String, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::unlink_file_at(self, fd, path).await + async_filesystem::HostDescriptor::unlink_file_at(self, fd, path).await })?) } fn access_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path_flags: sync_filesystem::PathFlags, path: String, access: sync_filesystem::AccessType, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::access_at(self, fd, path_flags.into(), path, access.into()) - .await + async_filesystem::HostDescriptor::access_at( + self, + fd, + path_flags.into(), + path, + access.into(), + ) + .await })?) } fn change_file_permissions_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path_flags: sync_filesystem::PathFlags, path: String, mode: sync_filesystem::Modes, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::change_file_permissions_at( + async_filesystem::HostDescriptor::change_file_permissions_at( self, fd, path_flags.into(), @@ -297,13 +295,13 @@ impl sync_filesystem::Host for T { fn change_directory_permissions_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path_flags: sync_filesystem::PathFlags, path: String, mode: sync_filesystem::Modes, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::change_directory_permissions_at( + async_filesystem::HostDescriptor::change_directory_permissions_at( self, fd, path_flags.into(), @@ -316,97 +314,131 @@ impl sync_filesystem::Host for T { fn lock_shared( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::lock_shared(self, fd).await + async_filesystem::HostDescriptor::lock_shared(self, fd).await })?) } fn lock_exclusive( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::lock_exclusive(self, fd).await + async_filesystem::HostDescriptor::lock_exclusive(self, fd).await })?) } fn try_lock_shared( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::try_lock_shared(self, fd).await + async_filesystem::HostDescriptor::try_lock_shared(self, fd).await })?) } fn try_lock_exclusive( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::try_lock_exclusive(self, fd).await + async_filesystem::HostDescriptor::try_lock_exclusive(self, fd).await })?) } - fn unlock(&mut self, fd: sync_filesystem::Descriptor) -> Result<(), sync_filesystem::Error> { + fn unlock( + &mut self, + fd: Resource, + ) -> Result<(), sync_filesystem::Error> { Ok(in_tokio(async { - async_filesystem::Host::unlock(self, fd).await + async_filesystem::HostDescriptor::unlock(self, fd).await })?) } fn read_via_stream( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, offset: sync_filesystem::Filesize, - ) -> Result { - Ok(async_filesystem::Host::read_via_stream(self, fd, offset)?) + ) -> Result, sync_filesystem::Error> { + Ok(async_filesystem::HostDescriptor::read_via_stream( + self, fd, offset, + )?) } fn write_via_stream( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, offset: sync_filesystem::Filesize, - ) -> Result { - Ok(async_filesystem::Host::write_via_stream(self, fd, offset)?) + ) -> Result, sync_filesystem::Error> { + Ok(async_filesystem::HostDescriptor::write_via_stream( + self, fd, offset, + )?) } fn append_via_stream( &mut self, - fd: sync_filesystem::Descriptor, - ) -> Result { - Ok(async_filesystem::Host::append_via_stream(self, fd)?) + fd: Resource, + ) -> Result, sync_filesystem::Error> { + Ok(async_filesystem::HostDescriptor::append_via_stream( + self, fd, + )?) } fn is_same_object( &mut self, - a: sync_filesystem::Descriptor, - b: sync_filesystem::Descriptor, + a: Resource, + b: Resource, ) -> anyhow::Result { Ok(in_tokio(async { - async_filesystem::Host::is_same_object(self, a, b).await + async_filesystem::HostDescriptor::is_same_object(self, a, b).await })?) } fn metadata_hash( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, ) -> Result { - Ok(in_tokio(async { async_filesystem::Host::metadata_hash(self, fd).await })?.into()) + Ok( + in_tokio(async { async_filesystem::HostDescriptor::metadata_hash(self, fd).await })? + .into(), + ) } fn metadata_hash_at( &mut self, - fd: sync_filesystem::Descriptor, + fd: Resource, path_flags: sync_filesystem::PathFlags, path: String, ) -> Result { Ok(in_tokio(async { - async_filesystem::Host::metadata_hash_at(self, fd, path_flags.into(), path).await + async_filesystem::HostDescriptor::metadata_hash_at(self, fd, path_flags.into(), path) + .await })? .into()) } } +impl sync_filesystem::HostDirectoryEntryStream + for T +{ + fn read_directory_entry( + &mut self, + stream: Resource, + ) -> Result, sync_filesystem::Error> { + Ok(in_tokio(async { + async_filesystem::HostDirectoryEntryStream::read_directory_entry(self, stream).await + })? + .map(|e| e.into())) + } + + fn drop( + &mut self, + stream: Resource, + ) -> anyhow::Result<()> { + async_filesystem::HostDirectoryEntryStream::drop(self, stream) + } +} + impl From for sync_filesystem::ErrorCode { fn from(other: async_filesystem::ErrorCode) -> Self { use async_filesystem::ErrorCode; diff --git a/crates/wasi/src/preview2/host/instance_network.rs b/crates/wasi/src/preview2/host/instance_network.rs index 8c8b56974aa1..f85e411e1e94 100644 --- a/crates/wasi/src/preview2/host/instance_network.rs +++ b/crates/wasi/src/preview2/host/instance_network.rs @@ -1,10 +1,11 @@ use crate::preview2::bindings::sockets::instance_network::{self, Network}; -use crate::preview2::network::{HostNetwork, TableNetworkExt}; +use crate::preview2::network::{HostNetworkState, TableNetworkExt}; use crate::preview2::WasiView; +use wasmtime::component::Resource; impl instance_network::Host for T { - fn instance_network(&mut self) -> Result { - let network = HostNetwork::new(self.ctx().pool.clone()); + fn instance_network(&mut self) -> Result, anyhow::Error> { + let network = HostNetworkState::new(self.ctx().pool.clone()); let network = self.table_mut().push_network(network)?; Ok(network) } diff --git a/crates/wasi/src/preview2/host/io.rs b/crates/wasi/src/preview2/host/io.rs index 128272850348..ddf309ca0517 100644 --- a/crates/wasi/src/preview2/host/io.rs +++ b/crates/wasi/src/preview2/host/io.rs @@ -1,6 +1,6 @@ use crate::preview2::{ + bindings::io::poll::Pollable, bindings::io::streams::{self, InputStream, OutputStream}, - bindings::poll::poll::Pollable, filesystem::FileInputStream, poll::PollableFuture, stream::{ @@ -13,6 +13,7 @@ use std::any::Any; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; +use wasmtime::component::Resource; impl From for streams::StreamStatus { fn from(state: StreamState) -> Self { @@ -42,23 +43,221 @@ impl From for streams::Error { } #[async_trait::async_trait] -impl streams::Host for T { - fn drop_input_stream(&mut self, stream: InputStream) -> anyhow::Result<()> { - self.table_mut().delete_internal_input_stream(stream)?; +impl streams::Host for T {} + +#[async_trait::async_trait] +impl streams::HostOutputStream for T { + fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { + self.table_mut().delete_output_stream(stream)?; Ok(()) } - fn drop_output_stream(&mut self, stream: OutputStream) -> anyhow::Result<()> { - self.table_mut().delete_output_stream(stream)?; + fn check_write(&mut self, stream: Resource) -> Result { + let s = self.table_mut().get_output_stream_mut(&stream)?; + let mut ready = s.write_ready(); + let mut task = Context::from_waker(futures::task::noop_waker_ref()); + match Pin::new(&mut ready).poll(&mut task) { + Poll::Ready(Ok(permit)) => Ok(permit as u64), + Poll::Ready(Err(e)) => Err(e.into()), + Poll::Pending => Ok(0), + } + } + + fn write( + &mut self, + stream: Resource, + bytes: Vec, + ) -> Result<(), streams::Error> { + let s = self.table_mut().get_output_stream_mut(&stream)?; + HostOutputStream::write(s, bytes.into())?; + Ok(()) + } + + fn subscribe(&mut self, stream: Resource) -> anyhow::Result> { + // Ensure that table element is an output-stream: + let _ = self.table_mut().get_output_stream_mut(&stream)?; + + fn output_stream_ready<'a>(stream: &'a mut dyn Any) -> PollableFuture<'a> { + let stream = stream + .downcast_mut::>() + .expect("downcast to HostOutputStream failed"); + Box::pin(async move { + let _ = stream.write_ready().await?; + Ok(()) + }) + } + + Ok(self + .table_mut() + .push_host_pollable(HostPollable::TableEntry { + index: stream.rep(), + make_future: output_stream_ready, + })?) + } + + async fn blocking_write_and_flush( + &mut self, + stream: Resource, + bytes: Vec, + ) -> Result<(), streams::Error> { + let s = self.table_mut().get_output_stream_mut(&stream)?; + + if bytes.len() > 4096 { + return Err(streams::Error::trap(anyhow::anyhow!( + "Buffer too large for blocking-write-and-flush (expected at most 4096)" + ))); + } + + let mut bytes = bytes::Bytes::from(bytes); + while !bytes.is_empty() { + let permit = s.write_ready().await?; + let len = bytes.len().min(permit); + let chunk = bytes.split_to(len); + HostOutputStream::write(s, chunk)?; + } + + HostOutputStream::flush(s)?; + let _ = s.write_ready().await?; + + Ok(()) + } + + async fn blocking_write_zeroes_and_flush( + &mut self, + stream: Resource, + len: u64, + ) -> Result<(), streams::Error> { + let s = self.table_mut().get_output_stream_mut(&stream)?; + + if len > 4096 { + return Err(streams::Error::trap(anyhow::anyhow!( + "Buffer too large for blocking-write-zeroes-and-flush (expected at most 4096)" + ))); + } + + let mut len = len; + while len > 0 { + let permit = s.write_ready().await?; + let this_len = len.min(permit as u64); + HostOutputStream::write_zeroes(s, this_len as usize)?; + len -= this_len; + } + + HostOutputStream::flush(s)?; + let _ = s.write_ready().await?; + + Ok(()) + } + + fn write_zeroes( + &mut self, + stream: Resource, + len: u64, + ) -> Result<(), streams::Error> { + let s = self.table_mut().get_output_stream_mut(&stream)?; + HostOutputStream::write_zeroes(s, len as usize)?; + Ok(()) + } + + fn flush(&mut self, stream: Resource) -> Result<(), streams::Error> { + let s = self.table_mut().get_output_stream_mut(&stream)?; + HostOutputStream::flush(s)?; + Ok(()) + } + + async fn blocking_flush( + &mut self, + stream: Resource, + ) -> Result<(), streams::Error> { + let s = self.table_mut().get_output_stream_mut(&stream)?; + HostOutputStream::flush(s)?; + let _ = s.write_ready().await?; + Ok(()) + } + + async fn splice( + &mut self, + _dst: Resource, + _src: Resource, + _len: u64, + ) -> anyhow::Result> { + // TODO: We can't get two streams at the same time because they both + // carry the exclusive lifetime of `ctx`. When [`get_many_mut`] is + // stabilized, that could allow us to add a `get_many_stream_mut` or + // so which lets us do this. + // + // [`get_many_mut`]: https://doc.rust-lang.org/stable/std/collections/hash_map/struct.HashMap.html#method.get_many_mut + /* + let s: &mut Box = ctx + .table_mut() + .get_input_stream_mut(src) + ?; + let d: &mut Box = ctx + .table_mut() + .get_output_stream_mut(dst) + ?; + + let bytes_spliced: u64 = s.splice(&mut **d, len).await?; + + Ok(bytes_spliced) + */ + todo!("stream splice is not implemented") + } + + async fn blocking_splice( + &mut self, + _dst: Resource, + _src: Resource, + _len: u64, + ) -> anyhow::Result> { + // TODO: once splice is implemented, figure out what the blocking semantics are for waiting + // on src and dest here. + todo!("stream splice is not implemented") + } + + async fn forward( + &mut self, + _dst: Resource, + _src: Resource, + ) -> anyhow::Result> { + // TODO: We can't get two streams at the same time because they both + // carry the exclusive lifetime of `ctx`. When [`get_many_mut`] is + // stabilized, that could allow us to add a `get_many_stream_mut` or + // so which lets us do this. + // + // [`get_many_mut`]: https://doc.rust-lang.org/stable/std/collections/hash_map/struct.HashMap.html#method.get_many_mut + /* + let s: &mut Box = ctx + .table_mut() + .get_input_stream_mut(src) + ?; + let d: &mut Box = ctx + .table_mut() + .get_output_stream_mut(dst) + ?; + + let bytes_spliced: u64 = s.splice(&mut **d, len).await?; + + Ok(bytes_spliced) + */ + + todo!("stream forward is not implemented") + } +} + +#[async_trait::async_trait] +impl streams::HostInputStream for T { + fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { + self.table_mut().delete_internal_input_stream(stream)?; Ok(()) } async fn read( &mut self, - stream: InputStream, + stream: Resource, len: u64, ) -> anyhow::Result, streams::StreamStatus), ()>> { - match self.table_mut().get_internal_input_stream_mut(stream)? { + match self.table_mut().get_internal_input_stream_mut(&stream)? { InternalInputStream::Host(s) => { let (bytes, state) = match HostInputStream::read(s.as_mut(), len as usize) { Ok(a) => a, @@ -94,10 +293,10 @@ impl streams::Host for T { async fn blocking_read( &mut self, - stream: InputStream, + stream: Resource, len: u64, ) -> anyhow::Result, streams::StreamStatus), ()>> { - match self.table_mut().get_internal_input_stream_mut(stream)? { + match self.table_mut().get_internal_input_stream_mut(&stream)? { InternalInputStream::Host(s) => { s.ready().await?; let (bytes, state) = match HostInputStream::read(s.as_mut(), len as usize) { @@ -133,10 +332,10 @@ impl streams::Host for T { async fn skip( &mut self, - stream: InputStream, + stream: Resource, len: u64, ) -> anyhow::Result> { - match self.table_mut().get_internal_input_stream_mut(stream)? { + match self.table_mut().get_internal_input_stream_mut(&stream)? { InternalInputStream::Host(s) => { // TODO: the cast to usize should be fallible, use `.try_into()?` let (bytes_skipped, state) = match HostInputStream::skip(s.as_mut(), len as usize) { @@ -172,10 +371,10 @@ impl streams::Host for T { async fn blocking_skip( &mut self, - stream: InputStream, + stream: Resource, len: u64, ) -> anyhow::Result> { - match self.table_mut().get_internal_input_stream_mut(stream)? { + match self.table_mut().get_internal_input_stream_mut(&stream)? { InternalInputStream::Host(s) => { s.ready().await?; // TODO: the cast to usize should be fallible, use `.try_into()?` @@ -210,9 +409,9 @@ impl streams::Host for T { } } - fn subscribe_to_input_stream(&mut self, stream: InputStream) -> anyhow::Result { + fn subscribe(&mut self, stream: Resource) -> anyhow::Result> { // Ensure that table element is an input-stream: - let pollable = match self.table_mut().get_internal_input_stream_mut(stream)? { + let pollable = match self.table_mut().get_internal_input_stream_mut(&stream)? { InternalInputStream::Host(_) => { fn input_stream_ready<'a>(stream: &'a mut dyn Any) -> PollableFuture<'a> { let stream = stream @@ -225,7 +424,7 @@ impl streams::Host for T { } HostPollable::TableEntry { - index: stream, + index: stream.rep(), make_future: input_stream_ready, } } @@ -237,179 +436,19 @@ impl streams::Host for T { }; Ok(self.table_mut().push_host_pollable(pollable)?) } - - /* -------------------------------------------------------------- - * - * OutputStream methods - * - * -------------------------------------------------------------- */ - - fn check_write(&mut self, stream: OutputStream) -> Result { - let s = self.table_mut().get_output_stream_mut(stream)?; - let mut ready = s.write_ready(); - let mut task = Context::from_waker(futures::task::noop_waker_ref()); - match Pin::new(&mut ready).poll(&mut task) { - Poll::Ready(Ok(permit)) => Ok(permit as u64), - Poll::Ready(Err(e)) => Err(e.into()), - Poll::Pending => Ok(0), - } - } - - async fn write(&mut self, stream: OutputStream, bytes: Vec) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(stream)?; - HostOutputStream::write(s, bytes.into())?; - Ok(()) - } - - fn subscribe_to_output_stream(&mut self, stream: OutputStream) -> anyhow::Result { - // Ensure that table element is an output-stream: - let _ = self.table_mut().get_output_stream_mut(stream)?; - - fn output_stream_ready<'a>(stream: &'a mut dyn Any) -> PollableFuture<'a> { - let stream = stream - .downcast_mut::>() - .expect("downcast to HostOutputStream failed"); - Box::pin(async move { - let _ = stream.write_ready().await?; - Ok(()) - }) - } - - Ok(self - .table_mut() - .push_host_pollable(HostPollable::TableEntry { - index: stream, - make_future: output_stream_ready, - })?) - } - - async fn blocking_write_and_flush( - &mut self, - stream: OutputStream, - bytes: Vec, - ) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(stream)?; - - if bytes.len() > 4096 { - return Err(streams::Error::trap(anyhow::anyhow!( - "Buffer too large for blocking-write-and-flush (expected at most 4096)" - ))); - } - - let mut bytes = bytes::Bytes::from(bytes); - while !bytes.is_empty() { - let permit = s.write_ready().await?; - let len = bytes.len().min(permit); - let chunk = bytes.split_to(len); - HostOutputStream::write(s, chunk)?; - } - - HostOutputStream::flush(s)?; - let _ = s.write_ready().await?; - - Ok(()) - } - - fn write_zeroes(&mut self, stream: OutputStream, len: u64) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(stream)?; - HostOutputStream::write_zeroes(s, len as usize)?; - Ok(()) - } - - fn flush(&mut self, stream: OutputStream) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(stream)?; - HostOutputStream::flush(s)?; - Ok(()) - } - async fn blocking_flush(&mut self, stream: OutputStream) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(stream)?; - HostOutputStream::flush(s)?; - let _ = s.write_ready().await?; - Ok(()) - } - - /* -------------------------------------------------------------- - * - * Aspirational methods - * - * -------------------------------------------------------------- */ - async fn splice( - &mut self, - _src: InputStream, - _dst: OutputStream, - _len: u64, - ) -> anyhow::Result> { - // TODO: We can't get two streams at the same time because they both - // carry the exclusive lifetime of `ctx`. When [`get_many_mut`] is - // stabilized, that could allow us to add a `get_many_stream_mut` or - // so which lets us do this. - // - // [`get_many_mut`]: https://doc.rust-lang.org/stable/std/collections/hash_map/struct.HashMap.html#method.get_many_mut - /* - let s: &mut Box = ctx - .table_mut() - .get_input_stream_mut(src) - ?; - let d: &mut Box = ctx - .table_mut() - .get_output_stream_mut(dst) - ?; - - let bytes_spliced: u64 = s.splice(&mut **d, len).await?; - - Ok(bytes_spliced) - */ - todo!("stream splice is not implemented") - } - - async fn blocking_splice( - &mut self, - _src: InputStream, - _dst: OutputStream, - _len: u64, - ) -> anyhow::Result> { - // TODO: once splice is implemented, figure out what the blocking semantics are for waiting - // on src and dest here. - todo!("stream splice is not implemented") - } - - async fn forward( - &mut self, - _src: InputStream, - _dst: OutputStream, - ) -> anyhow::Result> { - // TODO: We can't get two streams at the same time because they both - // carry the exclusive lifetime of `ctx`. When [`get_many_mut`] is - // stabilized, that could allow us to add a `get_many_stream_mut` or - // so which lets us do this. - // - // [`get_many_mut`]: https://doc.rust-lang.org/stable/std/collections/hash_map/struct.HashMap.html#method.get_many_mut - /* - let s: &mut Box = ctx - .table_mut() - .get_input_stream_mut(src) - ?; - let d: &mut Box = ctx - .table_mut() - .get_output_stream_mut(dst) - ?; - - let bytes_spliced: u64 = s.splice(&mut **d, len).await?; - - Ok(bytes_spliced) - */ - - todo!("stream forward is not implemented") - } } pub mod sync { use crate::preview2::{ - bindings::io::streams::{self as async_streams, Host as AsyncHost}, + bindings::io::streams::{ + self as async_streams, HostInputStream as AsyncHostInputStream, + HostOutputStream as AsyncHostOutputStream, + }, + bindings::sync_io::io::poll::Pollable, bindings::sync_io::io::streams::{self, InputStream, OutputStream}, - bindings::sync_io::poll::poll::Pollable, in_tokio, WasiView, }; + use wasmtime::component::Resource; // same boilerplate everywhere, converting between two identical types with different // definition sites. one day wasmtime-wit-bindgen will make all this unnecessary @@ -445,108 +484,146 @@ pub mod sync { } } - impl streams::Host for T { - fn drop_input_stream(&mut self, stream: InputStream) -> anyhow::Result<()> { - AsyncHost::drop_input_stream(self, stream) - } + impl streams::Host for T {} - fn drop_output_stream(&mut self, stream: OutputStream) -> anyhow::Result<()> { - AsyncHost::drop_output_stream(self, stream) + impl streams::HostOutputStream for T { + fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { + AsyncHostOutputStream::drop(self, stream) } - fn read( - &mut self, - stream: InputStream, - len: u64, - ) -> anyhow::Result, streams::StreamStatus), ()>> { - in_tokio(async { AsyncHost::read(self, stream, len).await }).map(xform) + fn check_write(&mut self, stream: Resource) -> Result { + Ok(AsyncHostOutputStream::check_write(self, stream)?) } - fn blocking_read( + fn write( &mut self, - stream: InputStream, - len: u64, - ) -> anyhow::Result, streams::StreamStatus), ()>> { - in_tokio(async { AsyncHost::blocking_read(self, stream, len).await }).map(xform) + stream: Resource, + bytes: Vec, + ) -> Result<(), streams::Error> { + Ok(AsyncHostOutputStream::write(self, stream, bytes)?) } - fn check_write(&mut self, stream: OutputStream) -> Result { - Ok(AsyncHost::check_write(self, stream)?) - } - fn write(&mut self, stream: OutputStream, bytes: Vec) -> Result<(), streams::Error> { + fn blocking_write_and_flush( + &mut self, + stream: Resource, + bytes: Vec, + ) -> Result<(), streams::Error> { Ok(in_tokio(async { - AsyncHost::write(self, stream, bytes).await + AsyncHostOutputStream::blocking_write_and_flush(self, stream, bytes).await })?) } - fn blocking_write_and_flush( + + fn blocking_write_zeroes_and_flush( &mut self, - stream: OutputStream, - bytes: Vec, + stream: Resource, + len: u64, ) -> Result<(), streams::Error> { Ok(in_tokio(async { - AsyncHost::blocking_write_and_flush(self, stream, bytes).await + AsyncHostOutputStream::blocking_write_zeroes_and_flush(self, stream, len).await })?) } - fn subscribe_to_output_stream(&mut self, stream: OutputStream) -> anyhow::Result { - AsyncHost::subscribe_to_output_stream(self, stream) + + fn subscribe( + &mut self, + stream: Resource, + ) -> anyhow::Result> { + Ok(AsyncHostOutputStream::subscribe(self, stream)?) } - fn write_zeroes(&mut self, stream: OutputStream, len: u64) -> Result<(), streams::Error> { - Ok(AsyncHost::write_zeroes(self, stream, len)?) + + fn write_zeroes( + &mut self, + stream: Resource, + len: u64, + ) -> Result<(), streams::Error> { + Ok(AsyncHostOutputStream::write_zeroes(self, stream, len)?) } - fn flush(&mut self, stream: OutputStream) -> Result<(), streams::Error> { - Ok(AsyncHost::flush(self, stream)?) + fn flush(&mut self, stream: Resource) -> Result<(), streams::Error> { + Ok(AsyncHostOutputStream::flush( + self, + Resource::new_borrow(stream.rep()), + )?) } - fn blocking_flush(&mut self, stream: OutputStream) -> Result<(), streams::Error> { + + fn blocking_flush(&mut self, stream: Resource) -> Result<(), streams::Error> { Ok(in_tokio(async { - AsyncHost::blocking_flush(self, stream).await + AsyncHostOutputStream::blocking_flush(self, Resource::new_borrow(stream.rep())) + .await })?) } - fn skip( + fn splice( &mut self, - stream: InputStream, + dst: Resource, + src: Resource, len: u64, ) -> anyhow::Result> { - in_tokio(async { AsyncHost::skip(self, stream, len).await }).map(xform) + in_tokio(async { AsyncHostOutputStream::splice(self, dst, src, len).await }).map(xform) } - fn blocking_skip( + fn blocking_splice( &mut self, - stream: InputStream, + dst: Resource, + src: Resource, len: u64, ) -> anyhow::Result> { - in_tokio(async { AsyncHost::blocking_skip(self, stream, len).await }).map(xform) + in_tokio(async { AsyncHostOutputStream::blocking_splice(self, dst, src, len).await }) + .map(xform) } - fn splice( + fn forward( &mut self, - src: InputStream, - dst: OutputStream, - len: u64, + dst: Resource, + src: Resource, ) -> anyhow::Result> { - in_tokio(async { AsyncHost::splice(self, src, dst, len).await }).map(xform) + in_tokio(async { AsyncHostOutputStream::forward(self, dst, src).await }).map(xform) } + } - fn blocking_splice( + impl streams::HostInputStream for T { + fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { + AsyncHostInputStream::drop(self, stream) + } + + fn read( + &mut self, + stream: Resource, + len: u64, + ) -> anyhow::Result, streams::StreamStatus), ()>> { + in_tokio(async { AsyncHostInputStream::read(self, stream, len).await }).map(xform) + } + + fn blocking_read( &mut self, - src: InputStream, - dst: OutputStream, + stream: Resource, + len: u64, + ) -> anyhow::Result, streams::StreamStatus), ()>> { + in_tokio(async { AsyncHostInputStream::blocking_read(self, stream, len).await }) + .map(xform) + } + + fn skip( + &mut self, + stream: Resource, len: u64, ) -> anyhow::Result> { - in_tokio(async { AsyncHost::blocking_splice(self, src, dst, len).await }).map(xform) + in_tokio(async { AsyncHostInputStream::skip(self, stream, len).await }).map(xform) } - fn forward( + fn blocking_skip( &mut self, - src: InputStream, - dst: OutputStream, + stream: Resource, + len: u64, ) -> anyhow::Result> { - in_tokio(async { AsyncHost::forward(self, src, dst).await }).map(xform) + in_tokio(async { AsyncHostInputStream::blocking_skip(self, stream, len).await }) + .map(xform) } - fn subscribe_to_input_stream(&mut self, stream: InputStream) -> anyhow::Result { - AsyncHost::subscribe_to_input_stream(self, stream) + fn subscribe( + &mut self, + stream: Resource, + ) -> anyhow::Result> { + AsyncHostInputStream::subscribe(self, stream) } } } diff --git a/crates/wasi/src/preview2/host/network.rs b/crates/wasi/src/preview2/host/network.rs index 023a5d9f3026..03d588e5320c 100644 --- a/crates/wasi/src/preview2/host/network.rs +++ b/crates/wasi/src/preview2/host/network.rs @@ -5,9 +5,12 @@ use crate::preview2::bindings::sockets::network::{ use crate::preview2::network::TableNetworkExt; use crate::preview2::{TableError, WasiView}; use std::io; +use wasmtime::component::Resource; -impl network::Host for T { - fn drop_network(&mut self, this: network::Network) -> Result<(), anyhow::Error> { +impl network::Host for T {} + +impl crate::preview2::bindings::sockets::network::HostNetwork for T { + fn drop(&mut self, this: Resource) -> Result<(), anyhow::Error> { let table = self.table_mut(); table.delete_network(this)?; diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index 89f9bc240207..370da1adb5a6 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -1,13 +1,13 @@ use crate::preview2::bindings::{ + io::poll::Pollable, io::streams::{InputStream, OutputStream}, - poll::poll::Pollable, sockets::network::{self, ErrorCode, IpAddressFamily, IpSocketAddress, Network}, sockets::tcp::{self, ShutdownType}, }; use crate::preview2::network::TableNetworkExt; use crate::preview2::poll::TablePollableExt; use crate::preview2::stream::TableStreamExt; -use crate::preview2::tcp::{HostTcpSocket, HostTcpState, TableTcpSocketExt}; +use crate::preview2::tcp::{HostTcpSocketState, HostTcpState, TableTcpSocketExt}; use crate::preview2::{HostPollable, PollableFuture, WasiView}; use cap_net_ext::{Blocking, PoolExt, TcpListenerExt}; use cap_std::net::TcpListener; @@ -16,23 +16,26 @@ use rustix::io::Errno; use rustix::net::sockopt; use std::any::Any; use tokio::io::Interest; +use wasmtime::component::Resource; -impl tcp::Host for T { +impl tcp::Host for T {} + +impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { fn start_bind( &mut self, - this: tcp::TcpSocket, - network: Network, + this: Resource, + network: Resource, local_address: IpSocketAddress, ) -> Result<(), network::Error> { let table = self.table_mut(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; match socket.tcp_state { HostTcpState::Default => {} _ => return Err(ErrorCode::NotInProgress.into()), } - let network = table.get_network(network)?; + let network = table.get_network(&network)?; let binder = network.0.tcp_binder(local_address)?; // Perform the OS bind call. @@ -40,15 +43,15 @@ impl tcp::Host for T { &*socket.tcp_socket().as_socketlike_view::(), )?; - let socket = table.get_tcp_socket_mut(this)?; + let socket = table.get_tcp_socket_mut(&this)?; socket.tcp_state = HostTcpState::BindStarted; Ok(()) } - fn finish_bind(&mut self, this: tcp::TcpSocket) -> Result<(), network::Error> { + fn finish_bind(&mut self, this: Resource) -> Result<(), network::Error> { let table = self.table_mut(); - let socket = table.get_tcp_socket_mut(this)?; + let socket = table.get_tcp_socket_mut(&this)?; match socket.tcp_state { HostTcpState::BindStarted => {} @@ -62,13 +65,13 @@ impl tcp::Host for T { fn start_connect( &mut self, - this: tcp::TcpSocket, - network: Network, + this: Resource, + network: Resource, remote_address: IpSocketAddress, ) -> Result<(), network::Error> { let table = self.table_mut(); let r = { - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; match socket.tcp_state { HostTcpState::Default => {} @@ -76,7 +79,7 @@ impl tcp::Host for T { _ => return Err(ErrorCode::NotInProgress.into()), } - let network = table.get_network(network)?; + let network = table.get_network(&network)?; let connecter = network.0.tcp_connecter(remote_address)?; // Do an OS `connect`. Our socket is non-blocking, so it'll either... @@ -90,7 +93,7 @@ impl tcp::Host for T { match r { // succeed immediately, Ok(()) => { - let socket = table.get_tcp_socket_mut(this)?; + let socket = table.get_tcp_socket_mut(&this)?; socket.tcp_state = HostTcpState::ConnectReady; return Ok(()); } @@ -100,7 +103,7 @@ impl tcp::Host for T { Err(err) => return Err(err.into()), } - let socket = table.get_tcp_socket_mut(this)?; + let socket = table.get_tcp_socket_mut(&this)?; socket.tcp_state = HostTcpState::Connecting; Ok(()) @@ -108,10 +111,10 @@ impl tcp::Host for T { fn finish_connect( &mut self, - this: tcp::TcpSocket, - ) -> Result<(InputStream, OutputStream), network::Error> { + this: Resource, + ) -> Result<(Resource, Resource), network::Error> { let table = self.table_mut(); - let socket = table.get_tcp_socket_mut(this)?; + let socket = table.get_tcp_socket_mut(&this)?; match socket.tcp_state { HostTcpState::ConnectReady => {} @@ -141,15 +144,19 @@ impl tcp::Host for T { socket.tcp_state = HostTcpState::Connected; let (input, output) = socket.as_split(); - let input_stream = self.table_mut().push_input_stream_child(input, this)?; - let output_stream = self.table_mut().push_output_stream_child(output, this)?; + let input_stream = self + .table_mut() + .push_input_stream_child(input, Resource::::new_borrow(this.rep()))?; + let output_stream = self + .table_mut() + .push_output_stream_child(output, Resource::::new_borrow(this.rep()))?; Ok((input_stream, output_stream)) } - fn start_listen(&mut self, this: tcp::TcpSocket) -> Result<(), network::Error> { + fn start_listen(&mut self, this: Resource) -> Result<(), network::Error> { let table = self.table_mut(); - let socket = table.get_tcp_socket_mut(this)?; + let socket = table.get_tcp_socket_mut(&this)?; match socket.tcp_state { HostTcpState::Bound => {} @@ -168,9 +175,9 @@ impl tcp::Host for T { Ok(()) } - fn finish_listen(&mut self, this: tcp::TcpSocket) -> Result<(), network::Error> { + fn finish_listen(&mut self, this: Resource) -> Result<(), network::Error> { let table = self.table_mut(); - let socket = table.get_tcp_socket_mut(this)?; + let socket = table.get_tcp_socket_mut(&this)?; match socket.tcp_state { HostTcpState::ListenStarted => {} @@ -184,10 +191,17 @@ impl tcp::Host for T { fn accept( &mut self, - this: tcp::TcpSocket, - ) -> Result<(tcp::TcpSocket, InputStream, OutputStream), network::Error> { + this: Resource, + ) -> Result< + ( + Resource, + Resource, + Resource, + ), + network::Error, + > { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; match socket.tcp_state { HostTcpState::Listening => {} @@ -202,7 +216,7 @@ impl tcp::Host for T { .as_socketlike_view::() .accept_with(Blocking::No) })?; - let mut tcp_socket = HostTcpSocket::from_tcp_stream(connection)?; + let mut tcp_socket = HostTcpSocketState::from_tcp_stream(connection)?; // Mark the socket as connected so that we can exit early from methods like `start-bind`. tcp_socket.tcp_state = HostTcpState::Connected; @@ -210,19 +224,24 @@ impl tcp::Host for T { let (input, output) = tcp_socket.as_split(); let tcp_socket = self.table_mut().push_tcp_socket(tcp_socket)?; - let input_stream = self - .table_mut() - .push_input_stream_child(input, tcp_socket)?; - let output_stream = self - .table_mut() - .push_output_stream_child(output, tcp_socket)?; + let input_stream = self.table_mut().push_input_stream_child( + input, + Resource::::new_borrow(tcp_socket.rep()), + )?; + let output_stream = self.table_mut().push_output_stream_child( + output, + Resource::::new_borrow(tcp_socket.rep()), + )?; Ok((tcp_socket, input_stream, output_stream)) } - fn local_address(&mut self, this: tcp::TcpSocket) -> Result { + fn local_address( + &mut self, + this: Resource, + ) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; let addr = socket .tcp_socket() .as_socketlike_view::() @@ -230,9 +249,12 @@ impl tcp::Host for T { Ok(addr.into()) } - fn remote_address(&mut self, this: tcp::TcpSocket) -> Result { + fn remote_address( + &mut self, + this: Resource, + ) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; let addr = socket .tcp_socket() .as_socketlike_view::() @@ -240,9 +262,12 @@ impl tcp::Host for T { Ok(addr.into()) } - fn address_family(&mut self, this: tcp::TcpSocket) -> Result { + fn address_family( + &mut self, + this: Resource, + ) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; // If `SO_DOMAIN` is available, use it. // @@ -285,25 +310,29 @@ impl tcp::Host for T { } } - fn ipv6_only(&mut self, this: tcp::TcpSocket) -> Result { + fn ipv6_only(&mut self, this: Resource) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; Ok(sockopt::get_ipv6_v6only(socket.tcp_socket())?) } - fn set_ipv6_only(&mut self, this: tcp::TcpSocket, value: bool) -> Result<(), network::Error> { + fn set_ipv6_only( + &mut self, + this: Resource, + value: bool, + ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; Ok(sockopt::set_ipv6_v6only(socket.tcp_socket(), value)?) } fn set_listen_backlog_size( &mut self, - this: tcp::TcpSocket, + this: Resource, value: u64, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; match socket.tcp_state { HostTcpState::Listening => {} @@ -314,33 +343,41 @@ impl tcp::Host for T { Ok(rustix::net::listen(socket.tcp_socket(), value)?) } - fn keep_alive(&mut self, this: tcp::TcpSocket) -> Result { + fn keep_alive(&mut self, this: Resource) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; Ok(sockopt::get_socket_keepalive(socket.tcp_socket())?) } - fn set_keep_alive(&mut self, this: tcp::TcpSocket, value: bool) -> Result<(), network::Error> { + fn set_keep_alive( + &mut self, + this: Resource, + value: bool, + ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; Ok(sockopt::set_socket_keepalive(socket.tcp_socket(), value)?) } - fn no_delay(&mut self, this: tcp::TcpSocket) -> Result { + fn no_delay(&mut self, this: Resource) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; Ok(sockopt::get_tcp_nodelay(socket.tcp_socket())?) } - fn set_no_delay(&mut self, this: tcp::TcpSocket, value: bool) -> Result<(), network::Error> { + fn set_no_delay( + &mut self, + this: Resource, + value: bool, + ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; Ok(sockopt::set_tcp_nodelay(socket.tcp_socket(), value)?) } - fn unicast_hop_limit(&mut self, this: tcp::TcpSocket) -> Result { + fn unicast_hop_limit(&mut self, this: Resource) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; // We don't track whether the socket is IPv4 or IPv6 so try one and // fall back to the other. @@ -357,11 +394,11 @@ impl tcp::Host for T { fn set_unicast_hop_limit( &mut self, - this: tcp::TcpSocket, + this: Resource, value: u8, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; // We don't track whether the socket is IPv4 or IPv6 so try one and // fall back to the other. @@ -372,19 +409,22 @@ impl tcp::Host for T { } } - fn receive_buffer_size(&mut self, this: tcp::TcpSocket) -> Result { + fn receive_buffer_size( + &mut self, + this: Resource, + ) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; Ok(sockopt::get_socket_recv_buffer_size(socket.tcp_socket())? as u64) } fn set_receive_buffer_size( &mut self, - this: tcp::TcpSocket, + this: Resource, value: u64, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; Ok(sockopt::set_socket_recv_buffer_size( socket.tcp_socket(), @@ -392,19 +432,19 @@ impl tcp::Host for T { )?) } - fn send_buffer_size(&mut self, this: tcp::TcpSocket) -> Result { + fn send_buffer_size(&mut self, this: Resource) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; Ok(sockopt::get_socket_send_buffer_size(socket.tcp_socket())? as u64) } fn set_send_buffer_size( &mut self, - this: tcp::TcpSocket, + this: Resource, value: u64, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; Ok(sockopt::set_socket_send_buffer_size( socket.tcp_socket(), @@ -412,11 +452,11 @@ impl tcp::Host for T { )?) } - fn subscribe(&mut self, this: tcp::TcpSocket) -> anyhow::Result { + fn subscribe(&mut self, this: Resource) -> anyhow::Result> { fn make_tcp_socket_future<'a>(stream: &'a mut dyn Any) -> PollableFuture<'a> { let socket = stream - .downcast_mut::() - .expect("downcast to HostTcpSocket failed"); + .downcast_mut::() + .expect("downcast to HostTcpSocketState failed"); // Some states are ready immediately. match socket.tcp_state { @@ -440,7 +480,7 @@ impl tcp::Host for T { } let pollable = HostPollable::TableEntry { - index: this, + index: this.rep(), make_future: make_tcp_socket_future, }; @@ -449,11 +489,11 @@ impl tcp::Host for T { fn shutdown( &mut self, - this: tcp::TcpSocket, + this: Resource, shutdown_type: ShutdownType, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(this)?; + let socket = table.get_tcp_socket(&this)?; let how = match shutdown_type { ShutdownType::Receive => std::net::Shutdown::Read, @@ -468,7 +508,7 @@ impl tcp::Host for T { Ok(()) } - fn drop_tcp_socket(&mut self, this: tcp::TcpSocket) -> Result<(), anyhow::Error> { + fn drop(&mut self, this: Resource) -> Result<(), anyhow::Error> { let table = self.table_mut(); // As in the filesystem implementation, we assume closing a socket diff --git a/crates/wasi/src/preview2/host/tcp_create_socket.rs b/crates/wasi/src/preview2/host/tcp_create_socket.rs index d2559e7df7d5..c3f349177a8e 100644 --- a/crates/wasi/src/preview2/host/tcp_create_socket.rs +++ b/crates/wasi/src/preview2/host/tcp_create_socket.rs @@ -3,15 +3,16 @@ use crate::preview2::bindings::{ sockets::tcp::TcpSocket, sockets::tcp_create_socket, }; -use crate::preview2::tcp::{HostTcpSocket, TableTcpSocketExt}; +use crate::preview2::tcp::{HostTcpSocketState, TableTcpSocketExt}; use crate::preview2::WasiView; +use wasmtime::component::Resource; impl tcp_create_socket::Host for T { fn create_tcp_socket( &mut self, address_family: IpAddressFamily, - ) -> Result { - let socket = HostTcpSocket::new(address_family.into())?; + ) -> Result, network::Error> { + let socket = HostTcpSocketState::new(address_family.into())?; let socket = self.table_mut().push_tcp_socket(socket)?; Ok(socket) } diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index 6d9ad182dcec..5d1f02e55487 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -58,7 +58,7 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", interfaces: " - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:filesystem/types ", @@ -69,10 +69,15 @@ pub mod bindings { }, with: { "wasi:clocks/wall-clock": crate::preview2::bindings::clocks::wall_clock, + "wasi:filesystem/types/descriptor": super::super::filesystem::types::Descriptor, + "wasi:filesystem/types/directory-entry-stream": super::super::filesystem::types::DirectoryEntryStream, + "wasi:io/poll/pollable": super::super::io::poll::Pollable, + "wasi:io/streams/input-stream": super::super::io::streams::InputStream, + "wasi:io/streams/output-stream": super::super::io::streams::OutputStream, } }); } - pub use self::_internal::wasi::{filesystem, io, poll}; + pub use self::_internal::wasi::{filesystem, io}; } wasmtime::component::bindgen!({ @@ -88,49 +93,52 @@ pub mod bindings { // which in theory can be shared across interfaces, so this may // need fancier syntax in the future. only_imports: [ - "access-at", - "advise", - "blocking-flush", - "blocking-read", - "blocking-skip", - "blocking-splice", - "blocking-write", - "blocking-write-and-flush", - "change-directory-permissions-at", - "change-file-permissions-at", - "create-directory-at", - "forward", - "get-flags", - "get-type", - "is-same-object", - "link-at", - "lock-exclusive", - "lock-shared", - "metadata-hash", - "metadata-hash-at", - "open-at", - "poll-oneoff", - "read", - "read-directory", - "read-directory-entry", - "readlink-at", - "remove-directory-at", - "rename-at", - "set-size", - "set-times", - "set-times-at", - "skip", - "splice", - "stat", - "stat-at", - "symlink-at", - "sync", - "sync-data", - "try-lock-exclusive", - "try-lock-shared", - "unlink-file-at", - "unlock", - "write", + "[method]descriptor.access-at", + "[method]descriptor.advise", + "[method]descriptor.change-directory-permissions-at", + "[method]descriptor.change-file-permissions-at", + "[method]descriptor.create-directory-at", + "[method]descriptor.get-flags", + "[method]descriptor.get-type", + "[method]descriptor.is-same-object", + "[method]descriptor.link-at", + "[method]descriptor.lock-exclusive", + "[method]descriptor.lock-shared", + "[method]descriptor.metadata-hash", + "[method]descriptor.metadata-hash-at", + "[method]descriptor.open-at", + "[method]descriptor.read", + "[method]descriptor.read-directory", + "[method]descriptor.readlink-at", + "[method]descriptor.remove-directory-at", + "[method]descriptor.rename-at", + "[method]descriptor.set-size", + "[method]descriptor.set-times", + "[method]descriptor.set-times-at", + "[method]descriptor.stat", + "[method]descriptor.stat-at", + "[method]descriptor.symlink-at", + "[method]descriptor.sync", + "[method]descriptor.sync-data", + "[method]descriptor.try-lock-exclusive", + "[method]descriptor.try-lock-shared", + "[method]descriptor.unlink-file-at", + "[method]descriptor.unlock", + "[method]descriptor.write", + "[method]input-stream.read", + "[method]input-stream.blocking-read", + "[method]input-stream.blocking-skip", + "[method]input-stream.skip", + "[method]output-stream.forward", + "[method]output-stream.splice", + "[method]output-stream.blocking-splice", + "[method]output-stream.blocking-flush", + "[method]output-stream.blocking-write", + "[method]output-stream.blocking-write-and-flush", + "[method]output-stream.blocking-write-zeroes-and-flush", + "[method]directory-entry-stream.read-directory-entry", + "poll-list", + "poll-one", ], }, trappable_error_type: { diff --git a/crates/wasi/src/preview2/network.rs b/crates/wasi/src/preview2/network.rs index 4d462fcbd275..64c46f461044 100644 --- a/crates/wasi/src/preview2/network.rs +++ b/crates/wasi/src/preview2/network.rs @@ -1,32 +1,34 @@ +use crate::preview2::bindings::sockets::network::Network; use crate::preview2::{Table, TableError}; use cap_std::net::Pool; +use wasmtime::component::Resource; -pub(crate) struct HostNetwork(pub(crate) Pool); +pub(crate) struct HostNetworkState(pub(crate) Pool); -impl HostNetwork { +impl HostNetworkState { pub fn new(pool: Pool) -> Self { Self(pool) } } pub(crate) trait TableNetworkExt { - fn push_network(&mut self, network: HostNetwork) -> Result; - fn delete_network(&mut self, fd: u32) -> Result; - fn is_network(&self, fd: u32) -> bool; - fn get_network(&self, fd: u32) -> Result<&HostNetwork, TableError>; + fn push_network(&mut self, network: HostNetworkState) -> Result, TableError>; + fn delete_network(&mut self, fd: Resource) -> Result; + fn is_network(&self, fd: &Resource) -> bool; + fn get_network(&self, fd: &Resource) -> Result<&HostNetworkState, TableError>; } impl TableNetworkExt for Table { - fn push_network(&mut self, network: HostNetwork) -> Result { - self.push(Box::new(network)) + fn push_network(&mut self, network: HostNetworkState) -> Result, TableError> { + Ok(Resource::new_own(self.push(Box::new(network))?)) } - fn delete_network(&mut self, fd: u32) -> Result { - self.delete(fd) + fn delete_network(&mut self, fd: Resource) -> Result { + self.delete(fd.rep()) } - fn is_network(&self, fd: u32) -> bool { - self.is::(fd) + fn is_network(&self, fd: &Resource) -> bool { + self.is::(fd.rep()) } - fn get_network(&self, fd: u32) -> Result<&HostNetwork, TableError> { - self.get(fd) + fn get_network(&self, fd: &Resource) -> Result<&HostNetworkState, TableError> { + self.get(fd.rep()) } } diff --git a/crates/wasi/src/preview2/pipe.rs b/crates/wasi/src/preview2/pipe.rs index cfde46d8c127..c261760d7d93 100644 --- a/crates/wasi/src/preview2/pipe.rs +++ b/crates/wasi/src/preview2/pipe.rs @@ -10,45 +10,46 @@ use crate::preview2::{HostInputStream, HostOutputStream, OutputStreamError, StreamState}; use anyhow::{anyhow, Error}; use bytes::Bytes; +use std::sync::{Arc, Mutex}; use tokio::sync::mpsc; pub use crate::preview2::write_stream::AsyncWriteStream; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MemoryInputPipe { - buffer: std::io::Cursor, + buffer: Arc>, } impl MemoryInputPipe { pub fn new(bytes: Bytes) -> Self { Self { - buffer: std::io::Cursor::new(bytes), + buffer: Arc::new(Mutex::new(bytes)), } } pub fn is_empty(&self) -> bool { - self.buffer.get_ref().len() as u64 == self.buffer.position() + self.buffer.lock().unwrap().is_empty() } } #[async_trait::async_trait] impl HostInputStream for MemoryInputPipe { fn read(&mut self, size: usize) -> Result<(Bytes, StreamState), Error> { - if self.is_empty() { + let mut buffer = self.buffer.lock().unwrap(); + if buffer.is_empty() { return Ok((Bytes::new(), StreamState::Closed)); } - let mut dest = bytes::BytesMut::zeroed(size); - let nbytes = std::io::Read::read(&mut self.buffer, dest.as_mut())?; - dest.truncate(nbytes); - - let state = if self.is_empty() { + let size = size.min(buffer.len()); + let read = buffer.split_to(size); + let state = if buffer.is_empty() { StreamState::Closed } else { StreamState::Open }; - Ok((dest.freeze(), state)) + Ok((read, state)) } + async fn ready(&mut self) -> Result<(), Error> { Ok(()) } @@ -57,7 +58,7 @@ impl HostInputStream for MemoryInputPipe { #[derive(Debug, Clone)] pub struct MemoryOutputPipe { capacity: usize, - buffer: std::sync::Arc>, + buffer: Arc>, } impl MemoryOutputPipe { @@ -212,6 +213,7 @@ impl HostInputStream for AsyncReadStream { } /// An output stream that consumes all input written to it, and is always ready. +#[derive(Copy, Clone)] pub struct SinkOutputStream; #[async_trait::async_trait] @@ -231,6 +233,7 @@ impl HostOutputStream for SinkOutputStream { } /// A stream that is ready immediately, but will always report that it's closed. +#[derive(Copy, Clone)] pub struct ClosedInputStream; #[async_trait::async_trait] @@ -245,6 +248,7 @@ impl HostInputStream for ClosedInputStream { } /// An output stream that is always closed. +#[derive(Copy, Clone)] pub struct ClosedOutputStream; #[async_trait::async_trait] diff --git a/crates/wasi/src/preview2/poll.rs b/crates/wasi/src/preview2/poll.rs index 0a947fef3dec..8bce303639f1 100644 --- a/crates/wasi/src/preview2/poll.rs +++ b/crates/wasi/src/preview2/poll.rs @@ -1,5 +1,5 @@ use crate::preview2::{ - bindings::poll::poll::{self, Pollable}, + bindings::io::poll::{self, Pollable}, Table, TableError, WasiView, }; use anyhow::Result; @@ -8,17 +8,18 @@ use std::collections::{hash_map::Entry, HashMap}; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; +use wasmtime::component::Resource; pub type PollableFuture<'a> = Pin> + Send + 'a>>; pub type MakeFuture = for<'a> fn(&'a mut dyn Any) -> PollableFuture<'a>; pub type ClosureFuture = Box PollableFuture<'static> + Send + Sync + 'static>; -/// A host representation of the `wasi:poll/poll.pollable` resource. +/// A host representation of the `wasi:io/poll.pollable` resource. /// /// A pollable is not the same thing as a Rust Future: the same pollable may be used to /// repeatedly check for readiness of a given condition, e.g. if a stream is readable /// or writable. So, rather than containing a Future, which can only become Ready once, a -/// HostPollable contains a way to create a Future in each call to poll_oneoff. +/// HostPollable contains a way to create a Future in each call to `poll_list`. pub enum HostPollable { /// Create a Future by calling a fn on another resource in the table. This /// indirection means the created Future can use a mut borrow of another @@ -31,35 +32,36 @@ pub enum HostPollable { } pub trait TablePollableExt { - fn push_host_pollable(&mut self, p: HostPollable) -> Result; - fn get_host_pollable_mut(&mut self, fd: u32) -> Result<&mut HostPollable, TableError>; - fn delete_host_pollable(&mut self, fd: u32) -> Result; + fn push_host_pollable(&mut self, p: HostPollable) -> Result, TableError>; + fn get_host_pollable_mut( + &mut self, + fd: &Resource, + ) -> Result<&mut HostPollable, TableError>; + fn delete_host_pollable(&mut self, fd: Resource) -> Result; } impl TablePollableExt for Table { - fn push_host_pollable(&mut self, p: HostPollable) -> Result { - match p { - HostPollable::TableEntry { index, .. } => self.push_child(Box::new(p), index), - HostPollable::Closure { .. } => self.push(Box::new(p)), - } + fn push_host_pollable(&mut self, p: HostPollable) -> Result, TableError> { + Ok(Resource::new_own(match p { + HostPollable::TableEntry { index, .. } => self.push_child(Box::new(p), index)?, + HostPollable::Closure { .. } => self.push(Box::new(p))?, + })) } - fn get_host_pollable_mut(&mut self, fd: u32) -> Result<&mut HostPollable, TableError> { - self.get_mut::(fd) + fn get_host_pollable_mut( + &mut self, + fd: &Resource, + ) -> Result<&mut HostPollable, TableError> { + self.get_mut::(fd.rep()) } - fn delete_host_pollable(&mut self, fd: u32) -> Result { - self.delete::(fd) + fn delete_host_pollable(&mut self, fd: Resource) -> Result { + self.delete::(fd.rep()) } } #[async_trait::async_trait] impl poll::Host for T { - fn drop_pollable(&mut self, pollable: Pollable) -> Result<()> { - self.table_mut().delete_host_pollable(pollable)?; - Ok(()) - } - - async fn poll_oneoff(&mut self, pollables: Vec) -> Result> { - type ReadylistIndex = usize; + async fn poll_list(&mut self, pollables: Vec>) -> Result> { + type ReadylistIndex = u32; let table = self.table_mut(); @@ -67,7 +69,8 @@ impl poll::Host for T { let mut closure_futures: Vec<(PollableFuture<'_>, Vec)> = Vec::new(); for (ix, p) in pollables.iter().enumerate() { - match table.get_host_pollable_mut(*p)? { + let ix: u32 = ix.try_into()?; + match table.get_host_pollable_mut(&p)? { HostPollable::Closure(f) => closure_futures.push((f(), vec![ix])), HostPollable::TableEntry { index, make_future } => { match table_futures.entry(*index) { @@ -88,26 +91,24 @@ impl poll::Host for T { closure_futures.push((make_future(entry), readylist_indices)); } - struct PollOneoff<'a> { + struct PollList<'a> { elems: Vec<(PollableFuture<'a>, Vec)>, } - impl<'a> Future for PollOneoff<'a> { - type Output = Result>; + impl<'a> Future for PollList<'a> { + type Output = Result>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut any_ready = false; - let mut results = vec![false; self.elems.len()]; + let mut results = Vec::new(); for (fut, readylist_indicies) in self.elems.iter_mut() { match fut.as_mut().poll(cx) { Poll::Ready(Ok(())) => { - for r in readylist_indicies { - results[*r] = true; - } + results.extend_from_slice(readylist_indicies); any_ready = true; } Poll::Ready(Err(e)) => { return Poll::Ready(Err( - e.context(format!("poll_oneoff {readylist_indicies:?}")) + e.context(format!("poll_list {readylist_indicies:?}")) )); } Poll::Pending => {} @@ -121,28 +122,60 @@ impl poll::Host for T { } } - Ok(PollOneoff { + Ok(PollList { elems: closure_futures, } .await?) } + + async fn poll_one(&mut self, pollable: Resource) -> Result<()> { + use anyhow::Context; + + let table = self.table_mut(); + + let closure_future = match table.get_host_pollable_mut(&pollable)? { + HostPollable::Closure(f) => f(), + HostPollable::TableEntry { index, make_future } => { + let index = *index; + let make_future = *make_future; + make_future(table.get_as_any_mut(index)?) + } + }; + + closure_future.await.context("poll_one") + } +} + +#[async_trait::async_trait] +impl crate::preview2::bindings::io::poll::HostPollable for T { + fn drop(&mut self, pollable: Resource) -> Result<()> { + self.table_mut().delete_host_pollable(pollable)?; + Ok(()) + } } pub mod sync { use crate::preview2::{ - bindings::poll::poll::Host as AsyncHost, - bindings::sync_io::poll::poll::{self, Pollable}, + bindings::io::poll::{Host as AsyncHost, HostPollable as AsyncHostPollable}, + bindings::sync_io::io::poll::{self, Pollable}, in_tokio, WasiView, }; use anyhow::Result; + use wasmtime::component::Resource; impl poll::Host for T { - fn drop_pollable(&mut self, pollable: Pollable) -> Result<()> { - AsyncHost::drop_pollable(self, pollable) + fn poll_list(&mut self, pollables: Vec>) -> Result> { + in_tokio(async { AsyncHost::poll_list(self, pollables).await }) } - fn poll_oneoff(&mut self, pollables: Vec) -> Result> { - in_tokio(async { AsyncHost::poll_oneoff(self, pollables).await }) + fn poll_one(&mut self, pollable: Resource) -> Result<()> { + in_tokio(async { AsyncHost::poll_one(self, pollable).await }) + } + } + + impl crate::preview2::bindings::sync_io::io::poll::HostPollable for T { + fn drop(&mut self, pollable: Resource) -> Result<()> { + AsyncHostPollable::drop(self, pollable) } } } diff --git a/crates/wasi/src/preview2/preview1.rs b/crates/wasi/src/preview2/preview1.rs index a44330b25b11..4a432704de0e 100644 --- a/crates/wasi/src/preview2/preview1.rs +++ b/crates/wasi/src/preview2/preview1.rs @@ -4,8 +4,8 @@ use crate::preview2::bindings::cli::{ }; use crate::preview2::bindings::clocks::{monotonic_clock, wall_clock}; use crate::preview2::bindings::filesystem::{preopens, types as filesystem}; +use crate::preview2::bindings::io::poll; use crate::preview2::bindings::io::streams; -use crate::preview2::bindings::poll; use crate::preview2::filesystem::TableFsExt; use crate::preview2::host::filesystem::TableReaddirExt; use crate::preview2::{bindings, IsATTY, TableError, WasiView}; @@ -18,13 +18,14 @@ use std::ops::{Deref, DerefMut}; use std::slice; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; +use wasmtime::component::Resource; use wiggle::tracing::instrument; use wiggle::{GuestError, GuestPtr, GuestSlice, GuestSliceMut, GuestStrCow, GuestType}; #[derive(Clone, Debug)] struct File { /// The handle to the preview2 descriptor that this file is referencing. - fd: filesystem::Descriptor, + fd: u32, /// The current-position pointer. position: Arc, @@ -54,26 +55,26 @@ impl BlockingMode { async fn read( &self, host: &mut impl streams::Host, - input_stream: streams::InputStream, + input_stream: Resource, max_size: usize, ) -> Result<(Vec, streams::StreamStatus), types::Error> { let max_size = max_size.try_into().unwrap_or(u64::MAX); match self { - BlockingMode::Blocking => { - stream_res(streams::Host::blocking_read(host, input_stream, max_size).await) - } + BlockingMode::Blocking => stream_res( + streams::HostInputStream::blocking_read(host, input_stream, max_size).await, + ), BlockingMode::NonBlocking => { - stream_res(streams::Host::read(host, input_stream, max_size).await) + stream_res(streams::HostInputStream::read(host, input_stream, max_size).await) } } } async fn write( &self, - host: &mut (impl streams::Host + poll::poll::Host), - output_stream: streams::OutputStream, + host: &mut (impl streams::Host + poll::Host), + output_stream: Resource, mut bytes: &[u8], ) -> Result { - use streams::Host as Streams; + use streams::HostOutputStream as Streams; match self { BlockingMode::Blocking => { @@ -84,13 +85,15 @@ impl BlockingMode { let (chunk, rest) = bytes.split_at(len); bytes = rest; - Streams::blocking_write_and_flush(host, output_stream, Vec::from(chunk)).await? + let borrow = Resource::new_borrow(output_stream.rep()); + Streams::blocking_write_and_flush(host, borrow, Vec::from(chunk)).await? } Ok(total) } BlockingMode::NonBlocking => { - let n = match Streams::check_write(host, output_stream) { + let borrow = Resource::new_borrow(output_stream.rep()); + let n = match Streams::check_write(host, borrow) { Ok(n) => n, Err(e) if matches!(e.downcast_ref(), Some(streams::WriteError::Closed)) => 0, Err(e) => Err(e)?, @@ -101,7 +104,8 @@ impl BlockingMode { return Ok(0); } - match Streams::write(host, output_stream, bytes[..len].to_vec()).await { + let borrow = Resource::new_borrow(output_stream.rep()); + match Streams::write(host, borrow, bytes[..len].to_vec()) { Ok(()) => {} Err(e) if matches!(e.downcast_ref(), Some(streams::WriteError::Closed)) => { return Ok(0) @@ -109,7 +113,8 @@ impl BlockingMode { Err(e) => Err(e)?, } - match Streams::blocking_flush(host, output_stream).await { + let borrow = Resource::new_borrow(output_stream.rep()); + match Streams::blocking_flush(host, borrow).await { Ok(()) => {} Err(e) if matches!(e.downcast_ref(), Some(streams::WriteError::Closed)) => { return Ok(0) @@ -125,19 +130,10 @@ impl BlockingMode { #[derive(Clone, Debug)] enum Descriptor { - Stdin { - input_stream: streams::InputStream, - isatty: IsATTY, - }, - Stdout { - output_stream: streams::OutputStream, - isatty: IsATTY, - }, - Stderr { - output_stream: streams::OutputStream, - isatty: IsATTY, - }, - PreopenDirectory((filesystem::Descriptor, String)), + Stdin { input_stream: u32, isatty: IsATTY }, + Stdout { output_stream: u32, isatty: IsATTY }, + Stderr { output_stream: u32, isatty: IsATTY }, + PreopenDirectory((u32, String)), File(File), } @@ -185,13 +181,14 @@ impl Descriptors { input_stream: host .get_stdin() .context("failed to call `get-stdin`") - .map_err(types::Error::trap)?, + .map_err(types::Error::trap)? + .rep(), isatty: if let Some(term_in) = host .get_terminal_stdin() .context("failed to call `get-terminal-stdin`") .map_err(types::Error::trap)? { - host.drop_terminal_input(term_in) + terminal_input::HostTerminalInput::drop(host, term_in) .context("failed to call `drop-terminal-input`") .map_err(types::Error::trap)?; IsATTY::Yes @@ -203,13 +200,14 @@ impl Descriptors { output_stream: host .get_stdout() .context("failed to call `get-stdout`") - .map_err(types::Error::trap)?, + .map_err(types::Error::trap)? + .rep(), isatty: if let Some(term_out) = host .get_terminal_stdout() .context("failed to call `get-terminal-stdout`") .map_err(types::Error::trap)? { - host.drop_terminal_output(term_out) + terminal_output::HostTerminalOutput::drop(host, term_out) .context("failed to call `drop-terminal-output`") .map_err(types::Error::trap)?; IsATTY::Yes @@ -221,13 +219,14 @@ impl Descriptors { output_stream: host .get_stderr() .context("failed to call `get-stderr`") - .map_err(types::Error::trap)?, + .map_err(types::Error::trap)? + .rep(), isatty: if let Some(term_out) = host .get_terminal_stderr() .context("failed to call `get-terminal-stderr`") .map_err(types::Error::trap)? { - host.drop_terminal_output(term_out) + terminal_output::HostTerminalOutput::drop(host, term_out) .context("failed to call `drop-terminal-output`") .map_err(types::Error::trap)?; IsATTY::Yes @@ -241,7 +240,7 @@ impl Descriptors { .context("failed to call `get-directories`") .map_err(types::Error::trap)? { - descriptors.push(Descriptor::PreopenDirectory(dir))?; + descriptors.push(Descriptor::PreopenDirectory((dir.0.rep(), dir.1)))?; } Ok(descriptors) } @@ -349,7 +348,9 @@ impl Transaction<'_, T> { fn get_file(&mut self, fd: types::Fd) -> Result<&File> { let fd = fd.into(); match self.descriptors.get_mut().get(&fd) { - Some(Descriptor::File(file @ File { fd, .. })) if self.view.table().is_file(*fd) => { + Some(Descriptor::File(file @ File { fd, .. })) + if self.view.table().is_file(&Resource::new_borrow(*fd)) => + { Ok(file) } _ => Err(types::Errno::Badf.into()), @@ -361,7 +362,11 @@ impl Transaction<'_, T> { fn get_file_mut(&mut self, fd: types::Fd) -> Result<&mut File> { let fd = fd.into(); match self.descriptors.get_mut().get_mut(&fd) { - Some(Descriptor::File(file)) if self.view.table().is_file(file.fd) => Ok(file), + Some(Descriptor::File(file)) + if self.view.table().is_file(&Resource::new_borrow(file.fd)) => + { + Ok(file) + } _ => Err(types::Errno::Badf.into()), } } @@ -375,7 +380,9 @@ impl Transaction<'_, T> { fn get_seekable(&mut self, fd: types::Fd) -> Result<&File> { let fd = fd.into(); match self.descriptors.get_mut().get(&fd) { - Some(Descriptor::File(file @ File { fd, .. })) if self.view.table().is_file(*fd) => { + Some(Descriptor::File(file @ File { fd, .. })) + if self.view.table().is_file(&Resource::new_borrow(*fd)) => + { Ok(file) } Some( @@ -389,31 +396,35 @@ impl Transaction<'_, T> { } /// Returns [`filesystem::Descriptor`] corresponding to `fd` - fn get_fd(&mut self, fd: types::Fd) -> Result { + fn get_fd(&mut self, fd: types::Fd) -> Result> { match self.get_descriptor(fd)? { - Descriptor::File(File { fd, .. }) => Ok(*fd), - Descriptor::PreopenDirectory((fd, _)) => Ok(*fd), - Descriptor::Stdin { input_stream, .. } => Ok(*input_stream), - Descriptor::Stdout { output_stream, .. } | Descriptor::Stderr { output_stream, .. } => { - Ok(*output_stream) + Descriptor::File(File { fd, .. }) => Ok(Resource::new_borrow(*fd)), + Descriptor::PreopenDirectory((fd, _)) => Ok(Resource::new_borrow(*fd)), + Descriptor::Stdin { .. } | Descriptor::Stdout { .. } | Descriptor::Stderr { .. } => { + Err(types::Errno::Badf.into()) } } } /// Returns [`filesystem::Descriptor`] corresponding to `fd` /// if it describes a [`Descriptor::File`] of [`crate::preview2::filesystem::File`] type - fn get_file_fd(&mut self, fd: types::Fd) -> Result { - self.get_file(fd).map(|File { fd, .. }| *fd) + fn get_file_fd(&mut self, fd: types::Fd) -> Result> { + self.get_file(fd) + .map(|File { fd, .. }| Resource::new_borrow(*fd)) } /// Returns [`filesystem::Descriptor`] corresponding to `fd` /// if it describes a [`Descriptor::File`] or [`Descriptor::PreopenDirectory`] /// of [`crate::preview2::filesystem::Dir`] type - fn get_dir_fd(&mut self, fd: types::Fd) -> Result { + fn get_dir_fd(&mut self, fd: types::Fd) -> Result> { let fd = fd.into(); match self.descriptors.get_mut().get(&fd) { - Some(Descriptor::File(File { fd, .. })) if self.view.table().is_dir(*fd) => Ok(*fd), - Some(Descriptor::PreopenDirectory((fd, _))) => Ok(*fd), + Some(Descriptor::File(File { fd, .. })) + if self.view.table().is_dir(&Resource::new_borrow(*fd)) => + { + Ok(Resource::new_borrow(*fd)) + } + Some(Descriptor::PreopenDirectory((fd, _))) => Ok(Resource::new_borrow(*fd)), _ => Err(types::Errno::Badf.into()), } } @@ -448,7 +459,7 @@ trait WasiPreview1ViewExt: /// Lazily initializes [`WasiPreview1Adapter`] returned by [`WasiPreview1View::adapter_mut`] /// and returns [`filesystem::Descriptor`] corresponding to `fd` - fn get_fd(&mut self, fd: types::Fd) -> Result { + fn get_fd(&mut self, fd: types::Fd) -> Result, types::Error> { let mut st = self.transact()?; let fd = st.get_fd(fd)?; Ok(fd) @@ -457,7 +468,10 @@ trait WasiPreview1ViewExt: /// Lazily initializes [`WasiPreview1Adapter`] returned by [`WasiPreview1View::adapter_mut`] /// and returns [`filesystem::Descriptor`] corresponding to `fd` /// if it describes a [`Descriptor::File`] of [`crate::preview2::filesystem::File`] type - fn get_file_fd(&mut self, fd: types::Fd) -> Result { + fn get_file_fd( + &mut self, + fd: types::Fd, + ) -> Result, types::Error> { let mut st = self.transact()?; let fd = st.get_file_fd(fd)?; Ok(fd) @@ -467,7 +481,10 @@ trait WasiPreview1ViewExt: /// and returns [`filesystem::Descriptor`] corresponding to `fd` /// if it describes a [`Descriptor::File`] or [`Descriptor::PreopenDirectory`] /// of [`crate::preview2::filesystem::Dir`] type - fn get_dir_fd(&mut self, fd: types::Fd) -> Result { + fn get_dir_fd( + &mut self, + fd: types::Fd, + ) -> Result, types::Error> { let mut st = self.transact()?; let fd = st.get_dir_fd(fd)?; Ok(fd) @@ -476,13 +493,13 @@ trait WasiPreview1ViewExt: impl WasiPreview1ViewExt for T {} -pub fn add_to_linker_async( +pub fn add_to_linker_async( linker: &mut wasmtime::Linker, ) -> anyhow::Result<()> { wasi_snapshot_preview1::add_to_linker(linker, |t| t) } -pub fn add_to_linker_sync( +pub fn add_to_linker_sync( linker: &mut wasmtime::Linker, ) -> anyhow::Result<()> { sync::add_wasi_snapshot_preview1_to_linker(linker, |t| t) @@ -849,7 +866,7 @@ impl< + bindings::cli::exit::Host + bindings::filesystem::preopens::Host + bindings::filesystem::types::Host - + bindings::poll::poll::Host + + bindings::io::poll::Host + bindings::random::random::Host + bindings::io::streams::Host + bindings::clocks::monotonic_clock::Host @@ -1018,16 +1035,17 @@ impl< .clone(); match desc { Descriptor::Stdin { input_stream, .. } => { - streams::Host::drop_input_stream(self, input_stream) + streams::HostInputStream::drop(self, Resource::new_own(input_stream)) .context("failed to call `drop-input-stream`") } Descriptor::Stdout { output_stream, .. } | Descriptor::Stderr { output_stream, .. } => { - streams::Host::drop_output_stream(self, output_stream) + streams::HostOutputStream::drop(self, Resource::new_own(output_stream)) .context("failed to call `drop-output-stream`") } - Descriptor::File(File { fd, .. }) | Descriptor::PreopenDirectory((fd, _)) => self - .drop_descriptor(fd) - .context("failed to call `drop-descriptor`"), + Descriptor::File(File { fd, .. }) | Descriptor::PreopenDirectory((fd, _)) => { + filesystem::HostDescriptor::drop(self, Resource::new_own(fd)) + .context("failed to call `drop-descriptor`") + } } .map_err(types::Error::trap) } @@ -1115,13 +1133,16 @@ impl< .. }) => (*fd, *blocking_mode, *append), }; - let flags = self.get_flags(fd).await.map_err(|e| { - e.try_into() - .context("failed to call `get-flags`") - .unwrap_or_else(types::Error::trap) - })?; + let flags = self + .get_flags(Resource::new_borrow(fd)) + .await + .map_err(|e| { + e.try_into() + .context("failed to call `get-flags`") + .unwrap_or_else(types::Error::trap) + })?; let fs_filetype = self - .get_type(fd) + .get_type(Resource::new_borrow(fd)) .await .map_err(|e| { e.try_into() @@ -1225,20 +1246,27 @@ impl< data_access_timestamp, data_modification_timestamp, status_change_timestamp, - } = self.stat(fd).await.map_err(|e| { + } = self.stat(Resource::new_borrow(fd)).await.map_err(|e| { e.try_into() .context("failed to call `stat`") .unwrap_or_else(types::Error::trap) })?; - let metadata_hash = self.metadata_hash(fd).await.map_err(|e| { - e.try_into() - .context("failed to call `metadata_hash`") - .unwrap_or_else(types::Error::trap) - })?; + let metadata_hash = + self.metadata_hash(Resource::new_borrow(fd)) + .await + .map_err(|e| { + e.try_into() + .context("failed to call `metadata_hash`") + .unwrap_or_else(types::Error::trap) + })?; let filetype = type_.try_into().map_err(types::Error::trap)?; - let atim = data_access_timestamp.try_into()?; - let mtim = data_modification_timestamp.try_into()?; - let ctim = status_change_timestamp.try_into()?; + let zero = wall_clock::Datetime { + seconds: 0, + nanoseconds: 0, + }; + let atim = data_access_timestamp.unwrap_or(zero).try_into()?; + let mtim = data_modification_timestamp.unwrap_or(zero).try_into()?; + let ctim = status_change_timestamp.unwrap_or(zero).try_into()?; Ok(types::Filestat { dev: 1, ino: metadata_hash.lower, @@ -1313,17 +1341,19 @@ impl< blocking_mode, position, .. - }) if self.table().is_file(fd) => { + }) if self.table().is_file(&Resource::new_borrow(fd)) => { let Some(buf) = first_non_empty_iovec(iovs)? else { return Ok(0); }; let pos = position.load(Ordering::Relaxed); - let stream = self.read_via_stream(fd, pos).map_err(|e| { - e.try_into() - .context("failed to call `read-via-stream`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self + .read_via_stream(Resource::new_borrow(fd), pos) + .map_err(|e| { + e.try_into() + .context("failed to call `read-via-stream`") + .unwrap_or_else(types::Error::trap) + })?; let (read, state) = blocking_mode.read(self, stream, buf.len()).await?; let n = read.len().try_into()?; let pos = pos.checked_add(n).ok_or(types::Errno::Overflow)?; @@ -1336,9 +1366,9 @@ impl< return Ok(0); }; let (read, state) = stream_res( - streams::Host::blocking_read( + streams::HostInputStream::blocking_read( self, - input_stream, + Resource::new_borrow(input_stream), buf.len().try_into().unwrap_or(u64::MAX), ) .await, @@ -1372,16 +1402,18 @@ impl< let (mut buf, read, state) = match desc { Descriptor::File(File { fd, blocking_mode, .. - }) if self.table().is_file(fd) => { + }) if self.table().is_file(&Resource::new_borrow(fd)) => { let Some(buf) = first_non_empty_iovec(iovs)? else { return Ok(0); }; - let stream = self.read_via_stream(fd, offset).map_err(|e| { - e.try_into() - .context("failed to call `read-via-stream`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self + .read_via_stream(Resource::new_borrow(fd), offset) + .map_err(|e| { + e.try_into() + .context("failed to call `read-via-stream`") + .unwrap_or_else(types::Error::trap) + })?; let (read, state) = blocking_mode.read(self, stream, buf.len()).await?; (buf, read, state) } @@ -1418,24 +1450,28 @@ impl< blocking_mode, append, position, - }) if self.table().is_file(fd) => { + }) if self.table().is_file(&Resource::new_borrow(fd)) => { let Some(buf) = first_non_empty_ciovec(ciovs)? else { return Ok(0); }; let (stream, pos) = if append { - let stream = self.append_via_stream(fd).map_err(|e| { - e.try_into() - .context("failed to call `append-via-stream`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self + .append_via_stream(Resource::new_borrow(fd)) + .map_err(|e| { + e.try_into() + .context("failed to call `append-via-stream`") + .unwrap_or_else(types::Error::trap) + })?; (stream, 0) } else { let position = position.load(Ordering::Relaxed); - let stream = self.write_via_stream(fd, position).map_err(|e| { - e.try_into() - .context("failed to call `write-via-stream`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self + .write_via_stream(Resource::new_borrow(fd), position) + .map_err(|e| { + e.try_into() + .context("failed to call `write-via-stream`") + .unwrap_or_else(types::Error::trap) + })?; (stream, position) }; let n = blocking_mode.write(self, stream, &buf).await?; @@ -1450,7 +1486,7 @@ impl< return Ok(0); }; Ok(BlockingMode::Blocking - .write(self, output_stream, &buf) + .write(self, Resource::new_borrow(output_stream), &buf) .await? .try_into()?) } @@ -1471,15 +1507,17 @@ impl< let n = match desc { Descriptor::File(File { fd, blocking_mode, .. - }) if self.table().is_file(fd) => { + }) if self.table().is_file(&Resource::new_borrow(fd)) => { let Some(buf) = first_non_empty_ciovec(ciovs)? else { return Ok(0); }; - let stream = self.write_via_stream(fd, offset).map_err(|e| { - e.try_into() - .context("failed to call `write-via-stream`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self + .write_via_stream(Resource::new_borrow(fd), offset) + .map_err(|e| { + e.try_into() + .context("failed to call `write-via-stream`") + .unwrap_or_else(types::Error::trap) + })?; blocking_mode.write(self, stream, &buf).await? } Descriptor::Stdout { .. } | Descriptor::Stderr { .. } => { @@ -1551,11 +1589,12 @@ impl< .checked_add_signed(offset) .ok_or(types::Errno::Inval)?, types::Whence::End => { - let filesystem::DescriptorStat { size, .. } = self.stat(fd).await.map_err(|e| { - e.try_into() - .context("failed to call `stat`") - .unwrap_or_else(types::Error::trap) - })?; + let filesystem::DescriptorStat { size, .. } = + self.stat(Resource::new_borrow(fd)).await.map_err(|e| { + e.try_into() + .context("failed to call `stat`") + .unwrap_or_else(types::Error::trap) + })?; size.checked_add_signed(offset).ok_or(types::Errno::Inval)? } _ => return Err(types::Errno::Inval.into()), @@ -1596,16 +1635,22 @@ impl< cookie: types::Dircookie, ) -> Result { let fd = self.get_dir_fd(fd)?; - let stream = self.read_directory(fd).await.map_err(|e| { - e.try_into() - .context("failed to call `read-directory`") - .unwrap_or_else(types::Error::trap) - })?; - let dir_metadata_hash = self.metadata_hash(fd).await.map_err(|e| { - e.try_into() - .context("failed to call `metadata-hash`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self + .read_directory(Resource::new_borrow(fd.rep())) + .await + .map_err(|e| { + e.try_into() + .context("failed to call `read-directory`") + .unwrap_or_else(types::Error::trap) + })?; + let dir_metadata_hash = self + .metadata_hash(Resource::new_borrow(fd.rep())) + .await + .map_err(|e| { + e.try_into() + .context("failed to call `metadata-hash`") + .unwrap_or_else(types::Error::trap) + })?; let cookie = cookie.try_into().map_err(|_| types::Errno::Overflow)?; let head = [ @@ -1643,7 +1688,11 @@ impl< .unwrap_or_else(types::Error::trap) })?; let metadata_hash = self - .metadata_hash_at(fd, filesystem::PathFlags::empty(), name.clone()) + .metadata_hash_at( + Resource::new_borrow(fd.rep()), + filesystem::PathFlags::empty(), + name.clone(), + ) .await .map_err(|e| { e.try_into() @@ -1709,7 +1758,8 @@ impl< ) -> Result<(), types::Error> { let dirfd = self.get_dir_fd(dirfd)?; let path = read_string(path)?; - self.create_directory_at(dirfd, path).await.map_err(|e| { + let borrow = Resource::new_borrow(dirfd.rep()); + self.create_directory_at(borrow, path).await.map_err(|e| { e.try_into() .context("failed to call `create-directory-at`") .unwrap_or_else(types::Error::trap) @@ -1735,7 +1785,11 @@ impl< data_modification_timestamp, status_change_timestamp, } = self - .stat_at(dirfd, flags.into(), path.clone()) + .stat_at( + Resource::new_borrow(dirfd.rep()), + flags.into(), + path.clone(), + ) .await .map_err(|e| { e.try_into() @@ -1751,9 +1805,13 @@ impl< .unwrap_or_else(types::Error::trap) })?; let filetype = type_.try_into().map_err(types::Error::trap)?; - let atim = data_access_timestamp.try_into()?; - let mtim = data_modification_timestamp.try_into()?; - let ctim = status_change_timestamp.try_into()?; + let zero = wall_clock::Datetime { + seconds: 0, + nanoseconds: 0, + }; + let atim = data_access_timestamp.unwrap_or(zero).try_into()?; + let mtim = data_modification_timestamp.unwrap_or(zero).try_into()?; + let ctim = status_change_timestamp.unwrap_or(zero).try_into()?; Ok(types::Filestat { dev: 1, ino: metadata_hash.lower, @@ -1859,8 +1917,10 @@ impl< let desc = self.transact()?.get_descriptor(dirfd)?.clone(); let dirfd = match desc { Descriptor::PreopenDirectory((fd, _)) => fd, - Descriptor::File(File { fd, .. }) if self.table().is_dir(fd) => fd, - Descriptor::File(File { fd, .. }) if !self.table().is_dir(fd) => { + Descriptor::File(File { fd, .. }) if self.table().is_dir(&Resource::new_borrow(fd)) => { + fd + } + Descriptor::File(File { fd: _, .. }) => { // NOTE: Unlike most other methods, legacy implementation returns `NOTDIR` here return Err(types::Errno::Notdir.into()); } @@ -1868,7 +1928,7 @@ impl< }; let fd = self .open_at( - dirfd, + Resource::new_borrow(dirfd), dirflags.into(), path, oflags.into(), @@ -1882,7 +1942,7 @@ impl< .unwrap_or_else(types::Error::trap) })?; let fd = self.transact()?.descriptors.get_mut().push_file(File { - fd, + fd: fd.rep(), position: Default::default(), append: fdflags.contains(types::Fdflags::APPEND), blocking_mode: BlockingMode::from_fdflags(&fdflags), @@ -1968,7 +2028,8 @@ impl< let dirfd = self.get_dir_fd(dirfd)?; let src_path = read_string(src_path)?; let dest_path = read_string(dest_path)?; - self.symlink_at(dirfd, src_path, dest_path) + let borrow = Resource::new_borrow(dirfd.rep()); + self.symlink_at(borrow, src_path, dest_path) .await .map_err(|e| { e.try_into() @@ -1985,7 +2046,8 @@ impl< ) -> Result<(), types::Error> { let dirfd = self.get_dir_fd(dirfd)?; let path = path.as_cow()?.to_string(); - self.unlink_file_at(dirfd, path).await.map_err(|e| { + let borrow = Resource::new_borrow(dirfd.rep()); + self.unlink_file_at(borrow, path).await.map_err(|e| { e.try_into() .context("failed to call `unlink-file-at`") .unwrap_or_else(types::Error::trap) diff --git a/crates/wasi/src/preview2/stdio.rs b/crates/wasi/src/preview2/stdio.rs index 08f4350462cc..e7a6d667ae81 100644 --- a/crates/wasi/src/preview2/stdio.rs +++ b/crates/wasi/src/preview2/stdio.rs @@ -3,10 +3,56 @@ use crate::preview2::bindings::cli::{ terminal_stdout, }; use crate::preview2::bindings::io::streams; -use crate::preview2::pipe::AsyncWriteStream; -use crate::preview2::{HostOutputStream, OutputStreamError, WasiView}; -use bytes::Bytes; -use is_terminal::IsTerminal; +use crate::preview2::pipe::{self, AsyncWriteStream}; +use crate::preview2::stream::TableStreamExt; +use crate::preview2::{HostInputStream, HostOutputStream, WasiView}; +use std::io::IsTerminal; +use wasmtime::component::Resource; + +/// A trait used to represent the standard input to a guest program. +/// +/// This is used to implement various WASI APIs via the method implementations +/// below. +/// +/// Built-in implementations are provided for [`Stdin`], +/// [`pipe::MemoryInputPipe`], and [`pipe::ClosedInputStream`]. +pub trait StdinStream: Send + Sync { + /// Creates a fresh stream which is reading stdin. + /// + /// Note that the returned stream must share state with all other streams + /// previously created. Guests may create multiple handles to the same stdin + /// and they should all be synchronized in their progress through the + /// program's input. + /// + /// Note that this means that if one handle becomes ready for reading they + /// all become ready for reading. Subsequently if one is read from it may + /// mean that all the others are no longer ready for reading. This is + /// basically a consequence of the way the WIT APIs are designed today. + fn stream(&self) -> Box; + + /// Returns whether this stream is backed by a TTY. + fn isatty(&self) -> bool; +} + +impl StdinStream for pipe::MemoryInputPipe { + fn stream(&self) -> Box { + Box::new(self.clone()) + } + + fn isatty(&self) -> bool { + false + } +} + +impl StdinStream for pipe::ClosedInputStream { + fn stream(&self) -> Box { + Box::new(self.clone()) + } + + fn isatty(&self) -> bool { + false + } +} mod worker_thread_stdin; pub use self::worker_thread_stdin::{stdin, Stdin}; @@ -17,55 +63,91 @@ pub use self::worker_thread_stdin::{stdin, Stdin}; // and tokio's stdout/err. const STDIO_BUFFER_SIZE: usize = 4096; -pub struct Stdout(AsyncWriteStream); - -pub fn stdout() -> Stdout { - Stdout(AsyncWriteStream::new( - STDIO_BUFFER_SIZE, - tokio::io::stdout(), - )) +/// Similar to [`StdinStream`], except for output. +pub trait StdoutStream: Send + Sync { + /// Returns a fresh new stream which can write to this output stream. + /// + /// Note that all output streams should output to the same logical source. + /// This means that it's possible for each independent stream to acquire a + /// separate "permit" to write and then act on that permit. Note that + /// additionally at this time once a permit is "acquired" there's no way to + /// release it, for example you can wait for readiness and then never + /// actually write in WASI. This means that acquisition of a permit for one + /// stream cannot discount the size of a permit another stream could + /// obtain. + /// + /// Implementations must be able to handle this + fn stream(&self) -> Box; + + /// Returns whether this stream is backed by a TTY. + fn isatty(&self) -> bool; } -impl IsTerminal for Stdout { - fn is_terminal(&self) -> bool { - std::io::stdout().is_terminal() + +impl StdoutStream for pipe::MemoryOutputPipe { + fn stream(&self) -> Box { + Box::new(self.clone()) + } + + fn isatty(&self) -> bool { + false } } -#[async_trait::async_trait] -impl HostOutputStream for Stdout { - fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError> { - self.0.write(bytes) + +impl StdoutStream for pipe::SinkOutputStream { + fn stream(&self) -> Box { + Box::new(self.clone()) } - fn flush(&mut self) -> Result<(), OutputStreamError> { - self.0.flush() + + fn isatty(&self) -> bool { + false + } +} + +impl StdoutStream for pipe::ClosedOutputStream { + fn stream(&self) -> Box { + Box::new(self.clone()) } - async fn write_ready(&mut self) -> Result { - self.0.write_ready().await + + fn isatty(&self) -> bool { + false } } -pub struct Stderr(AsyncWriteStream); +pub struct Stdout; -pub fn stderr() -> Stderr { - Stderr(AsyncWriteStream::new( - STDIO_BUFFER_SIZE, - tokio::io::stderr(), - )) +pub fn stdout() -> Stdout { + Stdout } -impl IsTerminal for Stderr { - fn is_terminal(&self) -> bool { - std::io::stderr().is_terminal() + +impl StdoutStream for Stdout { + fn stream(&self) -> Box { + Box::new(AsyncWriteStream::new( + STDIO_BUFFER_SIZE, + tokio::io::stdout(), + )) } -} -#[async_trait::async_trait] -impl HostOutputStream for Stderr { - fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError> { - self.0.write(bytes) + + fn isatty(&self) -> bool { + std::io::stdout().is_terminal() } - fn flush(&mut self) -> Result<(), OutputStreamError> { - self.0.flush() +} + +pub struct Stderr; + +pub fn stderr() -> Stderr { + Stderr +} + +impl StdoutStream for Stderr { + fn stream(&self) -> Box { + Box::new(AsyncWriteStream::new( + STDIO_BUFFER_SIZE, + tokio::io::stderr(), + )) } - async fn write_ready(&mut self) -> Result { - self.0.write_ready().await + + fn isatty(&self) -> bool { + std::io::stderr().is_terminal() } } @@ -75,71 +157,75 @@ pub enum IsATTY { No, } -pub(crate) struct StdioInput { - pub input_stream: streams::InputStream, - pub isatty: IsATTY, -} - -pub(crate) struct StdioOutput { - pub output_stream: streams::OutputStream, - pub isatty: IsATTY, -} - impl stdin::Host for T { - fn get_stdin(&mut self) -> Result { - Ok(self.ctx().stdin.input_stream) + fn get_stdin(&mut self) -> Result, anyhow::Error> { + let stream = self.ctx_mut().stdin.stream(); + Ok(self.table_mut().push_input_stream(stream)?) } } impl stdout::Host for T { - fn get_stdout(&mut self) -> Result { - Ok(self.ctx().stdout.output_stream) + fn get_stdout(&mut self) -> Result, anyhow::Error> { + let stream = self.ctx_mut().stdout.stream(); + Ok(self.table_mut().push_output_stream(stream)?) } } impl stderr::Host for T { - fn get_stderr(&mut self) -> Result { - Ok(self.ctx().stderr.output_stream) + fn get_stderr(&mut self) -> Result, anyhow::Error> { + let stream = self.ctx_mut().stderr.stream(); + Ok(self.table_mut().push_output_stream(stream)?) } } -struct HostTerminalInput; -struct HostTerminalOutput; +pub struct HostTerminalInput; +pub struct HostTerminalOutput; -impl terminal_input::Host for T { - fn drop_terminal_input(&mut self, r: terminal_input::TerminalInput) -> anyhow::Result<()> { - self.table_mut().delete::(r)?; +impl terminal_input::Host for T {} +impl crate::preview2::bindings::cli::terminal_input::HostTerminalInput for T { + fn drop(&mut self, r: Resource) -> anyhow::Result<()> { + self.table_mut().delete::(r.rep())?; Ok(()) } } -impl terminal_output::Host for T { - fn drop_terminal_output(&mut self, r: terminal_output::TerminalOutput) -> anyhow::Result<()> { - self.table_mut().delete::(r)?; +impl terminal_output::Host for T {} +impl crate::preview2::bindings::cli::terminal_output::HostTerminalOutput for T { + fn drop(&mut self, r: Resource) -> anyhow::Result<()> { + self.table_mut().delete::(r.rep())?; Ok(()) } } impl terminal_stdin::Host for T { - fn get_terminal_stdin(&mut self) -> anyhow::Result> { - if let IsATTY::Yes = self.ctx().stdin.isatty { - Ok(Some(self.table_mut().push(Box::new(HostTerminalInput))?)) + fn get_terminal_stdin( + &mut self, + ) -> anyhow::Result>> { + if self.ctx().stdin.isatty() { + let fd = self.table_mut().push(Box::new(HostTerminalInput))?; + Ok(Some(Resource::new_own(fd))) } else { Ok(None) } } } impl terminal_stdout::Host for T { - fn get_terminal_stdout(&mut self) -> anyhow::Result> { - if let IsATTY::Yes = self.ctx().stdout.isatty { - Ok(Some(self.table_mut().push(Box::new(HostTerminalOutput))?)) + fn get_terminal_stdout( + &mut self, + ) -> anyhow::Result>> { + if self.ctx().stdout.isatty() { + let fd = self.table_mut().push(Box::new(HostTerminalOutput))?; + Ok(Some(Resource::new_own(fd))) } else { Ok(None) } } } impl terminal_stderr::Host for T { - fn get_terminal_stderr(&mut self) -> anyhow::Result> { - if let IsATTY::Yes = self.ctx().stderr.isatty { - Ok(Some(self.table_mut().push(Box::new(HostTerminalOutput))?)) + fn get_terminal_stderr( + &mut self, + ) -> anyhow::Result>> { + if self.ctx().stderr.isatty() { + let fd = self.table_mut().push(Box::new(HostTerminalOutput))?; + Ok(Some(Resource::new_own(fd))) } else { Ok(None) } diff --git a/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs b/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs index e1505dff844b..bf933ad8398e 100644 --- a/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs +++ b/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs @@ -23,10 +23,11 @@ //! This module is one that's likely to change over time though as new systems //! are encountered along with preexisting bugs. +use crate::preview2::stdio::StdinStream; use crate::preview2::{HostInputStream, StreamState}; use anyhow::Error; use bytes::{Bytes, BytesMut}; -use std::io::Read; +use std::io::{IsTerminal, Read}; use std::mem; use std::sync::{Condvar, Mutex, OnceLock}; use tokio::sync::Notify; @@ -103,8 +104,12 @@ pub fn stdin() -> Stdin { Stdin } -impl is_terminal::IsTerminal for Stdin { - fn is_terminal(&self) -> bool { +impl StdinStream for Stdin { + fn stream(&self) -> Box { + Box::new(Stdin) + } + + fn isatty(&self) -> bool { std::io::stdin().is_terminal() } } diff --git a/crates/wasi/src/preview2/stream.rs b/crates/wasi/src/preview2/stream.rs index 2c8a46b7f99d..7a61747d2511 100644 --- a/crates/wasi/src/preview2/stream.rs +++ b/crates/wasi/src/preview2/stream.rs @@ -1,8 +1,10 @@ +use crate::preview2::bindings::io::streams::{InputStream, OutputStream}; use crate::preview2::filesystem::FileInputStream; use crate::preview2::{Table, TableError}; use anyhow::Error; use bytes::Bytes; use std::fmt; +use wasmtime::component::Resource; /// An error which should be reported to Wasm as a runtime error, rather than /// an error which should trap Wasm execution. The definition for runtime @@ -175,94 +177,128 @@ pub(crate) trait InternalTableStreamExt { fn push_internal_input_stream( &mut self, istream: InternalInputStream, - ) -> Result; - fn push_internal_input_stream_child( + ) -> Result, TableError>; + fn push_internal_input_stream_child( &mut self, istream: InternalInputStream, - parent: u32, - ) -> Result; + parent: Resource, + ) -> Result, TableError>; fn get_internal_input_stream_mut( &mut self, - fd: u32, + fd: &Resource, ) -> Result<&mut InternalInputStream, TableError>; - fn delete_internal_input_stream(&mut self, fd: u32) -> Result; + fn delete_internal_input_stream( + &mut self, + fd: Resource, + ) -> Result; } impl InternalTableStreamExt for Table { fn push_internal_input_stream( &mut self, istream: InternalInputStream, - ) -> Result { - self.push(Box::new(istream)) + ) -> Result, TableError> { + Ok(Resource::new_own(self.push(Box::new(istream))?)) } - fn push_internal_input_stream_child( + fn push_internal_input_stream_child( &mut self, istream: InternalInputStream, - parent: u32, - ) -> Result { - self.push_child(Box::new(istream), parent) + parent: Resource, + ) -> Result, TableError> { + Ok(Resource::new_own( + self.push_child(Box::new(istream), parent.rep())?, + )) } fn get_internal_input_stream_mut( &mut self, - fd: u32, + fd: &Resource, ) -> Result<&mut InternalInputStream, TableError> { - self.get_mut(fd) + self.get_mut(fd.rep()) } - fn delete_internal_input_stream(&mut self, fd: u32) -> Result { - self.delete(fd) + fn delete_internal_input_stream( + &mut self, + fd: Resource, + ) -> Result { + self.delete(fd.rep()) } } /// Extension trait for managing [`HostInputStream`]s and [`HostOutputStream`]s in the [`Table`]. pub trait TableStreamExt { /// Push a [`HostInputStream`] into a [`Table`], returning the table index. - fn push_input_stream(&mut self, istream: Box) -> Result; + fn push_input_stream( + &mut self, + istream: Box, + ) -> Result, TableError>; /// Same as [`push_input_stream`](Self::push_output_stream) except assigns a parent resource to /// the input-stream created. - fn push_input_stream_child( + fn push_input_stream_child( &mut self, istream: Box, - parent: u32, - ) -> Result; + parent: Resource, + ) -> Result, TableError>; /// Get a mutable reference to a [`HostInputStream`] in a [`Table`]. - fn get_input_stream_mut(&mut self, fd: u32) -> Result<&mut dyn HostInputStream, TableError>; + fn get_input_stream_mut( + &mut self, + fd: &Resource, + ) -> Result<&mut dyn HostInputStream, TableError>; /// Remove [`HostInputStream`] from table: - fn delete_input_stream(&mut self, fd: u32) -> Result, TableError>; + fn delete_input_stream( + &mut self, + fd: Resource, + ) -> Result, TableError>; /// Push a [`HostOutputStream`] into a [`Table`], returning the table index. - fn push_output_stream(&mut self, ostream: Box) - -> Result; + fn push_output_stream( + &mut self, + ostream: Box, + ) -> Result, TableError>; /// Same as [`push_output_stream`](Self::push_output_stream) except assigns a parent resource /// to the output-stream created. - fn push_output_stream_child( + fn push_output_stream_child( &mut self, ostream: Box, - parent: u32, - ) -> Result; + parent: Resource, + ) -> Result, TableError>; /// Get a mutable reference to a [`HostOutputStream`] in a [`Table`]. - fn get_output_stream_mut(&mut self, fd: u32) -> Result<&mut dyn HostOutputStream, TableError>; + fn get_output_stream_mut( + &mut self, + fd: &Resource, + ) -> Result<&mut dyn HostOutputStream, TableError>; /// Remove [`HostOutputStream`] from table: - fn delete_output_stream(&mut self, fd: u32) -> Result, TableError>; + fn delete_output_stream( + &mut self, + fd: Resource, + ) -> Result, TableError>; } impl TableStreamExt for Table { - fn push_input_stream(&mut self, istream: Box) -> Result { + fn push_input_stream( + &mut self, + istream: Box, + ) -> Result, TableError> { self.push_internal_input_stream(InternalInputStream::Host(istream)) } - fn push_input_stream_child( + fn push_input_stream_child( &mut self, istream: Box, - parent: u32, - ) -> Result { + parent: Resource, + ) -> Result, TableError> { self.push_internal_input_stream_child(InternalInputStream::Host(istream), parent) } - fn get_input_stream_mut(&mut self, fd: u32) -> Result<&mut dyn HostInputStream, TableError> { + fn get_input_stream_mut( + &mut self, + fd: &Resource, + ) -> Result<&mut dyn HostInputStream, TableError> { match self.get_internal_input_stream_mut(fd)? { InternalInputStream::Host(ref mut h) => Ok(h.as_mut()), _ => Err(TableError::WrongType), } } - fn delete_input_stream(&mut self, fd: u32) -> Result, TableError> { - let occ = self.entry(fd)?; + fn delete_input_stream( + &mut self, + fd: Resource, + ) -> Result, TableError> { + let occ = self.entry(fd.rep())?; match occ.get().downcast_ref::() { Some(InternalInputStream::Host(_)) => { let any = occ.remove_entry()?; @@ -278,22 +314,30 @@ impl TableStreamExt for Table { fn push_output_stream( &mut self, ostream: Box, - ) -> Result { - self.push(Box::new(ostream)) + ) -> Result, TableError> { + Ok(Resource::new_own(self.push(Box::new(ostream))?)) } - fn push_output_stream_child( + fn push_output_stream_child( &mut self, ostream: Box, - parent: u32, - ) -> Result { - self.push_child(Box::new(ostream), parent) + parent: Resource, + ) -> Result, TableError> { + Ok(Resource::new_own( + self.push_child(Box::new(ostream), parent.rep())?, + )) } - fn get_output_stream_mut(&mut self, fd: u32) -> Result<&mut dyn HostOutputStream, TableError> { - let boxed: &mut Box = self.get_mut(fd)?; + fn get_output_stream_mut( + &mut self, + fd: &Resource, + ) -> Result<&mut dyn HostOutputStream, TableError> { + let boxed: &mut Box = self.get_mut(fd.rep())?; Ok(boxed.as_mut()) } - fn delete_output_stream(&mut self, fd: u32) -> Result, TableError> { - self.delete(fd) + fn delete_output_stream( + &mut self, + fd: Resource, + ) -> Result, TableError> { + self.delete(fd.rep()) } } @@ -308,19 +352,9 @@ mod test { // Put it into the table: let ix = table.push_input_stream(Box::new(dummy)).unwrap(); // Get a mut ref to it: - let _ = table.get_input_stream_mut(ix).unwrap(); - // Fails at wrong type: - assert!(matches!( - table.get_output_stream_mut(ix), - Err(TableError::WrongType) - )); + let _ = table.get_input_stream_mut(&ix).unwrap(); // Delete it: let _ = table.delete_input_stream(ix).unwrap(); - // Now absent from table: - assert!(matches!( - table.get_input_stream_mut(ix), - Err(TableError::NotPresent) - )); } #[test] @@ -330,18 +364,8 @@ mod test { // Put it in the table: let ix = table.push_output_stream(Box::new(dummy)).unwrap(); // Get a mut ref to it: - let _ = table.get_output_stream_mut(ix).unwrap(); - // Fails at wrong type: - assert!(matches!( - table.get_input_stream_mut(ix), - Err(TableError::WrongType) - )); + let _ = table.get_output_stream_mut(&ix).unwrap(); // Delete it: let _ = table.delete_output_stream(ix).unwrap(); - // Now absent: - assert!(matches!( - table.get_output_stream_mut(ix), - Err(TableError::NotPresent) - )); } } diff --git a/crates/wasi/src/preview2/table.rs b/crates/wasi/src/preview2/table.rs index de9b50a6f772..c8d8cb142973 100644 --- a/crates/wasi/src/preview2/table.rs +++ b/crates/wasi/src/preview2/table.rs @@ -196,8 +196,7 @@ impl Table { } } - /// Get a mutable reference to a resource of a given type at a given index. Only one mutable - /// reference can be borrowed at any given time. Borrow failure results in a trapping error. + /// Get a mutable reference to a resource of a given type at a given index. pub fn get_mut(&mut self, key: u32) -> Result<&mut T, TableError> { if let Some(r) = self.map.get_mut(&key) { r.entry @@ -208,6 +207,15 @@ impl Table { } } + /// Get a mutable reference to a resource a a `&mut dyn Any`. + pub fn get_as_any_mut(&mut self, key: u32) -> Result<&mut dyn Any, TableError> { + if let Some(r) = self.map.get_mut(&key) { + Ok(&mut *r.entry) + } else { + Err(TableError::NotPresent) + } + } + /// Get an [`OccupiedEntry`] corresponding to a table entry, if it exists. This allows you to /// remove or replace the entry based on its contents. The methods available are a subset of /// [`std::collections::hash_map::OccupiedEntry`] - it does not give access to the key, it diff --git a/crates/wasi/src/preview2/tcp.rs b/crates/wasi/src/preview2/tcp.rs index 9721e2c3c37a..eb3dbe4cee0e 100644 --- a/crates/wasi/src/preview2/tcp.rs +++ b/crates/wasi/src/preview2/tcp.rs @@ -1,4 +1,5 @@ use super::{HostInputStream, HostOutputStream, OutputStreamError}; +use crate::preview2::bindings::sockets::tcp::TcpSocket; use crate::preview2::{ with_ambient_tokio_runtime, AbortOnDropJoinHandle, StreamState, Table, TableError, }; @@ -7,6 +8,7 @@ use cap_std::net::TcpListener; use io_lifetimes::raw::{FromRawSocketlike, IntoRawSocketlike}; use std::io; use std::sync::Arc; +use wasmtime::component::Resource; /// The state of a TCP socket. /// @@ -43,8 +45,8 @@ pub(crate) enum HostTcpState { /// /// The inner state is wrapped in an Arc because the same underlying socket is /// used for implementing the stream types. -pub(crate) struct HostTcpSocket { - /// The part of a `HostTcpSocket` which is reference-counted so that we +pub(crate) struct HostTcpSocketState { + /// The part of a `HostTcpSocketState` which is reference-counted so that we /// can pass it to async tasks. pub(crate) inner: Arc, @@ -213,7 +215,7 @@ impl HostOutputStream for TcpWriteStream { } } -impl HostTcpSocket { +impl HostTcpSocketState { /// Create a new socket in the given family. pub fn new(family: AddressFamily) -> io::Result { // Create a new host socket and set it to non-blocking, which is needed @@ -222,7 +224,7 @@ impl HostTcpSocket { Self::from_tcp_listener(tcp_listener) } - /// Create a `HostTcpSocket` from an existing socket. + /// Create a `HostTcpSocketState` from an existing socket. /// /// The socket must be in non-blocking mode. pub fn from_tcp_stream(tcp_socket: cap_std::net::TcpStream) -> io::Result { @@ -254,27 +256,45 @@ impl HostTcpSocket { } pub(crate) trait TableTcpSocketExt { - fn push_tcp_socket(&mut self, tcp_socket: HostTcpSocket) -> Result; - fn delete_tcp_socket(&mut self, fd: u32) -> Result; - fn is_tcp_socket(&self, fd: u32) -> bool; - fn get_tcp_socket(&self, fd: u32) -> Result<&HostTcpSocket, TableError>; - fn get_tcp_socket_mut(&mut self, fd: u32) -> Result<&mut HostTcpSocket, TableError>; + fn push_tcp_socket( + &mut self, + tcp_socket: HostTcpSocketState, + ) -> Result, TableError>; + fn delete_tcp_socket( + &mut self, + fd: Resource, + ) -> Result; + fn is_tcp_socket(&self, fd: &Resource) -> bool; + fn get_tcp_socket(&self, fd: &Resource) -> Result<&HostTcpSocketState, TableError>; + fn get_tcp_socket_mut( + &mut self, + fd: &Resource, + ) -> Result<&mut HostTcpSocketState, TableError>; } impl TableTcpSocketExt for Table { - fn push_tcp_socket(&mut self, tcp_socket: HostTcpSocket) -> Result { - self.push(Box::new(tcp_socket)) + fn push_tcp_socket( + &mut self, + tcp_socket: HostTcpSocketState, + ) -> Result, TableError> { + Ok(Resource::new_own(self.push(Box::new(tcp_socket))?)) } - fn delete_tcp_socket(&mut self, fd: u32) -> Result { - self.delete(fd) + fn delete_tcp_socket( + &mut self, + fd: Resource, + ) -> Result { + self.delete(fd.rep()) } - fn is_tcp_socket(&self, fd: u32) -> bool { - self.is::(fd) + fn is_tcp_socket(&self, fd: &Resource) -> bool { + self.is::(fd.rep()) } - fn get_tcp_socket(&self, fd: u32) -> Result<&HostTcpSocket, TableError> { - self.get(fd) + fn get_tcp_socket(&self, fd: &Resource) -> Result<&HostTcpSocketState, TableError> { + self.get(fd.rep()) } - fn get_tcp_socket_mut(&mut self, fd: u32) -> Result<&mut HostTcpSocket, TableError> { - self.get_mut(fd) + fn get_tcp_socket_mut( + &mut self, + fd: &Resource, + ) -> Result<&mut HostTcpSocketState, TableError> { + self.get_mut(fd.rep()) } } diff --git a/crates/wasi/wit/command-extended.wit b/crates/wasi/wit/command-extended.wit index 314e26dfce1b..3c56808e4abe 100644 --- a/crates/wasi/wit/command-extended.wit +++ b/crates/wasi/wit/command-extended.wit @@ -16,7 +16,7 @@ world command-extended { import wasi:random/random import wasi:random/insecure import wasi:random/insecure-seed - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/exit diff --git a/crates/wasi/wit/deps/cli/reactor.wit b/crates/wasi/wit/deps/cli/reactor.wit index 46e3186e5636..274d0644dc41 100644 --- a/crates/wasi/wit/deps/cli/reactor.wit +++ b/crates/wasi/wit/deps/cli/reactor.wit @@ -16,7 +16,7 @@ world reactor { import wasi:random/random import wasi:random/insecure import wasi:random/insecure-seed - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import environment diff --git a/crates/wasi/wit/deps/cli/terminal.wit b/crates/wasi/wit/deps/cli/terminal.wit index f32e74437484..b0a5bec2a2c7 100644 --- a/crates/wasi/wit/deps/cli/terminal.wit +++ b/crates/wasi/wit/deps/cli/terminal.wit @@ -1,31 +1,19 @@ interface terminal-input { /// The input side of a terminal. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type terminal-input = u32 + resource terminal-input // In the future, this may include functions for disabling echoing, // disabling input buffering so that keyboard events are sent through // immediately, querying supported features, and so on. - - /// Dispose of the specified terminal-input after which it may no longer - /// be used. - drop-terminal-input: func(this: terminal-input) } interface terminal-output { /// The output side of a terminal. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type terminal-output = u32 + resource terminal-output // In the future, this may include functions for querying the terminal // size, being notified of terminal size changes, querying supported // features, and so on. - - /// Dispose of the specified terminal-output, after which it may no longer - /// be used. - drop-terminal-output: func(this: terminal-output) } /// An interface providing an optional `terminal-input` for stdin as a diff --git a/crates/wasi/wit/deps/clocks/monotonic-clock.wit b/crates/wasi/wit/deps/clocks/monotonic-clock.wit index 50eb4de111af..703a5fb7a503 100644 --- a/crates/wasi/wit/deps/clocks/monotonic-clock.wit +++ b/crates/wasi/wit/deps/clocks/monotonic-clock.wit @@ -1,5 +1,3 @@ -package wasi:clocks - /// WASI Monotonic Clock is a clock API intended to let users measure elapsed /// time. /// @@ -11,7 +9,7 @@ package wasi:clocks /// /// It is intended for measuring elapsed time. interface monotonic-clock { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} /// A timestamp in nanoseconds. type instant = u64 diff --git a/crates/wasi/wit/deps/clocks/timezone.wit b/crates/wasi/wit/deps/clocks/timezone.wit index 2b6855668e1d..a872bffc7414 100644 --- a/crates/wasi/wit/deps/clocks/timezone.wit +++ b/crates/wasi/wit/deps/clocks/timezone.wit @@ -1,17 +1,6 @@ -package wasi:clocks - interface timezone { use wall-clock.{datetime} - /// A timezone. - /// - /// In timezones that recognize daylight saving time, also known as daylight - /// time and summer time, the information returned from the functions varies - /// over time to reflect these adjustments. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type timezone = u32 - /// Return information needed to display the given `datetime`. This includes /// the UTC offset, the time zone name, and a flag indicating whether /// daylight saving time is active. @@ -19,14 +8,10 @@ interface timezone { /// If the timezone cannot be determined for the given `datetime`, return a /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight /// saving time. - display: func(this: timezone, when: datetime) -> timezone-display + display: func(when: datetime) -> timezone-display /// The same as `display`, but only return the UTC offset. - utc-offset: func(this: timezone, when: datetime) -> s32 - - /// Dispose of the specified input-stream, after which it may no longer - /// be used. - drop-timezone: func(this: timezone) + utc-offset: func(when: datetime) -> s32 /// Information useful for displaying the timezone of a specific `datetime`. /// diff --git a/crates/wasi/wit/deps/clocks/wall-clock.wit b/crates/wasi/wit/deps/clocks/wall-clock.wit index 6137724f60b1..dae44a7308cd 100644 --- a/crates/wasi/wit/deps/clocks/wall-clock.wit +++ b/crates/wasi/wit/deps/clocks/wall-clock.wit @@ -1,5 +1,3 @@ -package wasi:clocks - /// WASI Wall Clock is a clock API intended to let users query the current /// time. The name "wall" makes an analogy to a "clock on the wall", which /// is not necessarily monotonic as it may be reset. diff --git a/crates/wasi/wit/deps/clocks/world.wit b/crates/wasi/wit/deps/clocks/world.wit new file mode 100644 index 000000000000..5c2dd411d2d0 --- /dev/null +++ b/crates/wasi/wit/deps/clocks/world.wit @@ -0,0 +1,7 @@ +package wasi:clocks + +world imports { + import monotonic-clock + import wall-clock + import timezone +} diff --git a/crates/wasi/wit/deps/filesystem/types.wit b/crates/wasi/wit/deps/filesystem/types.wit index e72a742de6c8..3f69bf997a29 100644 --- a/crates/wasi/wit/deps/filesystem/types.wit +++ b/crates/wasi/wit/deps/filesystem/types.wit @@ -17,6 +17,11 @@ /// `..` and symbolic link steps, reaches a directory outside of the base /// directory, or reaches a symlink to an absolute or rooted path in the /// underlying filesystem, the function fails with `error-code::not-permitted`. +/// +/// For more information about WASI path resolution and sandboxing, see +/// [WASI filesystem path resolution]. +/// +/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md interface types { use wasi:io/streams.{input-stream, output-stream} use wasi:clocks/wall-clock.{datetime} @@ -102,11 +107,20 @@ interface types { /// length in bytes of the pathname contained in the symbolic link. size: filesize, /// Last data access timestamp. - data-access-timestamp: datetime, + /// + /// If the `option` is none, the platform doesn't maintain an access + /// timestamp for this file. + data-access-timestamp: option, /// Last data modification timestamp. - data-modification-timestamp: datetime, - /// Last file status change timestamp. - status-change-timestamp: datetime, + /// + /// If the `option` is none, the platform doesn't maintain a + /// modification timestamp for this file. + data-modification-timestamp: option, + /// Last file status-change timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// status-change timestamp for this file. + status-change-timestamp: option, } /// Flags determining the method of how paths are resolved. @@ -277,13 +291,6 @@ interface types { no-reuse, } - /// A descriptor is a reference to a filesystem object, which may be a file, - /// directory, named pipe, special file, or other object on which filesystem - /// calls may be made. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type descriptor = u32 - /// A 128-bit hash value, split into parts because wasm doesn't have a /// 128-bit integer type. record metadata-hash-value { @@ -293,532 +300,499 @@ interface types { upper: u64, } - /// Return a stream for reading from a file, if available. - /// - /// May fail with an error-code describing why the file cannot be read. - /// - /// Multiple read, write, and append streams may be active on the same open - /// file and they do not interfere with each other. - /// - /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. - read-via-stream: func( - this: descriptor, - /// The offset within the file at which to start reading. - offset: filesize, - ) -> result - - /// Return a stream for writing to a file, if available. - /// - /// May fail with an error-code describing why the file cannot be written. - /// - /// Note: This allows using `write-stream`, which is similar to `write` in - /// POSIX. - write-via-stream: func( - this: descriptor, - /// The offset within the file at which to start writing. - offset: filesize, - ) -> result - - /// Return a stream for appending to a file, if available. - /// - /// May fail with an error-code describing why the file cannot be appended. - /// - /// Note: This allows using `write-stream`, which is similar to `write` with - /// `O_APPEND` in in POSIX. - append-via-stream: func( - this: descriptor, - ) -> result + /// A descriptor is a reference to a filesystem object, which may be a file, + /// directory, named pipe, special file, or other object on which filesystem + /// calls may be made. + resource descriptor { + /// Return a stream for reading from a file, if available. + /// + /// May fail with an error-code describing why the file cannot be read. + /// + /// Multiple read, write, and append streams may be active on the same open + /// file and they do not interfere with each other. + /// + /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. + read-via-stream: func( + /// The offset within the file at which to start reading. + offset: filesize, + ) -> result - /// Provide file advisory information on a descriptor. - /// - /// This is similar to `posix_fadvise` in POSIX. - advise: func( - this: descriptor, - /// The offset within the file to which the advisory applies. - offset: filesize, - /// The length of the region to which the advisory applies. - length: filesize, - /// The advice. - advice: advice - ) -> result<_, error-code> - - /// Synchronize the data of a file to disk. - /// - /// This function succeeds with no effect if the file descriptor is not - /// opened for writing. - /// - /// Note: This is similar to `fdatasync` in POSIX. - sync-data: func(this: descriptor) -> result<_, error-code> + /// Return a stream for writing to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be written. + /// + /// Note: This allows using `write-stream`, which is similar to `write` in + /// POSIX. + write-via-stream: func( + /// The offset within the file at which to start writing. + offset: filesize, + ) -> result + + /// Return a stream for appending to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be appended. + /// + /// Note: This allows using `write-stream`, which is similar to `write` with + /// `O_APPEND` in in POSIX. + append-via-stream: func() -> result - /// Get flags associated with a descriptor. - /// - /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. - /// - /// Note: This returns the value that was the `fs_flags` value returned - /// from `fdstat_get` in earlier versions of WASI. - get-flags: func(this: descriptor) -> result + /// Provide file advisory information on a descriptor. + /// + /// This is similar to `posix_fadvise` in POSIX. + advise: func( + /// The offset within the file to which the advisory applies. + offset: filesize, + /// The length of the region to which the advisory applies. + length: filesize, + /// The advice. + advice: advice + ) -> result<_, error-code> + + /// Synchronize the data of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fdatasync` in POSIX. + sync-data: func() -> result<_, error-code> - /// Get the dynamic type of a descriptor. - /// - /// Note: This returns the same value as the `type` field of the `fd-stat` - /// returned by `stat`, `stat-at` and similar. - /// - /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided - /// by `fstat` in POSIX. - /// - /// Note: This returns the value that was the `fs_filetype` value returned - /// from `fdstat_get` in earlier versions of WASI. - get-type: func(this: descriptor) -> result + /// Get flags associated with a descriptor. + /// + /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + /// + /// Note: This returns the value that was the `fs_flags` value returned + /// from `fdstat_get` in earlier versions of WASI. + get-flags: func() -> result - /// Adjust the size of an open file. If this increases the file's size, the - /// extra bytes are filled with zeros. - /// - /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. - set-size: func(this: descriptor, size: filesize) -> result<_, error-code> + /// Get the dynamic type of a descriptor. + /// + /// Note: This returns the same value as the `type` field of the `fd-stat` + /// returned by `stat`, `stat-at` and similar. + /// + /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided + /// by `fstat` in POSIX. + /// + /// Note: This returns the value that was the `fs_filetype` value returned + /// from `fdstat_get` in earlier versions of WASI. + get-type: func() -> result - /// Adjust the timestamps of an open file or directory. - /// - /// Note: This is similar to `futimens` in POSIX. - /// - /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. - set-times: func( - this: descriptor, - /// The desired values of the data access timestamp. - data-access-timestamp: new-timestamp, - /// The desired values of the data modification timestamp. - data-modification-timestamp: new-timestamp, - ) -> result<_, error-code> - - /// Read from a descriptor, without using and updating the descriptor's offset. - /// - /// This function returns a list of bytes containing the data that was - /// read, along with a bool which, when true, indicates that the end of the - /// file was reached. The returned list will contain up to `length` bytes; it - /// may return fewer than requested, if the end of the file is reached or - /// if the I/O operation is interrupted. - /// - /// In the future, this may change to return a `stream`. - /// - /// Note: This is similar to `pread` in POSIX. - read: func( - this: descriptor, - /// The maximum number of bytes to read. - length: filesize, - /// The offset within the file at which to read. - offset: filesize, - ) -> result, bool>, error-code> - - /// Write to a descriptor, without using and updating the descriptor's offset. - /// - /// It is valid to write past the end of a file; the file is extended to the - /// extent of the write, with bytes between the previous end and the start of - /// the write set to zero. - /// - /// In the future, this may change to take a `stream`. - /// - /// Note: This is similar to `pwrite` in POSIX. - write: func( - this: descriptor, - /// Data to write - buffer: list, - /// The offset within the file at which to write. - offset: filesize, - ) -> result - - /// Read directory entries from a directory. - /// - /// On filesystems where directories contain entries referring to themselves - /// and their parents, often named `.` and `..` respectively, these entries - /// are omitted. - /// - /// This always returns a new stream which starts at the beginning of the - /// directory. Multiple streams may be active on the same directory, and they - /// do not interfere with each other. - read-directory: func( - this: descriptor - ) -> result - - /// Synchronize the data and metadata of a file to disk. - /// - /// This function succeeds with no effect if the file descriptor is not - /// opened for writing. - /// - /// Note: This is similar to `fsync` in POSIX. - sync: func(this: descriptor) -> result<_, error-code> + /// Adjust the size of an open file. If this increases the file's size, the + /// extra bytes are filled with zeros. + /// + /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + set-size: func(size: filesize) -> result<_, error-code> - /// Create a directory. - /// - /// Note: This is similar to `mkdirat` in POSIX. - create-directory-at: func( - this: descriptor, - /// The relative path at which to create the directory. - path: string, - ) -> result<_, error-code> - - /// Return the attributes of an open file or directory. - /// - /// Note: This is similar to `fstat` in POSIX, except that it does not return - /// device and inode information. For testing whether two descriptors refer to - /// the same underlying filesystem object, use `is-same-object`. To obtain - /// additional data that can be used do determine whether a file has been - /// modified, use `metadata-hash`. - /// - /// Note: This was called `fd_filestat_get` in earlier versions of WASI. - stat: func(this: descriptor) -> result + /// Adjust the timestamps of an open file or directory. + /// + /// Note: This is similar to `futimens` in POSIX. + /// + /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + set-times: func( + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + /// The desired values of the data modification timestamp. + data-modification-timestamp: new-timestamp, + ) -> result<_, error-code> + + /// Read from a descriptor, without using and updating the descriptor's offset. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// file was reached. The returned list will contain up to `length` bytes; it + /// may return fewer than requested, if the end of the file is reached or + /// if the I/O operation is interrupted. + /// + /// In the future, this may change to return a `stream`. + /// + /// Note: This is similar to `pread` in POSIX. + read: func( + /// The maximum number of bytes to read. + length: filesize, + /// The offset within the file at which to read. + offset: filesize, + ) -> result, bool>, error-code> + + /// Write to a descriptor, without using and updating the descriptor's offset. + /// + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// In the future, this may change to take a `stream`. + /// + /// Note: This is similar to `pwrite` in POSIX. + write: func( + /// Data to write + buffer: list, + /// The offset within the file at which to write. + offset: filesize, + ) -> result + + /// Read directory entries from a directory. + /// + /// On filesystems where directories contain entries referring to themselves + /// and their parents, often named `.` and `..` respectively, these entries + /// are omitted. + /// + /// This always returns a new stream which starts at the beginning of the + /// directory. Multiple streams may be active on the same directory, and they + /// do not interfere with each other. + read-directory: func() -> result - /// Return the attributes of a file or directory. - /// - /// Note: This is similar to `fstatat` in POSIX, except that it does not - /// return device and inode information. See the `stat` description for a - /// discussion of alternatives. - /// - /// Note: This was called `path_filestat_get` in earlier versions of WASI. - stat-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path of the file or directory to inspect. - path: string, - ) -> result - - /// Adjust the timestamps of a file or directory. - /// - /// Note: This is similar to `utimensat` in POSIX. - /// - /// Note: This was called `path_filestat_set_times` in earlier versions of - /// WASI. - set-times-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path of the file or directory to operate on. - path: string, - /// The desired values of the data access timestamp. - data-access-timestamp: new-timestamp, - /// The desired values of the data modification timestamp. - data-modification-timestamp: new-timestamp, - ) -> result<_, error-code> - - /// Create a hard link. - /// - /// Note: This is similar to `linkat` in POSIX. - link-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - old-path-flags: path-flags, - /// The relative source path from which to link. - old-path: string, - /// The base directory for `new-path`. - new-descriptor: descriptor, - /// The relative destination path at which to create the hard link. - new-path: string, - ) -> result<_, error-code> - - /// Open a file or directory. - /// - /// The returned descriptor is not guaranteed to be the lowest-numbered - /// descriptor not currently open/ it is randomized to prevent applications - /// from depending on making assumptions about indexes, since this is - /// error-prone in multi-threaded contexts. The returned descriptor is - /// guaranteed to be less than 2**31. - /// - /// If `flags` contains `descriptor-flags::mutate-directory`, and the base - /// descriptor doesn't have `descriptor-flags::mutate-directory` set, - /// `open-at` fails with `error-code::read-only`. - /// - /// If `flags` contains `write` or `mutate-directory`, or `open-flags` - /// contains `truncate` or `create`, and the base descriptor doesn't have - /// `descriptor-flags::mutate-directory` set, `open-at` fails with - /// `error-code::read-only`. - /// - /// Note: This is similar to `openat` in POSIX. - open-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path of the object to open. - path: string, - /// The method by which to open the file. - open-flags: open-flags, - /// Flags to use for the resulting descriptor. - %flags: descriptor-flags, - /// Permissions to use when creating a new file. - modes: modes - ) -> result - - /// Read the contents of a symbolic link. - /// - /// If the contents contain an absolute or rooted path in the underlying - /// filesystem, this function fails with `error-code::not-permitted`. - /// - /// Note: This is similar to `readlinkat` in POSIX. - readlink-at: func( - this: descriptor, - /// The relative path of the symbolic link from which to read. - path: string, - ) -> result - - /// Remove a directory. - /// - /// Return `error-code::not-empty` if the directory is not empty. - /// - /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - remove-directory-at: func( - this: descriptor, - /// The relative path to a directory to remove. - path: string, - ) -> result<_, error-code> - - /// Rename a filesystem object. - /// - /// Note: This is similar to `renameat` in POSIX. - rename-at: func( - this: descriptor, - /// The relative source path of the file or directory to rename. - old-path: string, - /// The base directory for `new-path`. - new-descriptor: descriptor, - /// The relative destination path to which to rename the file or directory. - new-path: string, - ) -> result<_, error-code> - - /// Create a symbolic link (also known as a "symlink"). - /// - /// If `old-path` starts with `/`, the function fails with - /// `error-code::not-permitted`. - /// - /// Note: This is similar to `symlinkat` in POSIX. - symlink-at: func( - this: descriptor, - /// The contents of the symbolic link. - old-path: string, - /// The relative destination path at which to create the symbolic link. - new-path: string, - ) -> result<_, error-code> - - /// Check accessibility of a filesystem path. - /// - /// Check whether the given filesystem path names an object which is - /// readable, writable, or executable, or whether it exists. - /// - /// This does not a guarantee that subsequent accesses will succeed, as - /// filesystem permissions may be modified asynchronously by external - /// entities. - /// - /// Note: This is similar to `faccessat` with the `AT_EACCESS` flag in POSIX. - access-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path to check. - path: string, - /// The type of check to perform. - %type: access-type - ) -> result<_, error-code> - - /// Unlink a filesystem object that is not a directory. - /// - /// Return `error-code::is-directory` if the path refers to a directory. - /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - unlink-file-at: func( - this: descriptor, - /// The relative path to a file to unlink. - path: string, - ) -> result<_, error-code> - - /// Change the permissions of a filesystem object that is not a directory. - /// - /// Note that the ultimate meanings of these permissions is - /// filesystem-specific. - /// - /// Note: This is similar to `fchmodat` in POSIX. - change-file-permissions-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path to operate on. - path: string, - /// The new permissions for the filesystem object. - modes: modes, - ) -> result<_, error-code> - - /// Change the permissions of a directory. - /// - /// Note that the ultimate meanings of these permissions is - /// filesystem-specific. - /// - /// Unlike in POSIX, the `executable` flag is not reinterpreted as a "search" - /// flag. `read` on a directory implies readability and searchability, and - /// `execute` is not valid for directories. - /// - /// Note: This is similar to `fchmodat` in POSIX. - change-directory-permissions-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path to operate on. - path: string, - /// The new permissions for the directory. - modes: modes, - ) -> result<_, error-code> - - /// Request a shared advisory lock for an open file. - /// - /// This requests a *shared* lock; more than one shared lock can be held for - /// a file at the same time. - /// - /// If the open file has an exclusive lock, this function downgrades the lock - /// to a shared lock. If it has a shared lock, this function has no effect. - /// - /// This requests an *advisory* lock, meaning that the file could be accessed - /// by other programs that don't hold the lock. - /// - /// It is unspecified how shared locks interact with locks acquired by - /// non-WASI programs. - /// - /// This function blocks until the lock can be acquired. - /// - /// Not all filesystems support locking; on filesystems which don't support - /// locking, this function returns `error-code::unsupported`. - /// - /// Note: This is similar to `flock(fd, LOCK_SH)` in Unix. - lock-shared: func(this: descriptor) -> result<_, error-code> + /// Synchronize the data and metadata of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fsync` in POSIX. + sync: func() -> result<_, error-code> - /// Request an exclusive advisory lock for an open file. - /// - /// This requests an *exclusive* lock; no other locks may be held for the - /// file while an exclusive lock is held. - /// - /// If the open file has a shared lock and there are no exclusive locks held - /// for the file, this function upgrades the lock to an exclusive lock. If the - /// open file already has an exclusive lock, this function has no effect. - /// - /// This requests an *advisory* lock, meaning that the file could be accessed - /// by other programs that don't hold the lock. - /// - /// It is unspecified whether this function succeeds if the file descriptor - /// is not opened for writing. It is unspecified how exclusive locks interact - /// with locks acquired by non-WASI programs. - /// - /// This function blocks until the lock can be acquired. - /// - /// Not all filesystems support locking; on filesystems which don't support - /// locking, this function returns `error-code::unsupported`. - /// - /// Note: This is similar to `flock(fd, LOCK_EX)` in Unix. - lock-exclusive: func(this: descriptor) -> result<_, error-code> + /// Create a directory. + /// + /// Note: This is similar to `mkdirat` in POSIX. + create-directory-at: func( + /// The relative path at which to create the directory. + path: string, + ) -> result<_, error-code> - /// Request a shared advisory lock for an open file. - /// - /// This requests a *shared* lock; more than one shared lock can be held for - /// a file at the same time. - /// - /// If the open file has an exclusive lock, this function downgrades the lock - /// to a shared lock. If it has a shared lock, this function has no effect. - /// - /// This requests an *advisory* lock, meaning that the file could be accessed - /// by other programs that don't hold the lock. - /// - /// It is unspecified how shared locks interact with locks acquired by - /// non-WASI programs. - /// - /// This function returns `error-code::would-block` if the lock cannot be - /// acquired. - /// - /// Not all filesystems support locking; on filesystems which don't support - /// locking, this function returns `error-code::unsupported`. - /// - /// Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. - try-lock-shared: func(this: descriptor) -> result<_, error-code> + /// Return the attributes of an open file or directory. + /// + /// Note: This is similar to `fstat` in POSIX, except that it does not return + /// device and inode information. For testing whether two descriptors refer to + /// the same underlying filesystem object, use `is-same-object`. To obtain + /// additional data that can be used do determine whether a file has been + /// modified, use `metadata-hash`. + /// + /// Note: This was called `fd_filestat_get` in earlier versions of WASI. + stat: func() -> result - /// Request an exclusive advisory lock for an open file. - /// - /// This requests an *exclusive* lock; no other locks may be held for the - /// file while an exclusive lock is held. - /// - /// If the open file has a shared lock and there are no exclusive locks held - /// for the file, this function upgrades the lock to an exclusive lock. If the - /// open file already has an exclusive lock, this function has no effect. - /// - /// This requests an *advisory* lock, meaning that the file could be accessed - /// by other programs that don't hold the lock. - /// - /// It is unspecified whether this function succeeds if the file descriptor - /// is not opened for writing. It is unspecified how exclusive locks interact - /// with locks acquired by non-WASI programs. - /// - /// This function returns `error-code::would-block` if the lock cannot be - /// acquired. - /// - /// Not all filesystems support locking; on filesystems which don't support - /// locking, this function returns `error-code::unsupported`. - /// - /// Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. - try-lock-exclusive: func(this: descriptor) -> result<_, error-code> + /// Return the attributes of a file or directory. + /// + /// Note: This is similar to `fstatat` in POSIX, except that it does not + /// return device and inode information. See the `stat` description for a + /// discussion of alternatives. + /// + /// Note: This was called `path_filestat_get` in earlier versions of WASI. + stat-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to inspect. + path: string, + ) -> result + + /// Adjust the timestamps of a file or directory. + /// + /// Note: This is similar to `utimensat` in POSIX. + /// + /// Note: This was called `path_filestat_set_times` in earlier versions of + /// WASI. + set-times-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to operate on. + path: string, + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + /// The desired values of the data modification timestamp. + data-modification-timestamp: new-timestamp, + ) -> result<_, error-code> + + /// Create a hard link. + /// + /// Note: This is similar to `linkat` in POSIX. + link-at: func( + /// Flags determining the method of how the path is resolved. + old-path-flags: path-flags, + /// The relative source path from which to link. + old-path: string, + /// The base directory for `new-path`. + new-descriptor: borrow, + /// The relative destination path at which to create the hard link. + new-path: string, + ) -> result<_, error-code> + + /// Open a file or directory. + /// + /// The returned descriptor is not guaranteed to be the lowest-numbered + /// descriptor not currently open/ it is randomized to prevent applications + /// from depending on making assumptions about indexes, since this is + /// error-prone in multi-threaded contexts. The returned descriptor is + /// guaranteed to be less than 2**31. + /// + /// If `flags` contains `descriptor-flags::mutate-directory`, and the base + /// descriptor doesn't have `descriptor-flags::mutate-directory` set, + /// `open-at` fails with `error-code::read-only`. + /// + /// If `flags` contains `write` or `mutate-directory`, or `open-flags` + /// contains `truncate` or `create`, and the base descriptor doesn't have + /// `descriptor-flags::mutate-directory` set, `open-at` fails with + /// `error-code::read-only`. + /// + /// Note: This is similar to `openat` in POSIX. + open-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the object to open. + path: string, + /// The method by which to open the file. + open-flags: open-flags, + /// Flags to use for the resulting descriptor. + %flags: descriptor-flags, + /// Permissions to use when creating a new file. + modes: modes + ) -> result + + /// Read the contents of a symbolic link. + /// + /// If the contents contain an absolute or rooted path in the underlying + /// filesystem, this function fails with `error-code::not-permitted`. + /// + /// Note: This is similar to `readlinkat` in POSIX. + readlink-at: func( + /// The relative path of the symbolic link from which to read. + path: string, + ) -> result - /// Release a shared or exclusive lock on an open file. - /// - /// Note: This is similar to `flock(fd, LOCK_UN)` in Unix. - unlock: func(this: descriptor) -> result<_, error-code> + /// Remove a directory. + /// + /// Return `error-code::not-empty` if the directory is not empty. + /// + /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + remove-directory-at: func( + /// The relative path to a directory to remove. + path: string, + ) -> result<_, error-code> - /// Dispose of the specified `descriptor`, after which it may no longer - /// be used. - drop-descriptor: func(this: descriptor) + /// Rename a filesystem object. + /// + /// Note: This is similar to `renameat` in POSIX. + rename-at: func( + /// The relative source path of the file or directory to rename. + old-path: string, + /// The base directory for `new-path`. + new-descriptor: borrow, + /// The relative destination path to which to rename the file or directory. + new-path: string, + ) -> result<_, error-code> + + /// Create a symbolic link (also known as a "symlink"). + /// + /// If `old-path` starts with `/`, the function fails with + /// `error-code::not-permitted`. + /// + /// Note: This is similar to `symlinkat` in POSIX. + symlink-at: func( + /// The contents of the symbolic link. + old-path: string, + /// The relative destination path at which to create the symbolic link. + new-path: string, + ) -> result<_, error-code> + + /// Check accessibility of a filesystem path. + /// + /// Check whether the given filesystem path names an object which is + /// readable, writable, or executable, or whether it exists. + /// + /// This does not a guarantee that subsequent accesses will succeed, as + /// filesystem permissions may be modified asynchronously by external + /// entities. + /// + /// Note: This is similar to `faccessat` with the `AT_EACCESS` flag in POSIX. + access-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to check. + path: string, + /// The type of check to perform. + %type: access-type + ) -> result<_, error-code> + + /// Unlink a filesystem object that is not a directory. + /// + /// Return `error-code::is-directory` if the path refers to a directory. + /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + unlink-file-at: func( + /// The relative path to a file to unlink. + path: string, + ) -> result<_, error-code> + + /// Change the permissions of a filesystem object that is not a directory. + /// + /// Note that the ultimate meanings of these permissions is + /// filesystem-specific. + /// + /// Note: This is similar to `fchmodat` in POSIX. + change-file-permissions-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to operate on. + path: string, + /// The new permissions for the filesystem object. + modes: modes, + ) -> result<_, error-code> + + /// Change the permissions of a directory. + /// + /// Note that the ultimate meanings of these permissions is + /// filesystem-specific. + /// + /// Unlike in POSIX, the `executable` flag is not reinterpreted as a "search" + /// flag. `read` on a directory implies readability and searchability, and + /// `execute` is not valid for directories. + /// + /// Note: This is similar to `fchmodat` in POSIX. + change-directory-permissions-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to operate on. + path: string, + /// The new permissions for the directory. + modes: modes, + ) -> result<_, error-code> + + /// Request a shared advisory lock for an open file. + /// + /// This requests a *shared* lock; more than one shared lock can be held for + /// a file at the same time. + /// + /// If the open file has an exclusive lock, this function downgrades the lock + /// to a shared lock. If it has a shared lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified how shared locks interact with locks acquired by + /// non-WASI programs. + /// + /// This function blocks until the lock can be acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_SH)` in Unix. + lock-shared: func() -> result<_, error-code> - /// A stream of directory entries. - /// - /// This [represents a stream of `dir-entry`](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Streams). - type directory-entry-stream = u32 + /// Request an exclusive advisory lock for an open file. + /// + /// This requests an *exclusive* lock; no other locks may be held for the + /// file while an exclusive lock is held. + /// + /// If the open file has a shared lock and there are no exclusive locks held + /// for the file, this function upgrades the lock to an exclusive lock. If the + /// open file already has an exclusive lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified whether this function succeeds if the file descriptor + /// is not opened for writing. It is unspecified how exclusive locks interact + /// with locks acquired by non-WASI programs. + /// + /// This function blocks until the lock can be acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_EX)` in Unix. + lock-exclusive: func() -> result<_, error-code> + + /// Request a shared advisory lock for an open file. + /// + /// This requests a *shared* lock; more than one shared lock can be held for + /// a file at the same time. + /// + /// If the open file has an exclusive lock, this function downgrades the lock + /// to a shared lock. If it has a shared lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified how shared locks interact with locks acquired by + /// non-WASI programs. + /// + /// This function returns `error-code::would-block` if the lock cannot be + /// acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. + try-lock-shared: func() -> result<_, error-code> - /// Read a single directory entry from a `directory-entry-stream`. - read-directory-entry: func( - this: directory-entry-stream - ) -> result, error-code> + /// Request an exclusive advisory lock for an open file. + /// + /// This requests an *exclusive* lock; no other locks may be held for the + /// file while an exclusive lock is held. + /// + /// If the open file has a shared lock and there are no exclusive locks held + /// for the file, this function upgrades the lock to an exclusive lock. If the + /// open file already has an exclusive lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified whether this function succeeds if the file descriptor + /// is not opened for writing. It is unspecified how exclusive locks interact + /// with locks acquired by non-WASI programs. + /// + /// This function returns `error-code::would-block` if the lock cannot be + /// acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. + try-lock-exclusive: func() -> result<_, error-code> - /// Dispose of the specified `directory-entry-stream`, after which it may no longer - /// be used. - drop-directory-entry-stream: func(this: directory-entry-stream) + /// Release a shared or exclusive lock on an open file. + /// + /// Note: This is similar to `flock(fd, LOCK_UN)` in Unix. + unlock: func() -> result<_, error-code> - /// Test whether two descriptors refer to the same filesystem object. - /// - /// In POSIX, this corresponds to testing whether the two descriptors have the - /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. - /// wasi-filesystem does not expose device and inode numbers, so this function - /// may be used instead. - is-same-object: func(this: descriptor, other: descriptor) -> bool - - /// Return a hash of the metadata associated with a filesystem object referred - /// to by a descriptor. - /// - /// This returns a hash of the last-modification timestamp and file size, and - /// may also include the inode number, device number, birth timestamp, and - /// other metadata fields that may change when the file is modified or - /// replaced. It may also include a secret value chosen by the - /// implementation and not otherwise exposed. - /// - /// Implementations are encourated to provide the following properties: - /// - /// - If the file is not modified or replaced, the computed hash value should - /// usually not change. - /// - If the object is modified or replaced, the computed hash value should - /// usually change. - /// - The inputs to the hash should not be easily computable from the - /// computed hash. - /// - /// However, none of these is required. - metadata-hash: func( - this: descriptor, - ) -> result + /// Test whether two descriptors refer to the same filesystem object. + /// + /// In POSIX, this corresponds to testing whether the two descriptors have the + /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. + /// wasi-filesystem does not expose device and inode numbers, so this function + /// may be used instead. + is-same-object: func(other: borrow) -> bool + + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a descriptor. + /// + /// This returns a hash of the last-modification timestamp and file size, and + /// may also include the inode number, device number, birth timestamp, and + /// other metadata fields that may change when the file is modified or + /// replaced. It may also include a secret value chosen by the + /// implementation and not otherwise exposed. + /// + /// Implementations are encourated to provide the following properties: + /// + /// - If the file is not modified or replaced, the computed hash value should + /// usually not change. + /// - If the object is modified or replaced, the computed hash value should + /// usually change. + /// - The inputs to the hash should not be easily computable from the + /// computed hash. + /// + /// However, none of these is required. + metadata-hash: func() -> result - /// Return a hash of the metadata associated with a filesystem object referred - /// to by a directory descriptor and a relative path. - /// - /// This performs the same hash computation as `metadata-hash`. - metadata-hash-at: func( - this: descriptor, - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path of the file or directory to inspect. - path: string, - ) -> result + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a directory descriptor and a relative path. + /// + /// This performs the same hash computation as `metadata-hash`. + metadata-hash-at: func( + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to inspect. + path: string, + ) -> result + } + + /// A stream of directory entries. + resource directory-entry-stream { + /// Read a single directory entry from a `directory-entry-stream`. + read-directory-entry: func() -> result, error-code> + } } diff --git a/crates/wasi/wit/deps/filesystem/world.wit b/crates/wasi/wit/deps/filesystem/world.wit index b51f484f8383..5fa7eafdb850 100644 --- a/crates/wasi/wit/deps/filesystem/world.wit +++ b/crates/wasi/wit/deps/filesystem/world.wit @@ -1,6 +1,6 @@ package wasi:filesystem -world example-world { +world imports { import types import preopens } diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index dfcacd8feb73..1abc7a1ff2c4 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -3,7 +3,7 @@ // imported and exported interfaces. interface types { use wasi:io/streams.{input-stream, output-stream} - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} // This type corresponds to HTTP standard Methods. variant method { @@ -100,7 +100,7 @@ interface types { // Additional optional parameters that can be set when making a request. record request-options { // The following timeouts are specific to the HTTP protocol and work - // independently of the overall timeouts passed to `io.poll.poll-oneoff`. + // independently of the overall timeouts passed to `io.poll.poll-list`. // The timeout for the initial connect. connect-timeout-ms: option, @@ -142,19 +142,18 @@ interface types { // incoming-body. incoming-response-consume: func(response: /* borrow */ incoming-response) -> result - type incoming-body = u32 - drop-incoming-body: func(this: /* own */ incoming-body) - - // returned input-stream is a child - the implementation may trap if - // incoming-body is dropped (or consumed by call to - // incoming-body-finish) before the input-stream is dropped. - // May be called at most once. returns error if called additional times. - incoming-body-stream: func(this: /* borrow */ incoming-body) -> - result - // takes ownership of incoming-body. this will trap if the - // incoming-body-stream child is still alive! - incoming-body-finish: func(this: /* own */ incoming-body) -> - /* transitive child of the incoming-response of incoming-body */ future-trailers + resource incoming-body { + // returned input-stream is a child - the implementation may trap if + // incoming-body is dropped (or consumed by call to + // incoming-body-finish) before the input-stream is dropped. + // May be called at most once. returns error if called additional times. + %stream: func() -> + result + // takes ownership of incoming-body. this will trap if the + // incoming-body-stream child is still alive! + finish: func() -> + /* transitive child of the incoming-response of incoming-body */ future-trailers + } type future-trailers = u32 drop-future-trailers: func(this: /* own */ future-trailers) @@ -193,7 +192,7 @@ interface types { /// `future-incoming-response`, the client can call the non-blocking `get` /// method to get the result if it is available. If the result is not available, /// the client can call `listen` to get a `pollable` that can be passed to - /// `io.poll.poll-oneoff`. + /// `wasi:io/poll.poll-list`. type future-incoming-response = u32 drop-future-incoming-response: func(f: /* own */ future-incoming-response) /// option indicates readiness. diff --git a/crates/wasi/wit/deps/io/poll.wit b/crates/wasi/wit/deps/io/poll.wit new file mode 100644 index 000000000000..e95762b915db --- /dev/null +++ b/crates/wasi/wit/deps/io/poll.wit @@ -0,0 +1,34 @@ +package wasi:io + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +interface poll { + /// A "pollable" handle. + resource pollable + + /// Poll for completion on a set of pollables. + /// + /// This function takes a list of pollables, which identify I/O sources of + /// interest, and waits until one or more of the events is ready for I/O. + /// + /// The result `list` contains one or more indices of handles in the + /// argument list that is ready for I/O. + /// + /// If the list contains more elements than can be indexed with a `u32` + /// value, this function traps. + /// + /// A timeout can be implemented by adding a pollable from the + /// wasi-clocks API to the list. + /// + /// This function does not return a `result`; polling in itself does not + /// do any I/O so it doesn't fail. If any of the I/O sources identified by + /// the pollables has an error, it is indicated by marking the source as + /// being reaedy for I/O. + poll-list: func(in: list>) -> list + + /// Poll for completion on a single pollable. + /// + /// This function is similar to `poll-list`, but operates on only a single + /// pollable. When it returns, the handle is ready for I/O. + poll-one: func(in: borrow) +} diff --git a/crates/wasi/wit/deps/io/streams.wit b/crates/wasi/wit/deps/io/streams.wit index e2631f66a569..eeeff505890a 100644 --- a/crates/wasi/wit/deps/io/streams.wit +++ b/crates/wasi/wit/deps/io/streams.wit @@ -6,7 +6,7 @@ package wasi:io /// In the future, the component model is expected to add built-in stream types; /// when it does, they are expected to subsume this API. interface streams { - use wasi:poll/poll.{pollable} + use poll.{pollable} /// Streams provide a sequence of data and then end; once they end, they /// no longer provide any further data. @@ -24,115 +24,81 @@ interface streams { ended, } - /// An input bytestream. In the future, this will be replaced by handle - /// types. + /// An input bytestream. /// /// `input-stream`s are *non-blocking* to the extent practical on underlying /// platforms. I/O operations always return promptly; if fewer bytes are /// promptly available than requested, they return the number of bytes promptly /// available, which could even be zero. To wait for data to be available, - /// use the `subscribe-to-input-stream` function to obtain a `pollable` which - /// can be polled for using `wasi:poll/poll.poll_oneoff`. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type input-stream = u32 - - /// Perform a non-blocking read from the stream. - /// - /// This function returns a list of bytes containing the data that was - /// read, along with a `stream-status` which, indicates whether further - /// reads are expected to produce data. The returned list will contain up to - /// `len` bytes; it may return fewer than requested, but not more. An - /// empty list and `stream-status:open` indicates no more data is - /// available at this time, and that the pollable given by - /// `subscribe-to-input-stream` will be ready when more data is available. - /// - /// Once a stream has reached the end, subsequent calls to `read` or - /// `skip` will always report `stream-status:ended` rather than producing more - /// data. - /// - /// When the caller gives a `len` of 0, it represents a request to read 0 - /// bytes. This read should always succeed and return an empty list and - /// the current `stream-status`. - /// - /// The `len` parameter is a `u64`, which could represent a list of u8 which - /// is not possible to allocate in wasm32, or not desirable to allocate as - /// as a return value by the callee. The callee may return a list of bytes - /// less than `len` in size while more bytes are available for reading. - read: func( - this: input-stream, - /// The maximum number of bytes to read - len: u64 - ) -> result, stream-status>> - - /// Read bytes from a stream, after blocking until at least one byte can - /// be read. Except for blocking, identical to `read`. - blocking-read: func( - this: input-stream, - /// The maximum number of bytes to read - len: u64 - ) -> result, stream-status>> - - /// Skip bytes from a stream. - /// - /// This is similar to the `read` function, but avoids copying the - /// bytes into the instance. - /// - /// Once a stream has reached the end, subsequent calls to read or - /// `skip` will always report end-of-stream rather than producing more - /// data. - /// - /// This function returns the number of bytes skipped, along with a - /// `stream-status` indicating whether the end of the stream was - /// reached. The returned value will be at most `len`; it may be less. - skip: func( - this: input-stream, - /// The maximum number of bytes to skip. - len: u64, - ) -> result> + /// use the `subscribe` function to obtain a `pollable` which can be polled + /// for using `wasi:io/poll`. + resource input-stream { + /// Perform a non-blocking read from the stream. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a `stream-status` which, indicates whether further + /// reads are expected to produce data. The returned list will contain up to + /// `len` bytes; it may return fewer than requested, but not more. An + /// empty list and `stream-status:open` indicates no more data is + /// available at this time, and that the pollable given by `subscribe` + /// will be ready when more data is available. + /// + /// Once a stream has reached the end, subsequent calls to `read` or + /// `skip` will always report `stream-status:ended` rather than producing more + /// data. + /// + /// When the caller gives a `len` of 0, it represents a request to read 0 + /// bytes. This read should always succeed and return an empty list and + /// the current `stream-status`. + /// + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. + read: func( + /// The maximum number of bytes to read + len: u64 + ) -> result, stream-status>> - /// Skip bytes from a stream, after blocking until at least one byte - /// can be skipped. Except for blocking behavior, identical to `skip`. - blocking-skip: func( - this: input-stream, - /// The maximum number of bytes to skip. - len: u64, - ) -> result> + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, identical to `read`. + blocking-read: func( + /// The maximum number of bytes to read + len: u64 + ) -> result, stream-status>> - /// Create a `pollable` which will resolve once either the specified stream - /// has bytes available to read or the other end of the stream has been - /// closed. - /// The created `pollable` is a child resource of the `input-stream`. - /// Implementations may trap if the `input-stream` is dropped before - /// all derived `pollable`s created with this function are dropped. - subscribe-to-input-stream: func(this: input-stream) -> pollable + /// Skip bytes from a stream. + /// + /// This is similar to the `read` function, but avoids copying the + /// bytes into the instance. + /// + /// Once a stream has reached the end, subsequent calls to read or + /// `skip` will always report end-of-stream rather than producing more + /// data. + /// + /// This function returns the number of bytes skipped, along with a + /// `stream-status` indicating whether the end of the stream was + /// reached. The returned value will be at most `len`; it may be less. + skip: func( + /// The maximum number of bytes to skip. + len: u64, + ) -> result> - /// Dispose of the specified `input-stream`, after which it may no longer - /// be used. - /// Implementations may trap if this `input-stream` is dropped while child - /// `pollable` resources are still alive. - /// After this `input-stream` is dropped, implementations may report any - /// corresponding `output-stream` has `stream-state.closed`. - drop-input-stream: func(this: input-stream) + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. + blocking-skip: func( + /// The maximum number of bytes to skip. + len: u64, + ) -> result> - /// An output bytestream. In the future, this will be replaced by handle - /// types. - /// - /// `output-stream`s are *non-blocking* to the extent practical on - /// underlying platforms. Except where specified otherwise, I/O operations also - /// always return promptly, after the number of bytes that can be written - /// promptly, which could even be zero. To wait for the stream to be ready to - /// accept data, the `subscribe-to-output-stream` function to obtain a - /// `pollable` which can be polled for using `wasi:poll`. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type output-stream = u32 + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + subscribe: func() -> pollable + } /// An error for output-stream operations. /// @@ -146,155 +112,174 @@ interface streams { /// future operations. closed } - /// Check readiness for writing. This function never blocks. - /// - /// Returns the number of bytes permitted for the next call to `write`, - /// or an error. Calling `write` with more bytes than this function has - /// permitted will trap. - /// - /// When this function returns 0 bytes, the `subscribe-to-output-stream` - /// pollable will become ready when this function will report at least - /// 1 byte, or an error. - check-write: func( - this: output-stream - ) -> result - /// Perform a write. This function never blocks. - /// - /// Precondition: check-write gave permit of Ok(n) and contents has a - /// length of less than or equal to n. Otherwise, this function will trap. + /// An output bytestream. /// - /// returns Err(closed) without writing if the stream has closed since - /// the last call to check-write provided a permit. - write: func( - this: output-stream, - contents: list - ) -> result<_, write-error> + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe` function to obtain a `pollable` which can be + /// polled for using `wasi:io/poll`. + resource output-stream { + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe` pollable will + /// become ready when this function will report at least 1 byte, or an + /// error. + check-write: func() -> result - /// Perform a write of up to 4096 bytes, and then flush the stream. Block - /// until all of these operations are complete, or an error occurs. - /// - /// This is a convenience wrapper around the use of `check-write`, - /// `subscribe-to-output-stream`, `write`, and `flush`, and is implemented - /// with the following pseudo-code: - /// - /// ```text - /// let pollable = subscribe-to-output-stream(this); - /// while !contents.is_empty() { - /// // Wait for the stream to become writable - /// poll-oneoff(pollable); - /// let Ok(n) = check-write(this); // eliding error handling - /// let len = min(n, contents.len()); - /// let (chunk, rest) = contents.split_at(len); - /// write(this, chunk); // eliding error handling - /// contents = rest; - /// } - /// flush(this); - /// // Wait for completion of `flush` - /// poll-oneoff(pollable); - /// // Check for any errors that arose during `flush` - /// let _ = check-write(this); // eliding error handling - /// ``` - blocking-write-and-flush: func( - this: output-stream, - contents: list - ) -> result<_, write-error> + /// Perform a write. This function never blocks. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. + /// + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. + write: func( + contents: list + ) -> result<_, write-error> - /// Request to flush buffered output. This function never blocks. - /// - /// This tells the output-stream that the caller intends any buffered - /// output to be flushed. the output which is expected to be flushed - /// is all that has been passed to `write` prior to this call. - /// - /// Upon calling this function, the `output-stream` will not accept any - /// writes (`check-write` will return `ok(0)`) until the flush has - /// completed. The `subscribe-to-output-stream` pollable will become ready - /// when the flush has completed and the stream can accept more writes. - flush: func( - this: output-stream, - ) -> result<_, write-error> + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write`, and `flush`, and is implemented with the + /// following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while !contents.is_empty() { + /// // Wait for the stream to become writable + /// poll-one(pollable); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, contents.len()); + /// let (chunk, rest) = contents.split_at(len); + /// this.write(chunk ); // eliding error handling + /// contents = rest; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// poll-one(pollable); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + blocking-write-and-flush: func( + contents: list + ) -> result<_, write-error> - /// Request to flush buffered output, and block until flush completes - /// and stream is ready for writing again. - blocking-flush: func( - this: output-stream, - ) -> result<_, write-error> + /// Request to flush buffered output. This function never blocks. + /// + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe` pollable will become ready when the + /// flush has completed and the stream can accept more writes. + flush: func() -> result<_, write-error> - /// Create a `pollable` which will resolve once the output-stream - /// is ready for more writing, or an error has occured. When this - /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an - /// error. - /// - /// If the stream is closed, this pollable is always ready immediately. - /// - /// The created `pollable` is a child resource of the `output-stream`. - /// Implementations may trap if the `output-stream` is dropped before - /// all derived `pollable`s created with this function are dropped. - subscribe-to-output-stream: func(this: output-stream) -> pollable + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + blocking-flush: func() -> result<_, write-error> - /// Write zeroes to a stream. - /// - /// this should be used precisely like `write` with the exact same - /// preconditions (must use check-write first), but instead of - /// passing a list of bytes, you simply pass the number of zero-bytes - /// that should be written. - write-zeroes: func( - this: output-stream, - /// The number of zero-bytes to write - len: u64 - ) -> result<_, write-error> + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occured. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + subscribe: func() -> pollable - /// Read from one stream and write to another. - /// - /// This function returns the number of bytes transferred; it may be less - /// than `len`. - /// - /// Unlike other I/O functions, this function blocks until all the data - /// read from the input stream has been written to the output stream. - splice: func( - this: output-stream, - /// The stream to read from - src: input-stream, - /// The number of bytes to splice - len: u64, - ) -> result> + /// Write zeroes to a stream. + /// + /// this should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + write-zeroes: func( + /// The number of zero-bytes to write + len: u64 + ) -> result<_, write-error> - /// Read from one stream and write to another, with blocking. - /// - /// This is similar to `splice`, except that it blocks until at least - /// one byte can be read. - blocking-splice: func( - this: output-stream, - /// The stream to read from - src: input-stream, - /// The number of bytes to splice - len: u64, - ) -> result> + /// Perform a write of up to 4096 zeroes, and then flush the stream. + /// Block until all of these operations are complete, or an error + /// occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with + /// the following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while num_zeroes != 0 { + /// // Wait for the stream to become writable + /// poll-one(pollable); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, num_zeroes); + /// this.write-zeroes(len); // eliding error handling + /// num_zeroes -= len; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// poll-one(pollable); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + blocking-write-zeroes-and-flush: func( + /// The number of zero-bytes to write + len: u64 + ) -> result<_, write-error> - /// Forward the entire contents of an input stream to an output stream. - /// - /// This function repeatedly reads from the input stream and writes - /// the data to the output stream, until the end of the input stream - /// is reached, or an error is encountered. - /// - /// Unlike other I/O functions, this function blocks until the end - /// of the input stream is seen and all the data has been written to - /// the output stream. - /// - /// This function returns the number of bytes transferred, and the status of - /// the output stream. - forward: func( - this: output-stream, - /// The stream to read from - src: input-stream - ) -> result> + /// Read from one stream and write to another. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + /// + /// Unlike other I/O functions, this function blocks until all the data + /// read from the input stream has been written to the output stream. + splice: func( + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result> + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until at least + /// one byte can be read. + blocking-splice: func( + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result> - /// Dispose of the specified `output-stream`, after which it may no longer - /// be used. - /// Implementations may trap if this `output-stream` is dropped while - /// child `pollable` resources are still alive. - /// After this `output-stream` is dropped, implementations may report any - /// corresponding `input-stream` has `stream-state.closed`. - drop-output-stream: func(this: output-stream) + /// Forward the entire contents of an input stream to an output stream. + /// + /// This function repeatedly reads from the input stream and writes + /// the data to the output stream, until the end of the input stream + /// is reached, or an error is encountered. + /// + /// Unlike other I/O functions, this function blocks until the end + /// of the input stream is seen and all the data has been written to + /// the output stream. + /// + /// This function returns the number of bytes transferred, and the status of + /// the output stream. + forward: func( + /// The stream to read from + src: input-stream + ) -> result> + } } diff --git a/crates/wasi/wit/deps/io/world.wit b/crates/wasi/wit/deps/io/world.wit new file mode 100644 index 000000000000..8738dba756d9 --- /dev/null +++ b/crates/wasi/wit/deps/io/world.wit @@ -0,0 +1,6 @@ +package wasi:io + +world imports { + import streams + import poll +} diff --git a/crates/wasi/wit/deps/logging/world.wit b/crates/wasi/wit/deps/logging/world.wit new file mode 100644 index 000000000000..7d49acfaddaa --- /dev/null +++ b/crates/wasi/wit/deps/logging/world.wit @@ -0,0 +1,5 @@ +package wasi:logging + +world imports { + import logging +} diff --git a/crates/wasi/wit/deps/poll/poll.wit b/crates/wasi/wit/deps/poll/poll.wit deleted file mode 100644 index a6334c5570fc..000000000000 --- a/crates/wasi/wit/deps/poll/poll.wit +++ /dev/null @@ -1,39 +0,0 @@ -package wasi:poll - -/// A poll API intended to let users wait for I/O events on multiple handles -/// at once. -interface poll { - /// A "pollable" handle. - /// - /// This is conceptually represents a `stream<_, _>`, or in other words, - /// a stream that one can wait on, repeatedly, but which does not itself - /// produce any data. It's temporary scaffolding until component-model's - /// async features are ready. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// `pollable` lifetimes are not automatically managed. Users must ensure - /// that they do not outlive the resource they reference. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type pollable = u32 - - /// Dispose of the specified `pollable`, after which it may no longer - /// be used. - drop-pollable: func(this: pollable) - - /// Poll for completion on a set of pollables. - /// - /// The "oneoff" in the name refers to the fact that this function must do a - /// linear scan through the entire list of subscriptions, which may be - /// inefficient if the number is large and the same subscriptions are used - /// many times. In the future, this is expected to be obsoleted by the - /// component model async proposal, which will include a scalable waiting - /// facility. - /// - /// The result list is the same length as the argument - /// list, and indicates the readiness of each corresponding - /// element in that / list, with true indicating ready. - poll-oneoff: func(in: list) -> list -} diff --git a/crates/wasi/wit/deps/random/random.wit b/crates/wasi/wit/deps/random/random.wit index f2bd6358c139..2a282dab7eb9 100644 --- a/crates/wasi/wit/deps/random/random.wit +++ b/crates/wasi/wit/deps/random/random.wit @@ -1,25 +1,25 @@ -package wasi:random - /// WASI Random is a random data API. /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. interface random { - /// Return `len` cryptographically-secure pseudo-random bytes. + /// Return `len` cryptographically-secure random or pseudo-random bytes. /// - /// This function must produce data from an adequately seeded - /// cryptographically-secure pseudo-random number generator (CSPRNG), so it - /// must not block, from the perspective of the calling program, and the - /// returned data is always unpredictable. + /// This function must produce data at least as cryptographically secure and + /// fast as an adequately seeded cryptographically-secure pseudo-random + /// number generator (CSPRNG). It must not block, from the perspective of + /// the calling program, under any circumstances, including on the first + /// request and on requests for numbers of bytes. The returned data must + /// always be unpredictable. /// - /// This function must always return fresh pseudo-random data. Deterministic - /// environments must omit this function, rather than implementing it with - /// deterministic data. + /// This function must always return fresh data. Deterministic environments + /// must omit this function, rather than implementing it with deterministic + /// data. get-random-bytes: func(len: u64) -> list - /// Return a cryptographically-secure pseudo-random `u64` value. + /// Return a cryptographically-secure random or pseudo-random `u64` value. /// - /// This function returns the same type of pseudo-random data as - /// `get-random-bytes`, represented as a `u64`. + /// This function returns the same type of data as `get-random-bytes`, + /// represented as a `u64`. get-random-u64: func() -> u64 } diff --git a/crates/wasi/wit/deps/random/world.wit b/crates/wasi/wit/deps/random/world.wit new file mode 100644 index 000000000000..41dc9ed10353 --- /dev/null +++ b/crates/wasi/wit/deps/random/world.wit @@ -0,0 +1,7 @@ +package wasi:random + +world imports { + import random + import insecure + import insecure-seed +} diff --git a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit index f15d19d037da..f998aae140ab 100644 --- a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit +++ b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit @@ -1,6 +1,6 @@ interface ip-name-lookup { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-address, ip-address-family} @@ -34,36 +34,28 @@ interface ip-name-lookup { /// - /// - /// - - resolve-addresses: func(network: network, name: string, address-family: option, include-unavailable: bool) -> result - - - - type resolve-address-stream = u32 - - /// Returns the next address from the resolver. - /// - /// This function should be called multiple times. On each call, it will - /// return the next address in connection order preference. If all - /// addresses have been exhausted, this function returns `none`. - /// After which, you should release the stream with `drop-resolve-address-stream`. - /// - /// This function never returns IPv4-mapped IPv6 addresses. - /// - /// # Typical errors - /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) - /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) - /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) - /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) - resolve-next-address: func(this: resolve-address-stream) -> result, error-code> - - /// Dispose of the specified `resolve-address-stream`, after which it may no longer be used. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-resolve-address-stream: func(this: resolve-address-stream) - - /// Create a `pollable` which will resolve once the stream is ready for I/O. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func(this: resolve-address-stream) -> pollable + resolve-addresses: func(network: borrow, name: string, address-family: option, include-unavailable: bool) -> result + + resource resolve-address-stream { + /// Returns the next address from the resolver. + /// + /// This function should be called multiple times. On each call, it will + /// return the next address in connection order preference. If all + /// addresses have been exhausted, this function returns `none`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + resolve-next-address: func() -> result, error-code> + + /// Create a `pollable` which will resolve once the stream is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable + } } diff --git a/crates/wasi/wit/deps/sockets/network.wit b/crates/wasi/wit/deps/sockets/network.wit index a198ea8017de..8214eaaf7211 100644 --- a/crates/wasi/wit/deps/sockets/network.wit +++ b/crates/wasi/wit/deps/sockets/network.wit @@ -1,18 +1,9 @@ -package wasi:sockets interface network { - /// An opaque resource that represents access to (a subset of) the network. + /// An opaque resource that represents access to (a subset of) the network. /// This enables context-based security for networking. /// There is no need for this to map 1:1 to a physical network interface. - /// - /// FYI, In the future this will be replaced by handle types. - type network = u32 - - /// Dispose of the specified `network`, after which it may no longer be used. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-network: func(this: network) - + resource network /// Error codes. /// diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit index 3922769b308e..175626cc7620 100644 --- a/crates/wasi/wit/deps/sockets/tcp.wit +++ b/crates/wasi/wit/deps/sockets/tcp.wit @@ -1,13 +1,9 @@ interface tcp { use wasi:io/streams.{input-stream, output-stream} - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-socket-address, ip-address-family} - /// A TCP socket handle. - type tcp-socket = u32 - - enum shutdown-type { /// Similar to `SHUT_RD` in POSIX. receive, @@ -20,249 +16,234 @@ interface tcp { } - /// Bind the socket to a specific network on the provided IP address and port. - /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// - /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will - /// implicitly bind the socket. - /// - /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - /// # Typical `start` errors - /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) - /// - `already-bound`: The socket is already bound. (EINVAL) - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - /// - /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) - /// - `address-in-use`: Address is already in use. (EADDRINUSE) - /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) - /// - `not-in-progress`: A `bind` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-bind: func(this: tcp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> - finish-bind: func(this: tcp-socket) -> result<_, error-code> - - /// Connect to a remote endpoint. - /// - /// On success: - /// - the socket is transitioned into the Connection state - /// - a pair of streams is returned that can be used to read & write to the connection - /// - /// # Typical `start` errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) - /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. - /// - `already-connected`: The socket is already in the Connection state. (EISCONN) - /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - /// - /// # Typical `finish` errors - /// - `timeout`: Connection timed out. (ETIMEDOUT) - /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) - /// - `connection-reset`: The connection was reset. (ECONNRESET) - /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) - /// - `not-in-progress`: A `connect` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-connect: func(this: tcp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> - /// Note: the returned `input-stream` and `output-stream` are child - /// resources of the `tcp-socket`. Implementations may trap if the - /// `tcp-socket` is dropped before both of these streams are dropped. - finish-connect: func(this: tcp-socket) -> result, error-code> - - /// Start listening for new connections. - /// - /// Transitions the socket into the Listener state. - /// - /// Unlike POSIX: - /// - this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - the socket must already be explicitly bound. - /// - /// # Typical `start` errors - /// - `not-bound`: The socket is not bound to any local address. (EDESTADDRREQ) - /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) - /// - `already-listening`: The socket is already in the Listener state. - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) - /// - /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) - /// - `not-in-progress`: A `listen` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-listen: func(this: tcp-socket) -> result<_, error-code> - finish-listen: func(this: tcp-socket) -> result<_, error-code> - - /// Accept a new client socket. - /// - /// The returned socket is bound and in the Connection state. - /// - /// On success, this function returns the newly accepted client socket along with - /// a pair of streams that can be used to read & write to the connection. - /// - /// Note: the returned `input-stream` and `output-stream` are child - /// resources of the returned `tcp-socket`. Implementations may trap if the - /// `tcp-socket` is dropped before its child streams are dropped. - /// - /// # Typical errors - /// - `not-listening`: Socket is not in the Listener state. (EINVAL) - /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) - /// - /// Host implementations must skip over transient errors returned by the native accept syscall. - /// - /// # References - /// - - /// - - /// - - /// - - accept: func(this: tcp-socket) -> result, error-code> - - /// Get the bound local address. - /// - /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. - /// - /// # References - /// - - /// - - /// - - /// - - local-address: func(this: tcp-socket) -> result - - /// Get the bound remote address. - /// - /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - remote-address: func(this: tcp-socket) -> result - - /// Whether this is a IPv4 or IPv6 socket. - /// - /// Equivalent to the SO_DOMAIN socket option. - address-family: func(this: tcp-socket) -> ip-address-family + /// A TCP socket handle. + resource tcp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will + /// implicitly bind the socket. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + /// - `already-bound`: The socket is already bound. (EINVAL) + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code> + finish-bind: func() -> result<_, error-code> + + /// Connect to a remote endpoint. + /// + /// On success: + /// - the socket is transitioned into the Connection state + /// - a pair of streams is returned that can be used to read & write to the connection + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `already-connected`: The socket is already in the Connection state. (EISCONN) + /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A `connect` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code> + finish-connect: func() -> result, error-code> + + /// Start listening for new connections. + /// + /// Transitions the socket into the Listener state. + /// + /// Unlike POSIX: + /// - this function is async. This enables interactive WASI hosts to inject permission prompts. + /// - the socket must already be explicitly bound. + /// + /// # Typical `start` errors + /// - `not-bound`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) + /// - `already-listening`: The socket is already in the Listener state. + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A `listen` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-listen: func() -> result<_, error-code> + finish-listen: func() -> result<_, error-code> + + /// Accept a new client socket. + /// + /// The returned socket is bound and in the Connection state. + /// + /// On success, this function returns the newly accepted client socket along with + /// a pair of streams that can be used to read & write to the connection. + /// + /// # Typical errors + /// - `not-listening`: Socket is not in the Listener state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// + /// Host implementations must skip over transient errors returned by the native accept syscall. + /// + /// # References + /// - + /// - + /// - + /// - + accept: func() -> result, error-code> + + /// Get the bound local address. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func() -> result + + /// Get the bound remote address. + /// + /// # Typical errors + /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func() -> result + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func() -> ip-address-family - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. - /// - `already-bound`: (set) The socket is already bound. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - ipv6-only: func(this: tcp-socket) -> result - set-ipv6-only: func(this: tcp-socket, value: bool) -> result<_, error-code> - - /// Hints the desired listen queue size. Implementations are free to ignore this. - /// - /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - set-listen-backlog-size: func(this: tcp-socket, value: u64) -> result<_, error-code> - - /// Equivalent to the SO_KEEPALIVE socket option. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - keep-alive: func(this: tcp-socket) -> result - set-keep-alive: func(this: tcp-socket, value: bool) -> result<_, error-code> - - /// Equivalent to the TCP_NODELAY socket option. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - no-delay: func(this: tcp-socket) -> result - set-no-delay: func(this: tcp-socket, value: bool) -> result<_, error-code> + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// + /// Equivalent to the IPV6_V6ONLY socket option. + /// + /// # Typical errors + /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + ipv6-only: func() -> result + set-ipv6-only: func(value: bool) -> result<_, error-code> + + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + set-listen-backlog-size: func(value: u64) -> result<_, error-code> + + /// Equivalent to the SO_KEEPALIVE socket option. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + keep-alive: func() -> result + set-keep-alive: func(value: bool) -> result<_, error-code> + + /// Equivalent to the TCP_NODELAY socket option. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + no-delay: func() -> result + set-no-delay: func(value: bool) -> result<_, error-code> - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// - /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `already-listening`: (set) The socket is already in the Listener state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - unicast-hop-limit: func(this: tcp-socket) -> result - set-unicast-hop-limit: func(this: tcp-socket, value: u8) -> result<_, error-code> - - /// The kernel buffer space reserved for sends/receives on this socket. - /// - /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. - /// In other words, after setting a value, reading the same setting back may return a different value. - /// - /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of - /// actual data to be sent/received by the application, because the kernel might also use the buffer space - /// for internal metadata structures. - /// - /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// - /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `already-listening`: (set) The socket is already in the Listener state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - receive-buffer-size: func(this: tcp-socket) -> result - set-receive-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> - send-buffer-size: func(this: tcp-socket) -> result - set-send-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> - - /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// - /// The created `pollable` is a child resource of the `tcp-socket`. - /// Implementations may trap if the `tcp-socket` is dropped before all - /// derived `pollable`s created with this function are dropped. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func(this: tcp-socket) -> pollable - - /// Initiate a graceful shutdown. - /// - /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read - /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. - /// Any data still in the receive queue at time of calling `shutdown` will be discarded. - /// - send: the socket is not expecting to send any more data to the peer. All subsequent write - /// operations on the `output-stream` associated with this socket will return an error. - /// - both: same effect as receive & send combined. - /// - /// The shutdown function does not close (drop) the socket. - /// - /// # Typical errors - /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - shutdown: func(this: tcp-socket, shutdown-type: shutdown-type) -> result<_, error-code> - - /// Dispose of the specified `tcp-socket`, after which it may no longer be used. - /// - /// Similar to the POSIX `close` function. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-tcp-socket: func(this: tcp-socket) + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `already-listening`: (set) The socket is already in the Listener state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + unicast-hop-limit: func() -> result + set-unicast-hop-limit: func(value: u8) -> result<_, error-code> + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. + /// In other words, after setting a value, reading the same setting back may return a different value. + /// + /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of + /// actual data to be sent/received by the application, because the kernel might also use the buffer space + /// for internal metadata structures. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `already-listening`: (set) The socket is already in the Listener state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + receive-buffer-size: func() -> result + set-receive-buffer-size: func(value: u64) -> result<_, error-code> + send-buffer-size: func() -> result + set-send-buffer-size: func(value: u64) -> result<_, error-code> + + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable + + /// Initiate a graceful shutdown. + /// + /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read + /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. + /// Any data still in the receive queue at time of calling `shutdown` will be discarded. + /// - send: the socket is not expecting to send any more data to the peer. All subsequent write + /// operations on the `output-stream` associated with this socket will return an error. + /// - both: same effect as receive & send combined. + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code> + } } diff --git a/crates/wasi/wit/deps/sockets/udp.wit b/crates/wasi/wit/deps/sockets/udp.wit index 700b9e247692..01e5b95b97b7 100644 --- a/crates/wasi/wit/deps/sockets/udp.wit +++ b/crates/wasi/wit/deps/sockets/udp.wit @@ -1,13 +1,9 @@ interface udp { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-socket-address, ip-address-family} - /// A UDP socket handle. - type udp-socket = u32 - - record datagram { data: list, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. remote-address: ip-socket-address, @@ -22,199 +18,197 @@ interface udp { - /// Bind the socket to a specific network on the provided IP address and port. - /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// - /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. - /// - /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - /// # Typical `start` errors - /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) - /// - `already-bound`: The socket is already bound. (EINVAL) - /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) - /// - /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) - /// - `address-in-use`: Address is already in use. (EADDRINUSE) - /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) - /// - `not-in-progress`: A `bind` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-bind: func(this: udp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> - finish-bind: func(this: udp-socket) -> result<_, error-code> - - /// Set the destination address. - /// - /// The local-address is updated based on the best network path to `remote-address`. - /// - /// When a destination address is set: - /// - all receive operations will only return datagrams sent from the provided `remote-address`. - /// - the `send` function can only be used to send to this destination. - /// - /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". - /// - /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - /// # Typical `start` errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. - /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) - /// - /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) - /// - `not-in-progress`: A `connect` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-connect: func(this: udp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> - finish-connect: func(this: udp-socket) -> result<_, error-code> - - /// Receive messages on the socket. - /// - /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. - /// The returned list may contain fewer elements than requested, but never more. - /// If `max-results` is 0, this function returns successfully with an empty list. - /// - /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. (EINVAL) - /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - /// - - /// - - /// - - /// - - receive: func(this: udp-socket, max-results: u64) -> result, error-code> - - /// Send messages on the socket. - /// - /// This function attempts to send all provided `datagrams` on the socket without blocking and - /// returns how many messages were actually sent (or queued for sending). - /// - /// This function semantically behaves the same as iterating the `datagrams` list and sequentially - /// sending each individual datagram until either the end of the list has been reached or the first error occurred. - /// If at least one datagram has been sent successfully, this function never returns an error. - /// - /// If the input list is empty, the function returns `ok(0)`. - /// - /// The remote address option is required. To send a message to the "connected" peer, - /// call `remote-address` to get their address. - /// - /// # Typical errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) - /// - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. - /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) - /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - /// - - /// - - /// - - /// - - send: func(this: udp-socket, datagrams: list) -> result - - /// Get the current bound address. - /// - /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. - /// - /// # References - /// - - /// - - /// - - /// - - local-address: func(this: udp-socket) -> result - - /// Get the address set with `connect`. - /// - /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - remote-address: func(this: udp-socket) -> result - - /// Whether this is a IPv4 or IPv6 socket. - /// - /// Equivalent to the SO_DOMAIN socket option. - address-family: func(this: udp-socket) -> ip-address-family - - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. - /// - `already-bound`: (set) The socket is already bound. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - ipv6-only: func(this: udp-socket) -> result - set-ipv6-only: func(this: udp-socket, value: bool) -> result<_, error-code> - - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - unicast-hop-limit: func(this: udp-socket) -> result - set-unicast-hop-limit: func(this: udp-socket, value: u8) -> result<_, error-code> - - /// The kernel buffer space reserved for sends/receives on this socket. - /// - /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. - /// In other words, after setting a value, reading the same setting back may return a different value. - /// - /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of - /// actual data to be sent/received by the application, because the kernel might also use the buffer space - /// for internal metadata structures. - /// - /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - receive-buffer-size: func(this: udp-socket) -> result - set-receive-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> - send-buffer-size: func(this: udp-socket) -> result - set-send-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> - - /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func(this: udp-socket) -> pollable - - /// Dispose of the specified `udp-socket`, after which it may no longer be used. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-udp-socket: func(this: udp-socket) + /// A UDP socket handle. + resource udp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + /// - `already-bound`: The socket is already bound. (EINVAL) + /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code> + finish-bind: func() -> result<_, error-code> + + /// Set the destination address. + /// + /// The local-address is updated based on the best network path to `remote-address`. + /// + /// When a destination address is set: + /// - all receive operations will only return datagrams sent from the provided `remote-address`. + /// - the `send` function can only be used to send to this destination. + /// + /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A `connect` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code> + finish-connect: func() -> result<_, error-code> + + /// Receive messages on the socket. + /// + /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. + /// The returned list may contain fewer elements than requested, but never more. + /// If `max-results` is 0, this function returns successfully with an empty list. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. (EINVAL) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + receive: func(max-results: u64) -> result, error-code> + + /// Send messages on the socket. + /// + /// This function attempts to send all provided `datagrams` on the socket without blocking and + /// returns how many messages were actually sent (or queued for sending). + /// + /// This function semantically behaves the same as iterating the `datagrams` list and sequentially + /// sending each individual datagram until either the end of the list has been reached or the first error occurred. + /// If at least one datagram has been sent successfully, this function never returns an error. + /// + /// If the input list is empty, the function returns `ok(0)`. + /// + /// The remote address option is required. To send a message to the "connected" peer, + /// call `remote-address` to get their address. + /// + /// # Typical errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) + /// - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. + /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + send: func(datagrams: list) -> result + + /// Get the current bound address. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func() -> result + + /// Get the address set with `connect`. + /// + /// # Typical errors + /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func() -> result + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func() -> ip-address-family + + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// + /// Equivalent to the IPV6_V6ONLY socket option. + /// + /// # Typical errors + /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + ipv6-only: func() -> result + set-ipv6-only: func(value: bool) -> result<_, error-code> + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + unicast-hop-limit: func() -> result + set-unicast-hop-limit: func(value: u8) -> result<_, error-code> + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. + /// In other words, after setting a value, reading the same setting back may return a different value. + /// + /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of + /// actual data to be sent/received by the application, because the kernel might also use the buffer space + /// for internal metadata structures. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + receive-buffer-size: func() -> result + set-receive-buffer-size: func(value: u64) -> result<_, error-code> + send-buffer-size: func() -> result + set-send-buffer-size: func(value: u64) -> result<_, error-code> + + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable + } } diff --git a/crates/wasi/wit/deps/sockets/world.wit b/crates/wasi/wit/deps/sockets/world.wit new file mode 100644 index 000000000000..12f3c2868177 --- /dev/null +++ b/crates/wasi/wit/deps/sockets/world.wit @@ -0,0 +1,11 @@ +package wasi:sockets + +world imports { + import instance-network + import network + import udp + import udp-create-socket + import tcp + import tcp-create-socket + import ip-name-lookup +} diff --git a/crates/wasi/wit/main.wit b/crates/wasi/wit/main.wit index 753770ad22ab..739e1bd4ac48 100644 --- a/crates/wasi/wit/main.wit +++ b/crates/wasi/wit/main.wit @@ -18,7 +18,7 @@ world preview1-adapter-reactor { import wasi:random/random import wasi:random/insecure import wasi:random/insecure-seed - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/exit diff --git a/crates/wasi/wit/test.wit b/crates/wasi/wit/test.wit index 4543cb194af1..0b6bd28e997d 100644 --- a/crates/wasi/wit/test.wit +++ b/crates/wasi/wit/test.wit @@ -2,6 +2,7 @@ world test-reactor { import wasi:cli/environment + import wasi:io/poll import wasi:io/streams import wasi:filesystem/types import wasi:filesystem/preopens @@ -19,7 +20,7 @@ world test-reactor { } world test-command { - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/stdin @@ -28,7 +29,7 @@ world test-command { } world test-command-with-sockets { - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/stdin diff --git a/crates/wasmtime/src/component/resources.rs b/crates/wasmtime/src/component/resources.rs index ded0665a66a1..9ed13faf3208 100644 --- a/crates/wasmtime/src/component/resources.rs +++ b/crates/wasmtime/src/component/resources.rs @@ -208,7 +208,7 @@ pub struct Resource { /// discard this resource when the borrow duration has finished. /// /// * `NOT_IN_TABLE` / `u32::MAX - 1` - this indicates that this is an owned - /// resource not present in any store's stable. This resource is not lent + /// resource not present in any store's table. This resource is not lent /// out. It can be passed as an `(own $t)` directly into a guest's table /// or it can be passed as a borrow to a guest which will insert it into /// a host store's table for dynamic borrow tracking. @@ -451,7 +451,16 @@ unsafe impl Lift for Resource { impl fmt::Debug for Resource { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Resource").field("rep", &self.rep).finish() + let state = match self.state.load(Relaxed) { + BORROW => "borrow", + NOT_IN_TABLE => "own (not in table)", + TAKEN => "taken", + _ => "own", + }; + f.debug_struct("Resource") + .field("rep", &self.rep) + .field("state", &state) + .finish() } } diff --git a/src/commands/run.rs b/src/commands/run.rs index 93c67c1463b1..a75dfdb6c2a1 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -746,7 +746,7 @@ impl RunCommand { #[cfg(feature = "component-model")] fn ensure_allow_components(&self) -> Result<()> { if self.common.wasm.component_model != Some(true) { - bail!("cannot execute a component without `--wasm-features component-model`"); + bail!("cannot execute a component without `--wasm component-model`"); } Ok(()) @@ -937,10 +937,7 @@ impl RunCommand { builder.inherit_network(ambient_authority()); } - let data = store.data_mut(); - let table = Arc::get_mut(&mut data.preview2_table).unwrap(); - let ctx = builder.build(table)?; - data.preview2_ctx = Some(Arc::new(ctx)); + store.data_mut().preview2_ctx = Some(Arc::new(builder.build())); Ok(()) } } diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 90803257ae06..075dd2052ca5 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -3277,6 +3277,12 @@ user-id = 1 # Alex Crichton (alexcrichton) start = "2019-03-04" end = "2024-07-14" +[[trusted.wasmtime-wmemcheck]] +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2023-09-20" +end = "2024-09-27" + [[trusted.winapi-util]] criteria = "safe-to-deploy" user-id = 189 # Andrew Gallant (BurntSushi) diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 3908247ffcec..c1301305ab8d 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -1912,6 +1912,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wit-bindgen]] +version = "0.12.0" +when = "2023-09-18" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wit-bindgen-core]] version = "0.9.0" when = "2023-07-15" @@ -1926,6 +1933,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wit-bindgen-core]] +version = "0.12.0" +when = "2023-09-18" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wit-bindgen-rust]] version = "0.9.0" when = "2023-07-15" @@ -1940,6 +1954,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wit-bindgen-rust]] +version = "0.12.0" +when = "2023-09-18" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wit-bindgen-rust-lib]] version = "0.9.0" when = "2023-07-15" @@ -1954,6 +1975,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wit-bindgen-rust-lib]] +version = "0.12.0" +when = "2023-09-18" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wit-bindgen-rust-macro]] version = "0.9.0" when = "2023-07-15" @@ -1968,6 +1996,13 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" +[[publisher.wit-bindgen-rust-macro]] +version = "0.12.0" +when = "2023-09-18" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + [[publisher.wit-component]] version = "0.12.0" when = "2023-07-11" diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs index 4f7dd4968dc0..da2e7adedc97 100644 --- a/tests/all/cli_tests.rs +++ b/tests/all/cli_tests.rs @@ -737,7 +737,7 @@ fn component_missing_feature() -> Result<()> { assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!( - stderr.contains("cannot execute a component without `--wasm-features component-model`"), + stderr.contains("cannot execute a component without `--wasm component-model`"), "bad stderr: {stderr}" ); @@ -749,7 +749,7 @@ fn component_missing_feature() -> Result<()> { assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!( - stderr.contains("cannot execute a component without `--wasm-features component-model`"), + stderr.contains("cannot execute a component without `--wasm component-model`"), "bad stderr: {stderr}" ); From 17eeba0e9670ab5e579cadb03cffd096440f37a3 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 29 Sep 2023 13:27:52 -0700 Subject: [PATCH 029/199] Cranelift: return programmatic error rather than panic when temporaries run out. (#7114) * Cranelift: return programmatic error rather than panic when temporaries run out. Cranelift currently has a limit of `2^21` vregs per function body in VCode. This is a consequence of (performance-motivated) bitpacking of `Operand`s into `u32`s in regalloc2. As a result of this, it is possible to produce a function body that will fail to compile by running out of vreg temporaries during lowering. Currently, this results in a panic. Ideally, we would propagate the error upward and return it programmatically. This PR does that, with a "deferred error" mechanism. A cleaner solution would be to properly thread the `Result` types through all layers of lowering. However, that would require supporting `Result`s in ISLE, and that is a deeper language-design and `islec`-hacking question that I think we can tackle later if we continue to see motivating cases. The deferral works by returning a valid but bogus `ValueReg`s to the lowering rule, but storing the error and checking for it in the toplevel lowering loop. (Note that we have to return a bogus `v0` rather than `VReg::invalid()`, because the latter causes the `ValueRegs` to think there is no register provided.) This PR also includes a test at the Wasmtime level. Note that it takes ~22s to run on my (relatively fast) laptop, because it has to run until it runs out of VRegs in a debug build of the compiler. We could remove the test if we feel we're otherwise confident. Thanks to Venkkatesh Sekar for reporting this issue! The integration test uses one of the example inputs from the report. * Review feedback. * Handle alloc after a deferred error occurs. * Add uncommitted fix for previous. --- cranelift/codegen/src/machinst/abi.rs | 16 +++++----- cranelift/codegen/src/machinst/lower.rs | 8 ++++- cranelift/codegen/src/machinst/vcode.rs | 42 +++++++++++++++++++++++++ tests/all/code_too_large.rs | 36 +++++++++++++++++++++ tests/all/main.rs | 1 + 5 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 tests/all/code_too_large.rs diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs index 536f8886d272..fd0e71add362 100644 --- a/cranelift/codegen/src/machinst/abi.rs +++ b/cranelift/codegen/src/machinst/abi.rs @@ -1559,7 +1559,7 @@ impl Callee { // We need to dereference the pointer. let base = match &pointer { &ABIArgSlot::Reg { reg, ty, .. } => { - let tmp = vregs.alloc(ty).unwrap().only_reg().unwrap(); + let tmp = vregs.alloc_with_deferred_error(ty).only_reg().unwrap(); self.reg_args.push(ArgPair { vreg: Writable::from_reg(tmp), preg: reg.into(), @@ -1621,9 +1621,10 @@ impl Callee { if n < word_bits => { let signed = ext == ir::ArgumentExtension::Sext; - let dst = writable_value_regs(vregs.alloc(ty).unwrap()) - .only_reg() - .unwrap(); + let dst = + writable_value_regs(vregs.alloc_with_deferred_error(ty)) + .only_reg() + .unwrap(); ret.push(M::gen_extend( dst, from_reg, signed, from_bits, /* to_bits = */ word_bits, @@ -1664,9 +1665,10 @@ impl Callee { { assert_eq!(M::word_reg_class(), from_reg.class()); let signed = ext == ir::ArgumentExtension::Sext; - let dst = writable_value_regs(vregs.alloc(ty).unwrap()) - .only_reg() - .unwrap(); + let dst = + writable_value_regs(vregs.alloc_with_deferred_error(ty)) + .only_reg() + .unwrap(); ret.push(M::gen_extend( dst, from_reg, signed, from_bits, /* to_bits = */ word_bits, diff --git a/cranelift/codegen/src/machinst/lower.rs b/cranelift/codegen/src/machinst/lower.rs index e35a6bbb2bf7..f4f02890e7de 100644 --- a/cranelift/codegen/src/machinst/lower.rs +++ b/cranelift/codegen/src/machinst/lower.rs @@ -1069,6 +1069,12 @@ impl<'func, I: VCodeInst> Lower<'func, I> { } self.finish_bb(); + + // Check for any deferred vreg-temp allocation errors, and + // bubble one up at this time if it exists. + if let Some(e) = self.vregs.take_deferred_error() { + return Err(e); + } } // Now that we've emitted all instructions into the @@ -1319,7 +1325,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { impl<'func, I: VCodeInst> Lower<'func, I> { /// Get a new temp. pub fn alloc_tmp(&mut self, ty: Type) -> ValueRegs> { - writable_value_regs(self.vregs.alloc(ty).unwrap()) + writable_value_regs(self.vregs.alloc_with_deferred_error(ty)) } /// Emit a machine instruction. diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs index 4cd19809b2cc..7e88902634d2 100644 --- a/cranelift/codegen/src/machinst/vcode.rs +++ b/cranelift/codegen/src/machinst/vcode.rs @@ -1456,6 +1456,12 @@ pub struct VRegAllocator { /// reftype-status of each vreg) for efficient iteration. reftyped_vregs: Vec, + /// A deferred error, to be bubbled up to the top level of the + /// lowering algorithm. We take this approach because we cannot + /// currently propagate a `Result` upward through ISLE code (the + /// lowering rules) or some ABI code. + deferred_error: Option, + /// The type of instruction that this allocator makes registers for. _inst: core::marker::PhantomData, } @@ -1468,12 +1474,16 @@ impl VRegAllocator { vreg_types: vec![], reftyped_vregs_set: FxHashSet::default(), reftyped_vregs: vec![], + deferred_error: None, _inst: core::marker::PhantomData::default(), } } /// Allocate a fresh ValueRegs. pub fn alloc(&mut self, ty: Type) -> CodegenResult> { + if self.deferred_error.is_some() { + return Err(CodegenError::CodeTooLarge); + } let v = self.next_vreg; let (regclasses, tys) = I::rc_for_type(ty)?; self.next_vreg += regclasses.len(); @@ -1495,6 +1505,38 @@ impl VRegAllocator { Ok(regs) } + /// Allocate a fresh ValueRegs, deferring any out-of-vregs + /// errors. This is useful in places where we cannot bubble a + /// `CodegenResult` upward easily, and which are known to be + /// invoked from within the lowering loop that checks the deferred + /// error status below. + pub fn alloc_with_deferred_error(&mut self, ty: Type) -> ValueRegs { + match self.alloc(ty) { + Ok(x) => x, + Err(e) => { + self.deferred_error = Some(e); + self.bogus_for_deferred_error(ty) + } + } + } + + /// Take any deferred error that was accumulated by `alloc_with_deferred_error`. + pub fn take_deferred_error(&mut self) -> Option { + self.deferred_error.take() + } + + /// Produce an bogus VReg placeholder with the proper number of + /// registers for the given type. This is meant to be used with + /// deferred allocation errors (see `Lower::alloc_tmp()`). + fn bogus_for_deferred_error(&self, ty: Type) -> ValueRegs { + let (regclasses, _tys) = I::rc_for_type(ty).expect("must have valid type"); + match regclasses { + &[rc0] => ValueRegs::one(VReg::new(0, rc0).into()), + &[rc0, rc1] => ValueRegs::two(VReg::new(0, rc0).into(), VReg::new(1, rc1).into()), + _ => panic!("Value must reside in 1 or 2 registers"), + } + } + /// Set the type of this virtual register. pub fn set_vreg_type(&mut self, vreg: VirtualReg, ty: Type) { if self.vreg_types.len() <= vreg.index() { diff --git a/tests/all/code_too_large.rs b/tests/all/code_too_large.rs new file mode 100644 index 000000000000..c7c5080dcbce --- /dev/null +++ b/tests/all/code_too_large.rs @@ -0,0 +1,36 @@ +#![cfg(not(miri))] + +use anyhow::Result; +use wasmtime::*; + +#[test] +fn code_too_large_without_panic() -> Result<()> { + const N: usize = 120000; + + // Build a module with a function whose body will allocate too many + // temporaries for our current (Cranelift-based) compiler backend to + // handle. This test ensures that we propagate the failure upward + // and return it programmatically, rather than panic'ing. If we ever + // improve our compiler backend to actually handle such a large + // function body, we'll need to increase the limits here too! + let mut s = String::new(); + s.push_str("(module\n"); + s.push_str("(table 1 1 funcref)\n"); + s.push_str("(func (export \"\") (result i32)\n"); + s.push_str("i32.const 0\n"); + for _ in 0..N { + s.push_str("table.get 0\n"); + s.push_str("ref.is_null\n"); + } + s.push_str("))\n"); + + let store = Store::<()>::default(); + let result = Module::new(store.engine(), &s); + match result { + Err(e) => assert!(e + .to_string() + .starts_with("Compilation error: Code for function is too large")), + Ok(_) => panic!("Please adjust limits to make the module too large to compile!"), + } + Ok(()) +} diff --git a/tests/all/main.rs b/tests/all/main.rs index 1c3e6d4af483..54c8229b8eb1 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -3,6 +3,7 @@ mod async_functions; mod call_hook; mod cli_tests; +mod code_too_large; mod component_model; mod coredump; mod custom_signal_handler; From a7e47627ad5e42cd4c1129c23913b56c5cea5998 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 29 Sep 2023 18:15:40 -0500 Subject: [PATCH 030/199] Remove some usage of UnsafeCell in the adapter (#7117) * Fix compile warnings in preview1 adapter This isn't part of the "main build" so it's not inheriting the "deny warnings" flag passed in CI, so fixup some warnings which have leaked through over time. * Remove some usage of `UnsafeCell` in the adapter Move to abstractions such as `OnceCell` from the standard library as well as `RefCell` to avoid some unsafety. This shouldn't have any practical ramifications on the adapter, mostly just trying to help over-time maintenance. * Deny test-programs warnings in CI --- crates/test-programs/build.rs | 11 + .../src/descriptors.rs | 74 +- .../src/lib.rs | 1404 ++++++++--------- .../src/macros.rs | 3 +- 4 files changed, 718 insertions(+), 774 deletions(-) diff --git a/crates/test-programs/build.rs b/crates/test-programs/build.rs index 0de514283caa..efa0d20d63eb 100644 --- a/crates/test-programs/build.rs +++ b/crates/test-programs/build.rs @@ -48,6 +48,7 @@ fn build_and_generate_tests() { .arg("--package=wasi-sockets-tests") .env("CARGO_TARGET_DIR", &out_dir) .env("CARGO_PROFILE_DEV_DEBUG", "1") + .env("RUSTFLAGS", rustflags()) .env_remove("CARGO_ENCODED_RUSTFLAGS"); if BUILD_WASI_HTTP_TESTS { cmd.arg("--package=wasi-http-tests"); @@ -153,6 +154,7 @@ fn build_adapter(out_dir: &PathBuf, name: &str, features: &[&str]) -> Vec { .arg("--package=wasi-preview1-component-adapter") .arg("--target=wasm32-unknown-unknown") .env("CARGO_TARGET_DIR", out_dir) + .env("RUSTFLAGS", rustflags()) .env_remove("CARGO_ENCODED_RUSTFLAGS"); for f in features { cmd.arg(f); @@ -173,6 +175,15 @@ fn build_adapter(out_dir: &PathBuf, name: &str, features: &[&str]) -> Vec { fs::read(&adapter).unwrap() } +fn rustflags() -> &'static str { + match option_env!("RUSTFLAGS") { + // If we're in CI which is denying warnings then deny warnings to code + // built here too to keep the tree warning-free. + Some(s) if s.contains("-D warnings") => "-D warnings", + _ => "", + } +} + // Builds components out of modules, and creates an `${out_dir}/${package}_component.rs` file that // exposes a `get_component(&str) -> Component` // and a contains a `use self::{component} as _;` for each module that ensures that the user defines diff --git a/crates/wasi-preview1-component-adapter/src/descriptors.rs b/crates/wasi-preview1-component-adapter/src/descriptors.rs index 354de6f96801..997d8856b0fe 100644 --- a/crates/wasi-preview1-component-adapter/src/descriptors.rs +++ b/crates/wasi-preview1-component-adapter/src/descriptors.rs @@ -5,7 +5,7 @@ use crate::bindings::wasi::filesystem::types as filesystem; use crate::bindings::wasi::io::streams::{InputStream, OutputStream}; use crate::bindings::wasi::sockets::tcp; use crate::{BlockingMode, BumpArena, File, ImportAlloc, TrappingUnwrap, WasmStr}; -use core::cell::{Cell, UnsafeCell}; +use core::cell::{Cell, OnceCell, UnsafeCell}; use core::mem::MaybeUninit; use wasi::{Errno, Fd}; @@ -26,10 +26,10 @@ pub enum Descriptor { /// type-specific operations like seeking. pub struct Streams { /// The input stream, if present. - pub input: UnsafeCell>, + pub input: OnceCell, /// The output stream, if present. - pub output: UnsafeCell>, + pub output: OnceCell, /// Information about the source of the stream. pub type_: StreamType, @@ -38,7 +38,7 @@ pub struct Streams { impl Streams { /// Return the input stream, initializing it on the fly if needed. pub fn get_read_stream(&self) -> Result<&InputStream, Errno> { - match unsafe { &*self.input.get() } { + match self.input.get() { Some(wasi_stream) => Ok(wasi_stream), None => { let input = match &self.type_ { @@ -56,17 +56,15 @@ impl Streams { } _ => return Err(wasi::ERRNO_BADF), }; - unsafe { - *self.input.get() = Some(input); - Ok((*self.input.get()).as_ref().trapping_unwrap()) - } + self.input.set(input).trapping_unwrap(); + Ok(self.input.get().trapping_unwrap()) } } } /// Return the output stream, initializing it on the fly if needed. pub fn get_write_stream(&self) -> Result<&OutputStream, Errno> { - match unsafe { &*self.output.get() } { + match self.output.get() { Some(wasi_stream) => Ok(wasi_stream), None => { let output = match &self.type_ { @@ -88,10 +86,8 @@ impl Streams { } _ => return Err(wasi::ERRNO_BADF), }; - unsafe { - *self.output.get() = Some(output); - Ok((*self.output.get()).as_ref().trapping_unwrap()) - } + self.output.set(output).trapping_unwrap(); + Ok(self.output.get().trapping_unwrap()) } } } @@ -148,38 +144,45 @@ impl Descriptors { }; let stdin_isatty = match terminal_stdin::get_terminal_stdin() { - Some(t) => IsATTY::Yes, + Some(_) => IsATTY::Yes, None => IsATTY::No, }; let stdout_isatty = match terminal_stdout::get_terminal_stdout() { - Some(t) => IsATTY::Yes, + Some(_) => IsATTY::Yes, None => IsATTY::No, }; let stderr_isatty = match terminal_stderr::get_terminal_stderr() { - Some(t) => IsATTY::Yes, + Some(_) => IsATTY::Yes, None => IsATTY::No, }; + fn new_once(val: T) -> OnceCell { + let cell = OnceCell::new(); + let _ = cell.set(val); + cell + } + d.push(Descriptor::Streams(Streams { - input: UnsafeCell::new(Some(stdin::get_stdin())), - output: UnsafeCell::new(None), + input: new_once(stdin::get_stdin()), + output: OnceCell::new(), type_: StreamType::Stdio(stdin_isatty), })) .trapping_unwrap(); d.push(Descriptor::Streams(Streams { - input: UnsafeCell::new(None), - output: UnsafeCell::new(Some(stdout::get_stdout())), + input: OnceCell::new(), + output: new_once(stdout::get_stdout()), type_: StreamType::Stdio(stdout_isatty), })) .trapping_unwrap(); d.push(Descriptor::Streams(Streams { - input: UnsafeCell::new(None), - output: UnsafeCell::new(Some(stderr::get_stderr())), + input: OnceCell::new(), + output: new_once(stderr::get_stderr()), type_: StreamType::Stdio(stderr_isatty), })) .trapping_unwrap(); #[link(wasm_import_module = "wasi:filesystem/preopens")] + #[allow(improper_ctypes)] // FIXME(bytecodealliance/wit-bindgen#684) extern "C" { #[link_name = "get-directories"] fn get_preopens_import(rval: *mut PreopenList); @@ -204,8 +207,8 @@ impl Descriptors { // stdio (0,1,2) and no others, so that preopens are 3.. let descriptor_type = descriptor.get_type().trapping_unwrap(); d.push(Descriptor::Streams(Streams { - input: UnsafeCell::new(None), - output: UnsafeCell::new(None), + input: OnceCell::new(), + output: OnceCell::new(), type_: StreamType::File(File { fd: descriptor, descriptor_type, @@ -294,7 +297,7 @@ impl Descriptors { // Internal: close a fd, returning the descriptor. fn close_(&mut self, fd: Fd) -> Result { // Throw an error if closing an fd which is already closed - match self.get_mut(fd)? { + match self.get(fd)? { Descriptor::Closed(_) => Err(wasi::ERRNO_BADF)?, _ => {} } @@ -337,10 +340,14 @@ impl Descriptors { // A bunch of helper functions implemented in terms of the above pub functions: - pub fn get_stream_with_error(&self, fd: Fd, error: Errno) -> Result<&Streams, Errno> { - match self.get(fd)? { + pub fn get_stream_with_error_mut( + &mut self, + fd: Fd, + error: Errno, + ) -> Result<&mut Streams, Errno> { + match self.get_mut(fd)? { Descriptor::Streams(streams) => Ok(streams), - Descriptor::Closed(_) => Err(wasi::ERRNO_BADF), + Descriptor::Closed(_) => Err(error), } } @@ -392,12 +399,7 @@ impl Descriptors { .. }) => Ok(file), Descriptor::Streams(Streams { - type_: - StreamType::File( - file @ File { - descriptor_type: _, .. - }, - ), + type_: StreamType::File(File { .. }), .. }) => Err(wasi::ERRNO_NOTDIR), _ => Err(wasi::ERRNO_BADF), @@ -408,8 +410,8 @@ impl Descriptors { self.get_file_with_error(fd, wasi::ERRNO_SPIPE) } - pub fn get_seekable_stream(&self, fd: Fd) -> Result<&Streams, Errno> { - self.get_stream_with_error(fd, wasi::ERRNO_SPIPE) + pub fn get_seekable_stream_mut(&mut self, fd: Fd) -> Result<&mut Streams, Errno> { + self.get_stream_with_error_mut(fd, wasi::ERRNO_SPIPE) } pub fn get_read_stream(&self, fd: Fd) -> Result<&InputStream, Errno> { diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs index e5096ce21633..46f9c887772b 100644 --- a/crates/wasi-preview1-component-adapter/src/lib.rs +++ b/crates/wasi-preview1-component-adapter/src/lib.rs @@ -5,11 +5,13 @@ use crate::bindings::wasi::io::poll; use crate::bindings::wasi::io::streams; use crate::bindings::wasi::random::random; use crate::bindings::wasi::sockets::network; -use core::cell::{Cell, UnsafeCell}; +use core::cell::OnceCell; +use core::cell::{Cell, RefCell, RefMut, UnsafeCell}; use core::cmp::min; use core::ffi::c_void; use core::hint::black_box; use core::mem::{self, align_of, forget, size_of, ManuallyDrop, MaybeUninit}; +use core::ops::{Deref, DerefMut}; use core::ptr::{self, null_mut}; use core::slice; use poll::Pollable; @@ -346,23 +348,24 @@ pub unsafe extern "C" fn environ_sizes_get( /// Note: This is similar to `clock_getres` in POSIX. #[no_mangle] pub extern "C" fn clock_res_get(id: Clockid, resolution: &mut Timestamp) -> Errno { - State::with(|state| { - match id { - CLOCKID_MONOTONIC => { - let res = monotonic_clock::resolution(); - *resolution = res; - } - CLOCKID_REALTIME => { - let res = wall_clock::resolution(); - *resolution = Timestamp::from(res.seconds) - .checked_mul(1_000_000_000) - .and_then(|ns| ns.checked_add(res.nanoseconds.into())) - .ok_or(ERRNO_OVERFLOW)?; - } - _ => unreachable!(), + match id { + CLOCKID_MONOTONIC => { + *resolution = monotonic_clock::resolution(); + ERRNO_SUCCESS } - Ok(()) - }) + CLOCKID_REALTIME => { + let res = wall_clock::resolution(); + *resolution = match Timestamp::from(res.seconds) + .checked_mul(1_000_000_000) + .and_then(|ns| ns.checked_add(res.nanoseconds.into())) + { + Some(ns) => ns, + None => return ERRNO_OVERFLOW, + }; + ERRNO_SUCCESS + } + _ => ERRNO_BADF, + } } /// Return the time value of a clock. @@ -373,28 +376,23 @@ pub unsafe extern "C" fn clock_time_get( _precision: Timestamp, time: &mut Timestamp, ) -> Errno { - if matches!( - get_allocation_state(), - AllocationState::StackAllocated | AllocationState::StateAllocated - ) { - State::with(|state| match id { - CLOCKID_MONOTONIC => { - *time = monotonic_clock::now(); - Ok(()) - } - CLOCKID_REALTIME => { - let res = wall_clock::now(); - *time = Timestamp::from(res.seconds) - .checked_mul(1_000_000_000) - .and_then(|ns| ns.checked_add(res.nanoseconds.into())) - .ok_or(ERRNO_OVERFLOW)?; - Ok(()) - } - _ => Err(ERRNO_BADF), - }) - } else { - *time = Timestamp::from(0u64); - ERRNO_SUCCESS + match id { + CLOCKID_MONOTONIC => { + *time = monotonic_clock::now(); + ERRNO_SUCCESS + } + CLOCKID_REALTIME => { + let res = wall_clock::now(); + *time = match Timestamp::from(res.seconds) + .checked_mul(1_000_000_000) + .and_then(|ns| ns.checked_add(res.nanoseconds.into())) + { + Some(ns) => ns, + None => return ERRNO_OVERFLOW, + }; + ERRNO_SUCCESS + } + _ => ERRNO_BADF, } } @@ -417,25 +415,23 @@ pub unsafe extern "C" fn fd_advise( _ => return ERRNO_INVAL, }; State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_seekable_file(fd)?; - file.fd.advise(offset, len, advice)?; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_seekable_file(fd)?; + file.fd.advise(offset, len, advice)?; + Ok(()) }) } /// Force the allocation of space in a file. /// Note: This is similar to `posix_fallocate` in POSIX. #[no_mangle] -pub unsafe extern "C" fn fd_allocate(fd: Fd, offset: Filesize, len: Filesize) -> Errno { +pub unsafe extern "C" fn fd_allocate(fd: Fd, _offset: Filesize, _len: Filesize) -> Errno { State::with(|state| { - state.with_descriptors(|ds| { - // For not-files, fail with BADF - let file = ds.get_file(fd)?; - // For all files, fail with NOTSUP, because this call does not exist in preview 2. - Err(wasi::ERRNO_NOTSUP) - }) + let ds = state.descriptors(); + // For not-files, fail with BADF + ds.get_file(fd)?; + // For all files, fail with NOTSUP, because this call does not exist in preview 2. + Err(wasi::ERRNO_NOTSUP) }) } @@ -451,7 +447,7 @@ pub unsafe extern "C" fn fd_close(fd: Fd) -> Errno { drop(state.dirent_cache.stream.replace(None)); } - let _ = state.with_descriptors_mut(|ds: &mut Descriptors| ds.close(fd))?; + state.descriptors_mut().close(fd)?; Ok(()) }) } @@ -461,11 +457,10 @@ pub unsafe extern "C" fn fd_close(fd: Fd) -> Errno { #[no_mangle] pub unsafe extern "C" fn fd_datasync(fd: Fd) -> Errno { State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_file(fd)?; - file.fd.sync_data()?; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_file(fd)?; + file.fd.sync_data()?; + Ok(()) }) } @@ -474,125 +469,124 @@ pub unsafe extern "C" fn fd_datasync(fd: Fd) -> Errno { #[no_mangle] pub unsafe extern "C" fn fd_fdstat_get(fd: Fd, stat: *mut Fdstat) -> Errno { State::with(|state| { - state.with_descriptors(|ds| { - match ds.get(fd)? { - Descriptor::Streams(Streams { - type_: StreamType::File(file), - .. - }) => { - let flags = file.fd.get_flags()?; - let type_ = file.fd.get_type()?; - match type_ { - filesystem::DescriptorType::Directory => { - // Hard-coded set of rights expected by many userlands: - let fs_rights_base = wasi::RIGHTS_PATH_CREATE_DIRECTORY - | wasi::RIGHTS_PATH_CREATE_FILE - | wasi::RIGHTS_PATH_LINK_SOURCE - | wasi::RIGHTS_PATH_LINK_TARGET - | wasi::RIGHTS_PATH_OPEN - | wasi::RIGHTS_FD_READDIR - | wasi::RIGHTS_PATH_READLINK - | wasi::RIGHTS_PATH_RENAME_SOURCE - | wasi::RIGHTS_PATH_RENAME_TARGET - | wasi::RIGHTS_PATH_SYMLINK - | wasi::RIGHTS_PATH_REMOVE_DIRECTORY - | wasi::RIGHTS_PATH_UNLINK_FILE - | wasi::RIGHTS_PATH_FILESTAT_GET - | wasi::RIGHTS_PATH_FILESTAT_SET_TIMES - | wasi::RIGHTS_FD_FILESTAT_GET - | wasi::RIGHTS_FD_FILESTAT_SET_TIMES; - - let fs_rights_inheriting = fs_rights_base - | wasi::RIGHTS_FD_DATASYNC - | wasi::RIGHTS_FD_READ - | wasi::RIGHTS_FD_SEEK - | wasi::RIGHTS_FD_FDSTAT_SET_FLAGS - | wasi::RIGHTS_FD_SYNC - | wasi::RIGHTS_FD_TELL - | wasi::RIGHTS_FD_WRITE - | wasi::RIGHTS_FD_ADVISE - | wasi::RIGHTS_FD_ALLOCATE - | wasi::RIGHTS_FD_FILESTAT_GET - | wasi::RIGHTS_FD_FILESTAT_SET_SIZE - | wasi::RIGHTS_FD_FILESTAT_SET_TIMES - | wasi::RIGHTS_POLL_FD_READWRITE; - - stat.write(Fdstat { - fs_filetype: wasi::FILETYPE_DIRECTORY, - fs_flags: 0, - fs_rights_base, - fs_rights_inheriting, - }); - Ok(()) - } - _ => { - let fs_filetype = type_.into(); + let ds = state.descriptors(); + match ds.get(fd)? { + Descriptor::Streams(Streams { + type_: StreamType::File(file), + .. + }) => { + let flags = file.fd.get_flags()?; + let type_ = file.fd.get_type()?; + match type_ { + filesystem::DescriptorType::Directory => { + // Hard-coded set of rights expected by many userlands: + let fs_rights_base = wasi::RIGHTS_PATH_CREATE_DIRECTORY + | wasi::RIGHTS_PATH_CREATE_FILE + | wasi::RIGHTS_PATH_LINK_SOURCE + | wasi::RIGHTS_PATH_LINK_TARGET + | wasi::RIGHTS_PATH_OPEN + | wasi::RIGHTS_FD_READDIR + | wasi::RIGHTS_PATH_READLINK + | wasi::RIGHTS_PATH_RENAME_SOURCE + | wasi::RIGHTS_PATH_RENAME_TARGET + | wasi::RIGHTS_PATH_SYMLINK + | wasi::RIGHTS_PATH_REMOVE_DIRECTORY + | wasi::RIGHTS_PATH_UNLINK_FILE + | wasi::RIGHTS_PATH_FILESTAT_GET + | wasi::RIGHTS_PATH_FILESTAT_SET_TIMES + | wasi::RIGHTS_FD_FILESTAT_GET + | wasi::RIGHTS_FD_FILESTAT_SET_TIMES; + + let fs_rights_inheriting = fs_rights_base + | wasi::RIGHTS_FD_DATASYNC + | wasi::RIGHTS_FD_READ + | wasi::RIGHTS_FD_SEEK + | wasi::RIGHTS_FD_FDSTAT_SET_FLAGS + | wasi::RIGHTS_FD_SYNC + | wasi::RIGHTS_FD_TELL + | wasi::RIGHTS_FD_WRITE + | wasi::RIGHTS_FD_ADVISE + | wasi::RIGHTS_FD_ALLOCATE + | wasi::RIGHTS_FD_FILESTAT_GET + | wasi::RIGHTS_FD_FILESTAT_SET_SIZE + | wasi::RIGHTS_FD_FILESTAT_SET_TIMES + | wasi::RIGHTS_POLL_FD_READWRITE; + + stat.write(Fdstat { + fs_filetype: wasi::FILETYPE_DIRECTORY, + fs_flags: 0, + fs_rights_base, + fs_rights_inheriting, + }); + Ok(()) + } + _ => { + let fs_filetype = type_.into(); - let mut fs_flags = 0; - let mut fs_rights_base = !0; - if !flags.contains(filesystem::DescriptorFlags::READ) { - fs_rights_base &= !RIGHTS_FD_READ; - } - if !flags.contains(filesystem::DescriptorFlags::WRITE) { - fs_rights_base &= !RIGHTS_FD_WRITE; - } - if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) { - fs_flags |= FDFLAGS_DSYNC; - } - if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) { - fs_flags |= FDFLAGS_RSYNC; - } - if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) { - fs_flags |= FDFLAGS_SYNC; - } - if file.append { - fs_flags |= FDFLAGS_APPEND; - } - if matches!(file.blocking_mode, BlockingMode::NonBlocking) { - fs_flags |= FDFLAGS_NONBLOCK; - } - let fs_rights_inheriting = fs_rights_base; - - stat.write(Fdstat { - fs_filetype, - fs_flags, - fs_rights_base, - fs_rights_inheriting, - }); - Ok(()) + let mut fs_flags = 0; + let mut fs_rights_base = !0; + if !flags.contains(filesystem::DescriptorFlags::READ) { + fs_rights_base &= !RIGHTS_FD_READ; + } + if !flags.contains(filesystem::DescriptorFlags::WRITE) { + fs_rights_base &= !RIGHTS_FD_WRITE; + } + if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) { + fs_flags |= FDFLAGS_DSYNC; + } + if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) { + fs_flags |= FDFLAGS_RSYNC; } + if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) { + fs_flags |= FDFLAGS_SYNC; + } + if file.append { + fs_flags |= FDFLAGS_APPEND; + } + if matches!(file.blocking_mode, BlockingMode::NonBlocking) { + fs_flags |= FDFLAGS_NONBLOCK; + } + let fs_rights_inheriting = fs_rights_base; + + stat.write(Fdstat { + fs_filetype, + fs_flags, + fs_rights_base, + fs_rights_inheriting, + }); + Ok(()) } } - Descriptor::Streams(Streams { - input, - output, - type_: StreamType::Stdio(isatty), - }) => { - let fs_flags = 0; - let mut fs_rights_base = 0; - if (*input.get()).is_some() { - fs_rights_base |= RIGHTS_FD_READ; - } - if (*output.get()).is_some() { - fs_rights_base |= RIGHTS_FD_WRITE; - } - let fs_rights_inheriting = fs_rights_base; - stat.write(Fdstat { - fs_filetype: isatty.filetype(), - fs_flags, - fs_rights_base, - fs_rights_inheriting, - }); - Ok(()) + } + Descriptor::Streams(Streams { + input, + output, + type_: StreamType::Stdio(isatty), + }) => { + let fs_flags = 0; + let mut fs_rights_base = 0; + if input.get().is_some() { + fs_rights_base |= RIGHTS_FD_READ; } - Descriptor::Closed(_) => Err(ERRNO_BADF), - Descriptor::Streams(Streams { - input: _, - output: _, - type_: StreamType::Socket(_), - }) => unreachable!(), + if output.get().is_some() { + fs_rights_base |= RIGHTS_FD_WRITE; + } + let fs_rights_inheriting = fs_rights_base; + stat.write(Fdstat { + fs_filetype: isatty.filetype(), + fs_flags, + fs_rights_base, + fs_rights_inheriting, + }); + Ok(()) } - }) + Descriptor::Closed(_) => Err(ERRNO_BADF), + Descriptor::Streams(Streams { + input: _, + output: _, + type_: StreamType::Socket(_), + }) => unreachable!(), + } }) } @@ -606,22 +600,21 @@ pub unsafe extern "C" fn fd_fdstat_set_flags(fd: Fd, flags: Fdflags) -> Errno { } State::with(|state| { - state.with_descriptors_mut(|ds: &mut Descriptors| { - let file = match ds.get_mut(fd)? { - Descriptor::Streams(Streams { - type_: StreamType::File(file), - .. - }) if !file.is_dir() => file, - _ => Err(wasi::ERRNO_BADF)?, - }; - file.append = flags & FDFLAGS_APPEND == FDFLAGS_APPEND; - file.blocking_mode = if flags & FDFLAGS_NONBLOCK == FDFLAGS_NONBLOCK { - BlockingMode::NonBlocking - } else { - BlockingMode::Blocking - }; - Ok(()) - }) + let mut ds = state.descriptors_mut(); + let file = match ds.get_mut(fd)? { + Descriptor::Streams(Streams { + type_: StreamType::File(file), + .. + }) if !file.is_dir() => file, + _ => Err(wasi::ERRNO_BADF)?, + }; + file.append = flags & FDFLAGS_APPEND == FDFLAGS_APPEND; + file.blocking_mode = if flags & FDFLAGS_NONBLOCK == FDFLAGS_NONBLOCK { + BlockingMode::NonBlocking + } else { + BlockingMode::Blocking + }; + Ok(()) }) } @@ -629,14 +622,15 @@ pub unsafe extern "C" fn fd_fdstat_set_flags(fd: Fd, flags: Fdflags) -> Errno { #[no_mangle] pub unsafe extern "C" fn fd_fdstat_set_rights( fd: Fd, - fs_rights_base: Rights, - fs_rights_inheriting: Rights, + _fs_rights_base: Rights, + _fs_rights_inheriting: Rights, ) -> Errno { State::with(|state| { - state.with_descriptors(|ds| match ds.get(fd)? { + let ds = state.descriptors(); + match ds.get(fd)? { Descriptor::Streams(..) => Ok(()), Descriptor::Closed(..) => Err(wasi::ERRNO_BADF), - }) + } }) } @@ -644,47 +638,46 @@ pub unsafe extern "C" fn fd_fdstat_set_rights( #[no_mangle] pub unsafe extern "C" fn fd_filestat_get(fd: Fd, buf: *mut Filestat) -> Errno { State::with(|state| { - state.with_descriptors(|ds| { - match ds.get(fd)? { - Descriptor::Streams(Streams { - type_: StreamType::File(file), - .. - }) => { - let stat = file.fd.stat()?; - let metadata_hash = file.fd.metadata_hash()?; - let filetype = stat.type_.into(); - *buf = Filestat { - dev: 1, - ino: metadata_hash.lower, - filetype, - nlink: stat.link_count, - size: stat.size, - atim: datetime_to_timestamp(stat.data_access_timestamp), - mtim: datetime_to_timestamp(stat.data_modification_timestamp), - ctim: datetime_to_timestamp(stat.status_change_timestamp), - }; - Ok(()) - } - // Stdio is all zero fields, except for filetype character device - Descriptor::Streams(Streams { - type_: StreamType::Stdio(isatty), - .. - }) => { - *buf = Filestat { - dev: 0, - ino: 0, - filetype: isatty.filetype(), - nlink: 0, - size: 0, - atim: 0, - mtim: 0, - ctim: 0, - }; - Ok(()) - } - _ => Err(wasi::ERRNO_BADF), + let ds = state.descriptors(); + match ds.get(fd)? { + Descriptor::Streams(Streams { + type_: StreamType::File(file), + .. + }) => { + let stat = file.fd.stat()?; + let metadata_hash = file.fd.metadata_hash()?; + let filetype = stat.type_.into(); + *buf = Filestat { + dev: 1, + ino: metadata_hash.lower, + filetype, + nlink: stat.link_count, + size: stat.size, + atim: datetime_to_timestamp(stat.data_access_timestamp), + mtim: datetime_to_timestamp(stat.data_modification_timestamp), + ctim: datetime_to_timestamp(stat.status_change_timestamp), + }; + Ok(()) } - }) + // Stdio is all zero fields, except for filetype character device + Descriptor::Streams(Streams { + type_: StreamType::Stdio(isatty), + .. + }) => { + *buf = Filestat { + dev: 0, + ino: 0, + filetype: isatty.filetype(), + nlink: 0, + size: 0, + atim: 0, + mtim: 0, + ctim: 0, + }; + Ok(()) + } + _ => Err(wasi::ERRNO_BADF), + } }) } @@ -693,11 +686,10 @@ pub unsafe extern "C" fn fd_filestat_get(fd: Fd, buf: *mut Filestat) -> Errno { #[no_mangle] pub unsafe extern "C" fn fd_filestat_set_size(fd: Fd, size: Filesize) -> Errno { State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_file(fd)?; - file.fd.set_size(size)?; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_file(fd)?; + file.fd.set_size(size)?; + Ok(()) }) } @@ -736,11 +728,10 @@ pub unsafe extern "C" fn fd_filestat_set_times( mtim, fst_flags & FSTFLAGS_MTIM_NOW == FSTFLAGS_MTIM_NOW, )?; - state.with_descriptors(|ds| { - let file = ds.get_file(fd)?; - file.fd.set_times(atim, mtim)?; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_file(fd)?; + file.fd.set_times(atim, mtim)?; + Ok(()) }) } @@ -768,23 +759,22 @@ pub unsafe extern "C" fn fd_pread( let ptr = (*iovs_ptr).buf; let len = (*iovs_ptr).buf_len; - state.with_descriptors(|ds| { - let file = ds.get_file(fd)?; - let (data, end) = state - .import_alloc - .with_buffer(ptr, len, || file.fd.read(len as u64, offset))?; - assert_eq!(data.as_ptr(), ptr); - assert!(data.len() <= len); - - let len = data.len(); - forget(data); - if !end && len == 0 { - Err(ERRNO_INTR) - } else { - *nread = len; - Ok(()) - } - }) + let ds = state.descriptors(); + let file = ds.get_file(fd)?; + let (data, end) = state + .import_alloc + .with_buffer(ptr, len, || file.fd.read(len as u64, offset))?; + assert_eq!(data.as_ptr(), ptr); + assert!(data.len() <= len); + + let len = data.len(); + forget(data); + if !end && len == 0 { + Err(ERRNO_INTR) + } else { + *nread = len; + Ok(()) + } }) } @@ -796,22 +786,21 @@ pub unsafe extern "C" fn fd_prestat_get(fd: Fd, buf: *mut Prestat) -> Errno { AllocationState::StackAllocated | AllocationState::StateAllocated ) { State::with(|state| { - state.with_descriptors(|ds| { - if let Some(preopen) = ds.get_preopen(fd) { - buf.write(Prestat { - tag: 0, - u: PrestatU { - dir: PrestatDir { - pr_name_len: preopen.path.len, - }, + let ds = state.descriptors(); + if let Some(preopen) = ds.get_preopen(fd) { + buf.write(Prestat { + tag: 0, + u: PrestatU { + dir: PrestatDir { + pr_name_len: preopen.path.len, }, - }); + }, + }); - Ok(()) - } else { - Err(ERRNO_BADF) - } - }) + Ok(()) + } else { + Err(ERRNO_BADF) + } }) } else { ERRNO_BADF @@ -822,18 +811,17 @@ pub unsafe extern "C" fn fd_prestat_get(fd: Fd, buf: *mut Prestat) -> Errno { #[no_mangle] pub unsafe extern "C" fn fd_prestat_dir_name(fd: Fd, path: *mut u8, path_max_len: Size) -> Errno { State::with(|state| { - state.with_descriptors(|ds| { - if let Some(preopen) = ds.get_preopen(fd) { - if preopen.path.len > path_max_len as usize { - Err(ERRNO_NAMETOOLONG) - } else { - ptr::copy_nonoverlapping(preopen.path.ptr, path, preopen.path.len); - Ok(()) - } + let ds = state.descriptors(); + if let Some(preopen) = ds.get_preopen(fd) { + if preopen.path.len > path_max_len as usize { + Err(ERRNO_NAMETOOLONG) } else { - Err(ERRNO_NOTDIR) + ptr::copy_nonoverlapping(preopen.path.ptr, path, preopen.path.len); + Ok(()) } - }) + } else { + Err(ERRNO_NOTDIR) + } }) } @@ -861,12 +849,11 @@ pub unsafe extern "C" fn fd_pwrite( let len = (*iovs_ptr).buf_len; State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_seekable_file(fd)?; - let bytes = file.fd.write(slice::from_raw_parts(ptr, len), offset)?; - *nwritten = bytes as usize; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_seekable_file(fd)?; + let bytes = file.fd.write(slice::from_raw_parts(ptr, len), offset)?; + *nwritten = bytes as usize; + Ok(()) }) } @@ -893,43 +880,42 @@ pub unsafe extern "C" fn fd_read( let len = (*iovs_ptr).buf_len; State::with(|state| { - state.with_descriptors(|ds| { - match ds.get(fd)? { - Descriptor::Streams(streams) => { - let blocking_mode = if let StreamType::File(file) = &streams.type_ { - file.blocking_mode - } else { - BlockingMode::Blocking - }; + let ds = state.descriptors(); + match ds.get(fd)? { + Descriptor::Streams(streams) => { + let blocking_mode = if let StreamType::File(file) = &streams.type_ { + file.blocking_mode + } else { + BlockingMode::Blocking + }; - let read_len = u64::try_from(len).trapping_unwrap(); - let wasi_stream = streams.get_read_stream()?; - let (data, stream_stat) = state - .import_alloc - .with_buffer(ptr, len, || blocking_mode.read(wasi_stream, read_len)) - .map_err(|_| ERRNO_IO)?; + let read_len = u64::try_from(len).trapping_unwrap(); + let wasi_stream = streams.get_read_stream()?; + let (data, stream_stat) = state + .import_alloc + .with_buffer(ptr, len, || blocking_mode.read(wasi_stream, read_len)) + .map_err(|_| ERRNO_IO)?; - assert_eq!(data.as_ptr(), ptr); - assert!(data.len() <= len); + assert_eq!(data.as_ptr(), ptr); + assert!(data.len() <= len); - // If this is a file, keep the current-position pointer up to date. - if let StreamType::File(file) = &streams.type_ { - file.position - .set(file.position.get() + data.len() as filesystem::Filesize); - } + // If this is a file, keep the current-position pointer up to date. + if let StreamType::File(file) = &streams.type_ { + file.position + .set(file.position.get() + data.len() as filesystem::Filesize); + } - let len = data.len(); - forget(data); - if stream_stat == crate::streams::StreamStatus::Open && len == 0 { - Err(ERRNO_INTR) - } else { - *nread = len; - Ok(()) - } + let len = data.len(); + forget(data); + if stream_stat == crate::streams::StreamStatus::Open && len == 0 { + Err(ERRNO_INTR) + } else { + *nread = len; + Ok(()) } - Descriptor::Closed(_) => Err(ERRNO_BADF), } - }) + Descriptor::Closed(_) => Err(ERRNO_BADF), + } }) } @@ -973,113 +959,108 @@ pub unsafe extern "C" fn fd_readdir( // Compute the inode of `.` so that the iterator can produce an entry // for it. - state.with_descriptors(|ds| { - let dir = ds.get_dir(fd)?; - - let mut iter; - match stream { - // All our checks passed and a dirent cache was available with a - // prior stream. Construct an iterator which will yield its first - // entry from cache and is additionally resuming at the `cookie` - // specified. - Some(stream) => { - iter = DirectoryEntryIterator { - stream, - state, - cookie, - use_cache: true, - dir_descriptor: &dir.fd, - } + let ds = state.descriptors(); + let dir = ds.get_dir(fd)?; + + let mut iter; + match stream { + // All our checks passed and a dirent cache was available with a + // prior stream. Construct an iterator which will yield its first + // entry from cache and is additionally resuming at the `cookie` + // specified. + Some(stream) => { + iter = DirectoryEntryIterator { + stream, + state, + cookie, + use_cache: true, + dir_descriptor: &dir.fd, } + } - // Either a dirent stream wasn't previously available, a different - // cookie was requested, or a brand new directory is now being read. - // In these situations fall back to resuming reading the directory - // from scratch, and the `cookie` value indicates how many items - // need skipping. - None => { - iter = DirectoryEntryIterator { - state, - cookie: wasi::DIRCOOKIE_START, - use_cache: false, - stream: DirectoryEntryStream(dir.fd.read_directory()?), - dir_descriptor: &dir.fd, - }; + // Either a dirent stream wasn't previously available, a different + // cookie was requested, or a brand new directory is now being read. + // In these situations fall back to resuming reading the directory + // from scratch, and the `cookie` value indicates how many items + // need skipping. + None => { + iter = DirectoryEntryIterator { + state, + cookie: wasi::DIRCOOKIE_START, + use_cache: false, + stream: DirectoryEntryStream(dir.fd.read_directory()?), + dir_descriptor: &dir.fd, + }; - // Skip to the entry that is requested by the `cookie` - // parameter. - for _ in wasi::DIRCOOKIE_START..cookie { - match iter.next() { - Some(Ok(_)) => {} - Some(Err(e)) => return Err(e), - None => return Ok(()), - } + // Skip to the entry that is requested by the `cookie` + // parameter. + for _ in wasi::DIRCOOKIE_START..cookie { + match iter.next() { + Some(Ok(_)) => {} + Some(Err(e)) => return Err(e), + None => return Ok(()), } } - }; + } + }; - while buf.len() > 0 { - let (dirent, name) = match iter.next() { - Some(Ok(pair)) => pair, - Some(Err(e)) => return Err(e), - None => break, - }; + while buf.len() > 0 { + let (dirent, name) = match iter.next() { + Some(Ok(pair)) => pair, + Some(Err(e)) => return Err(e), + None => break, + }; - // Copy a `dirent` describing this entry into the destination `buf`, - // truncating it if it doesn't fit entirely. - let bytes = slice::from_raw_parts( - (&dirent as *const wasi::Dirent).cast::(), - size_of::(), - ); - let dirent_bytes_to_copy = buf.len().min(bytes.len()); - buf[..dirent_bytes_to_copy].copy_from_slice(&bytes[..dirent_bytes_to_copy]); - buf = &mut buf[dirent_bytes_to_copy..]; - - // Copy the name bytes into the output `buf`, truncating it if it - // doesn't fit. - // - // Note that this might be a 0-byte copy if the `dirent` was - // truncated or fit entirely into the destination. - let name_bytes_to_copy = buf.len().min(name.len()); - ptr::copy_nonoverlapping( - name.as_ptr().cast(), - buf.as_mut_ptr(), - name_bytes_to_copy, + // Copy a `dirent` describing this entry into the destination `buf`, + // truncating it if it doesn't fit entirely. + let bytes = slice::from_raw_parts( + (&dirent as *const wasi::Dirent).cast::(), + size_of::(), + ); + let dirent_bytes_to_copy = buf.len().min(bytes.len()); + buf[..dirent_bytes_to_copy].copy_from_slice(&bytes[..dirent_bytes_to_copy]); + buf = &mut buf[dirent_bytes_to_copy..]; + + // Copy the name bytes into the output `buf`, truncating it if it + // doesn't fit. + // + // Note that this might be a 0-byte copy if the `dirent` was + // truncated or fit entirely into the destination. + let name_bytes_to_copy = buf.len().min(name.len()); + ptr::copy_nonoverlapping(name.as_ptr().cast(), buf.as_mut_ptr(), name_bytes_to_copy); + + buf = &mut buf[name_bytes_to_copy..]; + + // If the buffer is empty then that means the value may be + // truncated, so save the state of the iterator in our dirent cache + // and return. + // + // Note that `cookie - 1` is stored here since `iter.cookie` stores + // the address of the next item, and we're rewinding one item since + // the current item is truncated and will want to resume from that + // in the future. + // + // Additionally note that this caching step is skipped if the name + // to store doesn't actually fit in the dirent cache's path storage. + // In that case there's not much we can do and let the next call to + // `fd_readdir` start from scratch. + if buf.len() == 0 && name.len() <= DIRENT_CACHE { + let DirectoryEntryIterator { stream, cookie, .. } = iter; + state.dirent_cache.stream.set(Some(stream)); + state.dirent_cache.for_fd.set(fd); + state.dirent_cache.cookie.set(cookie - 1); + state.dirent_cache.cached_dirent.set(dirent); + ptr::copy( + name.as_ptr().cast::(), + (*state.dirent_cache.path_data.get()).as_mut_ptr() as *mut u8, + name.len(), ); - - buf = &mut buf[name_bytes_to_copy..]; - - // If the buffer is empty then that means the value may be - // truncated, so save the state of the iterator in our dirent cache - // and return. - // - // Note that `cookie - 1` is stored here since `iter.cookie` stores - // the address of the next item, and we're rewinding one item since - // the current item is truncated and will want to resume from that - // in the future. - // - // Additionally note that this caching step is skipped if the name - // to store doesn't actually fit in the dirent cache's path storage. - // In that case there's not much we can do and let the next call to - // `fd_readdir` start from scratch. - if buf.len() == 0 && name.len() <= DIRENT_CACHE { - let DirectoryEntryIterator { stream, cookie, .. } = iter; - state.dirent_cache.stream.set(Some(stream)); - state.dirent_cache.for_fd.set(fd); - state.dirent_cache.cookie.set(cookie - 1); - state.dirent_cache.cached_dirent.set(dirent); - ptr::copy( - name.as_ptr().cast::(), - (*state.dirent_cache.path_data.get()).as_mut_ptr() as *mut u8, - name.len(), - ); - break; - } + break; } + } - *bufused = buf_len - buf.len(); - Ok(()) - }) + *bufused = buf_len - buf.len(); + Ok(()) }); struct DirectoryEntryIterator<'a> { @@ -1184,7 +1165,7 @@ pub unsafe extern "C" fn fd_readdir( /// would disappear if `dup2()` were to be removed entirely. #[no_mangle] pub unsafe extern "C" fn fd_renumber(fd: Fd, to: Fd) -> Errno { - State::with(|state| state.with_descriptors_mut(|ds| ds.renumber(fd, to))) + State::with(|state| state.descriptors_mut().renumber(fd, to)) } /// Move the offset of a file descriptor. @@ -1197,37 +1178,36 @@ pub unsafe extern "C" fn fd_seek( newoffset: *mut Filesize, ) -> Errno { State::with(|state| { - state.with_descriptors(|ds| { - let stream = ds.get_seekable_stream(fd)?; - - // Seeking only works on files. - if let StreamType::File(file) = &stream.type_ { - if let filesystem::DescriptorType::Directory = file.descriptor_type { - // This isn't really the "right" errno, but it is consistient with wasmtime's - // preview 1 tests. - return Err(ERRNO_BADF); - } - let from = match whence { - WHENCE_SET if offset >= 0 => offset, - WHENCE_CUR => match (file.position.get() as i64).checked_add(offset) { - Some(pos) if pos >= 0 => pos, - _ => return Err(ERRNO_INVAL), - }, - WHENCE_END => match (file.fd.stat()?.size as i64).checked_add(offset) { - Some(pos) if pos >= 0 => pos, - _ => return Err(ERRNO_INVAL), - }, - _ => return Err(ERRNO_INVAL), - }; - *stream.input.get() = None; - *stream.output.get() = None; - file.position.set(from as filesystem::Filesize); - *newoffset = from as filesystem::Filesize; - Ok(()) - } else { - Err(ERRNO_SPIPE) + let mut ds = state.descriptors_mut(); + let stream = ds.get_seekable_stream_mut(fd)?; + + // Seeking only works on files. + if let StreamType::File(file) = &mut stream.type_ { + if let filesystem::DescriptorType::Directory = file.descriptor_type { + // This isn't really the "right" errno, but it is consistient with wasmtime's + // preview 1 tests. + return Err(ERRNO_BADF); } - }) + let from = match whence { + WHENCE_SET if offset >= 0 => offset, + WHENCE_CUR => match (file.position.get() as i64).checked_add(offset) { + Some(pos) if pos >= 0 => pos, + _ => return Err(ERRNO_INVAL), + }, + WHENCE_END => match (file.fd.stat()?.size as i64).checked_add(offset) { + Some(pos) if pos >= 0 => pos, + _ => return Err(ERRNO_INVAL), + }, + _ => return Err(ERRNO_INVAL), + }; + drop(stream.input.take()); + drop(stream.output.take()); + file.position.set(from as filesystem::Filesize); + *newoffset = from as filesystem::Filesize; + Ok(()) + } else { + Err(ERRNO_SPIPE) + } }) } @@ -1236,11 +1216,10 @@ pub unsafe extern "C" fn fd_seek( #[no_mangle] pub unsafe extern "C" fn fd_sync(fd: Fd) -> Errno { State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_file(fd)?; - file.fd.sync()?; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_file(fd)?; + file.fd.sync()?; + Ok(()) }) } @@ -1249,11 +1228,10 @@ pub unsafe extern "C" fn fd_sync(fd: Fd) -> Errno { #[no_mangle] pub unsafe extern "C" fn fd_tell(fd: Fd, offset: *mut Filesize) -> Errno { State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_seekable_file(fd)?; - *offset = file.position.get() as Filesize; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_seekable_file(fd)?; + *offset = file.position.get() as Filesize; + Ok(()) }) } @@ -1285,35 +1263,34 @@ pub unsafe extern "C" fn fd_write( let bytes = slice::from_raw_parts(ptr, len); State::with(|state| { - state.with_descriptors(|ds| { - match ds.get(fd)? { - Descriptor::Streams(streams) => { - let wasi_stream = streams.get_write_stream()?; - - let nbytes = if let StreamType::File(file) = &streams.type_ { - file.blocking_mode.write(wasi_stream, bytes)? - } else { - // Use blocking writes on non-file streams (stdout, stderr, as sockets - // aren't currently used). - BlockingMode::Blocking.write(wasi_stream, bytes)? - }; - - // If this is a file, keep the current-position pointer up to date. - if let StreamType::File(file) = &streams.type_ { - // But don't update if we're in append mode. Strictly speaking, - // we should set the position to the new end of the file, but - // we don't have an API to do that atomically. - if !file.append { - file.position.set(file.position.get() + nbytes as u64); - } - } + let ds = state.descriptors(); + match ds.get(fd)? { + Descriptor::Streams(streams) => { + let wasi_stream = streams.get_write_stream()?; - *nwritten = nbytes; - Ok(()) + let nbytes = if let StreamType::File(file) = &streams.type_ { + file.blocking_mode.write(wasi_stream, bytes)? + } else { + // Use blocking writes on non-file streams (stdout, stderr, as sockets + // aren't currently used). + BlockingMode::Blocking.write(wasi_stream, bytes)? + }; + + // If this is a file, keep the current-position pointer up to date. + if let StreamType::File(file) = &streams.type_ { + // But don't update if we're in append mode. Strictly speaking, + // we should set the position to the new end of the file, but + // we don't have an API to do that atomically. + if !file.append { + file.position.set(file.position.get() + nbytes as u64); + } } - Descriptor::Closed(_) => Err(ERRNO_BADF), + + *nwritten = nbytes; + Ok(()) } - }) + Descriptor::Closed(_) => Err(ERRNO_BADF), + } }) } else { *nwritten = 0; @@ -1332,11 +1309,10 @@ pub unsafe extern "C" fn path_create_directory( let path = slice::from_raw_parts(path_ptr, path_len); State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_dir(fd)?; - file.fd.create_directory_at(path)?; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_dir(fd)?; + file.fd.create_directory_at(path)?; + Ok(()) }) } @@ -1354,23 +1330,22 @@ pub unsafe extern "C" fn path_filestat_get( let at_flags = at_flags_from_lookupflags(flags); State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_dir(fd)?; - let stat = file.fd.stat_at(at_flags, path)?; - let metadata_hash = file.fd.metadata_hash_at(at_flags, path)?; - let filetype = stat.type_.into(); - *buf = Filestat { - dev: 1, - ino: metadata_hash.lower, - filetype, - nlink: stat.link_count, - size: stat.size, - atim: datetime_to_timestamp(stat.data_access_timestamp), - mtim: datetime_to_timestamp(stat.data_modification_timestamp), - ctim: datetime_to_timestamp(stat.status_change_timestamp), - }; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_dir(fd)?; + let stat = file.fd.stat_at(at_flags, path)?; + let metadata_hash = file.fd.metadata_hash_at(at_flags, path)?; + let filetype = stat.type_.into(); + *buf = Filestat { + dev: 1, + ino: metadata_hash.lower, + filetype, + nlink: stat.link_count, + size: stat.size, + atim: datetime_to_timestamp(stat.data_access_timestamp), + mtim: datetime_to_timestamp(stat.data_modification_timestamp), + ctim: datetime_to_timestamp(stat.status_change_timestamp), + }; + Ok(()) }) } @@ -1401,11 +1376,10 @@ pub unsafe extern "C" fn path_filestat_set_times( fst_flags & FSTFLAGS_MTIM_NOW == FSTFLAGS_MTIM_NOW, )?; - state.with_descriptors(|ds| { - let file = ds.get_dir(fd)?; - file.fd.set_times_at(at_flags, path, atim, mtim)?; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_dir(fd)?; + file.fd.set_times_at(at_flags, path, atim, mtim)?; + Ok(()) }) } @@ -1426,12 +1400,11 @@ pub unsafe extern "C" fn path_link( let at_flags = at_flags_from_lookupflags(old_flags); State::with(|state| { - state.with_descriptors(|ds| { - let old = &ds.get_dir(old_fd)?.fd; - let new = &ds.get_dir(new_fd)?.fd; - old.link_at(at_flags, old_path, new, new_path)?; - Ok(()) - }) + let ds = state.descriptors(); + let old = &ds.get_dir(old_fd)?.fd; + let new = &ds.get_dir(new_fd)?.fd; + old.link_at(at_flags, old_path, new, new_path)?; + Ok(()) }) } @@ -1464,30 +1437,31 @@ pub unsafe extern "C" fn path_open( let append = fdflags & wasi::FDFLAGS_APPEND == wasi::FDFLAGS_APPEND; State::with(|state| { - state.with_descriptors_mut(|ds: &mut Descriptors| { - let file = ds.get_dir(fd)?; - let result = file.fd.open_at(at_flags, path, o_flags, flags, mode)?; - let descriptor_type = result.get_type()?; - let desc = Descriptor::Streams(Streams { - input: UnsafeCell::new(None), - output: UnsafeCell::new(None), - type_: StreamType::File(File { - fd: result, - descriptor_type, - position: Cell::new(0), - append, - blocking_mode: if fdflags & wasi::FDFLAGS_NONBLOCK == 0 { - BlockingMode::Blocking - } else { - BlockingMode::NonBlocking - }, - }), - }); + let result = state + .descriptors() + .get_dir(fd)? + .fd + .open_at(at_flags, path, o_flags, flags, mode)?; + let descriptor_type = result.get_type()?; + let desc = Descriptor::Streams(Streams { + input: OnceCell::new(), + output: OnceCell::new(), + type_: StreamType::File(File { + fd: result, + descriptor_type, + position: Cell::new(0), + append, + blocking_mode: if fdflags & wasi::FDFLAGS_NONBLOCK == 0 { + BlockingMode::Blocking + } else { + BlockingMode::NonBlocking + }, + }), + }); - let fd = ds.open(desc)?; - *opened_fd = fd; - Ok(()) - }) + let fd = state.descriptors_mut().open(desc)?; + *opened_fd = fd; + Ok(()) }) } @@ -1510,36 +1484,35 @@ pub unsafe extern "C" fn path_readlink( // so instead we handle this case specially. let use_state_buf = buf_len < PATH_MAX; - state.with_descriptors(|ds| { - let file = ds.get_dir(fd)?; - let path = if use_state_buf { - state - .import_alloc - .with_buffer(state.path_buf.get().cast(), PATH_MAX, || { - file.fd.readlink_at(path) - })? - } else { - state - .import_alloc - .with_buffer(buf, buf_len, || file.fd.readlink_at(path))? - }; + let ds = state.descriptors(); + let file = ds.get_dir(fd)?; + let path = if use_state_buf { + state + .import_alloc + .with_buffer(state.path_buf.get().cast(), PATH_MAX, || { + file.fd.readlink_at(path) + })? + } else { + state + .import_alloc + .with_buffer(buf, buf_len, || file.fd.readlink_at(path))? + }; - if use_state_buf { - // Preview1 follows POSIX in truncating the returned path if it - // doesn't fit. - let len = min(path.len(), buf_len); - ptr::copy_nonoverlapping(path.as_ptr().cast(), buf, len); - *bufused = len; - } else { - *bufused = path.len(); - } + if use_state_buf { + // Preview1 follows POSIX in truncating the returned path if it + // doesn't fit. + let len = min(path.len(), buf_len); + ptr::copy_nonoverlapping(path.as_ptr().cast(), buf, len); + *bufused = len; + } else { + *bufused = path.len(); + } - // The returned string's memory was allocated in `buf`, so don't separately - // free it. - forget(path); + // The returned string's memory was allocated in `buf`, so don't separately + // free it. + forget(path); - Ok(()) - }) + Ok(()) }) } @@ -1555,11 +1528,10 @@ pub unsafe extern "C" fn path_remove_directory( let path = slice::from_raw_parts(path_ptr, path_len); State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_dir(fd)?; - file.fd.remove_directory_at(path)?; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_dir(fd)?; + file.fd.remove_directory_at(path)?; + Ok(()) }) } @@ -1578,12 +1550,11 @@ pub unsafe extern "C" fn path_rename( let new_path = slice::from_raw_parts(new_path_ptr, new_path_len); State::with(|state| { - state.with_descriptors(|ds| { - let old = &ds.get_dir(old_fd)?.fd; - let new = &ds.get_dir(new_fd)?.fd; - old.rename_at(old_path, new, new_path)?; - Ok(()) - }) + let ds = state.descriptors(); + let old = &ds.get_dir(old_fd)?.fd; + let new = &ds.get_dir(new_fd)?.fd; + old.rename_at(old_path, new, new_path)?; + Ok(()) }) } @@ -1601,11 +1572,10 @@ pub unsafe extern "C" fn path_symlink( let new_path = slice::from_raw_parts(new_path_ptr, new_path_len); State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_dir(fd)?; - file.fd.symlink_at(old_path, new_path)?; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_dir(fd)?; + file.fd.symlink_at(old_path, new_path)?; + Ok(()) }) } @@ -1617,11 +1587,10 @@ pub unsafe extern "C" fn path_unlink_file(fd: Fd, path_ptr: *const u8, path_len: let path = slice::from_raw_parts(path_ptr, path_len); State::with(|state| { - state.with_descriptors(|ds| { - let file = ds.get_dir(fd)?; - file.fd.unlink_file_at(path)?; - Ok(()) - }) + let ds = state.descriptors(); + let file = ds.get_dir(fd)?; + file.fd.unlink_file_at(path)?; + Ok(()) }) } @@ -1776,21 +1745,22 @@ pub unsafe extern "C" fn poll_oneoff( } } - EVENTTYPE_FD_READ => state.with_descriptors(|ds| { - ds.get_read_stream(subscription.u.u.fd_read.file_descriptor) - .map(|stream| stream.subscribe()) - })?, + EVENTTYPE_FD_READ => state + .descriptors() + .get_read_stream(subscription.u.u.fd_read.file_descriptor) + .map(|stream| stream.subscribe())?, - EVENTTYPE_FD_WRITE => state.with_descriptors(|ds| { - ds.get_write_stream(subscription.u.u.fd_write.file_descriptor) - .map(|stream| stream.subscribe()) - })?, + EVENTTYPE_FD_WRITE => state + .descriptors() + .get_write_stream(subscription.u.u.fd_write.file_descriptor) + .map(|stream| stream.subscribe())?, _ => return Err(ERRNO_INVAL), }); } #[link(wasm_import_module = "wasi:io/poll")] + #[allow(improper_ctypes)] // FIXME(bytecodealliance/wit-bindgen#684) extern "C" { #[link_name = "poll-list"] fn poll_list_import(pollables: *const Pollable, len: usize, rval: *mut ReadyList); @@ -1836,84 +1806,82 @@ pub unsafe extern "C" fn poll_oneoff( EVENTTYPE_FD_READ => { type_ = wasi::EVENTTYPE_FD_READ; - state.with_descriptors(|ds| { - let desc = ds - .get(subscription.u.u.fd_read.file_descriptor) - .trapping_unwrap(); - match desc { - Descriptor::Streams(streams) => match &streams.type_ { - StreamType::File(file) => match file.fd.stat() { - Ok(stat) => { - let nbytes = stat.size.saturating_sub(file.position.get()); - ( - ERRNO_SUCCESS, - nbytes, - if nbytes == 0 { - EVENTRWFLAGS_FD_READWRITE_HANGUP - } else { - 0 - }, - ) - } - Err(e) => (e.into(), 1, 0), - }, - StreamType::Socket(connection) => { - unreachable!() // TODO - /* - match tcp::bytes_readable(*connection) { - Ok(result) => ( - ERRNO_SUCCESS, - result.0, - if result.1 { - EVENTRWFLAGS_FD_READWRITE_HANGUP - } else { - 0 - } - ) - Err(e) => { - (e.into(), 1, 0) - } - } - */ + let ds = state.descriptors(); + let desc = ds + .get(subscription.u.u.fd_read.file_descriptor) + .trapping_unwrap(); + match desc { + Descriptor::Streams(streams) => match &streams.type_ { + StreamType::File(file) => match file.fd.stat() { + Ok(stat) => { + let nbytes = stat.size.saturating_sub(file.position.get()); + ( + ERRNO_SUCCESS, + nbytes, + if nbytes == 0 { + EVENTRWFLAGS_FD_READWRITE_HANGUP + } else { + 0 + }, + ) } - StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0), + Err(e) => (e.into(), 1, 0), }, - _ => unreachable!(), - } - }) + StreamType::Socket(_connection) => { + unreachable!() // TODO + /* + match tcp::bytes_readable(*connection) { + Ok(result) => ( + ERRNO_SUCCESS, + result.0, + if result.1 { + EVENTRWFLAGS_FD_READWRITE_HANGUP + } else { + 0 + } + ) + Err(e) => { + (e.into(), 1, 0) + } + } + */ + } + StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0), + }, + _ => unreachable!(), + } } EVENTTYPE_FD_WRITE => { type_ = wasi::EVENTTYPE_FD_WRITE; - state.with_descriptors(|ds| { - let desc = ds - .get(subscription.u.u.fd_write.file_descriptor) - .trapping_unwrap(); - match desc { - Descriptor::Streams(streams) => match &streams.type_ { - StreamType::File(_) | StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0), - StreamType::Socket(connection) => { - unreachable!() // TODO - /* - match tcp::bytes_writable(connection) { - Ok(result) => ( - ERRNO_SUCCESS, - result.0, - if result.1 { - EVENTRWFLAGS_FD_READWRITE_HANGUP - } else { - 0 - } - ) - Err(e) => { - (e.into(), 0, 0) + let ds = state.descriptors(); + let desc = ds + .get(subscription.u.u.fd_write.file_descriptor) + .trapping_unwrap(); + match desc { + Descriptor::Streams(streams) => match &streams.type_ { + StreamType::File(_) | StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0), + StreamType::Socket(_connection) => { + unreachable!() // TODO + /* + match tcp::bytes_writable(connection) { + Ok(result) => ( + ERRNO_SUCCESS, + result.0, + if result.1 { + EVENTRWFLAGS_FD_READWRITE_HANGUP + } else { + 0 } + ) + Err(e) => { + (e.into(), 0, 0) } - */ - } - }, - _ => unreachable!(), - } - }) + } + */ + } + }, + _ => unreachable!(), + } } _ => unreachable!(), @@ -1948,7 +1916,7 @@ pub unsafe extern "C" fn proc_exit(rval: Exitcode) -> ! { /// Send a signal to the process of the calling thread. /// Note: This is similar to `raise` in POSIX. #[no_mangle] -pub unsafe extern "C" fn proc_raise(sig: Signal) -> Errno { +pub unsafe extern "C" fn proc_raise(_sig: Signal) -> Errno { unreachable!() } @@ -1994,7 +1962,7 @@ pub unsafe extern "C" fn random_get(buf: *mut u8, buf_len: Size) -> Errno { /// Accept a new incoming connection. /// Note: This is similar to `accept` in POSIX. #[no_mangle] -pub unsafe extern "C" fn sock_accept(fd: Fd, flags: Fdflags, connection: *mut Fd) -> Errno { +pub unsafe extern "C" fn sock_accept(_fd: Fd, _flags: Fdflags, _connection: *mut Fd) -> Errno { unreachable!() } @@ -2003,12 +1971,12 @@ pub unsafe extern "C" fn sock_accept(fd: Fd, flags: Fdflags, connection: *mut Fd /// the data into multiple buffers in the manner of `readv`. #[no_mangle] pub unsafe extern "C" fn sock_recv( - fd: Fd, - ri_data_ptr: *const Iovec, - ri_data_len: usize, - ri_flags: Riflags, - ro_datalen: *mut Size, - ro_flags: *mut Roflags, + _fd: Fd, + _ri_data_ptr: *const Iovec, + _ri_data_len: usize, + _ri_flags: Riflags, + _ro_datalen: *mut Size, + _ro_flags: *mut Roflags, ) -> Errno { unreachable!() } @@ -2018,11 +1986,11 @@ pub unsafe extern "C" fn sock_recv( /// the data from multiple buffers in the manner of `writev`. #[no_mangle] pub unsafe extern "C" fn sock_send( - fd: Fd, - si_data_ptr: *const Ciovec, - si_data_len: usize, - si_flags: Siflags, - so_datalen: *mut Size, + _fd: Fd, + _si_data_ptr: *const Ciovec, + _si_data_len: usize, + _si_flags: Siflags, + _so_datalen: *mut Size, ) -> Errno { unreachable!() } @@ -2030,7 +1998,7 @@ pub unsafe extern "C" fn sock_send( /// Shut down socket send and receive channels. /// Note: This is similar to `shutdown` in POSIX. #[no_mangle] -pub unsafe extern "C" fn sock_shutdown(fd: Fd, how: Sdflags) -> Errno { +pub unsafe extern "C" fn sock_shutdown(_fd: Fd, _how: Sdflags) -> Errno { unreachable!() } @@ -2278,13 +2246,7 @@ struct State { /// /// Do not use this member directly - use State::descriptors() to ensure /// lazy initialization happens. - descriptors: UnsafeCell>, - - /// Borrow state of `descriptors`. - /// - /// If it looks like we're kind re-implementing `RefCell`, it's because we - /// basically are; `RefCell` itself pulls in static initializers. - descriptors_borrowed: UnsafeCell, + descriptors: RefCell>, /// Auxiliary storage to handle the `path_readlink` function. path_buf: UnsafeCell>, @@ -2370,7 +2332,7 @@ const fn bump_arena_size() -> usize { start -= size_of::(); // Remove miscellaneous metadata also stored in state. - start -= 12 * size_of::(); + start -= 14 * size_of::(); // Everything else is the `command_data` allocation. start @@ -2381,7 +2343,7 @@ const fn bump_arena_size() -> usize { // below. #[cfg(target_arch = "wasm32")] const _: () = { - let _size_assert: [(); PAGE_SIZE] = [(); size_of::>()]; + let _size_assert: [(); PAGE_SIZE] = [(); size_of::()]; }; #[allow(unused)] @@ -2460,8 +2422,7 @@ impl State { magic1: MAGIC, magic2: MAGIC, import_alloc: ImportAlloc::new(), - descriptors: UnsafeCell::new(None), - descriptors_borrowed: UnsafeCell::new(false), + descriptors: RefCell::new(None), path_buf: UnsafeCell::new(MaybeUninit::uninit()), long_lived_arena: BumpArena::new(), args: Cell::new(None), @@ -2485,56 +2446,27 @@ impl State { } /// Accessor for the descriptors member that ensures it is properly initialized - fn with_descriptors T>(&self, fn_: F) -> T { - unsafe { - if core::mem::replace(&mut *self.descriptors_borrowed.get(), true) { - unreachable!(); // Don't borrow descriptors while they're already borrowed. - } - } - - let descriptors: &mut Option = unsafe { &mut *self.descriptors.get() }; - match descriptors { - None => { - *descriptors = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena)); - } - Some(_descriptors) => {} + fn descriptors<'a>(&'a self) -> impl Deref + 'a { + let mut d = self + .descriptors + .try_borrow_mut() + .unwrap_or_else(|_| unreachable!()); + if d.is_none() { + *d = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena)); } - let result = match descriptors { - Some(descriptors) => fn_(descriptors), - None => unreachable!(), - }; - - unsafe { - *self.descriptors_borrowed.get() = false; - } - - result + RefMut::map(d, |d| d.as_mut().unwrap_or_else(|| unreachable!())) } - fn with_descriptors_mut T>(&self, fn_: F) -> T { - unsafe { - if core::mem::replace(&mut *self.descriptors_borrowed.get(), true) { - unreachable!(); // Don't borrow descriptors while they're already borrowed. - } - } - - let descriptors: &mut Option = unsafe { &mut *self.descriptors.get() }; - match descriptors { - None => { - *descriptors = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena)); - } - Some(_descriptors) => {} + /// Mut accessor for the descriptors member that ensures it is properly initialized + fn descriptors_mut<'a>(&'a self) -> impl DerefMut + Deref + 'a { + let mut d = self + .descriptors + .try_borrow_mut() + .unwrap_or_else(|_| unreachable!()); + if d.is_none() { + *d = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena)); } - let result = match descriptors { - Some(descriptors) => fn_(descriptors), - None => unreachable!(), - }; - - unsafe { - *self.descriptors_borrowed.get() = false; - } - - result + RefMut::map(d, |d| d.as_mut().unwrap_or_else(|| unreachable!())) } fn get_environment(&self) -> &[StrTuple] { diff --git a/crates/wasi-preview1-component-adapter/src/macros.rs b/crates/wasi-preview1-component-adapter/src/macros.rs index c4eef95763c9..c1ec4a515f2a 100644 --- a/crates/wasi-preview1-component-adapter/src/macros.rs +++ b/crates/wasi-preview1-component-adapter/src/macros.rs @@ -4,12 +4,11 @@ //! literals. Replace the standard assert macros with simpler implementations. use crate::bindings::wasi::cli::stderr::get_stderr; -use crate::bindings::wasi::io::streams; #[allow(dead_code)] #[doc(hidden)] pub fn print(message: &[u8]) { - let _ = unsafe { get_stderr().blocking_write_and_flush(message) }; + let _ = get_stderr().blocking_write_and_flush(message); } /// A minimal `eprint` for debugging. From 270e92225da3acb5b0a583546b2d4598cb59ae2a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 29 Sep 2023 18:32:32 -0500 Subject: [PATCH 031/199] Remove `Table*Ext` traits for preview2 (#7118) * wasi: Add typed helpers to `Table` This will help cut down on the `Table*Ext` traits while retaining type safety. * Remove `TableNetworkExt` trait * Remove `TableTcpSocketExt` trait * Remove the `TableReaddirExt` trait * Remove `TableFsExt` trait This involed a fair bit of refactoring within the preview2-to-preview1 adapter to handle the new ownership of resources, but nothing major. * Remove `TableStreamExt` trait Additionally simplify some stream methods while I'm here. * Remove `TablePollableExt` trait * Fix tests * Fix some more tests * Use typed accessors for terminal-{input,output} * Remove dead code in `Table` --- crates/test-programs/tests/reactor.rs | 6 +- crates/wasi-http/src/types_impl.rs | 14 +- crates/wasi/src/preview2/filesystem.rs | 110 +++-- crates/wasi/src/preview2/host/clocks.rs | 9 +- crates/wasi/src/preview2/host/filesystem.rs | 385 +++++++----------- .../src/preview2/host/instance_network.rs | 8 +- crates/wasi/src/preview2/host/io.rs | 177 +++----- crates/wasi/src/preview2/host/network.rs | 3 +- crates/wasi/src/preview2/host/tcp.rs | 159 ++++---- .../src/preview2/host/tcp_create_socket.rs | 7 +- crates/wasi/src/preview2/mod.rs | 19 +- crates/wasi/src/preview2/network.rs | 29 +- crates/wasi/src/preview2/poll.rs | 74 +--- crates/wasi/src/preview2/preview1.rs | 280 ++++++------- crates/wasi/src/preview2/stdio.rs | 49 +-- crates/wasi/src/preview2/stream.rs | 202 +-------- crates/wasi/src/preview2/table.rs | 83 ++-- crates/wasi/src/preview2/tcp.rs | 69 +--- 18 files changed, 628 insertions(+), 1055 deletions(-) diff --git a/crates/test-programs/tests/reactor.rs b/crates/test-programs/tests/reactor.rs index a5cfed2beedd..4b20ff97ce0a 100644 --- a/crates/test-programs/tests/reactor.rs +++ b/crates/test-programs/tests/reactor.rs @@ -102,10 +102,8 @@ async fn reactor_tests() -> Result<()> { // Note, this works because of the add_to_linker invocations using the // `host` crate for `streams`, not because of `with` in the bindgen macro. let writepipe = preview2::pipe::MemoryOutputPipe::new(4096); - let table_ix = preview2::TableStreamExt::push_output_stream( - store.data_mut().table_mut(), - Box::new(writepipe.clone()), - )?; + let stream: preview2::OutputStream = Box::new(writepipe.clone()); + let table_ix = store.data_mut().table_mut().push_resource(stream)?; let r = reactor.call_write_strings_to(&mut store, table_ix).await?; assert_eq!(r, Ok(())); diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index a2abad080ebd..af91caea411d 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -17,9 +17,8 @@ use anyhow::Context; use std::any::Any; use wasmtime::component::Resource; use wasmtime_wasi::preview2::{ - bindings::io::poll::Pollable, bindings::io::streams::{InputStream, OutputStream}, - HostPollable, PollableFuture, TablePollableExt, TableStreamExt, + Pollable, PollableFuture, }; impl crate::bindings::http::types::Host for T { @@ -360,9 +359,10 @@ impl crate::bindings::http::types::Host for T { Box::pin(elem.downcast_mut::().unwrap().ready()) } + // FIXME: this should use `push_child_resource` let id = self .table() - .push_host_pollable(HostPollable::TableEntry { index, make_future })?; + .push_resource(Pollable::TableEntry { index, make_future })?; Ok(id) } @@ -489,7 +489,8 @@ impl crate::bindings::http::types::Host for T { ) } - let pollable = self.table().push_host_pollable(HostPollable::TableEntry { + // FIXME: this should use `push_child_resource` + let pollable = self.table().push_resource(Pollable::TableEntry { index: id, make_future, })?; @@ -504,7 +505,7 @@ impl crate::bindings::http::types::Host for T { let body = self.table().get_outgoing_body(id)?; if let Some(stream) = body.body_output_stream.take() { let dummy = Resource::::new_own(id); - let id = self.table().push_output_stream_child(stream, dummy)?; + let id = self.table().push_child_resource(stream, &dummy)?; Ok(Ok(id)) } else { Ok(Err(())) @@ -558,7 +559,8 @@ impl crate::bindings::http::types::HostIncomingBody for T { let body = self.table().get_incoming_body(&id)?; if let Some(stream) = body.stream.take() { - let stream = self.table().push_input_stream_child(Box::new(stream), id)?; + let stream = InputStream::Host(Box::new(stream)); + let stream = self.table().push_child_resource(stream, &id)?; return Ok(Ok(stream)); } diff --git a/crates/wasi/src/preview2/filesystem.rs b/crates/wasi/src/preview2/filesystem.rs index 3f3cfb1b4329..c360b874c801 100644 --- a/crates/wasi/src/preview2/filesystem.rs +++ b/crates/wasi/src/preview2/filesystem.rs @@ -1,13 +1,46 @@ -use crate::preview2::bindings::filesystem::types::Descriptor; +use crate::preview2::bindings::filesystem::types; use crate::preview2::{ AbortOnDropJoinHandle, HostOutputStream, OutputStreamError, StreamRuntimeError, StreamState, - Table, TableError, }; use anyhow::anyhow; use bytes::{Bytes, BytesMut}; use futures::future::{maybe_done, MaybeDone}; use std::sync::Arc; -use wasmtime::component::Resource; + +pub enum Descriptor { + File(File), + Dir(Dir), +} + +impl Descriptor { + pub fn file(&self) -> Result<&File, types::ErrorCode> { + match self { + Descriptor::File(f) => Ok(f), + Descriptor::Dir(_) => Err(types::ErrorCode::BadDescriptor), + } + } + + pub fn dir(&self) -> Result<&Dir, types::ErrorCode> { + match self { + Descriptor::Dir(d) => Ok(d), + Descriptor::File(_) => Err(types::ErrorCode::NotDirectory), + } + } + + pub fn is_file(&self) -> bool { + match self { + Descriptor::File(_) => true, + Descriptor::Dir(_) => false, + } + } + + pub fn is_dir(&self) -> bool { + match self { + Descriptor::File(_) => false, + Descriptor::Dir(_) => true, + } + } +} bitflags::bitflags! { #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -17,7 +50,7 @@ bitflags::bitflags! { } } -pub(crate) struct File { +pub struct File { /// Wrapped in an Arc because the same underlying file is used for /// implementing the stream types. Also needed for [`spawn_blocking`]. /// @@ -45,45 +78,6 @@ impl File { tokio::task::spawn_blocking(move || body(&f)).await.unwrap() } } -pub(crate) trait TableFsExt { - fn push_file(&mut self, file: File) -> Result, TableError>; - fn delete_file(&mut self, fd: Resource) -> Result; - fn is_file(&self, fd: &Resource) -> bool; - fn get_file(&self, fd: &Resource) -> Result<&File, TableError>; - - fn push_dir(&mut self, dir: Dir) -> Result, TableError>; - fn delete_dir(&mut self, fd: Resource) -> Result; - fn is_dir(&self, fd: &Resource) -> bool; - fn get_dir(&self, fd: &Resource) -> Result<&Dir, TableError>; -} - -impl TableFsExt for Table { - fn push_file(&mut self, file: File) -> Result, TableError> { - Ok(Resource::new_own(self.push(Box::new(file))?)) - } - fn delete_file(&mut self, fd: Resource) -> Result { - self.delete(fd.rep()) - } - fn is_file(&self, fd: &Resource) -> bool { - self.is::(fd.rep()) - } - fn get_file(&self, fd: &Resource) -> Result<&File, TableError> { - self.get(fd.rep()) - } - - fn push_dir(&mut self, dir: Dir) -> Result, TableError> { - Ok(Resource::new_own(self.push(Box::new(dir))?)) - } - fn delete_dir(&mut self, fd: Resource) -> Result { - self.delete(fd.rep()) - } - fn is_dir(&self, fd: &Resource) -> bool { - self.is::(fd.rep()) - } - fn get_dir(&self, fd: &Resource) -> Result<&Dir, TableError> { - self.get(fd.rep()) - } -} bitflags::bitflags! { #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -94,7 +88,7 @@ bitflags::bitflags! { } #[derive(Clone)] -pub(crate) struct Dir { +pub struct Dir { pub dir: Arc, pub perms: DirPerms, pub file_perms: FilePerms, @@ -121,7 +115,7 @@ impl Dir { } } -pub(crate) struct FileInputStream { +pub struct FileInputStream { file: Arc, position: u64, } @@ -278,3 +272,29 @@ impl HostOutputStream for FileOutputStream { } } } + +pub struct ReaddirIterator( + std::sync::Mutex< + Box> + Send + 'static>, + >, +); + +impl ReaddirIterator { + pub(crate) fn new( + i: impl Iterator> + Send + 'static, + ) -> Self { + ReaddirIterator(std::sync::Mutex::new(Box::new(i))) + } + pub(crate) fn next(&self) -> Result, types::Error> { + self.0.lock().unwrap().next().transpose() + } +} + +impl IntoIterator for ReaddirIterator { + type Item = Result; + type IntoIter = Box + Send>; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_inner().unwrap() + } +} diff --git a/crates/wasi/src/preview2/host/clocks.rs b/crates/wasi/src/preview2/host/clocks.rs index 3c9240af5608..15adedf5770d 100644 --- a/crates/wasi/src/preview2/host/clocks.rs +++ b/crates/wasi/src/preview2/host/clocks.rs @@ -4,9 +4,8 @@ use crate::preview2::bindings::{ clocks::monotonic_clock::{self, Instant}, clocks::timezone::{self, TimezoneDisplay}, clocks::wall_clock::{self, Datetime}, - io::poll::Pollable, }; -use crate::preview2::{HostPollable, TablePollableExt, WasiView}; +use crate::preview2::{Pollable, WasiView}; use cap_std::time::SystemTime; use wasmtime::component::Resource; @@ -60,9 +59,7 @@ impl monotonic_clock::Host for T { // Deadline is in the past, so pollable is always ready: Ok(self .table_mut() - .push_host_pollable(HostPollable::Closure(Box::new(|| { - Box::pin(async { Ok(()) }) - })))?) + .push_resource(Pollable::Closure(Box::new(|| Box::pin(async { Ok(()) }))))?) } else { let duration = if absolute { Duration::from_nanos(when - clock_now) @@ -79,7 +76,7 @@ impl monotonic_clock::Host for T { ); Ok(self .table_mut() - .push_host_pollable(HostPollable::Closure(Box::new(move || { + .push_resource(Pollable::Closure(Box::new(move || { Box::pin(async move { tracing::trace!( "mkf: deadline = {:?}, now = {:?}", diff --git a/crates/wasi/src/preview2/host/filesystem.rs b/crates/wasi/src/preview2/host/filesystem.rs index 374d4a455fa5..a9a57e46c489 100644 --- a/crates/wasi/src/preview2/host/filesystem.rs +++ b/crates/wasi/src/preview2/host/filesystem.rs @@ -1,10 +1,9 @@ use crate::preview2::bindings::clocks::wall_clock; -use crate::preview2::bindings::filesystem::types::{ - DirectoryEntryStream, HostDescriptor, HostDirectoryEntryStream, -}; +use crate::preview2::bindings::filesystem::types::{HostDescriptor, HostDirectoryEntryStream}; use crate::preview2::bindings::filesystem::{preopens, types}; -use crate::preview2::bindings::io::streams; -use crate::preview2::filesystem::{Dir, File, TableFsExt}; +use crate::preview2::bindings::io::streams::{InputStream, OutputStream}; +use crate::preview2::filesystem::{Descriptor, Dir, File, ReaddirIterator}; +use crate::preview2::filesystem::{FileInputStream, FileOutputStream}; use crate::preview2::{DirPerms, FilePerms, Table, TableError, WasiView}; use anyhow::Context; use wasmtime::component::Resource; @@ -27,7 +26,7 @@ impl preopens::Host for T { for (dir, name) in self.ctx().preopens.clone() { let fd = self .table_mut() - .push_dir(dir) + .push_resource(Descriptor::Dir(dir)) .with_context(|| format!("failed to push preopen {name}"))?; results.push((fd, name)); } @@ -59,7 +58,7 @@ impl HostDescriptor for T { Advice::NoReuse => A::NoReuse, }; - let f = self.table().get_file(&fd)?; + let f = self.table().get_resource(&fd)?.file()?; f.spawn_blocking(move |f| f.advise(offset, len, advice)) .await?; Ok(()) @@ -67,28 +66,28 @@ impl HostDescriptor for T { async fn sync_data(&mut self, fd: Resource) -> Result<(), types::Error> { let table = self.table(); - if table.is_file(&fd) { - let f = table.get_file(&fd)?; - match f.spawn_blocking(|f| f.sync_data()).await { - Ok(()) => Ok(()), - // On windows, `sync_data` uses `FileFlushBuffers` which fails with - // `ERROR_ACCESS_DENIED` if the file is not upen for writing. Ignore - // this error, for POSIX compatibility. - #[cfg(windows)] - Err(e) - if e.raw_os_error() - == Some(windows_sys::Win32::Foundation::ERROR_ACCESS_DENIED as _) => - { - Ok(()) + + match table.get_resource(&fd)? { + Descriptor::File(f) => { + match f.spawn_blocking(|f| f.sync_data()).await { + Ok(()) => Ok(()), + // On windows, `sync_data` uses `FileFlushBuffers` which fails with + // `ERROR_ACCESS_DENIED` if the file is not upen for writing. Ignore + // this error, for POSIX compatibility. + #[cfg(windows)] + Err(e) + if e.raw_os_error() + == Some(windows_sys::Win32::Foundation::ERROR_ACCESS_DENIED as _) => + { + Ok(()) + } + Err(e) => Err(e.into()), } - Err(e) => Err(e.into()), } - } else if table.is_dir(&fd) { - let d = table.get_dir(&fd)?; - d.spawn_blocking(|d| Ok(d.open(std::path::Component::CurDir)?.sync_data()?)) - .await - } else { - Err(ErrorCode::BadDescriptor.into()) + Descriptor::Dir(d) => { + d.spawn_blocking(|d| Ok(d.open(std::path::Component::CurDir)?.sync_data()?)) + .await + } } } @@ -114,30 +113,29 @@ impl HostDescriptor for T { } let table = self.table(); - if table.is_file(&fd) { - let f = table.get_file(&fd)?; - let flags = f.spawn_blocking(|f| f.get_fd_flags()).await?; - let mut flags = get_from_fdflags(flags); - if f.perms.contains(FilePerms::READ) { - flags |= DescriptorFlags::READ; - } - if f.perms.contains(FilePerms::WRITE) { - flags |= DescriptorFlags::WRITE; - } - Ok(flags) - } else if table.is_dir(&fd) { - let d = table.get_dir(&fd)?; - let flags = d.spawn_blocking(|d| d.get_fd_flags()).await?; - let mut flags = get_from_fdflags(flags); - if d.perms.contains(DirPerms::READ) { - flags |= DescriptorFlags::READ; + match table.get_resource(&fd)? { + Descriptor::File(f) => { + let flags = f.spawn_blocking(|f| f.get_fd_flags()).await?; + let mut flags = get_from_fdflags(flags); + if f.perms.contains(FilePerms::READ) { + flags |= DescriptorFlags::READ; + } + if f.perms.contains(FilePerms::WRITE) { + flags |= DescriptorFlags::WRITE; + } + Ok(flags) } - if d.perms.contains(DirPerms::MUTATE) { - flags |= DescriptorFlags::MUTATE_DIRECTORY; + Descriptor::Dir(d) => { + let flags = d.spawn_blocking(|d| d.get_fd_flags()).await?; + let mut flags = get_from_fdflags(flags); + if d.perms.contains(DirPerms::READ) { + flags |= DescriptorFlags::READ; + } + if d.perms.contains(DirPerms::MUTATE) { + flags |= DescriptorFlags::MUTATE_DIRECTORY; + } + Ok(flags) } - Ok(flags) - } else { - Err(ErrorCode::BadDescriptor.into()) } } @@ -147,14 +145,12 @@ impl HostDescriptor for T { ) -> Result { let table = self.table(); - if table.is_file(&fd) { - let f = table.get_file(&fd)?; - let meta = f.spawn_blocking(|f| f.metadata()).await?; - Ok(descriptortype_from(meta.file_type())) - } else if table.is_dir(&fd) { - Ok(types::DescriptorType::Directory) - } else { - Err(ErrorCode::BadDescriptor.into()) + match table.get_resource(&fd)? { + Descriptor::File(f) => { + let meta = f.spawn_blocking(|f| f.metadata()).await?; + Ok(descriptortype_from(meta.file_type())) + } + Descriptor::Dir(_) => Ok(types::DescriptorType::Directory), } } @@ -163,7 +159,7 @@ impl HostDescriptor for T { fd: Resource, size: types::Filesize, ) -> Result<(), types::Error> { - let f = self.table().get_file(&fd)?; + let f = self.table().get_resource(&fd)?.file()?; if !f.perms.contains(FilePerms::WRITE) { Err(ErrorCode::NotPermitted)?; } @@ -180,26 +176,25 @@ impl HostDescriptor for T { use fs_set_times::SetTimes; let table = self.table(); - if table.is_file(&fd) { - let f = table.get_file(&fd)?; - if !f.perms.contains(FilePerms::WRITE) { - return Err(ErrorCode::NotPermitted.into()); + match table.get_resource(&fd)? { + Descriptor::File(f) => { + if !f.perms.contains(FilePerms::WRITE) { + return Err(ErrorCode::NotPermitted.into()); + } + let atim = systemtimespec_from(atim)?; + let mtim = systemtimespec_from(mtim)?; + f.spawn_blocking(|f| f.set_times(atim, mtim)).await?; + Ok(()) } - let atim = systemtimespec_from(atim)?; - let mtim = systemtimespec_from(mtim)?; - f.spawn_blocking(|f| f.set_times(atim, mtim)).await?; - Ok(()) - } else if table.is_dir(&fd) { - let d = table.get_dir(&fd)?; - if !d.perms.contains(DirPerms::MUTATE) { - return Err(ErrorCode::NotPermitted.into()); + Descriptor::Dir(d) => { + if !d.perms.contains(DirPerms::MUTATE) { + return Err(ErrorCode::NotPermitted.into()); + } + let atim = systemtimespec_from(atim)?; + let mtim = systemtimespec_from(mtim)?; + d.spawn_blocking(|d| d.set_times(atim, mtim)).await?; + Ok(()) } - let atim = systemtimespec_from(atim)?; - let mtim = systemtimespec_from(mtim)?; - d.spawn_blocking(|d| d.set_times(atim, mtim)).await?; - Ok(()) - } else { - Err(ErrorCode::BadDescriptor.into()) } } @@ -214,7 +209,7 @@ impl HostDescriptor for T { let table = self.table(); - let f = table.get_file(&fd)?; + let f = table.get_resource(&fd)?.file()?; if !f.perms.contains(FilePerms::READ) { return Err(ErrorCode::NotPermitted.into()); } @@ -251,7 +246,7 @@ impl HostDescriptor for T { use system_interface::fs::FileIoExt; let table = self.table(); - let f = table.get_file(&fd)?; + let f = table.get_resource(&fd)?.file()?; if !f.perms.contains(FilePerms::WRITE) { return Err(ErrorCode::NotPermitted.into()); } @@ -268,7 +263,7 @@ impl HostDescriptor for T { fd: Resource, ) -> Result, types::Error> { let table = self.table_mut(); - let d = table.get_dir(&fd)?; + let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::READ) { return Err(ErrorCode::NotPermitted.into()); } @@ -325,33 +320,33 @@ impl HostDescriptor for T { Err(ReaddirError::Io(e)) => Err(types::Error::from(e)), Err(ReaddirError::IllegalSequence) => Err(ErrorCode::IllegalByteSequence.into()), }); - Ok(table.push_readdir(ReaddirIterator::new(entries))?) + Ok(table.push_resource(ReaddirIterator::new(entries))?) } async fn sync(&mut self, fd: Resource) -> Result<(), types::Error> { let table = self.table(); - if table.is_file(&fd) { - let f = table.get_file(&fd)?; - match f.spawn_blocking(|f| f.sync_all()).await { - Ok(()) => Ok(()), - // On windows, `sync_data` uses `FileFlushBuffers` which fails with - // `ERROR_ACCESS_DENIED` if the file is not upen for writing. Ignore - // this error, for POSIX compatibility. - #[cfg(windows)] - Err(e) - if e.raw_os_error() - == Some(windows_sys::Win32::Foundation::ERROR_ACCESS_DENIED as _) => - { - Ok(()) + + match table.get_resource(&fd)? { + Descriptor::File(f) => { + match f.spawn_blocking(|f| f.sync_all()).await { + Ok(()) => Ok(()), + // On windows, `sync_data` uses `FileFlushBuffers` which fails with + // `ERROR_ACCESS_DENIED` if the file is not upen for writing. Ignore + // this error, for POSIX compatibility. + #[cfg(windows)] + Err(e) + if e.raw_os_error() + == Some(windows_sys::Win32::Foundation::ERROR_ACCESS_DENIED as _) => + { + Ok(()) + } + Err(e) => Err(e.into()), } - Err(e) => Err(e.into()), } - } else if table.is_dir(&fd) { - let d = table.get_dir(&fd)?; - d.spawn_blocking(|d| Ok(d.open(std::path::Component::CurDir)?.sync_all()?)) - .await - } else { - Err(ErrorCode::BadDescriptor.into()) + Descriptor::Dir(d) => { + d.spawn_blocking(|d| Ok(d.open(std::path::Component::CurDir)?.sync_all()?)) + .await + } } } @@ -361,7 +356,7 @@ impl HostDescriptor for T { path: String, ) -> Result<(), types::Error> { let table = self.table(); - let d = table.get_dir(&fd)?; + let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -374,18 +369,17 @@ impl HostDescriptor for T { fd: Resource, ) -> Result { let table = self.table(); - if table.is_file(&fd) { - let f = table.get_file(&fd)?; - // No permissions check on stat: if opened, allowed to stat it - let meta = f.spawn_blocking(|f| f.metadata()).await?; - Ok(descriptorstat_from(meta)) - } else if table.is_dir(&fd) { - let d = table.get_dir(&fd)?; - // No permissions check on stat: if opened, allowed to stat it - let meta = d.spawn_blocking(|d| d.dir_metadata()).await?; - Ok(descriptorstat_from(meta)) - } else { - Err(ErrorCode::BadDescriptor.into()) + match table.get_resource(&fd)? { + Descriptor::File(f) => { + // No permissions check on stat: if opened, allowed to stat it + let meta = f.spawn_blocking(|f| f.metadata()).await?; + Ok(descriptorstat_from(meta)) + } + Descriptor::Dir(d) => { + // No permissions check on stat: if opened, allowed to stat it + let meta = d.spawn_blocking(|d| d.dir_metadata()).await?; + Ok(descriptorstat_from(meta)) + } } } @@ -396,7 +390,7 @@ impl HostDescriptor for T { path: String, ) -> Result { let table = self.table(); - let d = table.get_dir(&fd)?; + let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::READ) { return Err(ErrorCode::NotPermitted.into()); } @@ -420,7 +414,7 @@ impl HostDescriptor for T { use cap_fs_ext::DirExt; let table = self.table(); - let d = table.get_dir(&fd)?; + let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -458,11 +452,11 @@ impl HostDescriptor for T { new_path: String, ) -> Result<(), types::Error> { let table = self.table(); - let old_dir = table.get_dir(&fd)?; + let old_dir = table.get_resource(&fd)?.dir()?; if !old_dir.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } - let new_dir = table.get_dir(&new_descriptor)?; + let new_dir = table.get_resource(&new_descriptor)?.dir()?; if !new_dir.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -492,10 +486,7 @@ impl HostDescriptor for T { use types::{DescriptorFlags, OpenFlags}; let table = self.table_mut(); - if table.is_file(&fd) { - Err(ErrorCode::NotDirectory)?; - } - let d = table.get_dir(&fd)?; + let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::READ) { Err(ErrorCode::NotPermitted)?; } @@ -584,12 +575,15 @@ impl HostDescriptor for T { .await?; match opened { - OpenResult::Dir(dir) => Ok(table.push_dir(Dir::new(dir, d.perms, d.file_perms))?), - - OpenResult::File(file) => { - Ok(table.push_file(File::new(file, mask_file_perms(d.file_perms, flags)))?) + OpenResult::Dir(dir) => { + Ok(table.push_resource(Descriptor::Dir(Dir::new(dir, d.perms, d.file_perms)))?) } + OpenResult::File(file) => Ok(table.push_resource(Descriptor::File(File::new( + file, + mask_file_perms(d.file_perms, flags), + )))?), + OpenResult::NotDir => Err(ErrorCode::NotDirectory.into()), } } @@ -602,9 +596,7 @@ impl HostDescriptor for T { // tokio::fs::File just uses std::fs::File's Drop impl to close, so // it doesn't appear anyone else has found this to be a problem. // (Not that they could solve it without async drop...) - if table.delete_file(Resource::new_own(fd.rep())).is_err() { - table.delete_dir(fd)?; - } + table.delete_resource(fd)?; Ok(()) } @@ -615,7 +607,7 @@ impl HostDescriptor for T { path: String, ) -> Result { let table = self.table(); - let d = table.get_dir(&fd)?; + let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::READ) { return Err(ErrorCode::NotPermitted.into()); } @@ -632,7 +624,7 @@ impl HostDescriptor for T { path: String, ) -> Result<(), types::Error> { let table = self.table(); - let d = table.get_dir(&fd)?; + let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -647,11 +639,11 @@ impl HostDescriptor for T { new_path: String, ) -> Result<(), types::Error> { let table = self.table(); - let old_dir = table.get_dir(&fd)?; + let old_dir = table.get_resource(&fd)?.dir()?; if !old_dir.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } - let new_dir = table.get_dir(&new_fd)?; + let new_dir = table.get_resource(&new_fd)?.dir()?; if !new_dir.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -672,7 +664,7 @@ impl HostDescriptor for T { use cap_fs_ext::DirExt; let table = self.table(); - let d = table.get_dir(&fd)?; + let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -688,7 +680,7 @@ impl HostDescriptor for T { use cap_fs_ext::DirExt; let table = self.table(); - let d = table.get_dir(&fd)?; + let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::MUTATE) { return Err(ErrorCode::NotPermitted.into()); } @@ -759,14 +751,9 @@ impl HostDescriptor for T { &mut self, fd: Resource, offset: types::Filesize, - ) -> Result, types::Error> { - use crate::preview2::{ - filesystem::FileInputStream, - stream::{InternalInputStream, InternalTableStreamExt}, - }; - + ) -> Result, types::Error> { // Trap if fd lookup fails: - let f = self.table().get_file(&fd)?; + let f = self.table().get_resource(&fd)?.file()?; if !f.perms.contains(FilePerms::READ) { Err(types::ErrorCode::BadDescriptor)?; @@ -778,9 +765,7 @@ impl HostDescriptor for T { let reader = FileInputStream::new(clone, offset); // Insert the stream view into the table. Trap if the table is full. - let index = self - .table_mut() - .push_internal_input_stream(InternalInputStream::File(reader))?; + let index = self.table_mut().push_resource(InputStream::File(reader))?; Ok(index) } @@ -789,11 +774,9 @@ impl HostDescriptor for T { &mut self, fd: Resource, offset: types::Filesize, - ) -> Result, types::Error> { - use crate::preview2::{filesystem::FileOutputStream, TableStreamExt}; - + ) -> Result, types::Error> { // Trap if fd lookup fails: - let f = self.table().get_file(&fd)?; + let f = self.table().get_resource(&fd)?.file()?; if !f.perms.contains(FilePerms::WRITE) { Err(types::ErrorCode::BadDescriptor)?; @@ -804,9 +787,10 @@ impl HostDescriptor for T { // Create a stream view for it. let writer = FileOutputStream::write_at(clone, offset); + let writer: OutputStream = Box::new(writer); // Insert the stream view into the table. Trap if the table is full. - let index = self.table_mut().push_output_stream(Box::new(writer))?; + let index = self.table_mut().push_resource(writer)?; Ok(index) } @@ -814,11 +798,9 @@ impl HostDescriptor for T { fn append_via_stream( &mut self, fd: Resource, - ) -> Result, types::Error> { - use crate::preview2::{filesystem::FileOutputStream, TableStreamExt}; - + ) -> Result, types::Error> { // Trap if fd lookup fails: - let f = self.table().get_file(&fd)?; + let f = self.table().get_resource(&fd)?.file()?; if !f.perms.contains(FilePerms::WRITE) { Err(types::ErrorCode::BadDescriptor)?; @@ -828,9 +810,10 @@ impl HostDescriptor for T { // Create a stream view for it. let appender = FileOutputStream::append(clone); + let appender: OutputStream = Box::new(appender); // Insert the stream view into the table. Trap if the table is full. - let index = self.table_mut().push_output_stream(Box::new(appender))?; + let index = self.table_mut().push_resource(appender)?; Ok(index) } @@ -876,7 +859,7 @@ impl HostDescriptor for T { path: String, ) -> Result { let table = self.table(); - let d = table.get_dir(&fd)?; + let d = table.get_resource(&fd)?.dir()?; // No permissions check on metadata: if dir opened, allowed to stat it let meta = d .spawn_blocking(move |d| { @@ -898,12 +881,12 @@ impl HostDirectoryEntryStream for T { stream: Resource, ) -> Result, types::Error> { let table = self.table(); - let readdir = table.get_readdir(&stream)?; + let readdir = table.get_resource(&stream)?; readdir.next() } fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { - self.table_mut().delete_readdir(stream)?; + self.table_mut().delete_resource(stream)?; Ok(()) } } @@ -912,16 +895,15 @@ async fn get_descriptor_metadata( table: &Table, fd: Resource, ) -> Result { - if table.is_file(&fd) { - let f = table.get_file(&fd)?; - // No permissions check on metadata: if opened, allowed to stat it - Ok(f.spawn_blocking(|f| f.metadata()).await?) - } else if table.is_dir(&fd) { - let d = table.get_dir(&fd)?; - // No permissions check on metadata: if opened, allowed to stat it - Ok(d.spawn_blocking(|d| d.dir_metadata()).await?) - } else { - Err(ErrorCode::BadDescriptor.into()) + match table.get_resource(&fd)? { + Descriptor::File(f) => { + // No permissions check on metadata: if opened, allowed to stat it + Ok(f.spawn_blocking(|f| f.metadata()).await?) + } + Descriptor::Dir(d) => { + // No permissions check on metadata: if opened, allowed to stat it + Ok(d.spawn_blocking(|d| d.dir_metadata()).await?) + } } } @@ -1105,69 +1087,6 @@ fn symlink_follow(path_flags: types::PathFlags) -> bool { path_flags.contains(types::PathFlags::SYMLINK_FOLLOW) } -pub(crate) struct ReaddirIterator( - std::sync::Mutex< - Box> + Send + 'static>, - >, -); - -impl ReaddirIterator { - fn new( - i: impl Iterator> + Send + 'static, - ) -> Self { - ReaddirIterator(std::sync::Mutex::new(Box::new(i))) - } - fn next(&self) -> Result, types::Error> { - self.0.lock().unwrap().next().transpose() - } -} - -impl IntoIterator for ReaddirIterator { - type Item = Result; - type IntoIter = Box + Send>; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_inner().unwrap() - } -} - -pub(crate) trait TableReaddirExt { - fn push_readdir( - &mut self, - readdir: ReaddirIterator, - ) -> Result, TableError>; - fn delete_readdir( - &mut self, - fd: Resource, - ) -> Result; - fn get_readdir( - &self, - fd: &Resource, - ) -> Result<&ReaddirIterator, TableError>; -} - -impl TableReaddirExt for Table { - fn push_readdir( - &mut self, - readdir: ReaddirIterator, - ) -> Result, TableError> { - Ok(Resource::new_own(self.push(Box::new(readdir))?)) - } - fn delete_readdir( - &mut self, - fd: Resource, - ) -> Result { - self.delete(fd.rep()) - } - - fn get_readdir( - &self, - fd: &Resource, - ) -> Result<&ReaddirIterator, TableError> { - self.get(fd.rep()) - } -} - fn mask_file_perms(p: FilePerms, flags: types::DescriptorFlags) -> FilePerms { use types::DescriptorFlags; let mut out = FilePerms::empty(); @@ -1187,9 +1106,9 @@ mod test { fn table_readdir_works() { let mut table = Table::new(); let ix = table - .push_readdir(ReaddirIterator::new(std::iter::empty())) + .push_resource(ReaddirIterator::new(std::iter::empty())) .unwrap(); - let _ = table.get_readdir(&ix).unwrap(); - table.delete_readdir(ix).unwrap(); + let _ = table.get_resource(&ix).unwrap(); + table.delete_resource(ix).unwrap(); } } diff --git a/crates/wasi/src/preview2/host/instance_network.rs b/crates/wasi/src/preview2/host/instance_network.rs index f85e411e1e94..02bf5e8a4014 100644 --- a/crates/wasi/src/preview2/host/instance_network.rs +++ b/crates/wasi/src/preview2/host/instance_network.rs @@ -1,12 +1,12 @@ -use crate::preview2::bindings::sockets::instance_network::{self, Network}; -use crate::preview2::network::{HostNetworkState, TableNetworkExt}; +use crate::preview2::bindings::sockets::instance_network; +use crate::preview2::network::Network; use crate::preview2::WasiView; use wasmtime::component::Resource; impl instance_network::Host for T { fn instance_network(&mut self) -> Result, anyhow::Error> { - let network = HostNetworkState::new(self.ctx().pool.clone()); - let network = self.table_mut().push_network(network)?; + let network = Network::new(self.ctx().pool.clone()); + let network = self.table_mut().push_resource(network)?; Ok(network) } } diff --git a/crates/wasi/src/preview2/host/io.rs b/crates/wasi/src/preview2/host/io.rs index ddf309ca0517..504fccac1875 100644 --- a/crates/wasi/src/preview2/host/io.rs +++ b/crates/wasi/src/preview2/host/io.rs @@ -1,13 +1,8 @@ use crate::preview2::{ - bindings::io::poll::Pollable, bindings::io::streams::{self, InputStream, OutputStream}, - filesystem::FileInputStream, poll::PollableFuture, - stream::{ - HostInputStream, HostOutputStream, InternalInputStream, InternalTableStreamExt, - OutputStreamError, StreamRuntimeError, StreamState, TableStreamExt, - }, - HostPollable, TableError, TablePollableExt, WasiView, + stream::{OutputStreamError, StreamRuntimeError, StreamState}, + Pollable, TableError, WasiView, }; use std::any::Any; use std::future::Future; @@ -48,12 +43,12 @@ impl streams::Host for T {} #[async_trait::async_trait] impl streams::HostOutputStream for T { fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { - self.table_mut().delete_output_stream(stream)?; + self.table_mut().delete_resource(stream)?; Ok(()) } fn check_write(&mut self, stream: Resource) -> Result { - let s = self.table_mut().get_output_stream_mut(&stream)?; + let s = self.table_mut().get_resource_mut(&stream)?; let mut ready = s.write_ready(); let mut task = Context::from_waker(futures::task::noop_waker_ref()); match Pin::new(&mut ready).poll(&mut task) { @@ -68,31 +63,30 @@ impl streams::HostOutputStream for T { stream: Resource, bytes: Vec, ) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(&stream)?; - HostOutputStream::write(s, bytes.into())?; + self.table_mut() + .get_resource_mut(&stream)? + .write(bytes.into())?; Ok(()) } fn subscribe(&mut self, stream: Resource) -> anyhow::Result> { - // Ensure that table element is an output-stream: - let _ = self.table_mut().get_output_stream_mut(&stream)?; - fn output_stream_ready<'a>(stream: &'a mut dyn Any) -> PollableFuture<'a> { let stream = stream - .downcast_mut::>() - .expect("downcast to HostOutputStream failed"); + .downcast_mut::() + .expect("downcast to OutputStream failed"); Box::pin(async move { let _ = stream.write_ready().await?; Ok(()) }) } - Ok(self - .table_mut() - .push_host_pollable(HostPollable::TableEntry { + Ok(self.table_mut().push_child_resource( + Pollable::TableEntry { index: stream.rep(), make_future: output_stream_ready, - })?) + }, + &stream, + )?) } async fn blocking_write_and_flush( @@ -100,7 +94,7 @@ impl streams::HostOutputStream for T { stream: Resource, bytes: Vec, ) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(&stream)?; + let s = self.table_mut().get_resource_mut(&stream)?; if bytes.len() > 4096 { return Err(streams::Error::trap(anyhow::anyhow!( @@ -113,11 +107,11 @@ impl streams::HostOutputStream for T { let permit = s.write_ready().await?; let len = bytes.len().min(permit); let chunk = bytes.split_to(len); - HostOutputStream::write(s, chunk)?; + s.write(chunk)?; } - HostOutputStream::flush(s)?; - let _ = s.write_ready().await?; + s.flush()?; + s.write_ready().await?; Ok(()) } @@ -127,7 +121,7 @@ impl streams::HostOutputStream for T { stream: Resource, len: u64, ) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(&stream)?; + let s = self.table_mut().get_resource_mut(&stream)?; if len > 4096 { return Err(streams::Error::trap(anyhow::anyhow!( @@ -139,12 +133,12 @@ impl streams::HostOutputStream for T { while len > 0 { let permit = s.write_ready().await?; let this_len = len.min(permit as u64); - HostOutputStream::write_zeroes(s, this_len as usize)?; + s.write_zeroes(this_len as usize)?; len -= this_len; } - HostOutputStream::flush(s)?; - let _ = s.write_ready().await?; + s.flush()?; + s.write_ready().await?; Ok(()) } @@ -154,14 +148,14 @@ impl streams::HostOutputStream for T { stream: Resource, len: u64, ) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(&stream)?; - HostOutputStream::write_zeroes(s, len as usize)?; + self.table_mut() + .get_resource_mut(&stream)? + .write_zeroes(len as usize)?; Ok(()) } fn flush(&mut self, stream: Resource) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(&stream)?; - HostOutputStream::flush(s)?; + self.table_mut().get_resource_mut(&stream)?.flush()?; Ok(()) } @@ -169,9 +163,9 @@ impl streams::HostOutputStream for T { &mut self, stream: Resource, ) -> Result<(), streams::Error> { - let s = self.table_mut().get_output_stream_mut(&stream)?; - HostOutputStream::flush(s)?; - let _ = s.write_ready().await?; + let s = self.table_mut().get_resource_mut(&stream)?; + s.flush()?; + s.write_ready().await?; Ok(()) } @@ -194,7 +188,7 @@ impl streams::HostOutputStream for T { ?; let d: &mut Box = ctx .table_mut() - .get_output_stream_mut(dst) + .get_resource_mut(dst) ?; let bytes_spliced: u64 = s.splice(&mut **d, len).await?; @@ -233,7 +227,7 @@ impl streams::HostOutputStream for T { ?; let d: &mut Box = ctx .table_mut() - .get_output_stream_mut(dst) + .get_resource_mut(dst) ?; let bytes_spliced: u64 = s.splice(&mut **d, len).await?; @@ -248,7 +242,7 @@ impl streams::HostOutputStream for T { #[async_trait::async_trait] impl streams::HostInputStream for T { fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { - self.table_mut().delete_internal_input_stream(stream)?; + self.table_mut().delete_resource(stream)?; Ok(()) } @@ -257,9 +251,9 @@ impl streams::HostInputStream for T { stream: Resource, len: u64, ) -> anyhow::Result, streams::StreamStatus), ()>> { - match self.table_mut().get_internal_input_stream_mut(&stream)? { - InternalInputStream::Host(s) => { - let (bytes, state) = match HostInputStream::read(s.as_mut(), len as usize) { + match self.table_mut().get_resource_mut(&stream)? { + InputStream::Host(s) => { + let (bytes, state) = match s.read(len as usize) { Ok(a) => a, Err(e) => { if let Some(e) = e.downcast_ref::() { @@ -274,8 +268,8 @@ impl streams::HostInputStream for T { Ok(Ok((bytes.into(), state.into()))) } - InternalInputStream::File(s) => { - let (bytes, state) = match FileInputStream::read(s, len as usize).await { + InputStream::File(s) => { + let (bytes, state) = match s.read(len as usize).await { Ok(a) => a, Err(e) => { if let Some(e) = e.downcast_ref::() { @@ -296,38 +290,10 @@ impl streams::HostInputStream for T { stream: Resource, len: u64, ) -> anyhow::Result, streams::StreamStatus), ()>> { - match self.table_mut().get_internal_input_stream_mut(&stream)? { - InternalInputStream::Host(s) => { - s.ready().await?; - let (bytes, state) = match HostInputStream::read(s.as_mut(), len as usize) { - Ok(a) => a, - Err(e) => { - if let Some(e) = e.downcast_ref::() { - tracing::debug!("stream runtime error: {e:?}"); - return Ok(Err(())); - } else { - return Err(e); - } - } - }; - debug_assert!(bytes.len() <= len as usize); - Ok(Ok((bytes.into(), state.into()))) - } - InternalInputStream::File(s) => { - let (bytes, state) = match FileInputStream::read(s, len as usize).await { - Ok(a) => a, - Err(e) => { - if let Some(e) = e.downcast_ref::() { - tracing::debug!("stream runtime error: {e:?}"); - return Ok(Err(())); - } else { - return Err(e); - } - } - }; - Ok(Ok((bytes.into(), state.into()))) - } + if let InputStream::Host(s) = self.table_mut().get_resource_mut(&stream)? { + s.ready().await?; } + self.read(stream, len).await } async fn skip( @@ -335,10 +301,10 @@ impl streams::HostInputStream for T { stream: Resource, len: u64, ) -> anyhow::Result> { - match self.table_mut().get_internal_input_stream_mut(&stream)? { - InternalInputStream::Host(s) => { + match self.table_mut().get_resource_mut(&stream)? { + InputStream::Host(s) => { // TODO: the cast to usize should be fallible, use `.try_into()?` - let (bytes_skipped, state) = match HostInputStream::skip(s.as_mut(), len as usize) { + let (bytes_skipped, state) = match s.skip(len as usize) { Ok(a) => a, Err(e) => { if let Some(e) = e.downcast_ref::() { @@ -352,8 +318,8 @@ impl streams::HostInputStream for T { Ok(Ok((bytes_skipped as u64, state.into()))) } - InternalInputStream::File(s) => { - let (bytes_skipped, state) = match FileInputStream::skip(s, len as usize).await { + InputStream::File(s) => { + let (bytes_skipped, state) = match s.skip(len as usize).await { Ok(a) => a, Err(e) => { if let Some(e) = e.downcast_ref::() { @@ -374,67 +340,38 @@ impl streams::HostInputStream for T { stream: Resource, len: u64, ) -> anyhow::Result> { - match self.table_mut().get_internal_input_stream_mut(&stream)? { - InternalInputStream::Host(s) => { - s.ready().await?; - // TODO: the cast to usize should be fallible, use `.try_into()?` - let (bytes_skipped, state) = match HostInputStream::skip(s.as_mut(), len as usize) { - Ok(a) => a, - Err(e) => { - if let Some(e) = e.downcast_ref::() { - tracing::debug!("stream runtime error: {e:?}"); - return Ok(Err(())); - } else { - return Err(e); - } - } - }; - - Ok(Ok((bytes_skipped as u64, state.into()))) - } - InternalInputStream::File(s) => { - let (bytes_skipped, state) = match FileInputStream::skip(s, len as usize).await { - Ok(a) => a, - Err(e) => { - if let Some(e) = e.downcast_ref::() { - tracing::debug!("stream runtime error: {e:?}"); - return Ok(Err(())); - } else { - return Err(e); - } - } - }; - Ok(Ok((bytes_skipped as u64, state.into()))) - } + if let InputStream::Host(s) = self.table_mut().get_resource_mut(&stream)? { + s.ready().await?; } + self.skip(stream, len).await } fn subscribe(&mut self, stream: Resource) -> anyhow::Result> { // Ensure that table element is an input-stream: - let pollable = match self.table_mut().get_internal_input_stream_mut(&stream)? { - InternalInputStream::Host(_) => { + let pollable = match self.table_mut().get_resource(&stream)? { + InputStream::Host(_) => { fn input_stream_ready<'a>(stream: &'a mut dyn Any) -> PollableFuture<'a> { let stream = stream - .downcast_mut::() - .expect("downcast to InternalInputStream failed"); + .downcast_mut::() + .expect("downcast to InputStream failed"); match *stream { - InternalInputStream::Host(ref mut hs) => hs.ready(), + InputStream::Host(ref mut hs) => hs.ready(), _ => unreachable!(), } } - HostPollable::TableEntry { + Pollable::TableEntry { index: stream.rep(), make_future: input_stream_ready, } } // Files are always "ready" immediately (because we have no way to actually wait on // readiness in epoll) - InternalInputStream::File(_) => { - HostPollable::Closure(Box::new(|| Box::pin(futures::future::ready(Ok(()))))) + InputStream::File(_) => { + Pollable::Closure(Box::new(|| Box::pin(futures::future::ready(Ok(()))))) } }; - Ok(self.table_mut().push_host_pollable(pollable)?) + Ok(self.table_mut().push_child_resource(pollable, &stream)?) } } diff --git a/crates/wasi/src/preview2/host/network.rs b/crates/wasi/src/preview2/host/network.rs index 03d588e5320c..d1aa52d7fd05 100644 --- a/crates/wasi/src/preview2/host/network.rs +++ b/crates/wasi/src/preview2/host/network.rs @@ -2,7 +2,6 @@ use crate::preview2::bindings::sockets::network::{ self, ErrorCode, IpAddressFamily, IpSocketAddress, Ipv4Address, Ipv4SocketAddress, Ipv6Address, Ipv6SocketAddress, }; -use crate::preview2::network::TableNetworkExt; use crate::preview2::{TableError, WasiView}; use std::io; use wasmtime::component::Resource; @@ -13,7 +12,7 @@ impl crate::preview2::bindings::sockets::network::HostNetwork for T fn drop(&mut self, this: Resource) -> Result<(), anyhow::Error> { let table = self.table_mut(); - table.delete_network(this)?; + table.delete_resource(this)?; Ok(()) } diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index 370da1adb5a6..185807504fd2 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -1,14 +1,10 @@ use crate::preview2::bindings::{ - io::poll::Pollable, io::streams::{InputStream, OutputStream}, sockets::network::{self, ErrorCode, IpAddressFamily, IpSocketAddress, Network}, sockets::tcp::{self, ShutdownType}, }; -use crate::preview2::network::TableNetworkExt; -use crate::preview2::poll::TablePollableExt; -use crate::preview2::stream::TableStreamExt; -use crate::preview2::tcp::{HostTcpSocketState, HostTcpState, TableTcpSocketExt}; -use crate::preview2::{HostPollable, PollableFuture, WasiView}; +use crate::preview2::tcp::{TcpSocket, TcpState}; +use crate::preview2::{Pollable, PollableFuture, WasiView}; use cap_net_ext::{Blocking, PoolExt, TcpListenerExt}; use cap_std::net::TcpListener; use io_lifetimes::AsSocketlike; @@ -28,14 +24,14 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { local_address: IpSocketAddress, ) -> Result<(), network::Error> { let table = self.table_mut(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; match socket.tcp_state { - HostTcpState::Default => {} + TcpState::Default => {} _ => return Err(ErrorCode::NotInProgress.into()), } - let network = table.get_network(&network)?; + let network = table.get_resource(&network)?; let binder = network.0.tcp_binder(local_address)?; // Perform the OS bind call. @@ -43,22 +39,22 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { &*socket.tcp_socket().as_socketlike_view::(), )?; - let socket = table.get_tcp_socket_mut(&this)?; - socket.tcp_state = HostTcpState::BindStarted; + let socket = table.get_resource_mut(&this)?; + socket.tcp_state = TcpState::BindStarted; Ok(()) } fn finish_bind(&mut self, this: Resource) -> Result<(), network::Error> { let table = self.table_mut(); - let socket = table.get_tcp_socket_mut(&this)?; + let socket = table.get_resource_mut(&this)?; match socket.tcp_state { - HostTcpState::BindStarted => {} + TcpState::BindStarted => {} _ => return Err(ErrorCode::NotInProgress.into()), } - socket.tcp_state = HostTcpState::Bound; + socket.tcp_state = TcpState::Bound; Ok(()) } @@ -71,15 +67,15 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { ) -> Result<(), network::Error> { let table = self.table_mut(); let r = { - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; match socket.tcp_state { - HostTcpState::Default => {} - HostTcpState::Connected => return Err(ErrorCode::AlreadyConnected.into()), + TcpState::Default => {} + TcpState::Connected => return Err(ErrorCode::AlreadyConnected.into()), _ => return Err(ErrorCode::NotInProgress.into()), } - let network = table.get_network(&network)?; + let network = table.get_resource(&network)?; let connecter = network.0.tcp_connecter(remote_address)?; // Do an OS `connect`. Our socket is non-blocking, so it'll either... @@ -93,8 +89,8 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { match r { // succeed immediately, Ok(()) => { - let socket = table.get_tcp_socket_mut(&this)?; - socket.tcp_state = HostTcpState::ConnectReady; + let socket = table.get_resource_mut(&this)?; + socket.tcp_state = TcpState::ConnectReady; return Ok(()); } // continue in progress, @@ -103,8 +99,8 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { Err(err) => return Err(err.into()), } - let socket = table.get_tcp_socket_mut(&this)?; - socket.tcp_state = HostTcpState::Connecting; + let socket = table.get_resource_mut(&this)?; + socket.tcp_state = TcpState::Connecting; Ok(()) } @@ -114,11 +110,11 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, ) -> Result<(Resource, Resource), network::Error> { let table = self.table_mut(); - let socket = table.get_tcp_socket_mut(&this)?; + let socket = table.get_resource_mut(&this)?; match socket.tcp_state { - HostTcpState::ConnectReady => {} - HostTcpState::Connecting => { + TcpState::ConnectReady => {} + TcpState::Connecting => { // Do a `poll` to test for completion, using a timeout of zero // to avoid blocking. match rustix::event::poll( @@ -142,26 +138,22 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { _ => return Err(ErrorCode::NotInProgress.into()), }; - socket.tcp_state = HostTcpState::Connected; + socket.tcp_state = TcpState::Connected; let (input, output) = socket.as_split(); - let input_stream = self - .table_mut() - .push_input_stream_child(input, Resource::::new_borrow(this.rep()))?; - let output_stream = self - .table_mut() - .push_output_stream_child(output, Resource::::new_borrow(this.rep()))?; + let input_stream = self.table_mut().push_child_resource(input, &this)?; + let output_stream = self.table_mut().push_child_resource(output, &this)?; Ok((input_stream, output_stream)) } fn start_listen(&mut self, this: Resource) -> Result<(), network::Error> { let table = self.table_mut(); - let socket = table.get_tcp_socket_mut(&this)?; + let socket = table.get_resource_mut(&this)?; match socket.tcp_state { - HostTcpState::Bound => {} - HostTcpState::ListenStarted => return Err(ErrorCode::AlreadyListening.into()), - HostTcpState::Connected => return Err(ErrorCode::AlreadyConnected.into()), + TcpState::Bound => {} + TcpState::ListenStarted => return Err(ErrorCode::AlreadyListening.into()), + TcpState::Connected => return Err(ErrorCode::AlreadyConnected.into()), _ => return Err(ErrorCode::NotInProgress.into()), } @@ -170,21 +162,21 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { .as_socketlike_view::() .listen(None)?; - socket.tcp_state = HostTcpState::ListenStarted; + socket.tcp_state = TcpState::ListenStarted; Ok(()) } fn finish_listen(&mut self, this: Resource) -> Result<(), network::Error> { let table = self.table_mut(); - let socket = table.get_tcp_socket_mut(&this)?; + let socket = table.get_resource_mut(&this)?; match socket.tcp_state { - HostTcpState::ListenStarted => {} + TcpState::ListenStarted => {} _ => return Err(ErrorCode::NotInProgress.into()), } - socket.tcp_state = HostTcpState::Listening; + socket.tcp_state = TcpState::Listening; Ok(()) } @@ -201,11 +193,11 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { network::Error, > { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; match socket.tcp_state { - HostTcpState::Listening => {} - HostTcpState::Connected => return Err(ErrorCode::AlreadyConnected.into()), + TcpState::Listening => {} + TcpState::Connected => return Err(ErrorCode::AlreadyConnected.into()), _ => return Err(ErrorCode::NotInProgress.into()), } @@ -216,22 +208,17 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { .as_socketlike_view::() .accept_with(Blocking::No) })?; - let mut tcp_socket = HostTcpSocketState::from_tcp_stream(connection)?; + let mut tcp_socket = TcpSocket::from_tcp_stream(connection)?; // Mark the socket as connected so that we can exit early from methods like `start-bind`. - tcp_socket.tcp_state = HostTcpState::Connected; + tcp_socket.tcp_state = TcpState::Connected; let (input, output) = tcp_socket.as_split(); + let output: OutputStream = output; - let tcp_socket = self.table_mut().push_tcp_socket(tcp_socket)?; - let input_stream = self.table_mut().push_input_stream_child( - input, - Resource::::new_borrow(tcp_socket.rep()), - )?; - let output_stream = self.table_mut().push_output_stream_child( - output, - Resource::::new_borrow(tcp_socket.rep()), - )?; + let tcp_socket = self.table_mut().push_resource(tcp_socket)?; + let input_stream = self.table_mut().push_child_resource(input, &tcp_socket)?; + let output_stream = self.table_mut().push_child_resource(output, &tcp_socket)?; Ok((tcp_socket, input_stream, output_stream)) } @@ -241,7 +228,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, ) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; let addr = socket .tcp_socket() .as_socketlike_view::() @@ -254,7 +241,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, ) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; let addr = socket .tcp_socket() .as_socketlike_view::() @@ -267,7 +254,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, ) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; // If `SO_DOMAIN` is available, use it. // @@ -312,7 +299,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { fn ipv6_only(&mut self, this: Resource) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; Ok(sockopt::get_ipv6_v6only(socket.tcp_socket())?) } @@ -322,7 +309,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { value: bool, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; Ok(sockopt::set_ipv6_v6only(socket.tcp_socket(), value)?) } @@ -332,10 +319,10 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { value: u64, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; match socket.tcp_state { - HostTcpState::Listening => {} + TcpState::Listening => {} _ => return Err(ErrorCode::NotInProgress.into()), } @@ -345,7 +332,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { fn keep_alive(&mut self, this: Resource) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; Ok(sockopt::get_socket_keepalive(socket.tcp_socket())?) } @@ -355,13 +342,13 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { value: bool, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; Ok(sockopt::set_socket_keepalive(socket.tcp_socket(), value)?) } fn no_delay(&mut self, this: Resource) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; Ok(sockopt::get_tcp_nodelay(socket.tcp_socket())?) } @@ -371,13 +358,13 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { value: bool, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; Ok(sockopt::set_tcp_nodelay(socket.tcp_socket(), value)?) } fn unicast_hop_limit(&mut self, this: Resource) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; // We don't track whether the socket is IPv4 or IPv6 so try one and // fall back to the other. @@ -398,7 +385,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { value: u8, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; // We don't track whether the socket is IPv4 or IPv6 so try one and // fall back to the other. @@ -414,7 +401,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, ) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; Ok(sockopt::get_socket_recv_buffer_size(socket.tcp_socket())? as u64) } @@ -424,7 +411,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { value: u64, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; Ok(sockopt::set_socket_recv_buffer_size( socket.tcp_socket(), @@ -434,7 +421,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { fn send_buffer_size(&mut self, this: Resource) -> Result { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; Ok(sockopt::get_socket_send_buffer_size(socket.tcp_socket())? as u64) } @@ -444,7 +431,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { value: u64, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; Ok(sockopt::set_socket_send_buffer_size( socket.tcp_socket(), @@ -455,14 +442,14 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { fn subscribe(&mut self, this: Resource) -> anyhow::Result> { fn make_tcp_socket_future<'a>(stream: &'a mut dyn Any) -> PollableFuture<'a> { let socket = stream - .downcast_mut::() - .expect("downcast to HostTcpSocketState failed"); + .downcast_mut::() + .expect("downcast to TcpSocket failed"); // Some states are ready immediately. match socket.tcp_state { - HostTcpState::BindStarted - | HostTcpState::ListenStarted - | HostTcpState::ConnectReady => return Box::pin(async { Ok(()) }), + TcpState::BindStarted | TcpState::ListenStarted | TcpState::ConnectReady => { + return Box::pin(async { Ok(()) }) + } _ => {} } @@ -479,12 +466,12 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { join } - let pollable = HostPollable::TableEntry { + let pollable = Pollable::TableEntry { index: this.rep(), make_future: make_tcp_socket_future, }; - Ok(self.table_mut().push_host_pollable(pollable)?) + Ok(self.table_mut().push_child_resource(pollable, &this)?) } fn shutdown( @@ -493,7 +480,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { shutdown_type: ShutdownType, ) -> Result<(), network::Error> { let table = self.table(); - let socket = table.get_tcp_socket(&this)?; + let socket = table.get_resource(&this)?; let how = match shutdown_type { ShutdownType::Receive => std::net::Shutdown::Read, @@ -513,19 +500,19 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { // As in the filesystem implementation, we assume closing a socket // doesn't block. - let dropped = table.delete_tcp_socket(this)?; + let dropped = table.delete_resource(this)?; // If we might have an `event::poll` waiting on the socket, wake it up. #[cfg(not(unix))] { match dropped.tcp_state { - HostTcpState::Default - | HostTcpState::BindStarted - | HostTcpState::Bound - | HostTcpState::ListenStarted - | HostTcpState::ConnectReady => {} + TcpState::Default + | TcpState::BindStarted + | TcpState::Bound + | TcpState::ListenStarted + | TcpState::ConnectReady => {} - HostTcpState::Listening | HostTcpState::Connecting | HostTcpState::Connected => { + TcpState::Listening | TcpState::Connecting | TcpState::Connected => { match rustix::net::shutdown(&dropped.inner, rustix::net::Shutdown::ReadWrite) { Ok(()) | Err(Errno::NOTCONN) => {} Err(err) => Err(err).unwrap(), diff --git a/crates/wasi/src/preview2/host/tcp_create_socket.rs b/crates/wasi/src/preview2/host/tcp_create_socket.rs index c3f349177a8e..60fc34482399 100644 --- a/crates/wasi/src/preview2/host/tcp_create_socket.rs +++ b/crates/wasi/src/preview2/host/tcp_create_socket.rs @@ -1,9 +1,8 @@ use crate::preview2::bindings::{ sockets::network::{self, IpAddressFamily}, - sockets::tcp::TcpSocket, sockets::tcp_create_socket, }; -use crate::preview2::tcp::{HostTcpSocketState, TableTcpSocketExt}; +use crate::preview2::tcp::TcpSocket; use crate::preview2::WasiView; use wasmtime::component::Resource; @@ -12,8 +11,8 @@ impl tcp_create_socket::Host for T { &mut self, address_family: IpAddressFamily, ) -> Result, network::Error> { - let socket = HostTcpSocketState::new(address_family.into())?; - let socket = self.table_mut().push_tcp_socket(socket)?; + let socket = TcpSocket::new(address_family.into())?; + let socket = self.table_mut().push_resource(socket)?; Ok(socket) } } diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index 5d1f02e55487..e3f171a11973 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -37,14 +37,14 @@ pub use self::clocks::{HostMonotonicClock, HostWallClock}; pub use self::ctx::{WasiCtx, WasiCtxBuilder, WasiView}; pub use self::error::I32Exit; pub use self::filesystem::{DirPerms, FilePerms}; -pub use self::poll::{ClosureFuture, HostPollable, MakeFuture, PollableFuture, TablePollableExt}; +pub use self::poll::{ClosureFuture, MakeFuture, Pollable, PollableFuture}; pub use self::random::{thread_rng, Deterministic}; pub use self::stdio::{stderr, stdin, stdout, IsATTY, Stderr, Stdin, Stdout}; pub use self::stream::{ - HostInputStream, HostOutputStream, OutputStreamError, StreamRuntimeError, StreamState, - TableStreamExt, + HostInputStream, HostOutputStream, InputStream, OutputStream, OutputStreamError, + StreamRuntimeError, StreamState, }; -pub use self::table::{OccupiedEntry, Table, TableError}; +pub use self::table::{Table, TableError}; pub use cap_fs_ext::SystemTimeSpec; pub use cap_rand::RngCore; @@ -146,6 +146,17 @@ pub mod bindings { "wasi:filesystem/types"::"error-code": Error, "wasi:sockets/network"::"error-code": Error, }, + with: { + "wasi:sockets/network/network": super::network::Network, + "wasi:sockets/tcp/tcp-socket": super::tcp::TcpSocket, + "wasi:filesystem/types/directory-entry-stream": super::filesystem::ReaddirIterator, + "wasi:filesystem/types/descriptor": super::filesystem::Descriptor, + "wasi:io/streams/input-stream": super::stream::InputStream, + "wasi:io/streams/output-stream": super::stream::OutputStream, + "wasi:io/poll/pollable": super::poll::Pollable, + "wasi:cli/terminal-input/terminal-input": super::stdio::TerminalInput, + "wasi:cli/terminal-output/terminal-output": super::stdio::TerminalOutput, + }, }); pub use wasi::*; diff --git a/crates/wasi/src/preview2/network.rs b/crates/wasi/src/preview2/network.rs index 64c46f461044..f2f7bfd7de6e 100644 --- a/crates/wasi/src/preview2/network.rs +++ b/crates/wasi/src/preview2/network.rs @@ -1,34 +1,9 @@ -use crate::preview2::bindings::sockets::network::Network; -use crate::preview2::{Table, TableError}; use cap_std::net::Pool; -use wasmtime::component::Resource; -pub(crate) struct HostNetworkState(pub(crate) Pool); +pub struct Network(pub(crate) Pool); -impl HostNetworkState { +impl Network { pub fn new(pool: Pool) -> Self { Self(pool) } } - -pub(crate) trait TableNetworkExt { - fn push_network(&mut self, network: HostNetworkState) -> Result, TableError>; - fn delete_network(&mut self, fd: Resource) -> Result; - fn is_network(&self, fd: &Resource) -> bool; - fn get_network(&self, fd: &Resource) -> Result<&HostNetworkState, TableError>; -} - -impl TableNetworkExt for Table { - fn push_network(&mut self, network: HostNetworkState) -> Result, TableError> { - Ok(Resource::new_own(self.push(Box::new(network))?)) - } - fn delete_network(&mut self, fd: Resource) -> Result { - self.delete(fd.rep()) - } - fn is_network(&self, fd: &Resource) -> bool { - self.is::(fd.rep()) - } - fn get_network(&self, fd: &Resource) -> Result<&HostNetworkState, TableError> { - self.get(fd.rep()) - } -} diff --git a/crates/wasi/src/preview2/poll.rs b/crates/wasi/src/preview2/poll.rs index 8bce303639f1..68b6ce84c14f 100644 --- a/crates/wasi/src/preview2/poll.rs +++ b/crates/wasi/src/preview2/poll.rs @@ -1,7 +1,4 @@ -use crate::preview2::{ - bindings::io::poll::{self, Pollable}, - Table, TableError, WasiView, -}; +use crate::preview2::{bindings::io::poll, WasiView}; use anyhow::Result; use std::any::Any; use std::collections::{hash_map::Entry, HashMap}; @@ -19,8 +16,8 @@ pub type ClosureFuture = Box PollableFuture<'static> + Send + Sync + /// A pollable is not the same thing as a Rust Future: the same pollable may be used to /// repeatedly check for readiness of a given condition, e.g. if a stream is readable /// or writable. So, rather than containing a Future, which can only become Ready once, a -/// HostPollable contains a way to create a Future in each call to `poll_list`. -pub enum HostPollable { +/// Pollable contains a way to create a Future in each call to `poll_list`. +pub enum Pollable { /// Create a Future by calling a fn on another resource in the table. This /// indirection means the created Future can use a mut borrow of another /// resource in the Table (e.g. a stream) @@ -31,33 +28,6 @@ pub enum HostPollable { Closure(ClosureFuture), } -pub trait TablePollableExt { - fn push_host_pollable(&mut self, p: HostPollable) -> Result, TableError>; - fn get_host_pollable_mut( - &mut self, - fd: &Resource, - ) -> Result<&mut HostPollable, TableError>; - fn delete_host_pollable(&mut self, fd: Resource) -> Result; -} - -impl TablePollableExt for Table { - fn push_host_pollable(&mut self, p: HostPollable) -> Result, TableError> { - Ok(Resource::new_own(match p { - HostPollable::TableEntry { index, .. } => self.push_child(Box::new(p), index)?, - HostPollable::Closure { .. } => self.push(Box::new(p))?, - })) - } - fn get_host_pollable_mut( - &mut self, - fd: &Resource, - ) -> Result<&mut HostPollable, TableError> { - self.get_mut::(fd.rep()) - } - fn delete_host_pollable(&mut self, fd: Resource) -> Result { - self.delete::(fd.rep()) - } -} - #[async_trait::async_trait] impl poll::Host for T { async fn poll_list(&mut self, pollables: Vec>) -> Result> { @@ -70,19 +40,17 @@ impl poll::Host for T { for (ix, p) in pollables.iter().enumerate() { let ix: u32 = ix.try_into()?; - match table.get_host_pollable_mut(&p)? { - HostPollable::Closure(f) => closure_futures.push((f(), vec![ix])), - HostPollable::TableEntry { index, make_future } => { - match table_futures.entry(*index) { - Entry::Vacant(v) => { - v.insert((*make_future, vec![ix])); - } - Entry::Occupied(mut o) => { - let (_, v) = o.get_mut(); - v.push(ix); - } + match table.get_resource_mut(&p)? { + Pollable::Closure(f) => closure_futures.push((f(), vec![ix])), + Pollable::TableEntry { index, make_future } => match table_futures.entry(*index) { + Entry::Vacant(v) => { + v.insert((*make_future, vec![ix])); } - } + Entry::Occupied(mut o) => { + let (_, v) = o.get_mut(); + v.push(ix); + } + }, } } @@ -133,9 +101,9 @@ impl poll::Host for T { let table = self.table_mut(); - let closure_future = match table.get_host_pollable_mut(&pollable)? { - HostPollable::Closure(f) => f(), - HostPollable::TableEntry { index, make_future } => { + let closure_future = match table.get_resource_mut(&pollable)? { + Pollable::Closure(f) => f(), + Pollable::TableEntry { index, make_future } => { let index = *index; let make_future = *make_future; make_future(table.get_as_any_mut(index)?) @@ -149,14 +117,14 @@ impl poll::Host for T { #[async_trait::async_trait] impl crate::preview2::bindings::io::poll::HostPollable for T { fn drop(&mut self, pollable: Resource) -> Result<()> { - self.table_mut().delete_host_pollable(pollable)?; + self.table_mut().delete_resource(pollable)?; Ok(()) } } pub mod sync { use crate::preview2::{ - bindings::io::poll::{Host as AsyncHost, HostPollable as AsyncHostPollable}, + bindings::io::poll as async_poll, bindings::sync_io::io::poll::{self, Pollable}, in_tokio, WasiView, }; @@ -165,17 +133,17 @@ pub mod sync { impl poll::Host for T { fn poll_list(&mut self, pollables: Vec>) -> Result> { - in_tokio(async { AsyncHost::poll_list(self, pollables).await }) + in_tokio(async { async_poll::Host::poll_list(self, pollables).await }) } fn poll_one(&mut self, pollable: Resource) -> Result<()> { - in_tokio(async { AsyncHost::poll_one(self, pollable).await }) + in_tokio(async { async_poll::Host::poll_one(self, pollable).await }) } } impl crate::preview2::bindings::sync_io::io::poll::HostPollable for T { fn drop(&mut self, pollable: Resource) -> Result<()> { - AsyncHostPollable::drop(self, pollable) + async_poll::HostPollable::drop(self, pollable) } } } diff --git a/crates/wasi/src/preview2/preview1.rs b/crates/wasi/src/preview2/preview1.rs index 4a432704de0e..b035796cd8f3 100644 --- a/crates/wasi/src/preview2/preview1.rs +++ b/crates/wasi/src/preview2/preview1.rs @@ -6,14 +6,11 @@ use crate::preview2::bindings::clocks::{monotonic_clock, wall_clock}; use crate::preview2::bindings::filesystem::{preopens, types as filesystem}; use crate::preview2::bindings::io::poll; use crate::preview2::bindings::io::streams; -use crate::preview2::filesystem::TableFsExt; -use crate::preview2::host::filesystem::TableReaddirExt; use crate::preview2::{bindings, IsATTY, TableError, WasiView}; use anyhow::{anyhow, bail, Context}; use std::borrow::Borrow; -use std::cell::Cell; use std::collections::BTreeMap; -use std::mem::{size_of, size_of_val}; +use std::mem::{self, size_of, size_of_val}; use std::ops::{Deref, DerefMut}; use std::slice; use std::sync::atomic::{AtomicU64, Ordering}; @@ -22,10 +19,10 @@ use wasmtime::component::Resource; use wiggle::tracing::instrument; use wiggle::{GuestError, GuestPtr, GuestSlice, GuestSliceMut, GuestStrCow, GuestType}; -#[derive(Clone, Debug)] +#[derive(Debug)] struct File { /// The handle to the preview2 descriptor that this file is referencing. - fd: u32, + fd: Resource, /// The current-position pointer. position: Arc, @@ -128,12 +125,12 @@ impl BlockingMode { } } -#[derive(Clone, Debug)] +#[derive(Debug)] enum Descriptor { Stdin { input_stream: u32, isatty: IsATTY }, Stdout { output_stream: u32, isatty: IsATTY }, Stderr { output_stream: u32, isatty: IsATTY }, - PreopenDirectory((u32, String)), + PreopenDirectory((Resource, String)), File(File), } @@ -240,7 +237,7 @@ impl Descriptors { .context("failed to call `get-directories`") .map_err(types::Error::trap)? { - descriptors.push(Descriptor::PreopenDirectory((dir.0.rep(), dir.1)))?; + descriptors.push(Descriptor::PreopenDirectory((dir.0, dir.1)))?; } Ok(descriptors) } @@ -316,13 +313,13 @@ pub trait WasiPreview1View: WasiView { // call methods like [`TableFsExt::is_file`] and hiding complexity from preview1 method implementations. struct Transaction<'a, T: WasiPreview1View + ?Sized> { view: &'a mut T, - descriptors: Cell, + descriptors: Descriptors, } impl Drop for Transaction<'_, T> { /// Record changes in the [`WasiPreview1Adapter`] returned by [`WasiPreview1View::adapter_mut`] fn drop(&mut self) { - let descriptors = self.descriptors.take(); + let descriptors = mem::take(&mut self.descriptors); self.view.adapter_mut().descriptors = Some(descriptors); } } @@ -333,24 +330,19 @@ impl Transaction<'_, T> { /// # Errors /// /// Returns [`types::Errno::Badf`] if no [`Descriptor`] is found - fn get_descriptor(&mut self, fd: types::Fd) -> Result<&Descriptor> { + fn get_descriptor(&self, fd: types::Fd) -> Result<&Descriptor> { let fd = fd.into(); - let desc = self - .descriptors - .get_mut() - .get(&fd) - .ok_or(types::Errno::Badf)?; + let desc = self.descriptors.get(&fd).ok_or(types::Errno::Badf)?; Ok(desc) } /// Borrows [`File`] corresponding to `fd` /// if it describes a [`Descriptor::File`] of [`crate::preview2::filesystem::File`] type - fn get_file(&mut self, fd: types::Fd) -> Result<&File> { + fn get_file(&self, fd: types::Fd) -> Result<&File> { let fd = fd.into(); - match self.descriptors.get_mut().get(&fd) { - Some(Descriptor::File(file @ File { fd, .. })) - if self.view.table().is_file(&Resource::new_borrow(*fd)) => - { + match self.descriptors.get(&fd) { + Some(Descriptor::File(file @ File { fd, .. })) => { + self.view.table().get_resource(fd)?.file()?; Ok(file) } _ => Err(types::Errno::Badf.into()), @@ -361,10 +353,9 @@ impl Transaction<'_, T> { /// if it describes a [`Descriptor::File`] of [`crate::preview2::filesystem::File`] type fn get_file_mut(&mut self, fd: types::Fd) -> Result<&mut File> { let fd = fd.into(); - match self.descriptors.get_mut().get_mut(&fd) { - Some(Descriptor::File(file)) - if self.view.table().is_file(&Resource::new_borrow(file.fd)) => - { + match self.descriptors.get_mut(&fd) { + Some(Descriptor::File(file)) => { + self.view.table().get_resource(&file.fd)?.file()?; Ok(file) } _ => Err(types::Errno::Badf.into()), @@ -377,11 +368,11 @@ impl Transaction<'_, T> { /// # Errors /// /// Returns [`types::Errno::Spipe`] if the descriptor corresponds to stdio - fn get_seekable(&mut self, fd: types::Fd) -> Result<&File> { + fn get_seekable(&self, fd: types::Fd) -> Result<&File> { let fd = fd.into(); - match self.descriptors.get_mut().get(&fd) { + match self.descriptors.get(&fd) { Some(Descriptor::File(file @ File { fd, .. })) - if self.view.table().is_file(&Resource::new_borrow(*fd)) => + if self.view.table().get_resource(fd)?.is_file() => { Ok(file) } @@ -396,10 +387,10 @@ impl Transaction<'_, T> { } /// Returns [`filesystem::Descriptor`] corresponding to `fd` - fn get_fd(&mut self, fd: types::Fd) -> Result> { + fn get_fd(&self, fd: types::Fd) -> Result> { match self.get_descriptor(fd)? { - Descriptor::File(File { fd, .. }) => Ok(Resource::new_borrow(*fd)), - Descriptor::PreopenDirectory((fd, _)) => Ok(Resource::new_borrow(*fd)), + Descriptor::File(File { fd, .. }) => Ok(fd.borrowed()), + Descriptor::PreopenDirectory((fd, _)) => Ok(fd.borrowed()), Descriptor::Stdin { .. } | Descriptor::Stdout { .. } | Descriptor::Stderr { .. } => { Err(types::Errno::Badf.into()) } @@ -408,23 +399,22 @@ impl Transaction<'_, T> { /// Returns [`filesystem::Descriptor`] corresponding to `fd` /// if it describes a [`Descriptor::File`] of [`crate::preview2::filesystem::File`] type - fn get_file_fd(&mut self, fd: types::Fd) -> Result> { - self.get_file(fd) - .map(|File { fd, .. }| Resource::new_borrow(*fd)) + fn get_file_fd(&self, fd: types::Fd) -> Result> { + self.get_file(fd).map(|File { fd, .. }| fd.borrowed()) } /// Returns [`filesystem::Descriptor`] corresponding to `fd` /// if it describes a [`Descriptor::File`] or [`Descriptor::PreopenDirectory`] /// of [`crate::preview2::filesystem::Dir`] type - fn get_dir_fd(&mut self, fd: types::Fd) -> Result> { + fn get_dir_fd(&self, fd: types::Fd) -> Result> { let fd = fd.into(); - match self.descriptors.get_mut().get(&fd) { + match self.descriptors.get(&fd) { Some(Descriptor::File(File { fd, .. })) - if self.view.table().is_dir(&Resource::new_borrow(*fd)) => + if self.view.table().get_resource(fd)?.is_dir() => { - Ok(Resource::new_borrow(*fd)) + Ok(fd.borrowed()) } - Some(Descriptor::PreopenDirectory((fd, _))) => Ok(Resource::new_borrow(*fd)), + Some(Descriptor::PreopenDirectory((fd, _))) => Ok(fd.borrowed()), _ => Err(types::Errno::Badf.into()), } } @@ -460,7 +450,7 @@ trait WasiPreview1ViewExt: /// Lazily initializes [`WasiPreview1Adapter`] returned by [`WasiPreview1View::adapter_mut`] /// and returns [`filesystem::Descriptor`] corresponding to `fd` fn get_fd(&mut self, fd: types::Fd) -> Result, types::Error> { - let mut st = self.transact()?; + let st = self.transact()?; let fd = st.get_fd(fd)?; Ok(fd) } @@ -472,7 +462,7 @@ trait WasiPreview1ViewExt: &mut self, fd: types::Fd, ) -> Result, types::Error> { - let mut st = self.transact()?; + let st = self.transact()?; let fd = st.get_file_fd(fd)?; Ok(fd) } @@ -485,7 +475,7 @@ trait WasiPreview1ViewExt: &mut self, fd: types::Fd, ) -> Result, types::Error> { - let mut st = self.transact()?; + let st = self.transact()?; let fd = st.get_dir_fd(fd)?; Ok(fd) } @@ -1029,10 +1019,8 @@ impl< let desc = self .transact()? .descriptors - .get_mut() .remove(fd) - .ok_or(types::Errno::Badf)? - .clone(); + .ok_or(types::Errno::Badf)?; match desc { Descriptor::Stdin { input_stream, .. } => { streams::HostInputStream::drop(self, Resource::new_own(input_stream)) @@ -1043,7 +1031,7 @@ impl< .context("failed to call `drop-output-stream`") } Descriptor::File(File { fd, .. }) | Descriptor::PreopenDirectory((fd, _)) => { - filesystem::HostDescriptor::drop(self, Resource::new_own(fd)) + filesystem::HostDescriptor::drop(self, fd) .context("failed to call `drop-descriptor`") } } @@ -1131,18 +1119,15 @@ impl< blocking_mode, append, .. - }) => (*fd, *blocking_mode, *append), + }) => (fd.borrowed(), *blocking_mode, *append), }; - let flags = self - .get_flags(Resource::new_borrow(fd)) - .await - .map_err(|e| { - e.try_into() - .context("failed to call `get-flags`") - .unwrap_or_else(types::Error::trap) - })?; + let flags = self.get_flags(fd.borrowed()).await.map_err(|e| { + e.try_into() + .context("failed to call `get-flags`") + .unwrap_or_else(types::Error::trap) + })?; let fs_filetype = self - .get_type(Resource::new_borrow(fd)) + .get_type(fd.borrowed()) .await .map_err(|e| { e.try_into() @@ -1224,14 +1209,15 @@ impl< /// Return the attributes of an open file. #[instrument(skip(self))] async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result { - let desc = self.transact()?.get_descriptor(fd)?.clone(); + let t = self.transact()?; + let desc = t.get_descriptor(fd)?; match desc { Descriptor::Stdin { isatty, .. } | Descriptor::Stdout { isatty, .. } | Descriptor::Stderr { isatty, .. } => Ok(types::Filestat { dev: 0, ino: 0, - filetype: isatty.into(), + filetype: (*isatty).into(), nlink: 0, size: 0, atim: 0, @@ -1239,6 +1225,8 @@ impl< ctim: 0, }), Descriptor::PreopenDirectory((fd, _)) | Descriptor::File(File { fd, .. }) => { + let fd = fd.borrowed(); + drop(t); let filesystem::DescriptorStat { type_, link_count: nlink, @@ -1246,19 +1234,16 @@ impl< data_access_timestamp, data_modification_timestamp, status_change_timestamp, - } = self.stat(Resource::new_borrow(fd)).await.map_err(|e| { + } = self.stat(fd.borrowed()).await.map_err(|e| { e.try_into() .context("failed to call `stat`") .unwrap_or_else(types::Error::trap) })?; - let metadata_hash = - self.metadata_hash(Resource::new_borrow(fd)) - .await - .map_err(|e| { - e.try_into() - .context("failed to call `metadata_hash`") - .unwrap_or_else(types::Error::trap) - })?; + let metadata_hash = self.metadata_hash(fd).await.map_err(|e| { + e.try_into() + .context("failed to call `metadata_hash`") + .unwrap_or_else(types::Error::trap) + })?; let filetype = type_.try_into().map_err(types::Error::trap)?; let zero = wall_clock::Datetime { seconds: 0, @@ -1334,26 +1319,29 @@ impl< fd: types::Fd, iovs: &types::IovecArray<'a>, ) -> Result { - let desc = self.transact()?.get_descriptor(fd)?.clone(); + let t = self.transact()?; + let desc = t.get_descriptor(fd)?; let (mut buf, read, state) = match desc { Descriptor::File(File { fd, blocking_mode, position, .. - }) if self.table().is_file(&Resource::new_borrow(fd)) => { + }) if t.view.table().get_resource(fd)?.is_file() => { + let fd = fd.borrowed(); + let blocking_mode = *blocking_mode; + let position = position.clone(); + drop(t); let Some(buf) = first_non_empty_iovec(iovs)? else { return Ok(0); }; let pos = position.load(Ordering::Relaxed); - let stream = self - .read_via_stream(Resource::new_borrow(fd), pos) - .map_err(|e| { - e.try_into() - .context("failed to call `read-via-stream`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self.read_via_stream(fd.borrowed(), pos).map_err(|e| { + e.try_into() + .context("failed to call `read-via-stream`") + .unwrap_or_else(types::Error::trap) + })?; let (read, state) = blocking_mode.read(self, stream, buf.len()).await?; let n = read.len().try_into()?; let pos = pos.checked_add(n).ok_or(types::Errno::Overflow)?; @@ -1362,13 +1350,15 @@ impl< (buf, read, state) } Descriptor::Stdin { input_stream, .. } => { + let input = Resource::new_borrow(*input_stream); + drop(t); let Some(buf) = first_non_empty_iovec(iovs)? else { return Ok(0); }; let (read, state) = stream_res( streams::HostInputStream::blocking_read( self, - Resource::new_borrow(input_stream), + input, buf.len().try_into().unwrap_or(u64::MAX), ) .await, @@ -1398,22 +1388,24 @@ impl< iovs: &types::IovecArray<'a>, offset: types::Filesize, ) -> Result { - let desc = self.transact()?.get_descriptor(fd)?.clone(); + let t = self.transact()?; + let desc = t.get_descriptor(fd)?; let (mut buf, read, state) = match desc { Descriptor::File(File { fd, blocking_mode, .. - }) if self.table().is_file(&Resource::new_borrow(fd)) => { + }) if t.view.table().get_resource(fd)?.is_file() => { + let fd = fd.borrowed(); + let blocking_mode = *blocking_mode; + drop(t); let Some(buf) = first_non_empty_iovec(iovs)? else { return Ok(0); }; - let stream = self - .read_via_stream(Resource::new_borrow(fd), offset) - .map_err(|e| { - e.try_into() - .context("failed to call `read-via-stream`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self.read_via_stream(fd, offset).map_err(|e| { + e.try_into() + .context("failed to call `read-via-stream`") + .unwrap_or_else(types::Error::trap) + })?; let (read, state) = blocking_mode.read(self, stream, buf.len()).await?; (buf, read, state) } @@ -1443,30 +1435,32 @@ impl< fd: types::Fd, ciovs: &types::CiovecArray<'a>, ) -> Result { - let desc = self.transact()?.get_descriptor(fd)?.clone(); - match desc { + let t = self.transact()?; + let desc = t.get_descriptor(fd)?; + match *desc { Descriptor::File(File { - fd, + ref fd, blocking_mode, append, - position, - }) if self.table().is_file(&Resource::new_borrow(fd)) => { + ref position, + }) if t.view.table().get_resource(fd)?.is_file() => { + let fd = fd.borrowed(); + let position = position.clone(); + drop(t); let Some(buf) = first_non_empty_ciovec(ciovs)? else { return Ok(0); }; let (stream, pos) = if append { - let stream = self - .append_via_stream(Resource::new_borrow(fd)) - .map_err(|e| { - e.try_into() - .context("failed to call `append-via-stream`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self.append_via_stream(fd.borrowed()).map_err(|e| { + e.try_into() + .context("failed to call `append-via-stream`") + .unwrap_or_else(types::Error::trap) + })?; (stream, 0) } else { let position = position.load(Ordering::Relaxed); let stream = self - .write_via_stream(Resource::new_borrow(fd), position) + .write_via_stream(fd.borrowed(), position) .map_err(|e| { e.try_into() .context("failed to call `write-via-stream`") @@ -1482,6 +1476,7 @@ impl< Ok(n.try_into()?) } Descriptor::Stdout { output_stream, .. } | Descriptor::Stderr { output_stream, .. } => { + drop(t); let Some(buf) = first_non_empty_ciovec(ciovs)? else { return Ok(0); }; @@ -1503,21 +1498,24 @@ impl< ciovs: &types::CiovecArray<'a>, offset: types::Filesize, ) -> Result { - let desc = self.transact()?.get_descriptor(fd)?.clone(); - let n = match desc { + let t = self.transact()?; + let desc = t.get_descriptor(fd)?; + let n = match *desc { Descriptor::File(File { - fd, blocking_mode, .. - }) if self.table().is_file(&Resource::new_borrow(fd)) => { + ref fd, + blocking_mode, + .. + }) if t.view.table().get_resource(fd)?.is_file() => { + let fd = fd.borrowed(); + drop(t); let Some(buf) = first_non_empty_ciovec(ciovs)? else { return Ok(0); }; - let stream = self - .write_via_stream(Resource::new_borrow(fd), offset) - .map_err(|e| { - e.try_into() - .context("failed to call `write-via-stream`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self.write_via_stream(fd, offset).map_err(|e| { + e.try_into() + .context("failed to call `write-via-stream`") + .unwrap_or_else(types::Error::trap) + })?; blocking_mode.write(self, stream, &buf).await? } Descriptor::Stdout { .. } | Descriptor::Stderr { .. } => { @@ -1562,9 +1560,8 @@ impl< #[instrument(skip(self))] fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), types::Error> { let mut st = self.transact()?; - let descriptors = st.descriptors.get_mut(); - let desc = descriptors.remove(from).ok_or(types::Errno::Badf)?; - descriptors.insert(to.into(), desc); + let desc = st.descriptors.remove(from).ok_or(types::Errno::Badf)?; + st.descriptors.insert(to.into(), desc); Ok(()) } @@ -1577,11 +1574,11 @@ impl< offset: types::Filedelta, whence: types::Whence, ) -> Result { - let (fd, position) = { - let mut st = self.transact()?; - let File { fd, position, .. } = st.get_seekable(fd)?; - (*fd, Arc::clone(&position)) - }; + let t = self.transact()?; + let File { fd, position, .. } = t.get_seekable(fd)?; + let fd = fd.borrowed(); + let position = position.clone(); + drop(t); let pos = match whence { types::Whence::Set if offset >= 0 => offset as _, types::Whence::Cur => position @@ -1589,12 +1586,11 @@ impl< .checked_add_signed(offset) .ok_or(types::Errno::Inval)?, types::Whence::End => { - let filesystem::DescriptorStat { size, .. } = - self.stat(Resource::new_borrow(fd)).await.map_err(|e| { - e.try_into() - .context("failed to call `stat`") - .unwrap_or_else(types::Error::trap) - })?; + let filesystem::DescriptorStat { size, .. } = self.stat(fd).await.map_err(|e| { + e.try_into() + .context("failed to call `stat`") + .unwrap_or_else(types::Error::trap) + })?; size.checked_add_signed(offset).ok_or(types::Errno::Inval)? } _ => return Err(types::Errno::Inval.into()), @@ -1678,7 +1674,7 @@ impl< for (entry, d_next) in self .table_mut() // remove iterator from table and use it directly: - .delete_readdir(stream)? + .delete_resource(stream)? .into_iter() .zip(3u64..) { @@ -1914,21 +1910,19 @@ impl< flags |= filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC; } - let desc = self.transact()?.get_descriptor(dirfd)?.clone(); - let dirfd = match desc { - Descriptor::PreopenDirectory((fd, _)) => fd, - Descriptor::File(File { fd, .. }) if self.table().is_dir(&Resource::new_borrow(fd)) => { - fd - } - Descriptor::File(File { fd: _, .. }) => { - // NOTE: Unlike most other methods, legacy implementation returns `NOTDIR` here - return Err(types::Errno::Notdir.into()); + let t = self.transact()?; + let dirfd = match t.get_descriptor(dirfd)? { + Descriptor::PreopenDirectory((fd, _)) => fd.borrowed(), + Descriptor::File(File { fd, .. }) => { + t.view.table().get_resource(fd)?.dir()?; + fd.borrowed() } _ => return Err(types::Errno::Badf.into()), }; + drop(t); let fd = self .open_at( - Resource::new_borrow(dirfd), + dirfd, dirflags.into(), path, oflags.into(), @@ -1941,8 +1935,8 @@ impl< .context("failed to call `open-at`") .unwrap_or_else(types::Error::trap) })?; - let fd = self.transact()?.descriptors.get_mut().push_file(File { - fd: fd.rep(), + let fd = self.transact()?.descriptors.push_file(File { + fd, position: Default::default(), append: fdflags.contains(types::Fdflags::APPEND), blocking_mode: BlockingMode::from_fdflags(&fdflags), @@ -2140,3 +2134,13 @@ impl< todo!("preview1 sock_shutdown is not implemented") } } + +trait ResourceExt { + fn borrowed(&self) -> Resource; +} + +impl ResourceExt for Resource { + fn borrowed(&self) -> Resource { + Resource::new_borrow(self.rep()) + } +} diff --git a/crates/wasi/src/preview2/stdio.rs b/crates/wasi/src/preview2/stdio.rs index e7a6d667ae81..b13aabf5215f 100644 --- a/crates/wasi/src/preview2/stdio.rs +++ b/crates/wasi/src/preview2/stdio.rs @@ -4,7 +4,6 @@ use crate::preview2::bindings::cli::{ }; use crate::preview2::bindings::io::streams; use crate::preview2::pipe::{self, AsyncWriteStream}; -use crate::preview2::stream::TableStreamExt; use crate::preview2::{HostInputStream, HostOutputStream, WasiView}; use std::io::IsTerminal; use wasmtime::component::Resource; @@ -160,72 +159,68 @@ pub enum IsATTY { impl stdin::Host for T { fn get_stdin(&mut self) -> Result, anyhow::Error> { let stream = self.ctx_mut().stdin.stream(); - Ok(self.table_mut().push_input_stream(stream)?) + Ok(self + .table_mut() + .push_resource(streams::InputStream::Host(stream))?) } } impl stdout::Host for T { fn get_stdout(&mut self) -> Result, anyhow::Error> { let stream = self.ctx_mut().stdout.stream(); - Ok(self.table_mut().push_output_stream(stream)?) + Ok(self.table_mut().push_resource(stream)?) } } impl stderr::Host for T { fn get_stderr(&mut self) -> Result, anyhow::Error> { let stream = self.ctx_mut().stderr.stream(); - Ok(self.table_mut().push_output_stream(stream)?) + Ok(self.table_mut().push_resource(stream)?) } } -pub struct HostTerminalInput; -pub struct HostTerminalOutput; +pub struct TerminalInput; +pub struct TerminalOutput; impl terminal_input::Host for T {} -impl crate::preview2::bindings::cli::terminal_input::HostTerminalInput for T { - fn drop(&mut self, r: Resource) -> anyhow::Result<()> { - self.table_mut().delete::(r.rep())?; +impl terminal_input::HostTerminalInput for T { + fn drop(&mut self, r: Resource) -> anyhow::Result<()> { + self.table_mut().delete_resource(r)?; Ok(()) } } impl terminal_output::Host for T {} -impl crate::preview2::bindings::cli::terminal_output::HostTerminalOutput for T { - fn drop(&mut self, r: Resource) -> anyhow::Result<()> { - self.table_mut().delete::(r.rep())?; +impl terminal_output::HostTerminalOutput for T { + fn drop(&mut self, r: Resource) -> anyhow::Result<()> { + self.table_mut().delete_resource(r)?; Ok(()) } } impl terminal_stdin::Host for T { - fn get_terminal_stdin( - &mut self, - ) -> anyhow::Result>> { + fn get_terminal_stdin(&mut self) -> anyhow::Result>> { if self.ctx().stdin.isatty() { - let fd = self.table_mut().push(Box::new(HostTerminalInput))?; - Ok(Some(Resource::new_own(fd))) + let fd = self.table_mut().push_resource(TerminalInput)?; + Ok(Some(fd)) } else { Ok(None) } } } impl terminal_stdout::Host for T { - fn get_terminal_stdout( - &mut self, - ) -> anyhow::Result>> { + fn get_terminal_stdout(&mut self) -> anyhow::Result>> { if self.ctx().stdout.isatty() { - let fd = self.table_mut().push(Box::new(HostTerminalOutput))?; - Ok(Some(Resource::new_own(fd))) + let fd = self.table_mut().push_resource(TerminalOutput)?; + Ok(Some(fd)) } else { Ok(None) } } } impl terminal_stderr::Host for T { - fn get_terminal_stderr( - &mut self, - ) -> anyhow::Result>> { + fn get_terminal_stderr(&mut self) -> anyhow::Result>> { if self.ctx().stderr.isatty() { - let fd = self.table_mut().push(Box::new(HostTerminalOutput))?; - Ok(Some(Resource::new_own(fd))) + let fd = self.table_mut().push_resource(TerminalOutput)?; + Ok(Some(fd)) } else { Ok(None) } diff --git a/crates/wasi/src/preview2/stream.rs b/crates/wasi/src/preview2/stream.rs index 7a61747d2511..03a0ac1f0d46 100644 --- a/crates/wasi/src/preview2/stream.rs +++ b/crates/wasi/src/preview2/stream.rs @@ -1,10 +1,7 @@ -use crate::preview2::bindings::io::streams::{InputStream, OutputStream}; use crate::preview2::filesystem::FileInputStream; -use crate::preview2::{Table, TableError}; use anyhow::Error; use bytes::Bytes; use std::fmt; -use wasmtime::component::Resource; /// An error which should be reported to Wasm as a runtime error, rather than /// an error which should trap Wasm execution. The definition for runtime @@ -168,204 +165,9 @@ pub trait HostOutputStream: Send + Sync { } } -pub(crate) enum InternalInputStream { +pub enum InputStream { Host(Box), File(FileInputStream), } -pub(crate) trait InternalTableStreamExt { - fn push_internal_input_stream( - &mut self, - istream: InternalInputStream, - ) -> Result, TableError>; - fn push_internal_input_stream_child( - &mut self, - istream: InternalInputStream, - parent: Resource, - ) -> Result, TableError>; - fn get_internal_input_stream_mut( - &mut self, - fd: &Resource, - ) -> Result<&mut InternalInputStream, TableError>; - fn delete_internal_input_stream( - &mut self, - fd: Resource, - ) -> Result; -} -impl InternalTableStreamExt for Table { - fn push_internal_input_stream( - &mut self, - istream: InternalInputStream, - ) -> Result, TableError> { - Ok(Resource::new_own(self.push(Box::new(istream))?)) - } - fn push_internal_input_stream_child( - &mut self, - istream: InternalInputStream, - parent: Resource, - ) -> Result, TableError> { - Ok(Resource::new_own( - self.push_child(Box::new(istream), parent.rep())?, - )) - } - fn get_internal_input_stream_mut( - &mut self, - fd: &Resource, - ) -> Result<&mut InternalInputStream, TableError> { - self.get_mut(fd.rep()) - } - fn delete_internal_input_stream( - &mut self, - fd: Resource, - ) -> Result { - self.delete(fd.rep()) - } -} - -/// Extension trait for managing [`HostInputStream`]s and [`HostOutputStream`]s in the [`Table`]. -pub trait TableStreamExt { - /// Push a [`HostInputStream`] into a [`Table`], returning the table index. - fn push_input_stream( - &mut self, - istream: Box, - ) -> Result, TableError>; - /// Same as [`push_input_stream`](Self::push_output_stream) except assigns a parent resource to - /// the input-stream created. - fn push_input_stream_child( - &mut self, - istream: Box, - parent: Resource, - ) -> Result, TableError>; - /// Get a mutable reference to a [`HostInputStream`] in a [`Table`]. - fn get_input_stream_mut( - &mut self, - fd: &Resource, - ) -> Result<&mut dyn HostInputStream, TableError>; - /// Remove [`HostInputStream`] from table: - fn delete_input_stream( - &mut self, - fd: Resource, - ) -> Result, TableError>; - - /// Push a [`HostOutputStream`] into a [`Table`], returning the table index. - fn push_output_stream( - &mut self, - ostream: Box, - ) -> Result, TableError>; - /// Same as [`push_output_stream`](Self::push_output_stream) except assigns a parent resource - /// to the output-stream created. - fn push_output_stream_child( - &mut self, - ostream: Box, - parent: Resource, - ) -> Result, TableError>; - /// Get a mutable reference to a [`HostOutputStream`] in a [`Table`]. - fn get_output_stream_mut( - &mut self, - fd: &Resource, - ) -> Result<&mut dyn HostOutputStream, TableError>; - - /// Remove [`HostOutputStream`] from table: - fn delete_output_stream( - &mut self, - fd: Resource, - ) -> Result, TableError>; -} -impl TableStreamExt for Table { - fn push_input_stream( - &mut self, - istream: Box, - ) -> Result, TableError> { - self.push_internal_input_stream(InternalInputStream::Host(istream)) - } - fn push_input_stream_child( - &mut self, - istream: Box, - parent: Resource, - ) -> Result, TableError> { - self.push_internal_input_stream_child(InternalInputStream::Host(istream), parent) - } - fn get_input_stream_mut( - &mut self, - fd: &Resource, - ) -> Result<&mut dyn HostInputStream, TableError> { - match self.get_internal_input_stream_mut(fd)? { - InternalInputStream::Host(ref mut h) => Ok(h.as_mut()), - _ => Err(TableError::WrongType), - } - } - fn delete_input_stream( - &mut self, - fd: Resource, - ) -> Result, TableError> { - let occ = self.entry(fd.rep())?; - match occ.get().downcast_ref::() { - Some(InternalInputStream::Host(_)) => { - let any = occ.remove_entry()?; - match *any.downcast().expect("downcast checked above") { - InternalInputStream::Host(h) => Ok(h), - _ => unreachable!("variant checked above"), - } - } - _ => Err(TableError::WrongType), - } - } - - fn push_output_stream( - &mut self, - ostream: Box, - ) -> Result, TableError> { - Ok(Resource::new_own(self.push(Box::new(ostream))?)) - } - fn push_output_stream_child( - &mut self, - ostream: Box, - parent: Resource, - ) -> Result, TableError> { - Ok(Resource::new_own( - self.push_child(Box::new(ostream), parent.rep())?, - )) - } - fn get_output_stream_mut( - &mut self, - fd: &Resource, - ) -> Result<&mut dyn HostOutputStream, TableError> { - let boxed: &mut Box = self.get_mut(fd.rep())?; - Ok(boxed.as_mut()) - } - fn delete_output_stream( - &mut self, - fd: Resource, - ) -> Result, TableError> { - self.delete(fd.rep()) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn input_stream_in_table() { - let dummy = crate::preview2::pipe::ClosedInputStream; - let mut table = Table::new(); - // Put it into the table: - let ix = table.push_input_stream(Box::new(dummy)).unwrap(); - // Get a mut ref to it: - let _ = table.get_input_stream_mut(&ix).unwrap(); - // Delete it: - let _ = table.delete_input_stream(ix).unwrap(); - } - - #[test] - fn output_stream_in_table() { - let dummy = crate::preview2::pipe::SinkOutputStream; - let mut table = Table::new(); - // Put it in the table: - let ix = table.push_output_stream(Box::new(dummy)).unwrap(); - // Get a mut ref to it: - let _ = table.get_output_stream_mut(&ix).unwrap(); - // Delete it: - let _ = table.delete_output_stream(ix).unwrap(); - } -} +pub type OutputStream = Box; diff --git a/crates/wasi/src/preview2/table.rs b/crates/wasi/src/preview2/table.rs index c8d8cb142973..151ca2a0124f 100644 --- a/crates/wasi/src/preview2/table.rs +++ b/crates/wasi/src/preview2/table.rs @@ -1,5 +1,6 @@ use std::any::Any; use std::collections::{BTreeSet, HashMap}; +use wasmtime::component::Resource; #[derive(thiserror::Error, Debug)] pub enum TableError { @@ -63,32 +64,6 @@ impl TableEntry { } } -/// Like [`std::collections::hash_map::OccupiedEntry`], with a subset of -/// methods available in order to uphold [`Table`] invariants. -pub struct OccupiedEntry<'a> { - table: &'a mut Table, - index: u32, -} -impl<'a> OccupiedEntry<'a> { - /// Get the dynamically-typed reference to the resource. - pub fn get(&self) -> &(dyn Any + Send + Sync + 'static) { - self.table.map.get(&self.index).unwrap().entry.as_ref() - } - /// Get the dynamically-typed mutable reference to the resource. - pub fn get_mut(&mut self) -> &mut (dyn Any + Send + Sync + 'static) { - self.table.map.get_mut(&self.index).unwrap().entry.as_mut() - } - /// Remove the resource from the table, returning the contents of the - /// resource. - /// May fail with [`TableError::HasChildren`] if the entry has any - /// children, see [`Table::push_child`]. - /// If this method fails, the [`OccupiedEntry`] is consumed, but the - /// resource remains in the table. - pub fn remove_entry(self) -> Result, TableError> { - self.table.delete_entry(self.index).map(|e| e.entry) - } -} - impl Table { /// Create an empty table pub fn new() -> Self { @@ -107,13 +82,21 @@ impl Table { self.push_(TableEntry::new(entry, None)) } + /// Same as `push`, but typed. + pub fn push_resource(&mut self, entry: T) -> Result, TableError> + where + T: Send + Sync + 'static, + { + let idx = self.push(Box::new(entry))?; + Ok(Resource::new_own(idx)) + } + /// Insert a resource at the next available index, and track that it has a /// parent resource. /// /// The parent must exist to create a child. All children resources must /// be destroyed before a parent can be destroyed - otherwise [`Table::delete`] - /// or [`OccupiedEntry::remove_entry`] will fail with - /// [`TableError::HasChildren`]. + /// will fail with [`TableError::HasChildren`]. /// /// Parent-child relationships are tracked inside the table to ensure that /// a parent resource is not deleted while it has live children. This @@ -142,6 +125,20 @@ impl Table { Ok(child) } + /// Same as `push_child`, but typed. + pub fn push_child_resource( + &mut self, + entry: T, + parent: &Resource, + ) -> Result, TableError> + where + T: Send + Sync + 'static, + U: 'static, + { + let idx = self.push_child(Box::new(entry), parent.rep())?; + Ok(Resource::new_own(idx)) + } + fn push_(&mut self, e: TableEntry) -> Result { // NOTE: The performance of this new key calculation could be very bad once keys wrap // around. @@ -216,16 +213,17 @@ impl Table { } } - /// Get an [`OccupiedEntry`] corresponding to a table entry, if it exists. This allows you to - /// remove or replace the entry based on its contents. The methods available are a subset of - /// [`std::collections::hash_map::OccupiedEntry`] - it does not give access to the key, it - /// restricts replacing the entry to items of the same type, and it does not allow for deletion. - pub fn entry<'a>(&'a mut self, index: u32) -> Result, TableError> { - if self.map.contains_key(&index) { - Ok(OccupiedEntry { table: self, index }) - } else { - Err(TableError::NotPresent) - } + /// Same as `get`, but typed + pub fn get_resource(&self, key: &Resource) -> Result<&T, TableError> { + self.get(key.rep()) + } + + /// Same as `get_mut`, but typed + pub fn get_resource_mut( + &mut self, + key: &Resource, + ) -> Result<&mut T, TableError> { + self.get_mut(key.rep()) } fn delete_entry(&mut self, key: u32) -> Result { @@ -283,6 +281,15 @@ impl Table { } } + /// Same as `delete`, but typed + pub fn delete_resource(&mut self, resource: Resource) -> Result + where + T: Any, + { + debug_assert!(resource.owned()); + self.delete(resource.rep()) + } + /// Zip the values of the map with mutable references to table entries corresponding to each /// key. As the keys in the [HashMap] are unique, this iterator can give mutable references /// with the same lifetime as the mutable reference to the [Table]. diff --git a/crates/wasi/src/preview2/tcp.rs b/crates/wasi/src/preview2/tcp.rs index eb3dbe4cee0e..50b8a07c2f47 100644 --- a/crates/wasi/src/preview2/tcp.rs +++ b/crates/wasi/src/preview2/tcp.rs @@ -1,20 +1,17 @@ use super::{HostInputStream, HostOutputStream, OutputStreamError}; -use crate::preview2::bindings::sockets::tcp::TcpSocket; -use crate::preview2::{ - with_ambient_tokio_runtime, AbortOnDropJoinHandle, StreamState, Table, TableError, -}; +use crate::preview2::stream::{InputStream, OutputStream}; +use crate::preview2::{with_ambient_tokio_runtime, AbortOnDropJoinHandle, StreamState}; use cap_net_ext::{AddressFamily, Blocking, TcpListenerExt}; use cap_std::net::TcpListener; use io_lifetimes::raw::{FromRawSocketlike, IntoRawSocketlike}; use std::io; use std::sync::Arc; -use wasmtime::component::Resource; /// The state of a TCP socket. /// /// This represents the various states a socket can be in during the /// activities of binding, listening, accepting, and connecting. -pub(crate) enum HostTcpState { +pub(crate) enum TcpState { /// The initial state for a newly-created socket. Default, @@ -45,13 +42,13 @@ pub(crate) enum HostTcpState { /// /// The inner state is wrapped in an Arc because the same underlying socket is /// used for implementing the stream types. -pub(crate) struct HostTcpSocketState { - /// The part of a `HostTcpSocketState` which is reference-counted so that we +pub struct TcpSocket { + /// The part of a `TcpSocket` which is reference-counted so that we /// can pass it to async tasks. pub(crate) inner: Arc, /// The current state in the bind/listen/accept/connect progression. - pub(crate) tcp_state: HostTcpState, + pub(crate) tcp_state: TcpState, } pub(crate) struct TcpReadStream { @@ -215,7 +212,7 @@ impl HostOutputStream for TcpWriteStream { } } -impl HostTcpSocketState { +impl TcpSocket { /// Create a new socket in the given family. pub fn new(family: AddressFamily) -> io::Result { // Create a new host socket and set it to non-blocking, which is needed @@ -224,7 +221,7 @@ impl HostTcpSocketState { Self::from_tcp_listener(tcp_listener) } - /// Create a `HostTcpSocketState` from an existing socket. + /// Create a `TcpSocket` from an existing socket. /// /// The socket must be in non-blocking mode. pub fn from_tcp_stream(tcp_socket: cap_std::net::TcpStream) -> io::Result { @@ -239,7 +236,7 @@ impl HostTcpSocketState { Ok(Self { inner: Arc::new(stream), - tcp_state: HostTcpState::Default, + tcp_state: TcpState::Default, }) } @@ -248,53 +245,9 @@ impl HostTcpSocketState { } /// Create the input/output stream pair for a tcp socket. - pub fn as_split(&self) -> (Box, Box) { + pub fn as_split(&self) -> (InputStream, OutputStream) { let input = Box::new(TcpReadStream::new(self.inner.clone())); let output = Box::new(TcpWriteStream::new(self.inner.clone())); - (input, output) - } -} - -pub(crate) trait TableTcpSocketExt { - fn push_tcp_socket( - &mut self, - tcp_socket: HostTcpSocketState, - ) -> Result, TableError>; - fn delete_tcp_socket( - &mut self, - fd: Resource, - ) -> Result; - fn is_tcp_socket(&self, fd: &Resource) -> bool; - fn get_tcp_socket(&self, fd: &Resource) -> Result<&HostTcpSocketState, TableError>; - fn get_tcp_socket_mut( - &mut self, - fd: &Resource, - ) -> Result<&mut HostTcpSocketState, TableError>; -} - -impl TableTcpSocketExt for Table { - fn push_tcp_socket( - &mut self, - tcp_socket: HostTcpSocketState, - ) -> Result, TableError> { - Ok(Resource::new_own(self.push(Box::new(tcp_socket))?)) - } - fn delete_tcp_socket( - &mut self, - fd: Resource, - ) -> Result { - self.delete(fd.rep()) - } - fn is_tcp_socket(&self, fd: &Resource) -> bool { - self.is::(fd.rep()) - } - fn get_tcp_socket(&self, fd: &Resource) -> Result<&HostTcpSocketState, TableError> { - self.get(fd.rep()) - } - fn get_tcp_socket_mut( - &mut self, - fd: &Resource, - ) -> Result<&mut HostTcpSocketState, TableError> { - self.get_mut(fd.rep()) + (InputStream::Host(input), output) } } From d8ca357ed455cd8422ddddc5eb71c9063001eda0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 Oct 2023 10:35:14 -0500 Subject: [PATCH 032/199] Temporarily disable wasi-nn on CI (#7126) This appears to be failing, so temporarily disable it while the issues are worked out. --- .github/workflows/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 11a7701a6d57..a1ab4c206332 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -483,7 +483,8 @@ jobs: # Build and test the wasi-nn module. test_wasi_nn: needs: determine - if: needs.determine.outputs.run-full + # FIXME(#7125) flaky at the moment + if: needs.determine.outputs.run-full && false name: Test wasi-nn module runs-on: ubuntu-latest steps: From 80c45422e00a47a02799c71973954a7f8b95f7aa Mon Sep 17 00:00:00 2001 From: Tyler Rockwood Date: Mon, 2 Oct 2023 11:22:07 -0500 Subject: [PATCH 033/199] c-api: Expose host memory creation (#7115) * c-api: Expose host memory creation Signed-off-by: Tyler Rockwood * c-api: Review feedback Signed-off-by: Tyler Rockwood * c-api: Fix invalid reference in docs Signed-off-by: Tyler Rockwood * c-api: Fix doxygen documentation Signed-off-by: Tyler Rockwood --------- Signed-off-by: Tyler Rockwood --- crates/c-api/include/wasmtime/config.h | 93 +++++++++++++++ crates/c-api/src/config.rs | 158 ++++++++++++++++++++++++- 2 files changed, 248 insertions(+), 3 deletions(-) diff --git a/crates/c-api/include/wasmtime/config.h b/crates/c-api/include/wasmtime/config.h index 6ecebe7c04c7..38f1edc1b504 100644 --- a/crates/c-api/include/wasmtime/config.h +++ b/crates/c-api/include/wasmtime/config.h @@ -367,6 +367,99 @@ WASM_API_EXTERN void wasmtime_config_cranelift_flag_enable(wasm_config_t*, const */ WASM_API_EXTERN void wasmtime_config_cranelift_flag_set(wasm_config_t*, const char *key, const char *value); + +/** + * Return the data from a LinearMemory instance. + * + * The size in bytes as well as the maximum number of bytes that can be allocated should be + * returned as well. + * + * For more information about see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html + */ +typedef uint8_t *(*wasmtime_memory_get_callback_t)( + void *env, + size_t *byte_size, + size_t *maximum_byte_size); + +/** + * Grow the memory to the `new_size` in bytes. + * + * For more information about the parameters see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html#tymethod.grow_to + */ +typedef wasmtime_error_t *(*wasmtime_memory_grow_callback_t)( + void *env, + size_t new_size); + +/** + * A LinearMemory instance created from a #wasmtime_new_memory_callback_t. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html + */ +typedef struct { + /// User provided value to be passed to get_memory and grow_memory + void *env; + /// Callback to get the memory and size of this LinearMemory + wasmtime_memory_get_callback_t get_memory; + /// Callback to request growing the memory + wasmtime_memory_grow_callback_t grow_memory; + /// An optional finalizer for env + void (*finalizer)(void*); +} wasmtime_linear_memory_t; + +/** + * A callback to create a new LinearMemory from the specified parameters. + * + * The result should be written to `memory_ret` and wasmtime will own the values written + * into that struct. + * + * This callback must be thread-safe. + * + * For more information about the parameters see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.MemoryCreator.html#tymethod.new_memory + */ +typedef wasmtime_error_t *(*wasmtime_new_memory_callback_t)( + void *env, + const wasm_memorytype_t *ty, + size_t minimum, + size_t maximum, + size_t reserved_size_in_bytes, + size_t guard_size_in_bytes, + wasmtime_linear_memory_t *memory_ret); + +/** + * A representation of custom memory creator and methods for an instance of LinearMemory. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/trait.MemoryCreator.html + */ +typedef struct { + /// User provided value to be passed to new_memory + void* env; + /// The callback to create new memory, must be thread safe + wasmtime_new_memory_callback_t new_memory; + /// An optional finalizer for env. + void (*finalizer)(void*); +} wasmtime_memory_creator_t; + +/** + * Sets a custom memory creator. + * + * Custom memory creators are used when creating host Memory objects or when creating instance + * linear memories for the on-demand instance allocation strategy. + * + * The config does **not** take ownership of the #wasmtime_memory_creator_t passed in, but + * instead copies all the values in the struct. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.with_host_memory + */ +WASM_API_EXTERN void wasmtime_config_host_memory_creator_set( + wasm_config_t*, + wasmtime_memory_creator_t*); + #ifdef __cplusplus } // extern "C" #endif diff --git a/crates/c-api/src/config.rs b/crates/c-api/src/config.rs index fcd9fb4166f3..76fe4546b69b 100644 --- a/crates/c-api/src/config.rs +++ b/crates/c-api/src/config.rs @@ -2,10 +2,14 @@ // them with the default set of features enabled. #![cfg_attr(not(feature = "cache"), allow(unused_imports))] -use crate::{handle_result, wasmtime_error_t}; -use std::ffi::CStr; +use crate::{handle_result, wasm_memorytype_t, wasmtime_error_t}; +use std::mem::MaybeUninit; +use std::ops::Range; use std::os::raw::c_char; -use wasmtime::{Config, OptLevel, ProfilingStrategy, Strategy}; +use std::{ffi::CStr, sync::Arc}; +use wasmtime::{ + Config, LinearMemory, MemoryCreator, OptLevel, ProfilingStrategy, Result, Strategy, +}; #[repr(C)] #[derive(Clone)] @@ -255,3 +259,151 @@ pub unsafe extern "C" fn wasmtime_config_cranelift_flag_set( let value = CStr::from_ptr(value).to_str().expect("not valid utf-8"); c.config.cranelift_flag_set(flag, value); } + +pub type wasmtime_memory_get_callback_t = extern "C" fn( + env: *mut std::ffi::c_void, + byte_size: &mut usize, + maximum_byte_size: &mut usize, +) -> *mut u8; + +pub type wasmtime_memory_grow_callback_t = + extern "C" fn(env: *mut std::ffi::c_void, new_size: usize) -> Option>; + +#[repr(C)] +pub struct wasmtime_linear_memory_t { + env: *mut std::ffi::c_void, + get_memory: wasmtime_memory_get_callback_t, + grow_memory: wasmtime_memory_grow_callback_t, + finalizer: Option, +} + +pub type wasmtime_new_memory_callback_t = extern "C" fn( + env: *mut std::ffi::c_void, + ty: &wasm_memorytype_t, + minimum: usize, + maximum: usize, + reserved_size_in_bytes: usize, + guard_size_in_bytes: usize, + memory_ret: *mut wasmtime_linear_memory_t, +) -> Option>; + +struct CHostLinearMemory { + foreign: crate::ForeignData, + get_memory: wasmtime_memory_get_callback_t, + grow_memory: wasmtime_memory_grow_callback_t, +} + +unsafe impl LinearMemory for CHostLinearMemory { + fn byte_size(&self) -> usize { + let mut byte_size = 0; + let mut maximum_byte_size = 0; + let cb = self.get_memory; + cb(self.foreign.data, &mut byte_size, &mut maximum_byte_size); + return byte_size; + } + fn maximum_byte_size(&self) -> Option { + let mut byte_size = 0; + let mut maximum_byte_size = 0; + let cb = self.get_memory; + cb(self.foreign.data, &mut byte_size, &mut maximum_byte_size); + if maximum_byte_size == 0 { + None + } else { + Some(maximum_byte_size) + } + } + fn as_ptr(&self) -> *mut u8 { + let mut byte_size = 0; + let mut maximum_byte_size = 0; + let cb = self.get_memory; + cb(self.foreign.data, &mut byte_size, &mut maximum_byte_size) + } + fn wasm_accessible(&self) -> Range { + let mut byte_size = 0; + let mut maximum_byte_size = 0; + let cb = self.get_memory; + let ptr = cb(self.foreign.data, &mut byte_size, &mut maximum_byte_size); + Range { + start: ptr as usize, + end: ptr as usize + byte_size, + } + } + fn grow_to(&mut self, new_size: usize) -> Result<()> { + let cb = self.grow_memory; + let error = cb(self.foreign.data, new_size); + if let Some(err) = error { + Err((*err).into()) + } else { + Ok(()) + } + } +} + +#[repr(C)] +pub struct wasmtime_memory_creator_t { + env: *mut std::ffi::c_void, + new_memory: wasmtime_new_memory_callback_t, + finalizer: Option, +} + +struct CHostMemoryCreator { + foreign: crate::ForeignData, + new_memory: wasmtime_new_memory_callback_t, +} +unsafe impl Send for CHostMemoryCreator {} +unsafe impl Sync for CHostMemoryCreator {} + +unsafe impl MemoryCreator for CHostMemoryCreator { + fn new_memory( + &self, + ty: wasmtime::MemoryType, + minimum: usize, + maximum: Option, + reserved_size_in_bytes: Option, + guard_size_in_bytes: usize, + ) -> Result, String> { + let mut memory = MaybeUninit::uninit(); + let cb = self.new_memory; + let error = cb( + self.foreign.data, + &wasm_memorytype_t::new(ty), + minimum, + maximum.unwrap_or(usize::MAX), + reserved_size_in_bytes.unwrap_or(0), + guard_size_in_bytes, + memory.as_mut_ptr(), + ); + match error { + None => { + let memory = unsafe { memory.assume_init() }; + let foreign = crate::ForeignData { + data: memory.env, + finalizer: memory.finalizer, + }; + Ok(Box::new(CHostLinearMemory { + foreign, + get_memory: memory.get_memory, + grow_memory: memory.grow_memory, + })) + } + Some(err) => { + let err: anyhow::Error = (*err).into(); + Err(format!("{}", err)) + } + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_config_host_memory_creator_set( + c: &mut wasm_config_t, + creator: &wasmtime_memory_creator_t, +) { + c.config.with_host_memory(Arc::new(CHostMemoryCreator { + foreign: crate::ForeignData { + data: creator.env, + finalizer: creator.finalizer, + }, + new_memory: creator.new_memory, + })); +} From f15869b6a1f2348731bd232e91f459e0daa0dd79 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 Oct 2023 11:22:22 -0500 Subject: [PATCH 034/199] riscv64: Refactor and enable optimizing sign extensions (#7121) * riscv64: Remove some stray whitespace * riscv64: Refactor int-to-float emission Use ISLE matching to generate different instructions rather than having a method deeper in the backend select which instruction to use. Additionally remove the usage of the `normalize_fcvt_to_int` helper. * riscv64: Refactor and simplify `lower_bmask` Take a `Value` as input to simplify 128-bit handling and additionally simplify the rules a bit with other helpers. * riscv64: Refactor `normalize_cmp_value` and `truthy_to_reg` Remove the normalization completely by folding it into callers and refactor `truthy_to_reg` to perform sign extension internally in addition to handling 128-vs-64 * riscv64: Remove 128-bit handling from extension helper This is only needed in a small handful of spots which can pretty easily handle the sign extension on their own, and this further simplifies sign extension to purely dealing with a single register. * riscv64: Remove destination type in extension This is always `$I64`, so no need to pass it around. * riscv64: Remove `ext_if_need` helper Fold 128-bit handling into callers as necessary * riscv64: Refactor `sext` to take a `Value` This commit refactors the `sext` helper to take a single `Value` as an argument rather than an `XReg Type` combo. This will enable, in future commits, to pattern-match the structure of the input and skip the sign extension if possible. For example an `icmp` result is already sign-extended. This involved moving some `lower_*` declarations directly into `lower.isle`. Additionally a special-case of lowering `brif` was removed which caused regressions in generated code due to new `and` instructions being generated to sign-extend the result of an `icmp`. Once `zext` is also migrated to this pattern this regression should be fixed, so this should just be a temporary state of affairs. * riscv64: Fold `lower_umulhi` helper into lower.isle * riscv64: Fold `lower_uadd_overflow` into lower.isle * riscv64: Fold `lower_popcnt` into `lower.isle` This slightly pessmizes codegen when the `zbb` extension isn't available due to a sign extension for smaller-than-64-bit types, but this is possible to fix in the future if necessary and helps simplify this in the meantime. Additionally this lifts the special cases for `has_zbb` directly into the lowering rule. * riscv64: Refactor clz and cls This commit refactors the lowering rules for these two CLIF instructions to move the sext/zext operations to the top rather than having them buried in lowering rules. This additionally moves them to `lower.isle` instead of `inst.isle`. This did result in some minor duplication but overall feels a bit better due to them being less coupled now. * riscv64: Move `gen_bswap` to `lower.isle` Additionally shuffle rules around to remove the dependence on `zext` because when this zero-extension happens it's known that many of the extension-related optimizations are not applicable. This does pessimize code slightly but it's hopefully not too bad given the current size of the bswap lowering. * riscv64: Change `zext` to taking a `Value` This is similar to a prior commit for `sext` and will enable future optimizations to skip the zext entirely if the structure of the value allows. * riscv64: Fold `extend` helper into `zext` and `sext` This commit folds the existing rules of the `extend` helper into the corresponding functions of `zext` and `sext`. They are the only callers of `extend` and this keeps the sign extension-related logic truly in one final resting place now at this point. A new matcher `val_already_extended` is additionally added to add more cases in a follow-up commit to enable skipping sign-extension operations based on the structure of the `Value` input. * riscv64: Optimize some sign extensions As the culmination of all prior commits, this commit adds a few cases where a sign extension is not necessary in the backend and can be skipped. For example extending a sign-extended value is not required because the backend always extends to the full register width. Additionally extending the result of an `icmp` is not necessary since that always produces a 0 or 1 value at the full register width. --- cranelift/codegen/src/isa/riscv64/inst.isle | 480 ++----- .../codegen/src/isa/riscv64/inst/args.rs | 37 - .../codegen/src/isa/riscv64/inst/emit.rs | 6 +- cranelift/codegen/src/isa/riscv64/lower.isle | 365 ++++-- .../codegen/src/isa/riscv64/lower/isle.rs | 42 +- .../filetests/isa/riscv64/bitops.clif | 88 +- .../filetests/isa/riscv64/bswap.clif | 449 +++---- .../filetests/isa/riscv64/cls-zbb.clif | 34 +- .../filetests/isa/riscv64/condbr.clif | 10 +- .../filetests/isa/riscv64/popcnt.clif | 94 ++ .../filetests/isa/riscv64/select-float.clif | 40 +- .../filetests/isa/riscv64/select.clif | 145 ++- .../isa/riscv64/select_spectre_guard.clif | 1159 ++++++++--------- ..._guard_yes_spectre_i32_access_0_offset.wat | 60 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 64 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 80 +- ...0_guard_yes_spectre_i8_access_0_offset.wat | 54 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 64 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 80 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 54 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 62 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 62 +- ...f_guard_yes_spectre_i8_access_0_offset.wat | 54 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 62 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 62 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 54 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 46 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 50 +- ...0_guard_yes_spectre_i8_access_0_offset.wat | 48 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 46 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 50 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 48 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 42 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 42 +- ...f_guard_yes_spectre_i8_access_0_offset.wat | 48 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 42 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 42 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 60 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 69 +- ...0_guard_yes_spectre_i8_access_0_offset.wat | 60 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 69 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 52 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 46 +- ...0_guard_yes_spectre_i8_access_0_offset.wat | 52 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 46 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 52 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 46 +- ...f_guard_yes_spectre_i8_access_0_offset.wat | 52 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 46 +- 49 files changed, 2405 insertions(+), 2510 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/riscv64/popcnt.clif diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index aece273057ca..16545b74716b 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -1344,8 +1344,6 @@ (rule (rv_remu rs1 rs2) (alu_rrr (AluOPRRR.RemU) rs1 rs2)) - - ;; RV64M Extension ;; TODO: Enable these instructions only when we have the M extension @@ -1437,6 +1435,38 @@ (decl rv_fcvtsd (FReg) FReg) (rule (rv_fcvtsd rs1) (fpu_rr (FpuOPRR.FcvtSD) $F64 rs1)) +;; Helper for emitting the `fcvt.s.w` instruction. +(decl rv_fcvtsw (XReg) FReg) +(rule (rv_fcvtsw rs1) (fpu_rr (FpuOPRR.FcvtSw) $F32 rs1)) + +;; Helper for emitting the `fcvt.s.wu` instruction. +(decl rv_fcvtswu (XReg) FReg) +(rule (rv_fcvtswu rs1) (fpu_rr (FpuOPRR.FcvtSwU) $F32 rs1)) + +;; Helper for emitting the `fcvt.d.w` instruction. +(decl rv_fcvtdw (XReg) FReg) +(rule (rv_fcvtdw rs1) (fpu_rr (FpuOPRR.FcvtDW) $F32 rs1)) + +;; Helper for emitting the `fcvt.d.wu` instruction. +(decl rv_fcvtdwu (XReg) FReg) +(rule (rv_fcvtdwu rs1) (fpu_rr (FpuOPRR.FcvtDWU) $F32 rs1)) + +;; Helper for emitting the `fcvt.s.l` instruction. +(decl rv_fcvtsl (XReg) FReg) +(rule (rv_fcvtsl rs1) (fpu_rr (FpuOPRR.FcvtSL) $F32 rs1)) + +;; Helper for emitting the `fcvt.s.lu` instruction. +(decl rv_fcvtslu (XReg) FReg) +(rule (rv_fcvtslu rs1) (fpu_rr (FpuOPRR.FcvtSLU) $F32 rs1)) + +;; Helper for emitting the `fcvt.d.l` instruction. +(decl rv_fcvtdl (XReg) FReg) +(rule (rv_fcvtdl rs1) (fpu_rr (FpuOPRR.FcvtDL) $F32 rs1)) + +;; Helper for emitting the `fcvt.d.lu` instruction. +(decl rv_fcvtdlu (XReg) FReg) +(rule (rv_fcvtdlu rs1) (fpu_rr (FpuOPRR.FcvtDLu) $F32 rs1)) + ;; Helper for emitting the `fsgnj` ("Floating Point Sign Injection") instruction. ;; The output of this instruction is `rs1` with the sign bit from `rs2` ;; This implements the `copysign` operation @@ -1555,6 +1585,11 @@ (rule (rv_cpop rs1) (alu_rr_funct12 (AluOPRRI.Cpop) rs1)) +;; Helper for emitting the `cpopw` ("Count Population") instruction. +(decl rv_cpopw (XReg) XReg) +(rule (rv_cpopw rs1) + (alu_rr_funct12 (AluOPRRI.Cpopw) rs1)) + ;; Helper for emitting the `max` instruction. (decl rv_max (XReg XReg) XReg) (rule (rv_max rs1 rs2) @@ -1924,41 +1959,6 @@ (rule 0 (gen_or (fits_in_64 _) x y) (rv_or (value_regs_get x 0) (value_regs_get y 0))) - - -(decl gen_bswap (Type XReg) XReg) - -;; This is only here to make the rule below work. bswap.i8 isn't valid -(rule 0 (gen_bswap $I8 x) x) - -(rule 1 (gen_bswap (ty_int_ref_16_to_64 ty) x) - (if-let half_ty (ty_half_width ty)) - (if-let half_size (u64_to_imm12 (ty_bits half_ty))) - (let (;; This swaps the top bytes and zeroes the bottom bytes, so that - ;; we can or it with the bottom bytes later. - (swap_top XReg (gen_bswap half_ty x)) - (top XReg (rv_slli swap_top half_size)) - - ;; Get the top half, swap it, and zero extend it so we can `or` it - ;; with the bottom half. - (shifted XReg (rv_srli x half_size)) - (swap_bot XReg (gen_bswap half_ty shifted)) - (bot XReg (zext swap_bot half_ty $I64))) - (rv_or top bot))) - -;; With `zbb` we can use `rev8` and shift the result -(rule 2 (gen_bswap (int_fits_in_32 ty) x) - (if-let $true (has_zbb)) - (if-let shift_amt (u64_to_imm12 (u64_sub 64 (ty_bits ty)))) - (rv_srli (rv_rev8 x) shift_amt)) - -;; With `zbb` we can use `rev8` that does this -(rule 3 (gen_bswap $I64 x) - (if-let $true (has_zbb)) - (rv_rev8 x)) - - - (decl lower_bit_reverse (Reg Type) Reg) (rule @@ -2005,77 +2005,8 @@ (if-let $true (has_zbb)) (rv_ctz x)) -;; Count trailing zeros from a i128 bit value. -;; We count both halves separately and conditionally add them if it makes sense. -(decl lower_ctz_128 (ValueRegs) ValueRegs) -(rule (lower_ctz_128 x) - (let ((x_lo XReg (value_regs_get x 0)) - (x_hi XReg (value_regs_get x 1)) - ;; Count both halves - (high XReg (lower_ctz $I64 x_hi)) - (low XReg (lower_ctz $I64 x_lo)) - ;; Only add the top half if the bottom is zero - (high XReg (gen_select_reg (IntCC.Equal) x_lo (zero_reg) high (zero_reg))) - (result XReg (rv_add low high))) - (extend result (ExtendOp.Zero) $I64 $I128))) - -(decl lower_clz (Type XReg) XReg) -(rule (lower_clz ty rs) - (gen_cltz $true rs ty)) - -(rule 1 (lower_clz (fits_in_16 ty) r) - (if-let $true (has_zbb)) - (let ((tmp XReg (zext r ty $I64)) - (count XReg (rv_clz tmp)) - ;; We always do the operation on the full 64-bit register, so subtract 64 from the result. - (result XReg (rv_addi count (imm12_const_add (ty_bits ty) -64)))) - result)) - -(rule 2 (lower_clz $I32 r) - (if-let $true (has_zbb)) - (rv_clzw r)) - -(rule 2 (lower_clz $I64 r) - (if-let $true (has_zbb)) - (rv_clz r)) - - ;; Count leading zeros from a i128 bit value. ;; We count both halves separately and conditionally add them if it makes sense. -(decl lower_clz_i128 (ValueRegs) ValueRegs) -(rule (lower_clz_i128 x) - (let ((x_lo XReg (value_regs_get x 0)) - (x_hi XReg (value_regs_get x 1)) - ;; Count both halves - (high XReg (lower_clz $I64 x_hi)) - (low XReg (lower_clz $I64 x_lo)) - ;; Only add the bottom zeros if the top half is zero - (low XReg (gen_select_reg (IntCC.Equal) x_hi (zero_reg) low (zero_reg))) - (result XReg (rv_add high low))) - (extend result (ExtendOp.Zero) $I64 $I128))) - - -(decl lower_cls (Type XReg) XReg) -(rule (lower_cls ty r) - (let ((tmp XReg (sext r ty $I64)) - (tmp2 XReg (gen_select_reg (IntCC.SignedLessThan) tmp (zero_reg) (rv_not tmp) tmp)) - (tmp3 XReg (lower_clz ty tmp2))) - (rv_addi tmp3 (imm12_const -1)))) - -;; If the sign bit is set, we count the leading zeros of the inverted value. -;; Otherwise we can just count the leading zeros of the original value. -;; Subtract 1 since the sign bit does not count. -(decl lower_cls_i128 (ValueRegs) ValueRegs) -(rule (lower_cls_i128 x) - (let ((low XReg (value_regs_get x 0)) - (high XReg (value_regs_get x 1)) - (low XReg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (rv_not low) low)) - (high XReg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (rv_not high) high)) - (tmp ValueRegs (lower_clz_i128 (value_regs low high))) - (count XReg (value_regs_get tmp 0)) - (result XReg (rv_addi count (imm12_const -1)))) - (extend result (ExtendOp.Zero) $I64 $I128))) - (decl gen_cltz (bool XReg Type) XReg) (rule (gen_cltz leading rs ty) @@ -2085,122 +2016,96 @@ (_ Unit (emit (MInst.Cltz leading sum step tmp rs ty)))) sum)) - -;; Extends an integer if it is smaller than 64 bits. -(decl ext_int_if_need (bool ValueRegs Type) ValueRegs) -;;; For values smaller than 64 bits, we need to extend them to 64 bits -(rule 0 (ext_int_if_need $true val (fits_in_32 (ty_int ty))) - (extend val (ExtendOp.Signed) ty $I64)) -(rule 0 (ext_int_if_need $false val (fits_in_32 (ty_int ty))) - (extend val (ExtendOp.Zero) ty $I64)) -;; If the value is larger than one machine register, we don't need to do anything -(rule 1 (ext_int_if_need _ r $I64) r) -(rule 2 (ext_int_if_need _ r $I128) r) - - ;; Performs a zero extension of the given value -(decl zext (XReg Type Type) XReg) -(rule (zext val from_ty (fits_in_64 to_ty)) (value_regs_get (extend val (ExtendOp.Zero) from_ty to_ty) 0)) - -;; Performs a signed extension of the given value -(decl sext (XReg Type Type) XReg) -(rule (sext val from_ty (fits_in_64 to_ty)) (value_regs_get (extend val (ExtendOp.Signed) from_ty to_ty) 0)) - -(type ExtendOp - (enum - (Zero) - (Signed))) - -;; Performs either a sign or zero extension of the given value -(decl extend (ValueRegs ExtendOp Type Type) ValueRegs) - -;;; Generic Rules Extending to I64 -(decl pure extend_shift_op (ExtendOp) AluOPRRI) -(rule (extend_shift_op (ExtendOp.Zero)) (AluOPRRI.Srli)) -(rule (extend_shift_op (ExtendOp.Signed)) (AluOPRRI.Srai)) +(decl zext (Value) XReg) ;; In the most generic case, we shift left and then shift right. -;; The type of right shift is determined by the extend op. -(rule 0 (extend val extend_op (fits_in_32 from_ty) (fits_in_64 to_ty)) - (let ((val XReg (value_regs_get val 0)) - (shift Imm12 (imm_from_bits (u64_sub 64 (ty_bits from_ty)))) - (left XReg (rv_slli val shift)) - (shift_op AluOPRRI (extend_shift_op extend_op)) - (right XReg (alu_rr_imm12 shift_op left shift))) - right)) +(rule 0 (zext val @ (value_type (fits_in_32 ty))) + (let ((shift Imm12 (imm_from_bits (u64_sub 64 (ty_bits ty))))) + (rv_srli (rv_slli val shift) shift))) ;; If we are zero extending a U8 we can use a `andi` instruction. -(rule 1 (extend val (ExtendOp.Zero) $I8 (fits_in_64 to_ty)) - (let ((val XReg (value_regs_get val 0))) - (rv_andi val (imm12_const 255)))) - -;; When signed extending from 32 to 64 bits we can use a -;; `addiw val 0`. Also known as a `sext.w` -(rule 1 (extend val (ExtendOp.Signed) $I32 $I64) - (let ((val XReg (value_regs_get val 0))) - (rv_sextw val))) - +(rule 1 (zext val @ (value_type $I8)) + (rv_andi val (imm12_const 0xff))) ;; No point in trying to use `packh` here to zero extend 8 bit values ;; since we can just use `andi` instead which is part of the base ISA. ;; If we have the `zbkb` extension `packw` can be used to zero extend 16 bit values -(rule 1 (extend val (ExtendOp.Zero) $I16 (fits_in_64 _)) +(rule 1 (zext val @ (value_type $I16)) (if-let $true (has_zbkb)) - (let ((val XReg (value_regs_get val 0))) - (rv_packw val (zero_reg)))) + (rv_packw val (zero_reg))) ;; If we have the `zbkb` extension `pack` can be used to zero extend 32 bit registers -(rule 1 (extend val (ExtendOp.Zero) $I32 $I64) +(rule 1 (zext val @ (value_type $I32)) (if-let $true (has_zbkb)) - (let ((val XReg (value_regs_get val 0))) - (rv_pack val (zero_reg)))) - - -;; If we have the `zbb` extension we can use the dedicated `sext.b` instruction. -(rule 1 (extend val (ExtendOp.Signed) $I8 (fits_in_64 _)) - (if-let $true (has_zbb)) - (let ((val XReg (value_regs_get val 0))) - (rv_sextb val))) - -;; If we have the `zbb` extension we can use the dedicated `sext.h` instruction. -(rule 1 (extend val (ExtendOp.Signed) $I16 (fits_in_64 _)) - (if-let $true (has_zbb)) - (let ((val XReg (value_regs_get val 0))) - (rv_sexth val))) + (rv_pack val (zero_reg))) ;; If we have the `zbb` extension we can use the dedicated `zext.h` instruction. -(rule 2 (extend val (ExtendOp.Zero) $I16 (fits_in_64 _)) +(rule 2 (zext val @ (value_type $I16)) (if-let $true (has_zbb)) - (let ((val XReg (value_regs_get val 0))) - (rv_zexth val))) + (rv_zexth val)) ;; With `zba` we have a `zext.w` instruction -(rule 2 (extend val (ExtendOp.Zero) $I32 $I64) +(rule 2 (zext val @ (value_type $I32)) (if-let $true (has_zba)) - (let ((val XReg (value_regs_get val 0))) - (rv_zextw val))) - -;;; Signed rules extending to I128 -;; Extend the bottom part, and extract the sign bit from the bottom as the top -(rule 3 (extend val (ExtendOp.Signed) (fits_in_64 from_ty) $I128) - (let ((val XReg (value_regs_get val 0)) - (low XReg (sext val from_ty $I64)) - (high XReg (rv_srai low (imm12_const 63)))) - (value_regs low high))) + (rv_zextw val)) -;;; Unsigned rules extending to I128 -;; Extend the bottom register to I64 and then just zero out the top half. -(rule 3 (extend val (ExtendOp.Zero) (fits_in_64 from_ty) $I128) - (let ((val XReg (value_regs_get val 0)) - (low XReg (zext val from_ty $I64)) - (high XReg (imm $I64 0))) - (value_regs low high))) +;; Ignore sign extensions for values whose representation is already the full +;; register width. +(rule 3 (zext val) + (if (val_already_extended val)) + val) + +;; Performs a signed extension of the given value +(decl sext (Value) XReg) + +;; Same base case as `zext`, shift left-then-right. +(rule 0 (sext val @ (value_type (fits_in_32 ty))) + (let ((shift Imm12 (imm_from_bits (u64_sub 64 (ty_bits ty))))) + (rv_srai (rv_slli val shift) shift))) + +;; If we have the `zbb` extension we can use the dedicated `sext.b` instruction. +(rule 1 (sext val @ (value_type $I8)) + (if-let $true (has_zbb)) + (rv_sextb val)) -;; Catch all rule for ignoring extensions of the same type. -(rule 4 (extend val _ ty ty) val) +;; If we have the `zbb` extension we can use the dedicated `sext.h` instruction. +(rule 1 (sext val @ (value_type $I16)) + (if-let $true (has_zbb)) + (rv_sexth val)) +;; When signed extending from 32 to 64 bits we can use a +;; `addiw val 0`. Also known as a `sext.w` +(rule 1 (sext val @ (value_type $I32)) + (rv_sextw val)) + +;; Ignore sign extensions for values whose representation is already the full +;; register width. +(rule 2 (sext val) + (if (val_already_extended val)) + val) + +;; Helper matcher for when a value's representation is already sign or zero +;; extended to the full 64-bit register representation. This is used by `zext` +;; and `sext` above to skip the extension instruction entirely in some +;; circumstances. +(decl pure partial val_already_extended (Value) bool) +(rule 0 (val_already_extended v @ (value_type $I64)) $true) + +;; When extending our backend always extends to the full register width, so +;; there's no need to extend-an-extend. +(rule 1 (val_already_extended (uextend _)) $true) +(rule 1 (val_already_extended (sextend _)) $true) + +;; The result of `icmp` is zero or one, meaning that it's already sign extended +;; to the full register width. +(rule 1 (val_already_extended (icmp _ _ _)) $true) +(type ExtendOp + (enum + (Zero) + (Signed))) (decl lower_b128_binary (AluOPRRR ValueRegs ValueRegs) ValueRegs) (rule @@ -2212,15 +2117,6 @@ (high XReg (alu_rrr op (value_regs_get a 1) (value_regs_get b 1)))) (value_regs low high))) -(decl lower_umlhi (Type XReg XReg) XReg) -(rule 1 (lower_umlhi $I64 rs1 rs2) - (rv_mulhu rs1 rs2)) - -(rule (lower_umlhi ty rs1 rs2) - (let - ((tmp XReg (rv_mul (zext rs1 ty $I64) (zext rs2 ty $I64)))) - (rv_srli tmp (imm12_const (ty_bits ty))))) - (decl lower_smlhi (Type XReg XReg) XReg) (rule 1 (lower_smlhi $I64 rs1 rs2) @@ -2345,36 +2241,15 @@ (rv_bseti val (imm12_const (u64_as_i32 bit)))) -(decl gen_popcnt (Reg Type) Reg) -(rule - (gen_popcnt rs ty) +(decl gen_popcnt (XReg) Reg) +(rule (gen_popcnt rs) (let ((tmp WritableXReg (temp_writable_xreg)) (step WritableXReg (temp_writable_xreg)) (sum WritableXReg (temp_writable_xreg)) - (_ Unit (emit (MInst.Popcnt sum step tmp rs ty)))) + (_ Unit (emit (MInst.Popcnt sum step tmp rs $I64)))) (writable_reg_to_reg sum))) -(decl lower_popcnt (XReg Type) XReg) -(rule 1 (lower_popcnt rs ty) - (if-let $true (has_zbb)) - (rv_cpop (zext rs ty $I64))) - -(rule (lower_popcnt rs ty) - (if-let $false (has_zbb)) - (gen_popcnt rs ty)) - -(decl lower_popcnt_i128 (ValueRegs) ValueRegs) -(rule - (lower_popcnt_i128 a) - (let - ( ;; low part. - (low XReg (lower_popcnt (value_regs_get a 0) $I64)) - ;; high part. - (high XReg (lower_popcnt (value_regs_get a 1) $I64)) - ;; add toghter. - (result XReg (rv_add low high))) - (value_regs result (imm $I64 0)))) (decl lower_i128_rotl (ValueRegs ValueRegs) ValueRegs) (rule @@ -2685,9 +2560,6 @@ (_ Unit (emit (MInst.ElfTlsGetAddr dst name)))) dst)) -(decl int_convert_2_float_op (Type bool Type) FpuOPRR) -(extern constructor int_convert_2_float_op int_convert_2_float_op) - ;;;; (decl gen_fcvt_int (bool FReg bool Type Type) XReg) (rule @@ -2711,15 +2583,6 @@ (move_x_to_f tmp (float_int_of_same_size ty)))) -;;; lower icmp -(decl lower_icmp (IntCC ValueRegs ValueRegs Type) Reg) -(rule 1 (lower_icmp cc x y ty) - (if (signed_cond_code cc)) - (gen_icmp cc (ext_int_if_need $true x ty) (ext_int_if_need $true y ty) ty)) -(rule (lower_icmp cc x y ty) - (gen_icmp cc (ext_int_if_need $false x ty) (ext_int_if_need $false y ty) ty)) - - (decl i128_sub (ValueRegs ValueRegs) ValueRegs) (rule (i128_sub x y ) @@ -2734,28 +2597,6 @@ (high XReg (rv_sub high_tmp borrow))) (value_regs low high))) - -;;; Returns the sum in the first register, and the overflow test in the second. -(decl lower_uadd_overflow (XReg XReg Type) ValueRegs) - -(rule 1 - (lower_uadd_overflow x y $I64) - (let ((tmp XReg (rv_add x y)) - (test XReg (gen_icmp (IntCC.UnsignedLessThan) tmp x $I64))) - (value_regs tmp test))) - -(rule - (lower_uadd_overflow x y (fits_in_32 ty)) - (let ((tmp_x XReg (zext x ty $I64)) - (tmp_y XReg (zext y ty $I64)) - (sum XReg (rv_add tmp_x tmp_y)) - (test XReg (rv_srli sum (imm12_const (ty_bits ty))))) - (value_regs sum test))) - -;;; cc a b targets Type -(decl lower_br_icmp (IntCC ValueRegs ValueRegs VecMachLabel Type) Unit) -(extern constructor lower_br_icmp lower_br_icmp) - ;; int scalar zero regs. (decl int_zero_reg (Type) ValueRegs) (extern constructor int_zero_reg int_zero_reg) @@ -2763,39 +2604,20 @@ (decl lower_cond_br (IntCC ValueRegs VecMachLabel Type) Unit) (extern constructor lower_cond_br lower_cond_br) -(decl intcc_to_extend_op (IntCC) ExtendOp) -(extern constructor intcc_to_extend_op intcc_to_extend_op) - -;; Normalize a value for comparision. +;; Convert a truthy value, possibly of more than one register (an I128), to +;; one register. ;; -;; This ensures that types smaller than a register don't accidentally -;; pass undefined high bits when being compared as a full register. -(decl normalize_cmp_value (Type ValueRegs ExtendOp) ValueRegs) - -(rule 1 (normalize_cmp_value (fits_in_32 ity) r op) - (extend r op ity $I64)) - -(rule (normalize_cmp_value $I64 r _) r) -(rule (normalize_cmp_value $I128 r _) r) - -(decl normalize_fcvt_from_int (XReg Type ExtendOp) XReg) -(rule 2 (normalize_fcvt_from_int r (fits_in_16 ty) op) - (value_regs_get (extend r op ty $I64) 0)) -(rule 1 (normalize_fcvt_from_int r _ _) - r) - -;; Convert a truthy value, possibly of more than one register (an -;; I128), to one register. If narrower than 64 bits, must have already -;; been masked (e.g. by `normalize_cmp_value`). -(decl truthy_to_reg (Type ValueRegs) XReg) -(rule 1 (truthy_to_reg (fits_in_64 _) regs) - (value_regs_get regs 0)) -(rule 0 (truthy_to_reg $I128 regs) - (let ((lo XReg (value_regs_get regs 0)) +;; Zero-extends as necessary to ensure that the returned register only contains +;; nonzero if the input value was logically nonzero. +(decl truthy_to_reg (Value) XReg) +(rule 1 (truthy_to_reg val @ (value_type (fits_in_64 _))) + (zext val)) +(rule 0 (truthy_to_reg val @ (value_type $I128)) + (let ((regs ValueRegs val) + (lo XReg (value_regs_get regs 0)) (hi XReg (value_regs_get regs 1))) (rv_or lo hi))) - (decl label_to_br_target (MachLabel) CondBrTarget) (extern constructor label_to_br_target label_to_br_target) @@ -2807,21 +2629,10 @@ (emit_side_effect (SideEffectNoResult.Inst (MInst.Jal (vec_label_get targets 0))))) ;; Default behavior for branching based on an input value. -(rule - (lower_branch (brif v @ (value_type ty) _ _) targets) - (lower_cond_br (IntCC.NotEqual) (normalize_cmp_value ty v (ExtendOp.Zero)) targets ty)) - -;; Special case for SI128 to reify the comparison value and branch on it. -(rule 2 - (lower_branch (brif v @ (value_type $I128) _ _) targets) - (let ((zero ValueRegs (value_regs (zero_reg) (zero_reg))) - (cmp XReg (gen_icmp (IntCC.NotEqual) v zero $I128))) - (lower_cond_br (IntCC.NotEqual) cmp targets $I64))) - -;; Branching on the result of an icmp -(rule 1 - (lower_branch (brif (maybe_uextend (icmp cc a @ (value_type ty) b)) _ _) targets) - (lower_br_icmp cc a b targets ty)) +(rule (lower_branch (brif v @ (value_type (fits_in_64 ty)) _ _) targets) + (lower_cond_br (IntCC.NotEqual) (zext v) targets ty)) +(rule 2 (lower_branch (brif v @ (value_type $I128)_ _) targets) + (lower_cond_br (IntCC.NotEqual) (truthy_to_reg v) targets $I64)) ;; Branching on the result of an fcmp (rule 1 @@ -2978,45 +2789,18 @@ ;;;; Helpers for bmask ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(decl lower_bmask (Type Type ValueRegs) ValueRegs) - -;; Produces -1 if the 64-bit value is non-zero, and 0 otherwise. -;; If the type is smaller than 64 bits, we need to mask off the -;; high bits. -(rule - 0 - (lower_bmask (fits_in_64 _) (fits_in_64 in_ty) val) - (let ((input XReg (truthy_to_reg in_ty (normalize_cmp_value in_ty val (ExtendOp.Zero)))) - (non_zero XReg (rv_snez input))) - (value_reg (rv_neg non_zero)))) - -;; Bitwise-or the two registers that make up the 128-bit value, then recurse as -;; though it was a 64-bit value. -(rule - 1 - (lower_bmask (fits_in_64 ty) $I128 val) - (let ((lo XReg (value_regs_get val 0)) - (hi XReg (value_regs_get val 1)) - (combined XReg (rv_or lo hi))) - (lower_bmask ty $I64 (value_reg combined)))) - -;; Conversion of one 64-bit value to a 128-bit one. Duplicate the result of the -;; bmask of the 64-bit value into both result registers of the i128. -(rule - 2 - (lower_bmask $I128 (fits_in_64 in_ty) val) - (let ((res ValueRegs (lower_bmask $I64 in_ty val))) - (value_regs (value_regs_get res 0) (value_regs_get res 0)))) - -;; Conversion of one 64-bit value to a 128-bit one. Duplicate the result of -;; bmasking the 128-bit value to a 64-bit value into both registers of the -;; 128-bit result. -(rule - 3 - (lower_bmask $I128 $I128 val) - (let ((res ValueRegs (lower_bmask $I64 $I128 val))) - (value_regs (value_regs_get res 0) (value_regs_get res 0)))) - +;; Generates either 0 if `Value` is zero or -1 otherwise. +(decl gen_bmask (Value) XReg) +(rule (gen_bmask val) + (let ((non_zero XReg (rv_snez (truthy_to_reg val)))) + (rv_neg non_zero))) + +(decl lower_bmask (Value Type) ValueRegs) +(rule 0 (lower_bmask val (fits_in_64 _)) + (value_reg (gen_bmask val))) +(rule 1 (lower_bmask val $I128) + (let ((bits XReg (gen_bmask val))) + (value_regs bits bits))) ;;;; Helpers for physical registers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs index a75b07bfdfb8..c9f8201f0249 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/args.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs @@ -463,43 +463,6 @@ impl FpuOPRR { } } - pub(crate) fn int_convert_2_float_op(from: Type, is_type_signed: bool, to: Type) -> Self { - let type_32 = from.bits() == 32; - match to { - F32 => { - if is_type_signed { - if type_32 { - Self::FcvtSw - } else { - Self::FcvtSL - } - } else { - if type_32 { - Self::FcvtSwU - } else { - Self::FcvtSLU - } - } - } - F64 => { - if is_type_signed { - if type_32 { - Self::FcvtDW - } else { - Self::FcvtDL - } - } else { - if type_32 { - Self::FcvtDWU - } else { - Self::FcvtDLu - } - } - } - _ => unreachable!("to type:{}", to), - } - } - pub(crate) fn op_code(self) -> u32 { match self { FpuOPRR::FsqrtS diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs index eea635150c97..25b0419684bc 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs @@ -2479,7 +2479,11 @@ impl Inst { .emit(&[], sink, emit_info, state); //convert back. Inst::FpuRR { - alu_op: FpuOPRR::int_convert_2_float_op(I64, true, ty), + alu_op: if ty == F32 { + FpuOPRR::FcvtSL + } else { + FpuOPRR::FcvtDL + }, frm: Some(op.to_frm()), rd, rs: int_tmp.to_reg(), diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index cb8f8ff0e49f..1cbdee196f3e 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -294,12 +294,19 @@ (rv_vnmsac_vx z y x (unmasked) ty)) ;;; Rules for `uadd_overflow_trap` ;;;;;;;;;;;;; -(rule - (lower (has_type (fits_in_64 ty) (uadd_overflow_trap x y tc))) - (let ((res ValueRegs (lower_uadd_overflow x y ty)) - (_ InstOutput (gen_trapnz (value_regs_get res 1) tc))) - (value_regs_get res 0))) - +(rule 0 (lower (has_type (fits_in_32 ty) (uadd_overflow_trap x y tc))) + (let ((tmp_x XReg (zext x)) + (tmp_y XReg (zext y)) + (sum XReg (rv_add tmp_x tmp_y)) + (test XReg (rv_srli sum (imm12_const (ty_bits ty)))) + (_ InstOutput (gen_trapnz test tc))) + sum)) + +(rule 1 (lower (has_type $I64 (uadd_overflow_trap x y tc))) + (let ((tmp XReg (rv_add x y)) + (test XReg (gen_icmp (IntCC.UnsignedLessThan) tmp x $I64)) + (_ InstOutput (gen_trapnz test tc))) + tmp)) ;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Base case, simply subtracting things in registers. @@ -471,7 +478,7 @@ ;;;; Rules for `smulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (smulhi x y))) - (lower_smlhi ty (sext x ty $I64) (sext y ty $I64))) + (lower_smlhi ty (sext x) (sext y))) (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (smulhi x y))) (rv_vmulh_vv x y (unmasked) ty)) @@ -483,30 +490,34 @@ (rv_vmulh_vx x y (unmasked) ty)) ;;;; Rules for `umulhi` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (umulhi x y))) - (lower_umlhi ty (zext x ty $I64) (zext y ty $I64))) +(rule 0 (lower (has_type (fits_in_32 ty) (umulhi x y))) + (let ((tmp XReg (rv_mul (zext x) (zext y)))) + (rv_srli tmp (imm12_const (ty_bits ty))))) -(rule 1 (lower (has_type (ty_vec_fits_in_register ty) (umulhi x y))) +(rule 1 (lower (has_type $I64 (umulhi x y))) + (rv_mulhu x y)) + +(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (umulhi x y))) (rv_vmulhu_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (umulhi (splat x) y))) +(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (umulhi (splat x) y))) (rv_vmulhu_vx y x (unmasked) ty)) -(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (umulhi x (splat y)))) +(rule 4 (lower (has_type (ty_vec_fits_in_register ty) (umulhi x (splat y)))) (rv_vmulhu_vx x y (unmasked) ty)) ;;;; Rules for `div` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule -1 (lower (has_type (fits_in_32 ty) (udiv x y))) (let - ((y2 XReg (zext y ty $I64)) + ((y2 XReg (zext y)) (_ InstOutput (gen_div_by_zero y2))) - (rv_divuw (zext x ty $I64) y2))) + (rv_divuw (zext x) y2))) (rule -1 (lower (has_type (fits_in_32 ty) (sdiv x y))) (let - ((a XReg (sext x ty $I64)) - (b XReg (sext y ty $I64)) + ((a XReg (sext x)) + (b XReg (sext y)) (_ InstOutput (gen_div_overflow a b ty)) (_ InstOutput (gen_div_by_zero b))) (rv_divw a b))) @@ -526,25 +537,25 @@ (rule -1 (lower (has_type (fits_in_16 ty) (urem x y))) (let - ((y2 XReg (zext y ty $I64)) + ((y2 XReg (zext y)) (_ InstOutput (gen_div_by_zero y2))) - (rv_remuw (zext x ty $I64) y2))) + (rv_remuw (zext x) y2))) (rule -1 (lower (has_type (fits_in_16 ty) (srem x y))) (let - ((y2 XReg (sext y ty $I64)) + ((y2 XReg (sext y)) (_ InstOutput (gen_div_by_zero y2))) - (rv_remw (sext x ty $I64) y2))) + (rv_remw (sext x) y2))) (rule (lower (has_type $I32 (srem x y))) (let - ((y2 XReg (sext y $I32 $I64)) + ((y2 XReg (sext y)) (_ InstOutput (gen_div_by_zero y2))) (rv_remw x y2))) (rule (lower (has_type $I32 (urem x y))) (let - ((y2 XReg (zext y $I32 $I64)) + ((y2 XReg (zext y)) (_ InstOutput (gen_div_by_zero y2))) (rv_remuw x y2))) @@ -723,6 +734,7 @@ (value_regs hi_rev lo_rev))) ;;;; Rules for `bswap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (bswap x))) (gen_bswap ty x)) @@ -731,35 +743,137 @@ (gen_bswap $I64 (value_regs_get x 1)) (gen_bswap $I64 (value_regs_get x 0)))) +(rule 3 (lower (has_type (fits_in_32 ty) (bswap x))) + (if-let $true (has_zbb)) + (if-let shift_amt (u64_to_imm12 (u64_sub 64 (ty_bits ty)))) + (rv_srli (rv_rev8 x) shift_amt)) + +(rule 4 (lower (has_type $I64 (bswap x))) + (if-let $true (has_zbb)) + (rv_rev8 x)) + +(rule 5 (lower (has_type $I128 (bswap x))) + (if-let $true (has_zbb)) + (value_regs + (rv_rev8 (value_regs_get x 1)) + (rv_rev8 (value_regs_get x 0)))) + +;; Helper to generate a fallback byte-swap sequence when `zbb` isn't available. +(decl gen_bswap (Type XReg) XReg) + +;; This is only here to make the rule below work. bswap.i8 isn't valid +(rule 0 (gen_bswap $I8 x) x) +(rule 1 (gen_bswap (ty_int_ref_16_to_64 ty) x) + (if-let half_ty (ty_half_width ty)) + (if-let half_size (u64_to_imm12 (ty_bits half_ty))) + (let (;; This swaps the top bytes and zeroes the bottom bytes, so that + ;; we can or it with the bottom bytes later. + (swap_top XReg (gen_bswap half_ty x)) + (top XReg (rv_slli swap_top half_size)) + + ;; Get the top half, swap it, and zero extend it so we can `or` it + ;; with the bottom half. Note that zero extension here already knows + ;; that `zbb` isn't available and that `half_ty` is not `$I64`, so this + ;; falls back to the shift-then-shift sequence. + (shifted XReg (rv_srli x half_size)) + (swap_bot XReg (gen_bswap half_ty shifted)) + (shift Imm12 (imm_from_bits (u64_sub 64 (ty_bits half_ty)))) + (bot_shifted_left XReg (rv_slli swap_bot shift)) + (bot XReg (rv_srli bot_shifted_left shift))) + (rv_or top bot))) ;;;; Rules for `ctz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type (fits_in_64 ty) (ctz x))) (lower_ctz ty x)) (rule 1 (lower (has_type $I128 (ctz x))) - (lower_ctz_128 x)) + (let ((x_lo XReg (value_regs_get x 0)) + (x_hi XReg (value_regs_get x 1)) + ;; Count both halves + (high XReg (lower_ctz $I64 x_hi)) + (low XReg (lower_ctz $I64 x_lo)) + ;; Only add the top half if the bottom is zero + (high XReg (gen_select_reg (IntCC.Equal) x_lo (zero_reg) high (zero_reg))) + (result XReg (rv_add low high))) + (value_regs result (imm $I64 0)))) ;;;; Rules for `clz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_64 ty) (clz x))) - (lower_clz ty x)) +(rule 0 (lower (has_type (fits_in_64 ty) (clz x))) + (gen_cltz $true x ty)) (rule 1 (lower (has_type $I128 (clz x))) - (lower_clz_i128 x)) + (let ((x_lo XReg (value_regs_get x 0)) + (x_hi XReg (value_regs_get x 1)) + ;; Count both halves + (high XReg (gen_clz x_hi)) + (low XReg (gen_clz x_lo)) + ;; Only add the bottom zeros if the top half is zero + (low XReg (gen_select_reg (IntCC.Equal) x_hi (zero_reg) low (zero_reg)))) + (value_regs (rv_add high low) (imm $I64 0)))) + +(rule 2 (lower (has_type (fits_in_16 ty) (clz x))) + (if-let $true (has_zbb)) + (let ((tmp XReg (zext x)) + (count XReg (rv_clz tmp))) + ;; We always do the operation on the full 64-bit register, so subtract 64 from the result. + (rv_addi count (imm12_const_add (ty_bits ty) -64)))) + +(rule 3 (lower (has_type $I32 (clz x))) + (if-let $true (has_zbb)) + (rv_clzw x)) + +(rule 3 (lower (has_type $I64 (clz x))) + (if-let $true (has_zbb)) + (rv_clz x)) + +(decl gen_clz (XReg) XReg) +(rule 0 (gen_clz rs) + (gen_cltz $true rs $I64)) +(rule 1 (gen_clz rs) + (if-let $true (has_zbb)) + (rv_clz rs)) ;;;; Rules for `cls` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type (fits_in_64 ty) (cls x))) - (lower_cls ty x)) +(rule (lower (has_type (fits_in_64 ty) (cls x))) + (let ((tmp XReg (sext x)) + (tmp2 XReg (gen_select_reg (IntCC.SignedLessThan) tmp (zero_reg) (rv_not tmp) tmp)) + (tmp3 XReg (gen_clz tmp2))) + ;; clz counted the full register width, so subtract (64-$width), and then + ;; additionally subtract one more, meaning here -65+width is added. + (rv_addi tmp3 (imm12_const_add (ty_bits ty) -65)))) + +;; If the sign bit is set, we count the leading zeros of the inverted value. +;; Otherwise we can just count the leading zeros of the original value. +;; Subtract 1 since the sign bit does not count. (rule 1 (lower (has_type $I128 (cls x))) - (lower_cls_i128 x)) + (let ((low XReg (value_regs_get x 0)) + (high XReg (value_regs_get x 1)) + (low XReg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (rv_not low) low)) + (high XReg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (rv_not high) high)) + + ;; Count both halves + (high_cnt XReg (gen_clz high)) + (low_cnt XReg (gen_clz low)) + ;; Only add the bottom zeros if the top half is zero + (low_cnt XReg (gen_select_reg (IntCC.Equal) high (zero_reg) low_cnt (zero_reg))) + (count XReg (rv_add high_cnt low_cnt)) + (result XReg (rv_addi count (imm12_const -1)))) + (value_regs result (imm $I64 0)))) + ;;;; Rules for `uextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type out_ty (uextend val @ (value_type in_ty)))) - (extend val (ExtendOp.Zero) in_ty out_ty)) +(rule 0 (lower (has_type (fits_in_64 _) (uextend val))) + (zext val)) +(rule 1 (lower (has_type $I128 (uextend val))) + (value_regs (zext val) (imm $I64 0))) ;;;; Rules for `sextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type out_ty (sextend val @ (value_type in_ty)))) - (extend val (ExtendOp.Signed) in_ty out_ty)) +(rule 0 (lower (has_type (fits_in_64 _) (sextend val @ (value_type in_ty)))) + (sext val)) +(rule 1 (lower (has_type $I128 (sextend val @ (value_type in_ty)))) + (let ((lo XReg (sext val))) + (value_regs lo (rv_srai lo (imm12_const 63))))) ;; The instructions below are present in RV64I and sign-extend the result to 64 bits. @@ -796,11 +910,33 @@ ;;;; Rules for `popcnt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (popcnt x))) - (lower_popcnt x ty)) +(rule 0 (lower (has_type (fits_in_64 _) (popcnt x))) + (gen_popcnt (zext x))) (rule 1 (lower (has_type $I128 (popcnt x))) - (lower_popcnt_i128 x)) + (let + ((x ValueRegs x) + (low XReg (gen_popcnt (value_regs_get x 0))) + (high XReg (gen_popcnt (value_regs_get x 1))) + (result XReg (rv_add low high))) + (value_regs result (imm $I64 0)))) + +(rule 2 (lower (has_type (fits_in_64 _) (popcnt x))) + (if-let $true (has_zbb)) + (rv_cpop (zext x))) + +(rule 3 (lower (has_type $I32 (popcnt x))) + (if-let $true (has_zbb)) + (rv_cpopw x)) + +(rule 3 (lower (has_type $I128 (popcnt x))) + (if-let $true (has_zbb)) + (let + ((x ValueRegs x) + (low XReg (rv_cpop (value_regs_get x 0))) + (high XReg (rv_cpop (value_regs_get x 1))) + (result XReg (rv_add low high))) + (value_regs result (imm $I64 0)))) ;; Popcount using multiply. ;; This is popcount64c() from @@ -814,7 +950,7 @@ ;; ;; TODO: LLVM generates a much better implementation for I8X16. See: https://godbolt.org/z/qr6vf9Gr3 ;; For the other types it seems to be largely the same. -(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (popcnt x))) +(rule 4 (lower (has_type (ty_vec_fits_in_register ty) (popcnt x))) (if-let one (u64_to_uimm5 1)) (if-let two (u64_to_uimm5 2)) (if-let four (u64_to_uimm5 4)) @@ -909,7 +1045,7 @@ ;; zero extended. (rule 0 (lower (has_type (ty_int (fits_in_16 ty)) (ushr x y))) (if-let mask (u64_to_imm12 (shift_mask ty))) - (rv_srlw (zext x ty $I64) (rv_andi (value_regs_get y 0) mask))) + (rv_srlw (zext x) (rv_andi (value_regs_get y 0) mask))) ;; Using the 32bit version of `srl` automatically masks the shift amount. (rule 1 (lower (has_type $I32 (ushr x y))) @@ -921,7 +1057,7 @@ ;; When the RHS is known we can just encode it in the instruction. (rule 2 (lower (has_type (ty_int (fits_in_16 ty)) (ushr x (maybe_uextend (imm12_from_value y))))) - (rv_srliw (zext x ty $I64) (imm12_and y (shift_mask ty)))) + (rv_srliw (zext x) (imm12_and y (shift_mask ty)))) (rule 3 (lower (has_type $I32 (ushr x (maybe_uextend (imm12_from_value y))))) (rv_srliw x y)) @@ -963,7 +1099,7 @@ ;; zero extended. (rule 0 (lower (has_type (ty_int (fits_in_16 ty)) (sshr x y))) (if-let mask (u64_to_imm12 (shift_mask ty))) - (rv_sraw (sext x ty $I64) (rv_andi (value_regs_get y 0) mask))) + (rv_sraw (sext x) (rv_andi (value_regs_get y 0) mask))) ;; Using the 32bit version of `sra` automatically masks the shift amount. (rule 1 (lower (has_type $I32 (sshr x y))) @@ -975,7 +1111,7 @@ ;; When the RHS is known we can just encode it in the instruction. (rule 2 (lower (has_type (ty_int (fits_in_16 ty)) (sshr x (maybe_uextend (imm12_from_value y))))) - (rv_sraiw (sext x ty $I64) (imm12_and y (shift_mask ty)))) + (rv_sraiw (sext x) (imm12_and y (shift_mask ty)))) (rule 3 (lower (has_type $I32 (sshr x (maybe_uextend (imm12_from_value y))))) (rv_sraiw x y)) @@ -1019,14 +1155,14 @@ ;;;; Rules for `rotl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type (fits_in_64 ty) (rotl x y))) - (lower_rotl ty (zext x ty $I64) (value_regs_get y 0))) + (lower_rotl ty (zext x) (value_regs_get y 0))) (rule 1 (lower (has_type $I128 (rotl x y))) (lower_i128_rotl x y)) ;;;; Rules for `rotr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type (fits_in_64 ty) (rotr x y))) - (lower_rotr ty (zext x ty $I64) (value_regs_get y 0))) + (lower_rotr ty (zext x) (value_regs_get y 0))) (rule 1 (lower (has_type $I128 (rotr x y))) (lower_i128_rotr x y)) @@ -1120,7 +1256,7 @@ (rule 2 (lower (has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw flags (is_atomic_rmw_max_etc op $true) addr x))) - (gen_atomic_rmw_loop op ty addr (sext x ty $I64))) + (gen_atomic_rmw_loop op ty addr (sext x))) (rule 2 @@ -1128,7 +1264,7 @@ (lower (has_type (valid_atomic_transaction (fits_in_16 ty)) (atomic_rmw flags (is_atomic_rmw_max_etc op $false) addr x))) ;; - (gen_atomic_rmw_loop op ty addr (zext x ty $I64))) + (gen_atomic_rmw_loop op ty addr (zext x))) ;;;;; Rules for `AtomicRmwOp.Sub` (rule @@ -1189,7 +1325,7 @@ (let ((t0 WritableReg (temp_writable_reg ty)) (dst WritableReg (temp_writable_reg ty)) - (_ Unit (emit (MInst.AtomicCas (gen_atomic_offset p ty) t0 dst (zext e ty $I64) (gen_atomic_p p ty) x ty)))) + (_ Unit (emit (MInst.AtomicCas (gen_atomic_offset p ty) t0 dst (zext e) (gen_atomic_p p ty) x ty)))) (writable_reg_to_reg dst))) ;;;;; Rules for `ireduce`;;;;;;;;;;;;;;;;; @@ -1334,14 +1470,18 @@ (rv_seqz (rv_addi v (imm12_const 1)))) ;;;;; Rules for `select`;;;;;;;;; -(rule - (lower (has_type ty (select c @ (value_type cty) x y))) - (gen_select ty (truthy_to_reg cty (normalize_cmp_value cty c (ExtendOp.Zero))) x y)) +(rule 0 (lower (has_type ty (select c x y))) + (gen_select ty (truthy_to_reg c) x y)) -(rule 1 - (lower (has_type (fits_in_64 ty) (select (icmp cc a b @ (value_type (fits_in_64 in_ty))) x y))) - (let ((a XReg (truthy_to_reg in_ty (normalize_cmp_value in_ty a (intcc_to_extend_op cc)))) - (b XReg (truthy_to_reg in_ty (normalize_cmp_value in_ty b (intcc_to_extend_op cc))))) +(rule 1 (lower (has_type (fits_in_64 ty) (select (icmp cc a b @ (value_type (fits_in_64 in_ty))) x y))) + (let ((a XReg (zext a)) + (b XReg (zext b))) + (gen_select_reg cc a b x y))) + +(rule 2 (lower (has_type (fits_in_64 ty) (select (icmp cc a b @ (value_type (fits_in_64 in_ty))) x y))) + (if (signed_cond_code cc)) + (let ((a XReg (sext a)) + (b XReg (sext b))) (gen_select_reg cc a b x y))) ;;;;; Rules for `bitselect`;;;;;;;;; @@ -1356,7 +1496,7 @@ ;; For vectors, we also do the same operation. ;; We can technically use any type in the bitwise operations, but prefer ;; using the type of the inputs so that we avoid emitting unnecessary -;; `vsetvl` instructions. It's likeley that the vector unit is already +;; `vsetvl` instructions. it's likeley that the vector unit is already ;; configured for that type. (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (bitselect c x y))) (let ((tmp_x VReg (rv_vand_vv c x (unmasked) ty)) @@ -1404,58 +1544,70 @@ ;;;;; Rules for `smax`;;;;;;;;; -(rule 0 (lower (has_type (ty_int ty) (smax x y))) - (gen_int_select ty (IntSelectOP.Smax) (ext_int_if_need $true x ty) (ext_int_if_need $true y ty))) +(rule 0 (lower (has_type (fits_in_64 ty) (smax x y))) + (gen_int_select ty (IntSelectOP.Smax) (sext x) (sext y))) -(rule 1 (lower (has_type (ty_vec_fits_in_register ty) (smax x y))) +(rule 1 (lower (has_type $I128 (smax x y))) + (gen_int_select $I128 (IntSelectOP.Smax) x y)) + +(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (smax x y))) (rv_vmax_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (smax x (splat y)))) +(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (smax x (splat y)))) (rv_vmax_vx x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (smax (splat x) y))) +(rule 4 (lower (has_type (ty_vec_fits_in_register ty) (smax (splat x) y))) (rv_vmax_vx y x (unmasked) ty)) ;;;;; Rules for `smin`;;;;;;;;; -(rule 0 (lower (has_type (ty_int ty) (smin x y))) - (gen_int_select ty (IntSelectOP.Smin) (ext_int_if_need $true x ty) (ext_int_if_need $true y ty))) +(rule 0 (lower (has_type (fits_in_64 ty) (smin x y))) + (gen_int_select ty (IntSelectOP.Smin) (sext x) (sext y))) + +(rule 1 (lower (has_type $I128 (smin x y))) + (gen_int_select $I128 (IntSelectOP.Smin) x y)) -(rule 1 (lower (has_type (ty_vec_fits_in_register ty) (smin x y))) +(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (smin x y))) (rv_vmin_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (smin x (splat y)))) +(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (smin x (splat y)))) (rv_vmin_vx x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (smin (splat x) y))) +(rule 4 (lower (has_type (ty_vec_fits_in_register ty) (smin (splat x) y))) (rv_vmin_vx y x (unmasked) ty)) ;;;;; Rules for `umax`;;;;;;;;; -(rule 0 (lower (has_type (ty_int ty) (umax x y))) - (gen_int_select ty (IntSelectOP.Umax) (ext_int_if_need $false x ty) (ext_int_if_need $false y ty))) +(rule 0 (lower (has_type (fits_in_64 ty) (umax x y))) + (gen_int_select ty (IntSelectOP.Umax) (zext x) (zext y))) + +(rule 1 (lower (has_type $I128 (umax x y))) + (gen_int_select $I128 (IntSelectOP.Umax) x y)) -(rule 1 (lower (has_type (ty_vec_fits_in_register ty) (umax x y))) +(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (umax x y))) (rv_vmaxu_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (umax x (splat y)))) +(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (umax x (splat y)))) (rv_vmaxu_vx x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (umax (splat x) y))) +(rule 4 (lower (has_type (ty_vec_fits_in_register ty) (umax (splat x) y))) (rv_vmaxu_vx y x (unmasked) ty)) ;;;;; Rules for `umin`;;;;;;;;; -(rule 0 (lower (has_type (ty_int ty) (umin x y))) - (gen_int_select ty (IntSelectOP.Umin) (ext_int_if_need $false x ty) (ext_int_if_need $false y ty))) +(rule 0 (lower (has_type (fits_in_64 ty) (umin x y))) + (gen_int_select ty (IntSelectOP.Umin) (zext x) (zext y))) -(rule 1 (lower (has_type (ty_vec_fits_in_register ty) (umin x y))) +(rule 1 (lower (has_type $I128 (umin x y))) + (gen_int_select $I128 (IntSelectOP.Umin) x y)) + +(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (umin x y))) (rv_vminu_vv x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (umin x (splat y)))) +(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (umin x (splat y)))) (rv_vminu_vx x y (unmasked) ty)) -(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (umin (splat x) y))) +(rule 4 (lower (has_type (ty_vec_fits_in_register ty) (umin (splat x) y))) (rv_vminu_vx y x (unmasked) ty)) @@ -1601,9 +1753,16 @@ result)) (rule 0 (lower (icmp cc x @ (value_type (ty_int ty)) y)) - (lower_icmp cc x y ty)) + (gen_icmp cc (zext x) (zext y) ty)) + +(rule 1 (lower (icmp cc x @ (value_type (ty_int ty)) y)) + (if (signed_cond_code cc)) + (gen_icmp cc (sext x) (sext y) ty)) -(rule 1 (lower (icmp cc x @ (value_type (ty_vec_fits_in_register ty)) y)) +(rule 2 (lower (icmp cc x @ (value_type $I128) y)) + (gen_icmp cc x y $I128)) + +(rule 3 (lower (icmp cc x @ (value_type (ty_vec_fits_in_register ty)) y)) (gen_expand_mask ty (gen_icmp_mask ty cc x y))) @@ -1649,21 +1808,47 @@ (rv_vmerge_vim cvt zero is_nan from_ty))) ;;;;; Rules for `fcvt_from_sint`;;;;;;;;; -(rule 0 (lower (has_type (ty_scalar_float to) (fcvt_from_sint v @ (value_type from_ty)))) - (let ((float_op FpuOPRR (int_convert_2_float_op from_ty $true to)) - (value XReg (normalize_fcvt_from_int v from_ty (ExtendOp.Signed)))) - (fpu_rr float_op to value))) +(rule 0 (lower (has_type $F32 (fcvt_from_sint v @ (value_type (fits_in_16 ty))))) + (rv_fcvtsl (sext v))) + +(rule 1 (lower (has_type $F32 (fcvt_from_sint v @ (value_type $I32)))) + (rv_fcvtsw v)) + +(rule 1 (lower (has_type $F32 (fcvt_from_sint v @ (value_type $I64)))) + (rv_fcvtsl v)) + +(rule 0 (lower (has_type $F64 (fcvt_from_sint v @ (value_type (fits_in_16 ty))))) + (rv_fcvtdl (sext v))) + +(rule 1 (lower (has_type $F64 (fcvt_from_sint v @ (value_type $I32)))) + (rv_fcvtdw v)) -(rule 1 (lower (has_type (ty_vec_fits_in_register _) (fcvt_from_sint v @ (value_type from_ty)))) +(rule 1 (lower (has_type $F64 (fcvt_from_sint v @ (value_type $I64)))) + (rv_fcvtdl v)) + +(rule 2 (lower (has_type (ty_vec_fits_in_register _) (fcvt_from_sint v @ (value_type from_ty)))) (rv_vfcvt_f_x_v v (unmasked) from_ty)) ;;;;; Rules for `fcvt_from_uint`;;;;;;;;; -(rule 0 (lower (has_type (ty_scalar_float to) (fcvt_from_uint v @ (value_type from_ty)))) - (let ((float_op FpuOPRR (int_convert_2_float_op from_ty $false to)) - (value XReg (normalize_fcvt_from_int v from_ty (ExtendOp.Zero)))) - (fpu_rr float_op to value))) +(rule 0 (lower (has_type $F32 (fcvt_from_uint v @ (value_type (fits_in_16 ty))))) + (rv_fcvtslu (zext v))) + +(rule 1 (lower (has_type $F32 (fcvt_from_uint v @ (value_type $I32)))) + (rv_fcvtswu v)) + +(rule 1 (lower (has_type $F32 (fcvt_from_uint v @ (value_type $I64)))) + (rv_fcvtslu v)) + +(rule 0 (lower (has_type $F64 (fcvt_from_uint v @ (value_type (fits_in_16 ty))))) + (rv_fcvtdlu (zext v))) + +(rule 1 (lower (has_type $F64 (fcvt_from_uint v @ (value_type $I32)))) + (rv_fcvtdwu v)) + +(rule 1 (lower (has_type $F64 (fcvt_from_uint v @ (value_type $I64)))) + (rv_fcvtdlu v)) -(rule 1 (lower (has_type (ty_vec_fits_in_register _) (fcvt_from_uint v @ (value_type from_ty)))) +(rule 2 (lower (has_type (ty_vec_fits_in_register _) (fcvt_from_uint v @ (value_type from_ty)))) (rv_vfcvt_f_xu_v v (unmasked) from_ty)) ;;;;; Rules for `symbol_value`;;;;;;;;; @@ -1720,7 +1905,7 @@ (rule (lower (has_type ty (select_spectre_guard cmp @ (value_type cmp_ty) x @ (value_type arg_ty) y))) (let (;; Build a mask that is 0 or -1 depending on the input comparision value. ;; `lower_bmask` handles normalizing the input. - (mask ValueRegs (lower_bmask arg_ty cmp_ty cmp)) + (mask ValueRegs (lower_bmask cmp arg_ty)) ;; Using the mask above we can select either `x` or `y` by ;; performing a bitwise `and` on both sides and then merging them ;; together. We know that only the bits of one of the sides will be selected. @@ -1731,8 +1916,8 @@ ;;;;; Rules for `bmask`;;;;;;;;; (rule - (lower (has_type oty (bmask x @ (value_type ity)))) - (lower_bmask oty ity x)) + (lower (has_type oty (bmask x))) + (lower_bmask x oty)) ;; N.B.: the Ret itself is generated by the ABI. (rule (lower (return args)) @@ -1757,7 +1942,7 @@ ;; neg a1, a0 ;; max a0, a0, a1 (rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (iabs x))) - (let ((extended XReg (sext x ty $I64)) + (let ((extended XReg (sext x)) (negated XReg (rv_neg extended))) (max $I64 extended negated))) diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs index 88cf010f34ec..86a2653a631e 100644 --- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs +++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs @@ -3,7 +3,7 @@ // Pull in the ISLE generated code. #[allow(unused)] pub mod generated_code; -use generated_code::{Context, ExtendOp, MInst}; +use generated_code::{Context, MInst}; // Types that the generated ISLE code uses via `use super::*`. use self::generated_code::{VecAluOpRR, VecLmul}; @@ -174,22 +174,7 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> _ => unreachable!(), } } - fn intcc_to_extend_op(&mut self, cc: &IntCC) -> ExtendOp { - use IntCC::*; - match *cc { - Equal - | NotEqual - | UnsignedLessThan - | UnsignedGreaterThanOrEqual - | UnsignedGreaterThan - | UnsignedLessThanOrEqual => ExtendOp::Zero, - - SignedLessThan - | SignedGreaterThanOrEqual - | SignedGreaterThan - | SignedLessThanOrEqual => ExtendOp::Signed, - } - } + fn lower_cond_br( &mut self, cc: &IntCC, @@ -208,25 +193,6 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> .iter() .for_each(|i| self.emit(i)); } - fn lower_br_icmp( - &mut self, - cc: &IntCC, - a: ValueRegs, - b: ValueRegs, - targets: &VecMachLabel, - ty: Type, - ) -> Unit { - let test = generated_code::constructor_lower_icmp(self, cc, a, b, ty); - self.emit(&MInst::CondBr { - taken: CondBrTarget::Label(targets[0]), - not_taken: CondBrTarget::Label(targets[1]), - kind: IntegerCompare { - kind: IntCC::NotEqual, - rs1: test, - rs2: zero_reg(), - }, - }); - } fn load_ra(&mut self) -> Reg { if self.backend.flags.preserve_frame_pointers() { let tmp = self.temp_writable_reg(I64); @@ -446,10 +412,6 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> self.backend.isa_flags.has_zbs() } - fn int_convert_2_float_op(&mut self, from: Type, is_signed: bool, to: Type) -> FpuOPRR { - FpuOPRR::int_convert_2_float_op(from, is_signed, to) - } - fn gen_reg_offset_amode(&mut self, base: Reg, offset: i64, ty: Type) -> AMode { AMode::RegOffset(base, offset, ty) } diff --git a/cranelift/filetests/filetests/isa/riscv64/bitops.clif b/cranelift/filetests/filetests/isa/riscv64/bitops.clif index a8bcd7c2c6af..6b6d5abbc132 100644 --- a/cranelift/filetests/filetests/isa/riscv64/bitops.clif +++ b/cranelift/filetests/filetests/isa/riscv64/bitops.clif @@ -457,8 +457,8 @@ block0(v0: i8): ; srai a4,a2,56 ; not a0,a4 ; select_reg a2,a0,a4##condition=(a4 slt zero) -; clz a0,a2##ty=i8 tmp=a4 step=a5 -; addi a0,a0,-1 +; clz a0,a2##ty=i64 tmp=a4 step=a5 +; addi a0,a0,-57 ; ret ; ; Disassembled: @@ -471,9 +471,9 @@ block0(v0: i8): ; j 8 ; mv a2, a0 ; mv a0, zero -; addi a5, zero, 8 +; addi a5, zero, 0x40 ; addi a4, zero, 1 -; slli a4, a4, 7 +; slli a4, a4, 0x3f ; blez a5, 0x1c ; and t5, a4, a2 ; bne zero, t5, 0x14 @@ -481,7 +481,7 @@ block0(v0: i8): ; addi a5, a5, -1 ; srli a4, a4, 1 ; j -0x18 -; addi a0, a0, -1 +; addi a0, a0, -0x39 ; ret function %c(i16) -> i16 { @@ -496,8 +496,8 @@ block0(v0: i16): ; srai a4,a2,48 ; not a0,a4 ; select_reg a2,a0,a4##condition=(a4 slt zero) -; clz a0,a2##ty=i16 tmp=a4 step=a5 -; addi a0,a0,-1 +; clz a0,a2##ty=i64 tmp=a4 step=a5 +; addi a0,a0,-49 ; ret ; ; Disassembled: @@ -510,9 +510,9 @@ block0(v0: i16): ; j 8 ; mv a2, a0 ; mv a0, zero -; addi a5, zero, 0x10 +; addi a5, zero, 0x40 ; addi a4, zero, 1 -; slli a4, a4, 0xf +; slli a4, a4, 0x3f ; blez a5, 0x1c ; and t5, a4, a2 ; bne zero, t5, 0x14 @@ -520,7 +520,7 @@ block0(v0: i16): ; addi a5, a5, -1 ; srli a4, a4, 1 ; j -0x18 -; addi a0, a0, -1 +; addi a0, a0, -0x31 ; ret function %c(i32) -> i32 { @@ -534,8 +534,8 @@ block0(v0: i32): ; sext.w a2,a0 ; not a4,a2 ; select_reg a0,a4,a2##condition=(a2 slt zero) -; clz a4,a0##ty=i32 tmp=a2 step=a3 -; addi a0,a4,-1 +; clz a4,a0##ty=i64 tmp=a2 step=a3 +; addi a0,a4,-33 ; ret ; ; Disassembled: @@ -547,9 +547,9 @@ block0(v0: i32): ; j 8 ; mv a0, a4 ; mv a4, zero -; addi a3, zero, 0x20 +; addi a3, zero, 0x40 ; addi a2, zero, 1 -; slli a2, a2, 0x1f +; slli a2, a2, 0x3f ; blez a3, 0x1c ; and t5, a2, a0 ; bne zero, t5, 0x14 @@ -557,7 +557,7 @@ block0(v0: i32): ; addi a3, a3, -1 ; srli a2, a2, 1 ; j -0x18 -; addi a0, a4, -1 +; addi a0, a4, -0x21 ; ret function %c(i64) -> i64 { @@ -611,7 +611,6 @@ block0(v0: i128): ; clz a0,a5##ty=i64 tmp=a2 step=a4 ; select_reg a2,a0,zero##condition=(a3 eq zero) ; add a3,a1,a2 -; li a5,0 ; addi a0,a3,-1 ; li a1,0 ; ret @@ -655,7 +654,6 @@ block0(v0: i128): ; j 8 ; mv a2, a0 ; add a3, a1, a2 -; mv a5, zero ; addi a0, a3, -1 ; mv a1, zero ; ret @@ -889,22 +887,24 @@ block0(v0: i32): ; VCode: ; block0: -; mv a5,a0 -; popcnt a0,a5##ty=i32 tmp=a2 step=a3 +; slli a2,a0,32 +; srli a4,a2,32 +; popcnt a0,a4##ty=i64 tmp=a2 step=a1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mv a5, a0 +; slli a2, a0, 0x20 +; srli a4, a2, 0x20 ; mv a0, zero -; addi a3, zero, 0x20 +; addi a1, zero, 0x40 ; addi a2, zero, 1 -; slli a2, a2, 0x1f -; blez a3, 0x1c -; and t5, a2, a5 +; slli a2, a2, 0x3f +; blez a1, 0x1c +; and t5, a2, a4 ; beq zero, t5, 8 ; addi a0, a0, 1 -; addi a3, a3, -1 +; addi a1, a1, -1 ; srli a2, a2, 1 ; j -0x18 ; ret @@ -917,22 +917,24 @@ block0(v0: i16): ; VCode: ; block0: -; mv a5,a0 -; popcnt a0,a5##ty=i16 tmp=a2 step=a3 +; slli a2,a0,48 +; srli a4,a2,48 +; popcnt a0,a4##ty=i64 tmp=a2 step=a1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mv a5, a0 +; slli a2, a0, 0x30 +; srli a4, a2, 0x30 ; mv a0, zero -; addi a3, zero, 0x10 +; addi a1, zero, 0x40 ; addi a2, zero, 1 -; slli a2, a2, 0xf -; blez a3, 0x1c -; and t5, a2, a5 +; slli a2, a2, 0x3f +; blez a1, 0x1c +; and t5, a2, a4 ; beq zero, t5, 8 ; addi a0, a0, 1 -; addi a3, a3, -1 +; addi a1, a1, -1 ; srli a2, a2, 1 ; j -0x18 ; ret @@ -945,23 +947,23 @@ block0(v0: i8): ; VCode: ; block0: -; mv a5,a0 -; popcnt a0,a5##ty=i8 tmp=a2 step=a3 +; andi a2,a0,255 +; popcnt a0,a2##ty=i64 tmp=a4 step=a5 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mv a5, a0 +; andi a2, a0, 0xff ; mv a0, zero -; addi a3, zero, 8 -; addi a2, zero, 1 -; slli a2, a2, 7 -; blez a3, 0x1c -; and t5, a2, a5 +; addi a5, zero, 0x40 +; addi a4, zero, 1 +; slli a4, a4, 0x3f +; blez a5, 0x1c +; and t5, a4, a2 ; beq zero, t5, 8 ; addi a0, a0, 1 -; addi a3, a3, -1 -; srli a2, a2, 1 +; addi a5, a5, -1 +; srli a4, a4, 1 ; j -0x18 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/bswap.clif b/cranelift/filetests/filetests/isa/riscv64/bswap.clif index 020da5644da7..cbcfe12e57c0 100644 --- a/cranelift/filetests/filetests/isa/riscv64/bswap.clif +++ b/cranelift/filetests/filetests/isa/riscv64/bswap.clif @@ -12,16 +12,18 @@ block0(v0: i16): ; block0: ; slli a2,a0,8 ; srli a4,a0,8 -; andi a0,a4,255 -; or a0,a2,a0 +; slli a0,a4,56 +; srli a3,a0,56 +; or a0,a2,a3 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; slli a2, a0, 8 ; srli a4, a0, 8 -; andi a0, a4, 0xff -; or a0, a2, a0 +; slli a0, a4, 0x38 +; srli a3, a0, 0x38 +; or a0, a2, a3 ; ret function %bswap_i32(i32) -> i32 { @@ -34,34 +36,38 @@ block0(v0: i32): ; block0: ; slli a2,a0,8 ; srli a4,a0,8 -; andi a1,a4,255 -; or a2,a2,a1 -; slli a4,a2,16 -; srli a0,a0,16 -; slli a2,a0,8 -; srli a5,a0,8 -; andi a0,a5,255 -; or a2,a2,a0 -; slli a5,a2,48 -; srli a0,a5,48 -; or a0,a4,a0 +; slli a1,a4,56 +; srli a3,a1,56 +; or a4,a2,a3 +; slli a1,a4,16 +; srli a2,a0,16 +; slli a4,a2,8 +; srli a0,a2,8 +; slli a2,a0,56 +; srli a5,a2,56 +; or a0,a4,a5 +; slli a2,a0,48 +; srli a4,a2,48 +; or a0,a1,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; slli a2, a0, 8 ; srli a4, a0, 8 -; andi a1, a4, 0xff -; or a2, a2, a1 -; slli a4, a2, 0x10 -; srli a0, a0, 0x10 -; slli a2, a0, 8 -; srli a5, a0, 8 -; andi a0, a5, 0xff -; or a2, a2, a0 -; slli a5, a2, 0x30 -; srli a0, a5, 0x30 -; or a0, a4, a0 +; slli a1, a4, 0x38 +; srli a3, a1, 0x38 +; or a4, a2, a3 +; slli a1, a4, 0x10 +; srli a2, a0, 0x10 +; slli a4, a2, 8 +; srli a0, a2, 8 +; slli a2, a0, 0x38 +; srli a5, a2, 0x38 +; or a0, a4, a5 +; slli a2, a0, 0x30 +; srli a4, a2, 0x30 +; or a0, a1, a4 ; ret function %bswap_i64(i64) -> i64 { @@ -74,70 +80,78 @@ block0(v0: i64): ; block0: ; slli a2,a0,8 ; srli a4,a0,8 -; andi a1,a4,255 -; or a2,a2,a1 -; slli a4,a2,16 -; srli a1,a0,16 -; slli a2,a1,8 -; srli a5,a1,8 -; andi a1,a5,255 -; or a2,a2,a1 -; slli a5,a2,48 -; srli a1,a5,48 -; or a2,a4,a1 -; slli a4,a2,32 -; srli a0,a0,32 -; slli a2,a0,8 +; slli a1,a4,56 +; srli a3,a1,56 +; or a4,a2,a3 +; slli a1,a4,16 +; srli a2,a0,16 +; slli a4,a2,8 +; srli a2,a2,8 +; slli a2,a2,56 +; srli a5,a2,56 +; or a2,a4,a5 +; slli a2,a2,48 +; srli a4,a2,48 +; or a1,a1,a4 +; slli a2,a1,32 +; srli a5,a0,32 +; slli a0,a5,8 +; srli a3,a5,8 +; slli a4,a3,56 +; srli a1,a4,56 +; or a3,a0,a1 +; slli a4,a3,16 +; srli a0,a5,16 +; slli a3,a0,8 ; srli a5,a0,8 -; andi a1,a5,255 -; or a2,a2,a1 -; slli a5,a2,16 -; srli a0,a0,16 -; slli a2,a0,8 -; srli a0,a0,8 -; andi a0,a0,255 -; or a2,a2,a0 -; slli a0,a2,48 -; srli a0,a0,48 -; or a2,a5,a0 -; slli a5,a2,32 -; srli a0,a5,32 -; or a0,a4,a0 +; slli a0,a5,56 +; srli a5,a0,56 +; or a5,a3,a5 +; slli a0,a5,48 +; srli a3,a0,48 +; or a4,a4,a3 +; slli a0,a4,32 +; srli a3,a0,32 +; or a0,a2,a3 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; slli a2, a0, 8 ; srli a4, a0, 8 -; andi a1, a4, 0xff -; or a2, a2, a1 -; slli a4, a2, 0x10 -; srli a1, a0, 0x10 -; slli a2, a1, 8 -; srli a5, a1, 8 -; andi a1, a5, 0xff -; or a2, a2, a1 -; slli a5, a2, 0x30 -; srli a1, a5, 0x30 -; or a2, a4, a1 -; slli a4, a2, 0x20 -; srli a0, a0, 0x20 -; slli a2, a0, 8 +; slli a1, a4, 0x38 +; srli a3, a1, 0x38 +; or a4, a2, a3 +; slli a1, a4, 0x10 +; srli a2, a0, 0x10 +; slli a4, a2, 8 +; srli a2, a2, 8 +; slli a2, a2, 0x38 +; srli a5, a2, 0x38 +; or a2, a4, a5 +; slli a2, a2, 0x30 +; srli a4, a2, 0x30 +; or a1, a1, a4 +; slli a2, a1, 0x20 +; srli a5, a0, 0x20 +; slli a0, a5, 8 +; srli a3, a5, 8 +; slli a4, a3, 0x38 +; srli a1, a4, 0x38 +; or a3, a0, a1 +; slli a4, a3, 0x10 +; srli a0, a5, 0x10 +; slli a3, a0, 8 ; srli a5, a0, 8 -; andi a1, a5, 0xff -; or a2, a2, a1 -; slli a5, a2, 0x10 -; srli a0, a0, 0x10 -; slli a2, a0, 8 -; srli a0, a0, 8 -; andi a0, a0, 0xff -; or a2, a2, a0 -; slli a0, a2, 0x30 -; srli a0, a0, 0x30 -; or a2, a5, a0 -; slli a5, a2, 0x20 -; srli a0, a5, 0x20 -; or a0, a4, a0 +; slli a0, a5, 0x38 +; srli a5, a0, 0x38 +; or a5, a3, a5 +; slli a0, a5, 0x30 +; srli a3, a0, 0x30 +; or a4, a4, a3 +; slli a0, a4, 0x20 +; srli a3, a0, 0x20 +; or a0, a2, a3 ; ret function %bswap_i128(i128) -> i128 { @@ -147,161 +161,154 @@ block0(v0: i128): } ; VCode: -; add sp,-16 -; sd ra,8(sp) -; sd fp,0(sp) -; mv fp,sp -; sd s5,-8(sp) -; add sp,-16 ; block0: ; slli a3,a1,8 ; srli a5,a1,8 -; andi a2,a5,255 -; or a3,a3,a2 -; slli a5,a3,16 -; srli a2,a1,16 -; slli a3,a2,8 -; srli a2,a2,8 -; andi a2,a2,255 -; or a3,a3,a2 -; slli a2,a3,48 -; srli a2,a2,48 -; or a3,a5,a2 -; slli a5,a3,32 -; srli a2,a1,32 -; slli a3,a2,8 -; srli a1,a2,8 -; andi a1,a1,255 -; or a3,a3,a1 -; slli a1,a3,16 -; srli a2,a2,16 -; slli a3,a2,8 -; srli a2,a2,8 -; andi a2,a2,255 -; or a3,a3,a2 -; slli a2,a3,48 -; srli a2,a2,48 -; or a3,a1,a2 -; slli a1,a3,32 -; srli a1,a1,32 -; or a3,a5,a1 -; mv s5,a3 -; slli a5,a0,8 -; srli a1,a0,8 -; andi a3,a1,255 -; or a5,a5,a3 -; slli a1,a5,16 -; srli a3,a0,16 -; slli a5,a3,8 -; srli a2,a3,8 -; andi a3,a2,255 -; or a5,a5,a3 -; slli a2,a5,48 -; srli a3,a2,48 -; or a5,a1,a3 -; slli a1,a5,32 -; srli a3,a0,32 -; slli a5,a3,8 -; srli a2,a3,8 -; andi a4,a2,255 -; or a5,a5,a4 +; slli a2,a5,56 +; srli a4,a2,56 +; or a5,a3,a4 ; slli a2,a5,16 -; srli a3,a3,16 +; srli a3,a1,16 ; slli a5,a3,8 ; srli a3,a3,8 -; andi a3,a3,255 +; slli a3,a3,56 +; srli a3,a3,56 +; or a3,a5,a3 +; slli a3,a3,48 +; srli a5,a3,48 +; or a2,a2,a5 +; slli a3,a2,32 +; srli a1,a1,32 +; slli a2,a1,8 +; srli a4,a1,8 +; slli a5,a4,56 +; srli a4,a5,56 +; or a4,a2,a4 +; slli a5,a4,16 +; srli a1,a1,16 +; slli a4,a1,8 +; srli a1,a1,8 +; slli a1,a1,56 +; srli a1,a1,56 +; or a1,a4,a1 +; slli a1,a1,48 +; srli a4,a1,48 +; or a5,a5,a4 +; slli a1,a5,32 +; srli a4,a1,32 +; or a5,a3,a4 +; mv t1,a5 +; slli a1,a0,8 +; srli a3,a0,8 +; slli a5,a3,56 +; srli a2,a5,56 +; or a3,a1,a2 +; slli a5,a3,16 +; srli a1,a0,16 +; slli a3,a1,8 +; srli a1,a1,8 +; slli a1,a1,56 +; srli a4,a1,56 +; or a1,a3,a4 +; slli a1,a1,48 +; srli a3,a1,48 ; or a5,a5,a3 -; slli a3,a5,48 -; srli a3,a3,48 -; or a5,a2,a3 -; slli a2,a5,32 -; srli a3,a2,32 -; or a1,a1,a3 -; mv a0,s5 -; add sp,+16 -; ld s5,-8(sp) -; ld ra,8(sp) -; ld fp,0(sp) -; add sp,+16 +; slli a1,a5,32 +; srli a4,a0,32 +; slli a5,a4,8 +; srli a2,a4,8 +; slli a3,a2,56 +; srli a0,a3,56 +; or a2,a5,a0 +; slli a3,a2,16 +; srli a5,a4,16 +; slli a2,a5,8 +; srli a4,a5,8 +; slli a5,a4,56 +; srli a4,a5,56 +; or a4,a2,a4 +; slli a5,a4,48 +; srli a2,a5,48 +; or a3,a3,a2 +; slli a5,a3,32 +; srli a2,a5,32 +; or a1,a1,a2 +; mv a0,t1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi sp, sp, -0x10 -; sd ra, 8(sp) -; sd s0, 0(sp) -; mv s0, sp -; sd s5, -8(sp) -; addi sp, sp, -0x10 -; block1: ; offset 0x18 ; slli a3, a1, 8 ; srli a5, a1, 8 -; andi a2, a5, 0xff -; or a3, a3, a2 -; slli a5, a3, 0x10 -; srli a2, a1, 0x10 -; slli a3, a2, 8 -; srli a2, a2, 8 -; andi a2, a2, 0xff -; or a3, a3, a2 -; slli a2, a3, 0x30 -; srli a2, a2, 0x30 -; or a3, a5, a2 -; slli a5, a3, 0x20 -; srli a2, a1, 0x20 -; slli a3, a2, 8 -; srli a1, a2, 8 -; andi a1, a1, 0xff -; or a3, a3, a1 -; slli a1, a3, 0x10 -; srli a2, a2, 0x10 -; slli a3, a2, 8 -; srli a2, a2, 8 -; andi a2, a2, 0xff -; or a3, a3, a2 -; slli a2, a3, 0x30 -; srli a2, a2, 0x30 -; or a3, a1, a2 -; slli a1, a3, 0x20 -; srli a1, a1, 0x20 -; or a3, a5, a1 -; mv s5, a3 -; slli a5, a0, 8 -; srli a1, a0, 8 -; andi a3, a1, 0xff -; or a5, a5, a3 -; slli a1, a5, 0x10 -; srli a3, a0, 0x10 -; slli a5, a3, 8 -; srli a2, a3, 8 -; andi a3, a2, 0xff -; or a5, a5, a3 -; slli a2, a5, 0x30 -; srli a3, a2, 0x30 -; or a5, a1, a3 -; slli a1, a5, 0x20 -; srli a3, a0, 0x20 -; slli a5, a3, 8 -; srli a2, a3, 8 -; andi a4, a2, 0xff -; or a5, a5, a4 +; slli a2, a5, 0x38 +; srli a4, a2, 0x38 +; or a5, a3, a4 ; slli a2, a5, 0x10 -; srli a3, a3, 0x10 +; srli a3, a1, 0x10 ; slli a5, a3, 8 ; srli a3, a3, 8 -; andi a3, a3, 0xff +; slli a3, a3, 0x38 +; srli a3, a3, 0x38 +; or a3, a5, a3 +; slli a3, a3, 0x30 +; srli a5, a3, 0x30 +; or a2, a2, a5 +; slli a3, a2, 0x20 +; srli a1, a1, 0x20 +; slli a2, a1, 8 +; srli a4, a1, 8 +; slli a5, a4, 0x38 +; srli a4, a5, 0x38 +; or a4, a2, a4 +; slli a5, a4, 0x10 +; srli a1, a1, 0x10 +; slli a4, a1, 8 +; srli a1, a1, 8 +; slli a1, a1, 0x38 +; srli a1, a1, 0x38 +; or a1, a4, a1 +; slli a1, a1, 0x30 +; srli a4, a1, 0x30 +; or a5, a5, a4 +; slli a1, a5, 0x20 +; srli a4, a1, 0x20 +; or a5, a3, a4 +; mv t1, a5 +; slli a1, a0, 8 +; srli a3, a0, 8 +; slli a5, a3, 0x38 +; srli a2, a5, 0x38 +; or a3, a1, a2 +; slli a5, a3, 0x10 +; srli a1, a0, 0x10 +; slli a3, a1, 8 +; srli a1, a1, 8 +; slli a1, a1, 0x38 +; srli a4, a1, 0x38 +; or a1, a3, a4 +; slli a1, a1, 0x30 +; srli a3, a1, 0x30 ; or a5, a5, a3 -; slli a3, a5, 0x30 -; srli a3, a3, 0x30 -; or a5, a2, a3 -; slli a2, a5, 0x20 -; srli a3, a2, 0x20 -; or a1, a1, a3 -; mv a0, s5 -; addi sp, sp, 0x10 -; ld s5, -8(sp) -; ld ra, 8(sp) -; ld s0, 0(sp) -; addi sp, sp, 0x10 +; slli a1, a5, 0x20 +; srli a4, a0, 0x20 +; slli a5, a4, 8 +; srli a2, a4, 8 +; slli a3, a2, 0x38 +; srli a0, a3, 0x38 +; or a2, a5, a0 +; slli a3, a2, 0x10 +; srli a5, a4, 0x10 +; slli a2, a5, 8 +; srli a4, a5, 8 +; slli a5, a4, 0x38 +; srli a4, a5, 0x38 +; or a4, a2, a4 +; slli a5, a4, 0x30 +; srli a2, a5, 0x30 +; or a3, a3, a2 +; slli a5, a3, 0x20 +; srli a2, a5, 0x20 +; or a1, a1, a2 +; mv a0, t1 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif index eb5e98021ad2..3f9fd7b2f673 100644 --- a/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif +++ b/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif @@ -14,10 +14,8 @@ block0(v0: i8): ; sext.b a2,a0 ; not a4,a2 ; select_reg a0,a4,a2##condition=(a2 slt zero) -; andi a2,a0,255 -; clz a4,a2 -; addi a0,a4,-56 -; addi a0,a0,-1 +; clz a2,a0 +; addi a0,a2,-57 ; ret ; ; Disassembled: @@ -28,10 +26,8 @@ block0(v0: i8): ; mv a0, a2 ; j 8 ; mv a0, a4 -; andi a2, a0, 0xff -; .byte 0x13, 0x17, 0x06, 0x60 -; addi a0, a4, -0x38 -; addi a0, a0, -1 +; .byte 0x13, 0x16, 0x05, 0x60 +; addi a0, a2, -0x39 ; ret function %cls_i16(i16) -> i16 { @@ -45,10 +41,8 @@ block0(v0: i16): ; sext.h a2,a0 ; not a4,a2 ; select_reg a0,a4,a2##condition=(a2 slt zero) -; zext.h a2,a0 -; clz a4,a2 -; addi a0,a4,-48 -; addi a0,a0,-1 +; clz a2,a0 +; addi a0,a2,-49 ; ret ; ; Disassembled: @@ -59,10 +53,8 @@ block0(v0: i16): ; mv a0, a2 ; j 8 ; mv a0, a4 -; .byte 0x3b, 0x46, 0x05, 0x08 -; .byte 0x13, 0x17, 0x06, 0x60 -; addi a0, a4, -0x30 -; addi a0, a0, -1 +; .byte 0x13, 0x16, 0x05, 0x60 +; addi a0, a2, -0x31 ; ret function %cls_i32(i32) -> i32 { @@ -76,8 +68,8 @@ block0(v0: i32): ; sext.w a2,a0 ; not a4,a2 ; select_reg a0,a4,a2##condition=(a2 slt zero) -; clzw a2,a0 -; addi a0,a2,-1 +; clz a2,a0 +; addi a0,a2,-33 ; ret ; ; Disassembled: @@ -88,8 +80,8 @@ block0(v0: i32): ; mv a0, a2 ; j 8 ; mv a0, a4 -; .byte 0x1b, 0x16, 0x05, 0x60 -; addi a0, a2, -1 +; .byte 0x13, 0x16, 0x05, 0x60 +; addi a0, a2, -0x21 ; ret function %cls_i64(i64) -> i64 { @@ -133,7 +125,6 @@ block0(v0: i128): ; clz a1,a5 ; select_reg a3,a1,zero##condition=(a3 eq zero) ; add a5,a0,a3 -; li a1,0 ; addi a0,a5,-1 ; li a1,0 ; ret @@ -157,7 +148,6 @@ block0(v0: i128): ; j 8 ; mv a3, a1 ; add a5, a0, a3 -; mv a1, zero ; addi a0, a5, -1 ; mv a1, zero ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/condbr.clif b/cranelift/filetests/filetests/isa/riscv64/condbr.clif index e9d9d9d9addb..ee1e402883c5 100644 --- a/cranelift/filetests/filetests/isa/riscv64/condbr.clif +++ b/cranelift/filetests/filetests/isa/riscv64/condbr.clif @@ -311,7 +311,7 @@ block1: ; VCode: ; block0: -; ne a3,[a0,a1],[zerozero]##ty=i128 +; or a3,a0,a1 ; bne a3,zero,taken(label1),not_taken(label2) ; block1: ; j label3 @@ -322,12 +322,8 @@ block1: ; ; Disassembled: ; block0: ; offset 0x0 -; bnez a1, 8 -; beqz a0, 0xc -; addi a3, zero, 1 -; j 8 -; mv a3, zero -; block1: ; offset 0x14 +; or a3, a0, a1 +; block1: ; offset 0x4 ; ret function %i128_bricmp_eq(i128, i128) { diff --git a/cranelift/filetests/filetests/isa/riscv64/popcnt.clif b/cranelift/filetests/filetests/isa/riscv64/popcnt.clif new file mode 100644 index 000000000000..cb4b2847d861 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/popcnt.clif @@ -0,0 +1,94 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_zbb + +function %popcnt8(i8) -> i8 { +block0(v0: i8): + v1 = popcnt v0 + return v1 +} + +; VCode: +; block0: +; andi a2,a0,255 +; cpop a0,a2 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; andi a2, a0, 0xff +; .byte 0x13, 0x15, 0x26, 0x60 +; ret + +function %popcnt16(i16) -> i16 { +block0(v0: i16): + v1 = popcnt v0 + return v1 +} + +; VCode: +; block0: +; zext.h a2,a0 +; cpop a0,a2 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x3b, 0x46, 0x05, 0x08 +; .byte 0x13, 0x15, 0x26, 0x60 +; ret + +function %popcnt32(i32) -> i32 { +block0(v0: i32): + v1 = popcnt v0 + return v1 +} + +; VCode: +; block0: +; cpopw a0,a0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x1b, 0x15, 0x25, 0x60 +; ret + +function %popcnt64(i64) -> i64 { +block0(v0: i64): + v1 = popcnt v0 + return v1 +} + +; VCode: +; block0: +; cpop a0,a0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x13, 0x15, 0x25, 0x60 +; ret + +function %popcnt128(i128) -> i128 { +block0(v0: i128): + v1 = popcnt v0 + return v1 +} + +; VCode: +; block0: +; cpop a3,a0 +; cpop a5,a1 +; add a0,a3,a5 +; li a1,0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x93, 0x16, 0x25, 0x60 +; .byte 0x93, 0x97, 0x25, 0x60 +; add a0, a3, a5 +; mv a1, zero +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/select-float.clif b/cranelift/filetests/filetests/isa/riscv64/select-float.clif index 1a6ba5fc36c2..6a70010fb5b1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/select-float.clif +++ b/cranelift/filetests/filetests/isa/riscv64/select-float.clif @@ -223,24 +223,22 @@ block0(v0: i128, v1: f32, v2: f32): ; VCode: ; block0: ; fmv.d fa2,fa0 -; li a3,42 -; li a4,0 -; eq a3,[a0,a1],[a3,a4]##ty=i128 -; andi a2,a3,255 +; li a2,42 +; li a3,0 +; eq a2,[a0,a1],[a2,a3]##ty=i128 ; select_f32 fa0,fa2,fa1##condition=a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; fmv.d fa2, fa0 -; addi a3, zero, 0x2a -; mv a4, zero -; bne a1, a4, 0x10 -; bne a0, a3, 0xc -; addi a3, zero, 1 -; j 8 +; addi a2, zero, 0x2a ; mv a3, zero -; andi a2, a3, 0xff +; bne a1, a3, 0x10 +; bne a0, a2, 0xc +; addi a2, zero, 1 +; j 8 +; mv a2, zero ; beqz a2, 0xc ; fmv.d fa0, fa2 ; j 8 @@ -259,24 +257,22 @@ block0(v0: i128, v1: f64, v2: f64): ; VCode: ; block0: ; fmv.d fa2,fa0 -; li a3,42 -; li a4,0 -; eq a3,[a0,a1],[a3,a4]##ty=i128 -; andi a2,a3,255 +; li a2,42 +; li a3,0 +; eq a2,[a0,a1],[a2,a3]##ty=i128 ; select_f64 fa0,fa2,fa1##condition=a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; fmv.d fa2, fa0 -; addi a3, zero, 0x2a -; mv a4, zero -; bne a1, a4, 0x10 -; bne a0, a3, 0xc -; addi a3, zero, 1 -; j 8 +; addi a2, zero, 0x2a ; mv a3, zero -; andi a2, a3, 0xff +; bne a1, a3, 0x10 +; bne a0, a2, 0xc +; addi a2, zero, 1 +; j 8 +; mv a2, zero ; beqz a2, 0xc ; fmv.d fa0, fa2 ; j 8 diff --git a/cranelift/filetests/filetests/isa/riscv64/select.clif b/cranelift/filetests/filetests/isa/riscv64/select.clif index ef3011860af4..2633b9b7e2ca 100644 --- a/cranelift/filetests/filetests/isa/riscv64/select.clif +++ b/cranelift/filetests/filetests/isa/riscv64/select.clif @@ -119,35 +119,54 @@ block0(v0: i8, v1: i128, v2: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s10,-8(sp) +; add sp,-16 ; block0: -; mv a5,a0 -; mv t2,a1 -; li a0,42 -; andi a5,a5,255 +; mv s10,a1 +; li a5,42 ; andi a0,a0,255 -; eq a5,a5,a0##ty=i8 ; andi a5,a5,255 -; select_i128 [a0,a1],[t2,a2],[a3,a4]##condition=a5 +; eq a5,a0,a5##ty=i8 +; select_i128 [a0,a1],[s10,a2],[a3,a4]##condition=a5 +; add sp,+16 +; ld s10,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mv a5, a0 -; mv t2, a1 -; addi a0, zero, 0x2a -; andi a5, a5, 0xff +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s10, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 +; mv s10, a1 +; addi a5, zero, 0x2a ; andi a0, a0, 0xff -; bne a5, a0, 0xc +; andi a5, a5, 0xff +; bne a0, a5, 0xc ; addi a5, zero, 1 ; j 8 ; mv a5, zero -; andi a5, a5, 0xff ; beqz a5, 0x10 -; mv a0, t2 +; mv a0, s10 ; mv a1, a2 ; j 0xc ; mv a0, a3 ; mv a1, a4 +; addi sp, sp, 0x10 +; ld s10, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret function %select_icmp_i16_i8(i16, i8, i8) -> i8 { @@ -284,32 +303,30 @@ block0(v0: i16, v1: i128, v2: i128): ; VCode: ; block0: -; mv a7,a1 +; mv t2,a1 ; li a5,42 ; slli a0,a0,48 ; srli a0,a0,48 ; slli a5,a5,48 ; srli a5,a5,48 -; eq a0,a0,a5##ty=i16 -; andi a5,a0,255 -; select_i128 [a0,a1],[a7,a2],[a3,a4]##condition=a5 +; eq a5,a0,a5##ty=i16 +; select_i128 [a0,a1],[t2,a2],[a3,a4]##condition=a5 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mv a7, a1 +; mv t2, a1 ; addi a5, zero, 0x2a ; slli a0, a0, 0x30 ; srli a0, a0, 0x30 ; slli a5, a5, 0x30 ; srli a5, a5, 0x30 ; bne a0, a5, 0xc -; addi a0, zero, 1 +; addi a5, zero, 1 ; j 8 -; mv a0, zero -; andi a5, a0, 0xff +; mv a5, zero ; beqz a5, 0x10 -; mv a0, a7 +; mv a0, t2 ; mv a1, a2 ; j 0xc ; mv a0, a3 @@ -450,32 +467,30 @@ block0(v0: i32, v1: i128, v2: i128): ; VCode: ; block0: -; mv a7,a1 +; mv t2,a1 ; li a5,42 ; slli a0,a0,32 ; srli a0,a0,32 ; slli a5,a5,32 ; srli a5,a5,32 -; eq a0,a0,a5##ty=i32 -; andi a5,a0,255 -; select_i128 [a0,a1],[a7,a2],[a3,a4]##condition=a5 +; eq a5,a0,a5##ty=i32 +; select_i128 [a0,a1],[t2,a2],[a3,a4]##condition=a5 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mv a7, a1 +; mv t2, a1 ; addi a5, zero, 0x2a ; slli a0, a0, 0x20 ; srli a0, a0, 0x20 ; slli a5, a5, 0x20 ; srli a5, a5, 0x20 ; bne a0, a5, 0xc -; addi a0, zero, 1 +; addi a5, zero, 1 ; j 8 -; mv a0, zero -; andi a5, a0, 0xff +; mv a5, zero ; beqz a5, 0x10 -; mv a0, a7 +; mv a0, t2 ; mv a1, a2 ; j 0xc ; mv a0, a3 @@ -587,16 +602,15 @@ block0(v0: i64, v1: i128, v2: i128): ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s8,-8(sp) +; sd s6,-8(sp) ; add sp,-16 ; block0: -; mv s8,a1 +; mv s6,a1 ; li a5,42 ; eq a5,a0,a5##ty=i64 -; andi a5,a5,255 -; select_i128 [a0,a1],[s8,a2],[a3,a4]##condition=a5 +; select_i128 [a0,a1],[s6,a2],[a3,a4]##condition=a5 ; add sp,+16 -; ld s8,-8(sp) +; ld s6,-8(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -608,24 +622,23 @@ block0(v0: i64, v1: i128, v2: i128): ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s8, -8(sp) +; sd s6, -8(sp) ; addi sp, sp, -0x10 ; block1: ; offset 0x18 -; mv s8, a1 +; mv s6, a1 ; addi a5, zero, 0x2a ; bne a0, a5, 0xc ; addi a5, zero, 1 ; j 8 ; mv a5, zero -; andi a5, a5, 0xff ; beqz a5, 0x10 -; mv a0, s8 +; mv a0, s6 ; mv a1, a2 ; j 0xc ; mv a0, a3 ; mv a1, a4 ; addi sp, sp, 0x10 -; ld s8, -8(sp) +; ld s6, -8(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -645,7 +658,6 @@ block0(v0: i128, v1: i8, v2: i8): ; li a4,42 ; li a5,0 ; eq a4,[a0,a1],[a4,a5]##ty=i128 -; andi a4,a4,255 ; select_i8 a0,a2,a3##condition=a4 ; ret ; @@ -658,7 +670,6 @@ block0(v0: i128, v1: i8, v2: i8): ; addi a4, zero, 1 ; j 8 ; mv a4, zero -; andi a4, a4, 0xff ; beqz a4, 0xc ; mv a0, a2 ; j 8 @@ -679,7 +690,6 @@ block0(v0: i128, v1: i16, v2: i16): ; li a4,42 ; li a5,0 ; eq a4,[a0,a1],[a4,a5]##ty=i128 -; andi a4,a4,255 ; select_i16 a0,a2,a3##condition=a4 ; ret ; @@ -692,7 +702,6 @@ block0(v0: i128, v1: i16, v2: i16): ; addi a4, zero, 1 ; j 8 ; mv a4, zero -; andi a4, a4, 0xff ; beqz a4, 0xc ; mv a0, a2 ; j 8 @@ -713,7 +722,6 @@ block0(v0: i128, v1: i32, v2: i32): ; li a4,42 ; li a5,0 ; eq a4,[a0,a1],[a4,a5]##ty=i128 -; andi a4,a4,255 ; select_i32 a0,a2,a3##condition=a4 ; ret ; @@ -726,7 +734,6 @@ block0(v0: i128, v1: i32, v2: i32): ; addi a4, zero, 1 ; j 8 ; mv a4, zero -; andi a4, a4, 0xff ; beqz a4, 0xc ; mv a0, a2 ; j 8 @@ -747,7 +754,6 @@ block0(v0: i128, v1: i64, v2: i64): ; li a4,42 ; li a5,0 ; eq a4,[a0,a1],[a4,a5]##ty=i128 -; andi a4,a4,255 ; select_i64 a0,a2,a3##condition=a4 ; ret ; @@ -760,7 +766,6 @@ block0(v0: i128, v1: i64, v2: i64): ; addi a4, zero, 1 ; j 8 ; mv a4, zero -; andi a4, a4, 0xff ; beqz a4, 0xc ; mv a0, a2 ; j 8 @@ -782,19 +787,19 @@ block0(v0: i128, v1: i128, v2: i128): ; sd fp,0(sp) ; mv fp,sp ; sd s4,-8(sp) -; sd s6,-16(sp) -; sd s7,-24(sp) +; sd s5,-16(sp) +; sd s6,-24(sp) ; add sp,-32 ; block0: -; li s6,42 -; li s7,0 -; eq a0,[a0,a1],[s6,s7]##ty=i128 -; andi s4,a0,255 -; select_i128 [a0,a1],[a2,a3],[a4,a5]##condition=s4 +; li s5,42 +; li s6,0 +; eq a0,[a0,a1],[s5,s6]##ty=i128 +; select_i128 [s4,a1],[a2,a3],[a4,a5]##condition=a0 +; mv a0,s4 ; add sp,+32 ; ld s4,-8(sp) -; ld s6,-16(sp) -; ld s7,-24(sp) +; ld s5,-16(sp) +; ld s6,-24(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -807,28 +812,28 @@ block0(v0: i128, v1: i128, v2: i128): ; sd s0, 0(sp) ; mv s0, sp ; sd s4, -8(sp) -; sd s6, -0x10(sp) -; sd s7, -0x18(sp) +; sd s5, -0x10(sp) +; sd s6, -0x18(sp) ; addi sp, sp, -0x20 ; block1: ; offset 0x20 -; addi s6, zero, 0x2a -; mv s7, zero -; bne a1, s7, 0x10 -; bne a0, s6, 0xc +; addi s5, zero, 0x2a +; mv s6, zero +; bne a1, s6, 0x10 +; bne a0, s5, 0xc ; addi a0, zero, 1 ; j 8 ; mv a0, zero -; andi s4, a0, 0xff -; beqz s4, 0x10 -; mv a0, a2 +; beqz a0, 0x10 +; mv s4, a2 ; mv a1, a3 ; j 0xc -; mv a0, a4 +; mv s4, a4 ; mv a1, a5 +; mv a0, s4 ; addi sp, sp, 0x20 ; ld s4, -8(sp) -; ld s6, -0x10(sp) -; ld s7, -0x18(sp) +; ld s5, -0x10(sp) +; ld s6, -0x18(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 diff --git a/cranelift/filetests/filetests/isa/riscv64/select_spectre_guard.clif b/cranelift/filetests/filetests/isa/riscv64/select_spectre_guard.clif index dec1cc69cfd1..ab91c78b6c7e 100644 --- a/cranelift/filetests/filetests/isa/riscv64/select_spectre_guard.clif +++ b/cranelift/filetests/filetests/isa/riscv64/select_spectre_guard.clif @@ -13,34 +13,32 @@ block0(v0: i8, v1: i8, v2: i8): ; VCode: ; block0: ; li a3,42 -; andi a0,a0,255 +; andi a5,a0,255 ; andi a3,a3,255 -; eq a4,a0,a3##ty=i8 -; andi a3,a4,255 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; eq a3,a5,a3##ty=i8 +; sltu a3,zero,a3 +; sub a4,zero,a3 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; andi a0, a0, 0xff +; andi a5, a0, 0xff ; andi a3, a3, 0xff -; bne a0, a3, 0xc -; addi a4, zero, 1 +; bne a5, a3, 0xc +; addi a3, zero, 1 ; j 8 -; mv a4, zero -; andi a3, a4, 0xff -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a3, zero +; snez a3, a3 +; neg a4, a3 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i8, i16, i16) -> i16 { @@ -54,34 +52,32 @@ block0(v0: i8, v1: i16, v2: i16): ; VCode: ; block0: ; li a3,42 -; andi a0,a0,255 +; andi a5,a0,255 ; andi a3,a3,255 -; eq a4,a0,a3##ty=i8 -; andi a3,a4,255 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; eq a3,a5,a3##ty=i8 +; sltu a3,zero,a3 +; sub a4,zero,a3 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; andi a0, a0, 0xff +; andi a5, a0, 0xff ; andi a3, a3, 0xff -; bne a0, a3, 0xc -; addi a4, zero, 1 +; bne a5, a3, 0xc +; addi a3, zero, 1 ; j 8 -; mv a4, zero -; andi a3, a4, 0xff -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a3, zero +; snez a3, a3 +; neg a4, a3 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i8, i32, i32) -> i32 { @@ -95,34 +91,32 @@ block0(v0: i8, v1: i32, v2: i32): ; VCode: ; block0: ; li a3,42 -; andi a0,a0,255 +; andi a5,a0,255 ; andi a3,a3,255 -; eq a4,a0,a3##ty=i8 -; andi a3,a4,255 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; eq a3,a5,a3##ty=i8 +; sltu a3,zero,a3 +; sub a4,zero,a3 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; andi a0, a0, 0xff +; andi a5, a0, 0xff ; andi a3, a3, 0xff -; bne a0, a3, 0xc -; addi a4, zero, 1 +; bne a5, a3, 0xc +; addi a3, zero, 1 ; j 8 -; mv a4, zero -; andi a3, a4, 0xff -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a3, zero +; snez a3, a3 +; neg a4, a3 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i8, i64, i64) -> i64 { @@ -136,34 +130,32 @@ block0(v0: i8, v1: i64, v2: i64): ; VCode: ; block0: ; li a3,42 -; andi a0,a0,255 +; andi a5,a0,255 ; andi a3,a3,255 -; eq a4,a0,a3##ty=i8 -; andi a3,a4,255 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; eq a3,a5,a3##ty=i8 +; sltu a3,zero,a3 +; sub a4,zero,a3 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; andi a0, a0, 0xff +; andi a5, a0, 0xff ; andi a3, a3, 0xff -; bne a0, a3, 0xc -; addi a4, zero, 1 +; bne a5, a3, 0xc +; addi a3, zero, 1 ; j 8 -; mv a4, zero -; andi a3, a4, 0xff -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a3, zero +; snez a3, a3 +; neg a4, a3 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i8, i128, i128) -> i128 { @@ -179,26 +171,25 @@ block0(v0: i8, v1: i128, v2: i128): ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s8,-8(sp) +; sd s6,-8(sp) ; add sp,-16 ; block0: ; li a5,42 ; andi a0,a0,255 ; andi a5,a5,255 ; eq a5,a0,a5##ty=i8 -; andi a5,a5,255 -; sltu a0,zero,a5 -; sub s8,zero,a0 -; and a5,a1,s8 -; and a1,a2,s8 -; not a0,s8 -; not a2,s8 -; and a0,a3,a0 -; and a2,a4,a2 -; or a0,a5,a0 -; or a1,a1,a2 +; sltu a5,zero,a5 +; sub s6,zero,a5 +; and a0,a1,s6 +; and a5,a2,s6 +; not a2,s6 +; not a1,s6 +; and a2,a3,a2 +; and a1,a4,a1 +; or a0,a0,a2 +; or a1,a5,a1 ; add sp,+16 -; ld s8,-8(sp) +; ld s6,-8(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -210,7 +201,7 @@ block0(v0: i8, v1: i128, v2: i128): ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s8, -8(sp) +; sd s6, -8(sp) ; addi sp, sp, -0x10 ; block1: ; offset 0x18 ; addi a5, zero, 0x2a @@ -220,19 +211,18 @@ block0(v0: i8, v1: i128, v2: i128): ; addi a5, zero, 1 ; j 8 ; mv a5, zero -; andi a5, a5, 0xff -; snez a0, a5 -; neg s8, a0 -; and a5, a1, s8 -; and a1, a2, s8 -; not a0, s8 -; not a2, s8 -; and a0, a3, a0 -; and a2, a4, a2 -; or a0, a5, a0 -; or a1, a1, a2 +; snez a5, a5 +; neg s6, a5 +; and a0, a1, s6 +; and a5, a2, s6 +; not a2, s6 +; not a1, s6 +; and a2, a3, a2 +; and a1, a4, a1 +; or a0, a0, a2 +; or a1, a5, a1 ; addi sp, sp, 0x10 -; ld s8, -8(sp) +; ld s6, -8(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -248,39 +238,37 @@ block0(v0: i16, v1: i8, v2: i8): ; VCode: ; block0: -; li a4,42 -; slli a0,a0,48 -; srli a3,a0,48 -; slli a4,a4,48 -; srli a0,a4,48 -; eq a3,a3,a0##ty=i16 -; andi a4,a3,255 -; sltu a0,zero,a4 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a3,42 +; slli a5,a0,48 +; srli a4,a5,48 +; slli a3,a3,48 +; srli a5,a3,48 +; eq a3,a4,a5##ty=i16 +; sltu a4,zero,a3 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a4, zero, 0x2a -; slli a0, a0, 0x30 -; srli a3, a0, 0x30 -; slli a4, a4, 0x30 -; srli a0, a4, 0x30 -; bne a3, a0, 0xc +; addi a3, zero, 0x2a +; slli a5, a0, 0x30 +; srli a4, a5, 0x30 +; slli a3, a3, 0x30 +; srli a5, a3, 0x30 +; bne a4, a5, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; andi a4, a3, 0xff -; snez a0, a4 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; snez a4, a3 +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i16, i16, i16) -> i16 { @@ -293,39 +281,37 @@ block0(v0: i16, v1: i16, v2: i16): ; VCode: ; block0: -; li a4,42 -; slli a0,a0,48 -; srli a3,a0,48 -; slli a4,a4,48 -; srli a0,a4,48 -; eq a3,a3,a0##ty=i16 -; andi a4,a3,255 -; sltu a0,zero,a4 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a3,42 +; slli a5,a0,48 +; srli a4,a5,48 +; slli a3,a3,48 +; srli a5,a3,48 +; eq a3,a4,a5##ty=i16 +; sltu a4,zero,a3 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a4, zero, 0x2a -; slli a0, a0, 0x30 -; srli a3, a0, 0x30 -; slli a4, a4, 0x30 -; srli a0, a4, 0x30 -; bne a3, a0, 0xc +; addi a3, zero, 0x2a +; slli a5, a0, 0x30 +; srli a4, a5, 0x30 +; slli a3, a3, 0x30 +; srli a5, a3, 0x30 +; bne a4, a5, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; andi a4, a3, 0xff -; snez a0, a4 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; snez a4, a3 +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i16, i32, i32) -> i32 { @@ -338,39 +324,37 @@ block0(v0: i16, v1: i32, v2: i32): ; VCode: ; block0: -; li a4,42 -; slli a0,a0,48 -; srli a3,a0,48 -; slli a4,a4,48 -; srli a0,a4,48 -; eq a3,a3,a0##ty=i16 -; andi a4,a3,255 -; sltu a0,zero,a4 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a3,42 +; slli a5,a0,48 +; srli a4,a5,48 +; slli a3,a3,48 +; srli a5,a3,48 +; eq a3,a4,a5##ty=i16 +; sltu a4,zero,a3 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a4, zero, 0x2a -; slli a0, a0, 0x30 -; srli a3, a0, 0x30 -; slli a4, a4, 0x30 -; srli a0, a4, 0x30 -; bne a3, a0, 0xc +; addi a3, zero, 0x2a +; slli a5, a0, 0x30 +; srli a4, a5, 0x30 +; slli a3, a3, 0x30 +; srli a5, a3, 0x30 +; bne a4, a5, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; andi a4, a3, 0xff -; snez a0, a4 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; snez a4, a3 +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i16, i64, i64) -> i64 { @@ -383,39 +367,37 @@ block0(v0: i16, v1: i64, v2: i64): ; VCode: ; block0: -; li a4,42 -; slli a0,a0,48 -; srli a3,a0,48 -; slli a4,a4,48 -; srli a0,a4,48 -; eq a3,a3,a0##ty=i16 -; andi a4,a3,255 -; sltu a0,zero,a4 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a3,42 +; slli a5,a0,48 +; srli a4,a5,48 +; slli a3,a3,48 +; srli a5,a3,48 +; eq a3,a4,a5##ty=i16 +; sltu a4,zero,a3 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a4, zero, 0x2a -; slli a0, a0, 0x30 -; srli a3, a0, 0x30 -; slli a4, a4, 0x30 -; srli a0, a4, 0x30 -; bne a3, a0, 0xc +; addi a3, zero, 0x2a +; slli a5, a0, 0x30 +; srli a4, a5, 0x30 +; slli a3, a3, 0x30 +; srli a5, a3, 0x30 +; bne a4, a5, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; andi a4, a3, 0xff -; snez a0, a4 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; snez a4, a3 +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i16, i128, i128) -> i128 { @@ -427,6 +409,12 @@ block0(v0: i16, v1: i128, v2: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s8,-8(sp) +; add sp,-16 ; block0: ; li a5,42 ; slli a0,a0,48 @@ -434,21 +422,32 @@ block0(v0: i16, v1: i128, v2: i128): ; slli a5,a5,48 ; srli a5,a5,48 ; eq a5,a0,a5##ty=i16 -; andi a0,a5,255 -; sltu a5,zero,a0 -; sub a5,zero,a5 -; and a0,a1,a5 -; and a2,a2,a5 -; not a7,a5 -; not a1,a5 -; and a3,a3,a7 -; and a4,a4,a1 -; or a0,a0,a3 -; or a1,a2,a4 +; sltu a0,zero,a5 +; sub s8,zero,a0 +; and a5,a1,s8 +; and a1,a2,s8 +; not a0,s8 +; not a2,s8 +; and a0,a3,a0 +; and a2,a4,a2 +; or a0,a5,a0 +; or a1,a1,a2 +; add sp,+16 +; ld s8,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s8, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 ; addi a5, zero, 0x2a ; slli a0, a0, 0x30 ; srli a0, a0, 0x30 @@ -458,17 +457,21 @@ block0(v0: i16, v1: i128, v2: i128): ; addi a5, zero, 1 ; j 8 ; mv a5, zero -; andi a0, a5, 0xff -; snez a5, a0 -; neg a5, a5 -; and a0, a1, a5 -; and a2, a2, a5 -; not a7, a5 -; not a1, a5 -; and a3, a3, a7 -; and a4, a4, a1 -; or a0, a0, a3 -; or a1, a2, a4 +; snez a0, a5 +; neg s8, a0 +; and a5, a1, s8 +; and a1, a2, s8 +; not a0, s8 +; not a2, s8 +; and a0, a3, a0 +; and a2, a4, a2 +; or a0, a5, a0 +; or a1, a1, a2 +; addi sp, sp, 0x10 +; ld s8, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret function %f(i32, i8, i8) -> i8 { @@ -481,39 +484,37 @@ block0(v0: i32, v1: i8, v2: i8): ; VCode: ; block0: -; li a4,42 -; slli a0,a0,32 -; srli a3,a0,32 -; slli a4,a4,32 -; srli a0,a4,32 -; eq a3,a3,a0##ty=i32 -; andi a4,a3,255 -; sltu a0,zero,a4 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a3,42 +; slli a5,a0,32 +; srli a4,a5,32 +; slli a3,a3,32 +; srli a5,a3,32 +; eq a3,a4,a5##ty=i32 +; sltu a4,zero,a3 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a4, zero, 0x2a -; slli a0, a0, 0x20 -; srli a3, a0, 0x20 -; slli a4, a4, 0x20 -; srli a0, a4, 0x20 -; bne a3, a0, 0xc +; addi a3, zero, 0x2a +; slli a5, a0, 0x20 +; srli a4, a5, 0x20 +; slli a3, a3, 0x20 +; srli a5, a3, 0x20 +; bne a4, a5, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; andi a4, a3, 0xff -; snez a0, a4 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; snez a4, a3 +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i32, i16, i16) -> i16 { @@ -526,39 +527,37 @@ block0(v0: i32, v1: i16, v2: i16): ; VCode: ; block0: -; li a4,42 -; slli a0,a0,32 -; srli a3,a0,32 -; slli a4,a4,32 -; srli a0,a4,32 -; eq a3,a3,a0##ty=i32 -; andi a4,a3,255 -; sltu a0,zero,a4 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a3,42 +; slli a5,a0,32 +; srli a4,a5,32 +; slli a3,a3,32 +; srli a5,a3,32 +; eq a3,a4,a5##ty=i32 +; sltu a4,zero,a3 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a4, zero, 0x2a -; slli a0, a0, 0x20 -; srli a3, a0, 0x20 -; slli a4, a4, 0x20 -; srli a0, a4, 0x20 -; bne a3, a0, 0xc +; addi a3, zero, 0x2a +; slli a5, a0, 0x20 +; srli a4, a5, 0x20 +; slli a3, a3, 0x20 +; srli a5, a3, 0x20 +; bne a4, a5, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; andi a4, a3, 0xff -; snez a0, a4 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; snez a4, a3 +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i32, i32, i32) -> i32 { @@ -571,39 +570,37 @@ block0(v0: i32, v1: i32, v2: i32): ; VCode: ; block0: -; li a4,42 -; slli a0,a0,32 -; srli a3,a0,32 -; slli a4,a4,32 -; srli a0,a4,32 -; eq a3,a3,a0##ty=i32 -; andi a4,a3,255 -; sltu a0,zero,a4 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a3,42 +; slli a5,a0,32 +; srli a4,a5,32 +; slli a3,a3,32 +; srli a5,a3,32 +; eq a3,a4,a5##ty=i32 +; sltu a4,zero,a3 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a4, zero, 0x2a -; slli a0, a0, 0x20 -; srli a3, a0, 0x20 -; slli a4, a4, 0x20 -; srli a0, a4, 0x20 -; bne a3, a0, 0xc +; addi a3, zero, 0x2a +; slli a5, a0, 0x20 +; srli a4, a5, 0x20 +; slli a3, a3, 0x20 +; srli a5, a3, 0x20 +; bne a4, a5, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; andi a4, a3, 0xff -; snez a0, a4 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; snez a4, a3 +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i32, i64, i64) -> i64 { @@ -616,39 +613,37 @@ block0(v0: i32, v1: i64, v2: i64): ; VCode: ; block0: -; li a4,42 -; slli a0,a0,32 -; srli a3,a0,32 -; slli a4,a4,32 -; srli a0,a4,32 -; eq a3,a3,a0##ty=i32 -; andi a4,a3,255 -; sltu a0,zero,a4 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a3,42 +; slli a5,a0,32 +; srli a4,a5,32 +; slli a3,a3,32 +; srli a5,a3,32 +; eq a3,a4,a5##ty=i32 +; sltu a4,zero,a3 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a4, zero, 0x2a -; slli a0, a0, 0x20 -; srli a3, a0, 0x20 -; slli a4, a4, 0x20 -; srli a0, a4, 0x20 -; bne a3, a0, 0xc +; addi a3, zero, 0x2a +; slli a5, a0, 0x20 +; srli a4, a5, 0x20 +; slli a3, a3, 0x20 +; srli a5, a3, 0x20 +; bne a4, a5, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; andi a4, a3, 0xff -; snez a0, a4 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; snez a4, a3 +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i32, i128, i128) -> i128 { @@ -660,6 +655,12 @@ block0(v0: i32, v1: i128, v2: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s8,-8(sp) +; add sp,-16 ; block0: ; li a5,42 ; slli a0,a0,32 @@ -667,41 +668,56 @@ block0(v0: i32, v1: i128, v2: i128): ; slli a5,a5,32 ; srli a5,a5,32 ; eq a5,a0,a5##ty=i32 -; andi a0,a5,255 -; sltu a5,zero,a0 -; sub a5,zero,a5 -; and a0,a1,a5 -; and a2,a2,a5 -; not a7,a5 -; not a1,a5 -; and a3,a3,a7 -; and a4,a4,a1 -; or a0,a0,a3 -; or a1,a2,a4 +; sltu a0,zero,a5 +; sub s8,zero,a0 +; and a5,a1,s8 +; and a1,a2,s8 +; not a0,s8 +; not a2,s8 +; and a0,a3,a0 +; and a2,a4,a2 +; or a0,a5,a0 +; or a1,a1,a2 +; add sp,+16 +; ld s8,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s8, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 ; addi a5, zero, 0x2a ; slli a0, a0, 0x20 ; srli a0, a0, 0x20 -; slli a5, a5, 0x20 -; srli a5, a5, 0x20 -; bne a0, a5, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; andi a0, a5, 0xff -; snez a5, a0 -; neg a5, a5 -; and a0, a1, a5 -; and a2, a2, a5 -; not a7, a5 -; not a1, a5 -; and a3, a3, a7 -; and a4, a4, a1 -; or a0, a0, a3 -; or a1, a2, a4 +; slli a5, a5, 0x20 +; srli a5, a5, 0x20 +; bne a0, a5, 0xc +; addi a5, zero, 1 +; j 8 +; mv a5, zero +; snez a0, a5 +; neg s8, a0 +; and a5, a1, s8 +; and a1, a2, s8 +; not a0, s8 +; not a2, s8 +; and a0, a3, a0 +; and a2, a4, a2 +; or a0, a5, a0 +; or a1, a1, a2 +; addi sp, sp, 0x10 +; ld s8, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret function %f(i64, i8, i8) -> i8 { @@ -714,31 +730,29 @@ block0(v0: i64, v1: i8, v2: i8): ; VCode: ; block0: -; li a3,42 -; eq a0,a0,a3##ty=i64 -; andi a0,a0,255 -; sltu a3,zero,a0 -; sub a4,zero,a3 -; and a0,a1,a4 -; not a3,a4 -; and a4,a2,a3 -; or a0,a0,a4 +; li a5,42 +; eq a5,a0,a5##ty=i64 +; sltu a0,zero,a5 +; sub a3,zero,a0 +; and a4,a1,a3 +; not a0,a3 +; and a2,a2,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; bne a0, a3, 0xc -; addi a0, zero, 1 +; addi a5, zero, 0x2a +; bne a0, a5, 0xc +; addi a5, zero, 1 ; j 8 -; mv a0, zero -; andi a0, a0, 0xff -; snez a3, a0 -; neg a4, a3 -; and a0, a1, a4 -; not a3, a4 -; and a4, a2, a3 -; or a0, a0, a4 +; mv a5, zero +; snez a0, a5 +; neg a3, a0 +; and a4, a1, a3 +; not a0, a3 +; and a2, a2, a0 +; or a0, a4, a2 ; ret function %f(i64, i16, i16) -> i16 { @@ -751,31 +765,29 @@ block0(v0: i64, v1: i16, v2: i16): ; VCode: ; block0: -; li a3,42 -; eq a0,a0,a3##ty=i64 -; andi a0,a0,255 -; sltu a3,zero,a0 -; sub a4,zero,a3 -; and a0,a1,a4 -; not a3,a4 -; and a4,a2,a3 -; or a0,a0,a4 +; li a5,42 +; eq a5,a0,a5##ty=i64 +; sltu a0,zero,a5 +; sub a3,zero,a0 +; and a4,a1,a3 +; not a0,a3 +; and a2,a2,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; bne a0, a3, 0xc -; addi a0, zero, 1 +; addi a5, zero, 0x2a +; bne a0, a5, 0xc +; addi a5, zero, 1 ; j 8 -; mv a0, zero -; andi a0, a0, 0xff -; snez a3, a0 -; neg a4, a3 -; and a0, a1, a4 -; not a3, a4 -; and a4, a2, a3 -; or a0, a0, a4 +; mv a5, zero +; snez a0, a5 +; neg a3, a0 +; and a4, a1, a3 +; not a0, a3 +; and a2, a2, a0 +; or a0, a4, a2 ; ret function %f(i64, i32, i32) -> i32 { @@ -788,31 +800,29 @@ block0(v0: i64, v1: i32, v2: i32): ; VCode: ; block0: -; li a3,42 -; eq a0,a0,a3##ty=i64 -; andi a0,a0,255 -; sltu a3,zero,a0 -; sub a4,zero,a3 -; and a0,a1,a4 -; not a3,a4 -; and a4,a2,a3 -; or a0,a0,a4 +; li a5,42 +; eq a5,a0,a5##ty=i64 +; sltu a0,zero,a5 +; sub a3,zero,a0 +; and a4,a1,a3 +; not a0,a3 +; and a2,a2,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; bne a0, a3, 0xc -; addi a0, zero, 1 +; addi a5, zero, 0x2a +; bne a0, a5, 0xc +; addi a5, zero, 1 ; j 8 -; mv a0, zero -; andi a0, a0, 0xff -; snez a3, a0 -; neg a4, a3 -; and a0, a1, a4 -; not a3, a4 -; and a4, a2, a3 -; or a0, a0, a4 +; mv a5, zero +; snez a0, a5 +; neg a3, a0 +; and a4, a1, a3 +; not a0, a3 +; and a2, a2, a0 +; or a0, a4, a2 ; ret function %f(i64, i64, i64) -> i64 { @@ -825,31 +835,29 @@ block0(v0: i64, v1: i64, v2: i64): ; VCode: ; block0: -; li a3,42 -; eq a0,a0,a3##ty=i64 -; andi a0,a0,255 -; sltu a3,zero,a0 -; sub a4,zero,a3 -; and a0,a1,a4 -; not a3,a4 -; and a4,a2,a3 -; or a0,a0,a4 +; li a5,42 +; eq a5,a0,a5##ty=i64 +; sltu a0,zero,a5 +; sub a3,zero,a0 +; and a4,a1,a3 +; not a0,a3 +; and a2,a2,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; bne a0, a3, 0xc -; addi a0, zero, 1 +; addi a5, zero, 0x2a +; bne a0, a5, 0xc +; addi a5, zero, 1 ; j 8 -; mv a0, zero -; andi a0, a0, 0xff -; snez a3, a0 -; neg a4, a3 -; and a0, a1, a4 -; not a3, a4 -; and a4, a2, a3 -; or a0, a0, a4 +; mv a5, zero +; snez a0, a5 +; neg a3, a0 +; and a4, a1, a3 +; not a0, a3 +; and a2, a2, a0 +; or a0, a4, a2 ; ret function %f(i64, i128, i128) -> i128 { @@ -865,24 +873,23 @@ block0(v0: i64, v1: i128, v2: i128): ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s6,-8(sp) +; sd s10,-8(sp) ; add sp,-16 ; block0: ; li a5,42 -; eq a0,a0,a5##ty=i64 -; andi a5,a0,255 +; eq a5,a0,a5##ty=i64 ; sltu a5,zero,a5 -; sub s6,zero,a5 -; and a0,a1,s6 -; and a5,a2,s6 -; not a2,s6 -; not a1,s6 -; and a2,a3,a2 -; and a1,a4,a1 -; or a0,a0,a2 -; or a1,a5,a1 +; sub a5,zero,a5 +; and a0,a1,a5 +; and a2,a2,a5 +; not s10,a5 +; not a1,a5 +; and a3,a3,s10 +; and a4,a4,a1 +; or a0,a0,a3 +; or a1,a2,a4 ; add sp,+16 -; ld s6,-8(sp) +; ld s10,-8(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -894,27 +901,26 @@ block0(v0: i64, v1: i128, v2: i128): ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s6, -8(sp) +; sd s10, -8(sp) ; addi sp, sp, -0x10 ; block1: ; offset 0x18 ; addi a5, zero, 0x2a ; bne a0, a5, 0xc -; addi a0, zero, 1 +; addi a5, zero, 1 ; j 8 -; mv a0, zero -; andi a5, a0, 0xff +; mv a5, zero ; snez a5, a5 -; neg s6, a5 -; and a0, a1, s6 -; and a5, a2, s6 -; not a2, s6 -; not a1, s6 -; and a2, a3, a2 -; and a1, a4, a1 -; or a0, a0, a2 -; or a1, a5, a1 +; neg a5, a5 +; and a0, a1, a5 +; and a2, a2, a5 +; not s10, a5 +; not a1, a5 +; and a3, a3, s10 +; and a4, a4, a1 +; or a0, a0, a3 +; or a1, a2, a4 ; addi sp, sp, 0x10 -; ld s6, -8(sp) +; ld s10, -8(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -933,14 +939,13 @@ block0(v0: i128, v1: i8, v2: i8): ; block0: ; li a4,42 ; li a5,0 -; eq a4,[a0,a1],[a4,a5]##ty=i128 -; andi a4,a4,255 -; sltu a4,zero,a4 -; sub a0,zero,a4 -; and a2,a2,a0 -; not a4,a0 -; and a0,a3,a4 -; or a0,a2,a0 +; eq a1,[a0,a1],[a4,a5]##ty=i128 +; sltu a4,zero,a1 +; sub a4,zero,a4 +; and a0,a2,a4 +; not a2,a4 +; and a4,a3,a2 +; or a0,a0,a4 ; ret ; ; Disassembled: @@ -949,16 +954,15 @@ block0(v0: i128, v1: i8, v2: i8): ; mv a5, zero ; bne a1, a5, 0x10 ; bne a0, a4, 0xc -; addi a4, zero, 1 +; addi a1, zero, 1 ; j 8 -; mv a4, zero -; andi a4, a4, 0xff -; snez a4, a4 -; neg a0, a4 -; and a2, a2, a0 -; not a4, a0 -; and a0, a3, a4 -; or a0, a2, a0 +; mv a1, zero +; snez a4, a1 +; neg a4, a4 +; and a0, a2, a4 +; not a2, a4 +; and a4, a3, a2 +; or a0, a0, a4 ; ret function %f(i128, i16, i16) -> i16 { @@ -974,14 +978,13 @@ block0(v0: i128, v1: i16, v2: i16): ; block0: ; li a4,42 ; li a5,0 -; eq a4,[a0,a1],[a4,a5]##ty=i128 -; andi a4,a4,255 -; sltu a4,zero,a4 -; sub a0,zero,a4 -; and a2,a2,a0 -; not a4,a0 -; and a0,a3,a4 -; or a0,a2,a0 +; eq a1,[a0,a1],[a4,a5]##ty=i128 +; sltu a4,zero,a1 +; sub a4,zero,a4 +; and a0,a2,a4 +; not a2,a4 +; and a4,a3,a2 +; or a0,a0,a4 ; ret ; ; Disassembled: @@ -990,16 +993,15 @@ block0(v0: i128, v1: i16, v2: i16): ; mv a5, zero ; bne a1, a5, 0x10 ; bne a0, a4, 0xc -; addi a4, zero, 1 +; addi a1, zero, 1 ; j 8 -; mv a4, zero -; andi a4, a4, 0xff -; snez a4, a4 -; neg a0, a4 -; and a2, a2, a0 -; not a4, a0 -; and a0, a3, a4 -; or a0, a2, a0 +; mv a1, zero +; snez a4, a1 +; neg a4, a4 +; and a0, a2, a4 +; not a2, a4 +; and a4, a3, a2 +; or a0, a0, a4 ; ret function %f(i128, i32, i32) -> i32 { @@ -1015,14 +1017,13 @@ block0(v0: i128, v1: i32, v2: i32): ; block0: ; li a4,42 ; li a5,0 -; eq a4,[a0,a1],[a4,a5]##ty=i128 -; andi a4,a4,255 -; sltu a4,zero,a4 -; sub a0,zero,a4 -; and a2,a2,a0 -; not a4,a0 -; and a0,a3,a4 -; or a0,a2,a0 +; eq a1,[a0,a1],[a4,a5]##ty=i128 +; sltu a4,zero,a1 +; sub a4,zero,a4 +; and a0,a2,a4 +; not a2,a4 +; and a4,a3,a2 +; or a0,a0,a4 ; ret ; ; Disassembled: @@ -1031,16 +1032,15 @@ block0(v0: i128, v1: i32, v2: i32): ; mv a5, zero ; bne a1, a5, 0x10 ; bne a0, a4, 0xc -; addi a4, zero, 1 +; addi a1, zero, 1 ; j 8 -; mv a4, zero -; andi a4, a4, 0xff -; snez a4, a4 -; neg a0, a4 -; and a2, a2, a0 -; not a4, a0 -; and a0, a3, a4 -; or a0, a2, a0 +; mv a1, zero +; snez a4, a1 +; neg a4, a4 +; and a0, a2, a4 +; not a2, a4 +; and a4, a3, a2 +; or a0, a0, a4 ; ret function %f(i128, i64, i64) -> i64 { @@ -1056,14 +1056,13 @@ block0(v0: i128, v1: i64, v2: i64): ; block0: ; li a4,42 ; li a5,0 -; eq a4,[a0,a1],[a4,a5]##ty=i128 -; andi a4,a4,255 -; sltu a4,zero,a4 -; sub a0,zero,a4 -; and a2,a2,a0 -; not a4,a0 -; and a0,a3,a4 -; or a0,a2,a0 +; eq a1,[a0,a1],[a4,a5]##ty=i128 +; sltu a4,zero,a1 +; sub a4,zero,a4 +; and a0,a2,a4 +; not a2,a4 +; and a4,a3,a2 +; or a0,a0,a4 ; ret ; ; Disassembled: @@ -1072,16 +1071,15 @@ block0(v0: i128, v1: i64, v2: i64): ; mv a5, zero ; bne a1, a5, 0x10 ; bne a0, a4, 0xc -; addi a4, zero, 1 +; addi a1, zero, 1 ; j 8 -; mv a4, zero -; andi a4, a4, 0xff -; snez a4, a4 -; neg a0, a4 -; and a2, a2, a0 -; not a4, a0 -; and a0, a3, a4 -; or a0, a2, a0 +; mv a1, zero +; snez a4, a1 +; neg a4, a4 +; and a0, a2, a4 +; not a2, a4 +; and a4, a3, a2 +; or a0, a0, a4 ; ret function %f(i128, i128, i128) -> i128 { @@ -1094,65 +1092,40 @@ block0(v0: i128, v1: i128, v2: i128): } ; VCode: -; add sp,-16 -; sd ra,8(sp) -; sd fp,0(sp) -; mv fp,sp -; sd s8,-8(sp) -; add sp,-16 ; block0: -; li t2,42 -; li a6,0 -; eq a0,[a0,a1],[t2,a6]##ty=i128 -; andi a0,a0,255 -; sltu a0,zero,a0 -; sub s8,zero,a0 -; and a1,a2,s8 -; and a2,a3,s8 -; not a0,s8 -; not a3,s8 -; and a0,a4,a0 -; and a3,a5,a3 -; or a0,a1,a0 -; or a1,a2,a3 -; add sp,+16 -; ld s8,-8(sp) -; ld ra,8(sp) -; ld fp,0(sp) -; add sp,+16 +; li t1,42 +; li t2,0 +; eq a1,[a0,a1],[t1,t2]##ty=i128 +; sltu a0,zero,a1 +; sub a1,zero,a0 +; and a2,a2,a1 +; and a3,a3,a1 +; not t0,a1 +; not a0,a1 +; and a4,a4,t0 +; and a1,a5,a0 +; or a0,a2,a4 +; or a1,a3,a1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi sp, sp, -0x10 -; sd ra, 8(sp) -; sd s0, 0(sp) -; mv s0, sp -; sd s8, -8(sp) -; addi sp, sp, -0x10 -; block1: ; offset 0x18 -; addi t2, zero, 0x2a -; mv a6, zero -; bne a1, a6, 0x10 -; bne a0, t2, 0xc -; addi a0, zero, 1 +; addi t1, zero, 0x2a +; mv t2, zero +; bne a1, t2, 0x10 +; bne a0, t1, 0xc +; addi a1, zero, 1 ; j 8 -; mv a0, zero -; andi a0, a0, 0xff -; snez a0, a0 -; neg s8, a0 -; and a1, a2, s8 -; and a2, a3, s8 -; not a0, s8 -; not a3, s8 -; and a0, a4, a0 -; and a3, a5, a3 -; or a0, a1, a0 -; or a1, a2, a3 -; addi sp, sp, 0x10 -; ld s8, -8(sp) -; ld ra, 8(sp) -; ld s0, 0(sp) -; addi sp, sp, 0x10 +; mv a1, zero +; snez a0, a1 +; neg a1, a0 +; and a2, a2, a1 +; and a3, a3, a1 +; not t0, a1 +; not a0, a1 +; and a4, a4, t0 +; and a1, a5, a0 +; or a0, a2, a4 +; or a1, a3, a1 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat index cb4a70a8404b..b5aa9400304d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -41,44 +41,42 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; ld a0,8(a2) -;; addi a0,a0,-4 -;; ugt a4,a3,a0##ty=i64 -;; ld a0,0(a2) -;; add a0,a0,a3 -;; li a2,0 -;; andi a3,a4,255 -;; sltu a3,zero,a3 -;; sub a4,zero,a3 -;; and a2,a2,a4 -;; not a3,a4 -;; and a4,a0,a3 -;; or a0,a2,a4 -;; sw a1,0(a0) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a2) +;; addi a5,a5,-4 +;; ugt a3,a0,a5##ty=i64 +;; ld a5,0(a2) +;; add a5,a5,a0 +;; li a0,0 +;; sltu a2,zero,a3 +;; sub a2,zero,a2 +;; and a4,a0,a2 +;; not a0,a2 +;; and a2,a5,a0 +;; or a4,a4,a2 +;; sw a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; ld a0,8(a1) -;; addi a0,a0,-4 -;; ugt a3,a2,a0##ty=i64 -;; ld a0,0(a1) -;; add a0,a0,a2 -;; li a1,0 -;; andi a2,a3,255 -;; sltu a2,zero,a2 -;; sub a4,zero,a2 -;; and a1,a1,a4 -;; not a2,a4 +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a1) +;; addi a5,a5,-4 +;; ugt a2,a0,a5##ty=i64 +;; ld a5,0(a1) +;; add a5,a5,a0 +;; li a0,0 +;; sltu a1,zero,a2 +;; sub a2,zero,a1 ;; and a4,a0,a2 -;; or a0,a1,a4 -;; lw a0,0(a0) +;; not a0,a2 +;; and a2,a5,a0 +;; or a4,a4,a2 +;; lw a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index 7beb856e6ac4..665af0e6def1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -42,51 +42,49 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a2) -;; lui a3,-1 -;; addi a0,a3,-4 -;; add a4,a4,a0 -;; ugt a0,a5,a4##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a5 +;; srli a4,a3,32 +;; ld a3,8(a2) +;; lui a5,-1 +;; addi a5,a5,-4 +;; add a3,a3,a5 +;; ugt a3,a4,a3##ty=i64 +;; ld a5,0(a2) +;; add a4,a5,a4 ;; lui a5,1 ;; add a4,a4,a5 ;; li a5,0 -;; andi a0,a0,255 -;; sltu a0,zero,a0 -;; sub a2,zero,a0 -;; and a5,a5,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; sw a1,0(a4) +;; sltu a0,zero,a3 +;; sub a0,zero,a0 +;; and a2,a5,a0 +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a1) -;; lui a3,-1 -;; addi a0,a3,-4 -;; add a4,a4,a0 -;; ugt a0,a5,a4##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a5 +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,8(a1) +;; lui a2,-1 +;; addi a5,a2,-4 +;; add a3,a3,a5 +;; ugt a3,a4,a3##ty=i64 +;; ld a5,0(a1) +;; add a4,a5,a4 ;; lui a5,1 ;; add a4,a4,a5 ;; li a5,0 -;; andi a0,a0,255 -;; sltu a0,zero,a0 -;; sub a2,zero,a0 -;; and a5,a5,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; lw a0,0(a4) +;; sltu a0,zero,a3 +;; sub a0,zero,a0 +;; and a2,a5,a0 +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; lw a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 22d4a1643b34..f4b0f9c1f5b7 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -42,53 +42,51 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,[const(1)] -;; add a3,a5,a4 -;; ult a0,a3,a5##ty=i64 -;; trap_if heap_oob##(a0 ne zero) -;; ld a0,8(a2) -;; ugt a0,a3,a0##ty=i64 -;; ld a2,0(a2) -;; add a5,a2,a5 -;; ld a2,[const(0)] -;; add a5,a5,a2 -;; li a2,0 -;; andi a0,a0,255 -;; sltu a3,zero,a0 -;; sub a3,zero,a3 -;; and a0,a2,a3 -;; not a2,a3 -;; and a3,a5,a2 -;; or a5,a0,a3 -;; sw a1,0(a5) +;; srli a4,a3,32 +;; ld a3,[const(1)] +;; add a3,a4,a3 +;; ult a5,a3,a4##ty=i64 +;; trap_if heap_oob##(a5 ne zero) +;; ld a5,8(a2) +;; ugt a5,a3,a5##ty=i64 +;; ld a0,0(a2) +;; add a4,a0,a4 +;; ld a0,[const(0)] +;; add a4,a4,a0 +;; li a0,0 +;; sltu a5,zero,a5 +;; sub a2,zero,a5 +;; and a3,a0,a2 +;; not a5,a2 +;; and a2,a4,a5 +;; or a3,a3,a2 +;; sw a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,[const(1)] -;; add a3,a5,a4 -;; ult a0,a3,a5##ty=i64 -;; trap_if heap_oob##(a0 ne zero) -;; ld a0,8(a1) -;; ugt a0,a3,a0##ty=i64 -;; ld a1,0(a1) -;; add a5,a1,a5 -;; ld a1,[const(0)] -;; add a5,a5,a1 -;; li a1,0 -;; andi a0,a0,255 -;; sltu a2,zero,a0 -;; sub a3,zero,a2 -;; and a0,a1,a3 -;; not a1,a3 -;; and a3,a5,a1 -;; or a5,a0,a3 -;; lw a0,0(a5) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,[const(1)] +;; add a2,a4,a3 +;; ult a5,a2,a4##ty=i64 +;; trap_if heap_oob##(a5 ne zero) +;; ld a5,8(a1) +;; ugt a5,a2,a5##ty=i64 +;; ld a0,0(a1) +;; add a4,a0,a4 +;; ld a0,[const(0)] +;; add a4,a4,a0 +;; li a0,0 +;; sltu a5,zero,a5 +;; sub a1,zero,a5 +;; and a3,a0,a1 +;; not a5,a1 +;; and a1,a4,a5 +;; or a3,a3,a1 +;; lw a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat index 549c75ada61b..ec33de9fcad6 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -41,42 +41,40 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a2) -;; uge a3,a0,a5##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; li a0,0 -;; andi a2,a3,255 -;; sltu a2,zero,a2 -;; sub a3,zero,a2 -;; and a0,a0,a3 -;; not a2,a3 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; ld a4,8(a2) +;; uge a0,a5,a4##ty=i64 +;; ld a4,0(a2) +;; add a4,a4,a5 +;; li a5,0 +;; sltu a0,zero,a0 +;; sub a2,zero,a0 ;; and a3,a5,a2 -;; or a5,a0,a3 -;; sb a1,0(a5) +;; not a5,a2 +;; and a2,a4,a5 +;; or a3,a3,a2 +;; sb a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a1) -;; uge a2,a0,a5##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; li a0,0 -;; andi a1,a2,255 -;; sltu a1,zero,a1 -;; sub a3,zero,a1 -;; and a0,a0,a3 -;; not a1,a3 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; ld a4,8(a1) +;; uge a0,a5,a4##ty=i64 +;; ld a4,0(a1) +;; add a4,a4,a5 +;; li a5,0 +;; sltu a0,zero,a0 +;; sub a1,zero,a0 ;; and a3,a5,a1 -;; or a5,a0,a3 -;; lbu a0,0(a5) +;; not a5,a1 +;; and a1,a4,a5 +;; or a3,a3,a1 +;; lbu a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index f5e443f3788f..19db59b51168 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -42,51 +42,49 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a2) -;; lui a3,-1 -;; addi a0,a3,-1 -;; add a4,a4,a0 -;; ugt a0,a5,a4##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a5 +;; srli a4,a3,32 +;; ld a3,8(a2) +;; lui a5,-1 +;; addi a5,a5,-1 +;; add a3,a3,a5 +;; ugt a3,a4,a3##ty=i64 +;; ld a5,0(a2) +;; add a4,a5,a4 ;; lui a5,1 ;; add a4,a4,a5 ;; li a5,0 -;; andi a0,a0,255 -;; sltu a0,zero,a0 -;; sub a2,zero,a0 -;; and a5,a5,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; sb a1,0(a4) +;; sltu a0,zero,a3 +;; sub a0,zero,a0 +;; and a2,a5,a0 +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a1) -;; lui a3,-1 -;; addi a0,a3,-1 -;; add a4,a4,a0 -;; ugt a0,a5,a4##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a5 +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,8(a1) +;; lui a2,-1 +;; addi a5,a2,-1 +;; add a3,a3,a5 +;; ugt a3,a4,a3##ty=i64 +;; ld a5,0(a1) +;; add a4,a5,a4 ;; lui a5,1 ;; add a4,a4,a5 ;; li a5,0 -;; andi a0,a0,255 -;; sltu a0,zero,a0 -;; sub a2,zero,a0 -;; and a5,a5,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; lbu a0,0(a4) +;; sltu a0,zero,a3 +;; sub a0,zero,a0 +;; and a2,a5,a0 +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; lbu a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 4573556ee391..53b3403a7f5d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -42,53 +42,51 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,[const(1)] -;; add a3,a5,a4 -;; ult a0,a3,a5##ty=i64 -;; trap_if heap_oob##(a0 ne zero) -;; ld a0,8(a2) -;; ugt a0,a3,a0##ty=i64 -;; ld a2,0(a2) -;; add a5,a2,a5 -;; ld a2,[const(0)] -;; add a5,a5,a2 -;; li a2,0 -;; andi a0,a0,255 -;; sltu a3,zero,a0 -;; sub a3,zero,a3 -;; and a0,a2,a3 -;; not a2,a3 -;; and a3,a5,a2 -;; or a5,a0,a3 -;; sb a1,0(a5) +;; srli a4,a3,32 +;; ld a3,[const(1)] +;; add a3,a4,a3 +;; ult a5,a3,a4##ty=i64 +;; trap_if heap_oob##(a5 ne zero) +;; ld a5,8(a2) +;; ugt a5,a3,a5##ty=i64 +;; ld a0,0(a2) +;; add a4,a0,a4 +;; ld a0,[const(0)] +;; add a4,a4,a0 +;; li a0,0 +;; sltu a5,zero,a5 +;; sub a2,zero,a5 +;; and a3,a0,a2 +;; not a5,a2 +;; and a2,a4,a5 +;; or a3,a3,a2 +;; sb a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,[const(1)] -;; add a3,a5,a4 -;; ult a0,a3,a5##ty=i64 -;; trap_if heap_oob##(a0 ne zero) -;; ld a0,8(a1) -;; ugt a0,a3,a0##ty=i64 -;; ld a1,0(a1) -;; add a5,a1,a5 -;; ld a1,[const(0)] -;; add a5,a5,a1 -;; li a1,0 -;; andi a0,a0,255 -;; sltu a2,zero,a0 -;; sub a3,zero,a2 -;; and a0,a1,a3 -;; not a1,a3 -;; and a3,a5,a1 -;; or a5,a0,a3 -;; lbu a0,0(a5) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,[const(1)] +;; add a2,a4,a3 +;; ult a5,a2,a4##ty=i64 +;; trap_if heap_oob##(a5 ne zero) +;; ld a5,8(a1) +;; ugt a5,a2,a5##ty=i64 +;; ld a0,0(a1) +;; add a4,a0,a4 +;; ld a0,[const(0)] +;; add a4,a4,a0 +;; li a0,0 +;; sltu a5,zero,a5 +;; sub a1,zero,a5 +;; and a3,a0,a1 +;; not a5,a1 +;; and a1,a4,a5 +;; or a3,a3,a1 +;; lbu a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index 2a551c45a516..3dd2f91baac1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -41,42 +41,40 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a2) -;; ugt a3,a0,a5##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; li a0,0 -;; andi a2,a3,255 -;; sltu a2,zero,a2 -;; sub a3,zero,a2 -;; and a0,a0,a3 -;; not a2,a3 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; ld a4,8(a2) +;; ugt a0,a5,a4##ty=i64 +;; ld a4,0(a2) +;; add a4,a4,a5 +;; li a5,0 +;; sltu a0,zero,a0 +;; sub a2,zero,a0 ;; and a3,a5,a2 -;; or a5,a0,a3 -;; sw a1,0(a5) +;; not a5,a2 +;; and a2,a4,a5 +;; or a3,a3,a2 +;; sw a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a1) -;; ugt a2,a0,a5##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; li a0,0 -;; andi a1,a2,255 -;; sltu a1,zero,a1 -;; sub a3,zero,a1 -;; and a0,a0,a3 -;; not a1,a3 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; ld a4,8(a1) +;; ugt a0,a5,a4##ty=i64 +;; ld a4,0(a1) +;; add a4,a4,a5 +;; li a5,0 +;; sltu a0,zero,a0 +;; sub a1,zero,a0 ;; and a3,a5,a1 -;; or a5,a0,a3 -;; lw a0,0(a5) +;; not a5,a1 +;; and a1,a4,a5 +;; or a3,a3,a1 +;; lw a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index 44d02911ef90..f00d09c2bc70 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -41,46 +41,44 @@ ;; function u0:0: ;; block0: -;; slli a0,a0,32 -;; srli a4,a0,32 -;; ld a3,8(a2) -;; ugt a3,a4,a3##ty=i64 +;; slli a5,a0,32 +;; srli a3,a5,32 +;; ld a0,8(a2) +;; ugt a0,a3,a0##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a4 -;; lui a4,1 -;; add a2,a2,a4 -;; li a4,0 -;; andi a3,a3,255 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a3,a4,a5 -;; not a4,a5 -;; and a5,a2,a4 -;; or a2,a3,a5 -;; sw a1,0(a2) +;; add a2,a2,a3 +;; lui a3,1 +;; add a2,a2,a3 +;; li a3,0 +;; sltu a4,zero,a0 +;; sub a4,zero,a4 +;; and a5,a3,a4 +;; not a3,a4 +;; and a3,a2,a3 +;; or a5,a5,a3 +;; sw a1,0(a5) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a0,a0,32 -;; srli a3,a0,32 -;; ld a2,8(a1) -;; ugt a2,a3,a2##ty=i64 +;; slli a5,a0,32 +;; srli a2,a5,32 +;; ld a0,8(a1) +;; ugt a0,a2,a0##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a3 -;; lui a3,1 -;; add a1,a1,a3 -;; li a3,0 -;; andi a2,a2,255 -;; sltu a4,zero,a2 -;; sub a5,zero,a4 -;; and a2,a3,a5 -;; not a3,a5 -;; and a5,a1,a3 -;; or a1,a2,a5 -;; lw a0,0(a1) +;; add a1,a1,a2 +;; lui a2,1 +;; add a1,a1,a2 +;; li a2,0 +;; sltu a3,zero,a0 +;; sub a3,zero,a3 +;; and a5,a2,a3 +;; not a2,a3 +;; and a3,a1,a2 +;; or a5,a5,a3 +;; lw a0,0(a5) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index b8a7173a9229..1a0c58f33706 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,46 +41,44 @@ ;; function u0:0: ;; block0: -;; slli a0,a0,32 -;; srli a4,a0,32 -;; ld a3,8(a2) -;; ugt a3,a4,a3##ty=i64 +;; slli a5,a0,32 +;; srli a3,a5,32 +;; ld a0,8(a2) +;; ugt a0,a3,a0##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a4 -;; ld a4,[const(0)] -;; add a2,a2,a4 -;; li a4,0 -;; andi a3,a3,255 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a3,a4,a5 -;; not a4,a5 -;; and a5,a2,a4 -;; or a2,a3,a5 -;; sw a1,0(a2) +;; add a2,a2,a3 +;; ld a3,[const(0)] +;; add a2,a2,a3 +;; li a3,0 +;; sltu a4,zero,a0 +;; sub a4,zero,a4 +;; and a5,a3,a4 +;; not a3,a4 +;; and a3,a2,a3 +;; or a5,a5,a3 +;; sw a1,0(a5) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a0,a0,32 -;; srli a3,a0,32 -;; ld a2,8(a1) -;; ugt a2,a3,a2##ty=i64 +;; slli a5,a0,32 +;; srli a2,a5,32 +;; ld a0,8(a1) +;; ugt a0,a2,a0##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a3 -;; ld a3,[const(0)] -;; add a1,a1,a3 -;; li a3,0 -;; andi a2,a2,255 -;; sltu a4,zero,a2 -;; sub a5,zero,a4 -;; and a2,a3,a5 -;; not a3,a5 -;; and a5,a1,a3 -;; or a1,a2,a5 -;; lw a0,0(a1) +;; add a1,a1,a2 +;; ld a2,[const(0)] +;; add a1,a1,a2 +;; li a2,0 +;; sltu a3,zero,a0 +;; sub a3,zero,a3 +;; and a5,a2,a3 +;; not a2,a3 +;; and a3,a1,a2 +;; or a5,a5,a3 +;; lw a0,0(a5) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index 5719472c2b23..cf9be3f6d245 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -41,42 +41,40 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a2) -;; uge a3,a0,a5##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; li a0,0 -;; andi a2,a3,255 -;; sltu a2,zero,a2 -;; sub a3,zero,a2 -;; and a0,a0,a3 -;; not a2,a3 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; ld a4,8(a2) +;; uge a0,a5,a4##ty=i64 +;; ld a4,0(a2) +;; add a4,a4,a5 +;; li a5,0 +;; sltu a0,zero,a0 +;; sub a2,zero,a0 ;; and a3,a5,a2 -;; or a5,a0,a3 -;; sb a1,0(a5) +;; not a5,a2 +;; and a2,a4,a5 +;; or a3,a3,a2 +;; sb a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a1) -;; uge a2,a0,a5##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; li a0,0 -;; andi a1,a2,255 -;; sltu a1,zero,a1 -;; sub a3,zero,a1 -;; and a0,a0,a3 -;; not a1,a3 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; ld a4,8(a1) +;; uge a0,a5,a4##ty=i64 +;; ld a4,0(a1) +;; add a4,a4,a5 +;; li a5,0 +;; sltu a0,zero,a0 +;; sub a1,zero,a0 ;; and a3,a5,a1 -;; or a5,a0,a3 -;; lbu a0,0(a5) +;; not a5,a1 +;; and a1,a4,a5 +;; or a3,a3,a1 +;; lbu a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index f6d8b7c29431..fac8d232e735 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -41,46 +41,44 @@ ;; function u0:0: ;; block0: -;; slli a0,a0,32 -;; srli a4,a0,32 -;; ld a3,8(a2) -;; ugt a3,a4,a3##ty=i64 +;; slli a5,a0,32 +;; srli a3,a5,32 +;; ld a0,8(a2) +;; ugt a0,a3,a0##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a4 -;; lui a4,1 -;; add a2,a2,a4 -;; li a4,0 -;; andi a3,a3,255 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a3,a4,a5 -;; not a4,a5 -;; and a5,a2,a4 -;; or a2,a3,a5 -;; sb a1,0(a2) +;; add a2,a2,a3 +;; lui a3,1 +;; add a2,a2,a3 +;; li a3,0 +;; sltu a4,zero,a0 +;; sub a4,zero,a4 +;; and a5,a3,a4 +;; not a3,a4 +;; and a3,a2,a3 +;; or a5,a5,a3 +;; sb a1,0(a5) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a0,a0,32 -;; srli a3,a0,32 -;; ld a2,8(a1) -;; ugt a2,a3,a2##ty=i64 +;; slli a5,a0,32 +;; srli a2,a5,32 +;; ld a0,8(a1) +;; ugt a0,a2,a0##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a3 -;; lui a3,1 -;; add a1,a1,a3 -;; li a3,0 -;; andi a2,a2,255 -;; sltu a4,zero,a2 -;; sub a5,zero,a4 -;; and a2,a3,a5 -;; not a3,a5 -;; and a5,a1,a3 -;; or a1,a2,a5 -;; lbu a0,0(a1) +;; add a1,a1,a2 +;; lui a2,1 +;; add a1,a1,a2 +;; li a2,0 +;; sltu a3,zero,a0 +;; sub a3,zero,a3 +;; and a5,a2,a3 +;; not a2,a3 +;; and a3,a1,a2 +;; or a5,a5,a3 +;; lbu a0,0(a5) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index eb087e87bd28..e62b2b14f127 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -41,46 +41,44 @@ ;; function u0:0: ;; block0: -;; slli a0,a0,32 -;; srli a4,a0,32 -;; ld a3,8(a2) -;; ugt a3,a4,a3##ty=i64 +;; slli a5,a0,32 +;; srli a3,a5,32 +;; ld a0,8(a2) +;; ugt a0,a3,a0##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a4 -;; ld a4,[const(0)] -;; add a2,a2,a4 -;; li a4,0 -;; andi a3,a3,255 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a3,a4,a5 -;; not a4,a5 -;; and a5,a2,a4 -;; or a2,a3,a5 -;; sb a1,0(a2) +;; add a2,a2,a3 +;; ld a3,[const(0)] +;; add a2,a2,a3 +;; li a3,0 +;; sltu a4,zero,a0 +;; sub a4,zero,a4 +;; and a5,a3,a4 +;; not a3,a4 +;; and a3,a2,a3 +;; or a5,a5,a3 +;; sb a1,0(a5) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a0,a0,32 -;; srli a3,a0,32 -;; ld a2,8(a1) -;; ugt a2,a3,a2##ty=i64 +;; slli a5,a0,32 +;; srli a2,a5,32 +;; ld a0,8(a1) +;; ugt a0,a2,a0##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a3 -;; ld a3,[const(0)] -;; add a1,a1,a3 -;; li a3,0 -;; andi a2,a2,255 -;; sltu a4,zero,a2 -;; sub a5,zero,a4 -;; and a2,a3,a5 -;; not a3,a5 -;; and a5,a1,a3 -;; or a1,a2,a5 -;; lbu a0,0(a1) +;; add a1,a1,a2 +;; ld a2,[const(0)] +;; add a1,a1,a2 +;; li a2,0 +;; sltu a3,zero,a0 +;; sub a3,zero,a3 +;; and a5,a2,a3 +;; not a2,a3 +;; and a3,a1,a2 +;; or a5,a5,a3 +;; lbu a0,0(a5) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat index d139bc455b33..c52b742f2170 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -41,40 +41,38 @@ ;; function u0:0: ;; block0: -;; ld a4,8(a2) -;; addi a4,a4,-4 -;; ugt a5,a0,a4##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a0 -;; li a0,0 -;; andi a5,a5,255 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a5,a0,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; sw a1,0(a4) +;; ld a3,8(a2) +;; addi a3,a3,-4 +;; ugt a4,a0,a3##ty=i64 +;; ld a3,0(a2) +;; add a3,a3,a0 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a4,8(a1) -;; addi a4,a4,-4 -;; ugt a5,a0,a4##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a0 -;; li a0,0 -;; andi a5,a5,255 -;; sltu a1,zero,a5 -;; sub a2,zero,a1 -;; and a5,a0,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; lw a0,0(a4) +;; ld a3,8(a1) +;; addi a3,a3,-4 +;; ugt a4,a0,a3##ty=i64 +;; ld a3,0(a1) +;; add a3,a3,a0 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; lw a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index ef78d4658201..cf61dff531a1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -45,20 +45,19 @@ ;; lui a4,-1 ;; addi a4,a4,-4 ;; add a3,a3,a4 -;; ugt a3,a0,a3##ty=i64 +;; ugt a4,a0,a3##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a0 -;; lui a4,1 -;; add a2,a2,a4 -;; li a4,0 -;; andi a3,a3,255 -;; sltu a5,zero,a3 -;; sub a0,zero,a5 -;; and a3,a4,a0 -;; not a4,a0 +;; lui a3,1 +;; add a3,a2,a3 +;; li a2,0 +;; sltu a4,zero,a4 +;; sub a4,zero,a4 ;; and a0,a2,a4 -;; or a2,a3,a0 -;; sw a1,0(a2) +;; not a2,a4 +;; and a4,a3,a2 +;; or a0,a0,a4 +;; sw a1,0(a0) ;; j label1 ;; block1: ;; ret @@ -70,19 +69,18 @@ ;; addi a3,a3,-4 ;; add a2,a2,a3 ;; ugt a3,a0,a2##ty=i64 -;; ld a2,0(a1) -;; add a2,a2,a0 -;; lui a4,1 -;; add a2,a2,a4 -;; li a4,0 -;; andi a3,a3,255 -;; sltu a5,zero,a3 -;; sub a0,zero,a5 -;; and a3,a4,a0 -;; not a4,a0 -;; and a0,a2,a4 -;; or a2,a3,a0 -;; lw a0,0(a2) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lui a2,1 +;; add a2,a1,a2 +;; li a1,0 +;; sltu a3,zero,a3 +;; sub a4,zero,a3 +;; and a0,a1,a4 +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lw a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index d27e29e4b2f9..24bf1a08723d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -46,20 +46,19 @@ ;; ult a4,a3,a0##ty=i64 ;; trap_if heap_oob##(a4 ne zero) ;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 -;; ld a3,0(a2) -;; add a3,a3,a0 -;; ld a5,[const(0)] -;; add a3,a3,a5 -;; li a5,0 -;; andi a4,a4,255 -;; sltu a0,zero,a4 -;; sub a2,zero,a0 -;; and a4,a5,a2 -;; not a5,a2 -;; and a2,a3,a5 -;; or a3,a4,a2 -;; sw a1,0(a3) +;; ugt a3,a3,a4##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; ld a4,[const(0)] +;; add a2,a2,a4 +;; li a4,0 +;; sltu a3,zero,a3 +;; sub a5,zero,a3 +;; and a3,a4,a5 +;; not a4,a5 +;; and a5,a2,a4 +;; or a2,a3,a5 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret @@ -71,20 +70,19 @@ ;; ult a3,a2,a0##ty=i64 ;; trap_if heap_oob##(a3 ne zero) ;; ld a3,8(a1) -;; ugt a4,a2,a3##ty=i64 +;; ugt a2,a2,a3##ty=i64 ;; ld a3,0(a1) ;; add a3,a3,a0 -;; ld a5,[const(0)] -;; add a3,a3,a5 -;; li a5,0 -;; andi a4,a4,255 -;; sltu a0,zero,a4 -;; sub a1,zero,a0 -;; and a4,a5,a1 -;; not a5,a1 -;; and a1,a3,a5 -;; or a3,a4,a1 -;; lw a0,0(a3) +;; ld a4,[const(0)] +;; add a3,a3,a4 +;; li a4,0 +;; sltu a5,zero,a2 +;; sub a5,zero,a5 +;; and a1,a4,a5 +;; not a4,a5 +;; and a5,a3,a4 +;; or a1,a1,a5 +;; lw a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat index 5a3bc080b7db..8602214200a3 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -42,37 +42,35 @@ ;; function u0:0: ;; block0: ;; ld a3,8(a2) -;; uge a4,a0,a3##ty=i64 -;; ld a3,0(a2) -;; add a3,a3,a0 -;; li a5,0 -;; andi a4,a4,255 -;; sltu a0,zero,a4 -;; sub a2,zero,a0 -;; and a4,a5,a2 -;; not a5,a2 -;; and a2,a3,a5 -;; or a3,a4,a2 -;; sb a1,0(a3) +;; uge a3,a0,a3##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; li a4,0 +;; sltu a3,zero,a3 +;; sub a5,zero,a3 +;; and a3,a4,a5 +;; not a4,a5 +;; and a5,a2,a4 +;; or a2,a3,a5 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a3,8(a1) -;; uge a4,a0,a3##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; li a5,0 -;; andi a4,a4,255 -;; sltu a0,zero,a4 -;; sub a1,zero,a0 -;; and a4,a5,a1 -;; not a5,a1 -;; and a1,a3,a5 -;; or a3,a4,a1 -;; lbu a0,0(a3) +;; ld a2,8(a1) +;; uge a3,a0,a2##ty=i64 +;; ld a2,0(a1) +;; add a2,a2,a0 +;; li a4,0 +;; sltu a3,zero,a3 +;; sub a5,zero,a3 +;; and a1,a4,a5 +;; not a3,a5 +;; and a5,a2,a3 +;; or a1,a1,a5 +;; lbu a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index 422e1bc1c3de..6b6da4a049a2 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -45,20 +45,19 @@ ;; lui a4,-1 ;; addi a4,a4,-1 ;; add a3,a3,a4 -;; ugt a3,a0,a3##ty=i64 +;; ugt a4,a0,a3##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a0 -;; lui a4,1 -;; add a2,a2,a4 -;; li a4,0 -;; andi a3,a3,255 -;; sltu a5,zero,a3 -;; sub a0,zero,a5 -;; and a3,a4,a0 -;; not a4,a0 +;; lui a3,1 +;; add a3,a2,a3 +;; li a2,0 +;; sltu a4,zero,a4 +;; sub a4,zero,a4 ;; and a0,a2,a4 -;; or a2,a3,a0 -;; sb a1,0(a2) +;; not a2,a4 +;; and a4,a3,a2 +;; or a0,a0,a4 +;; sb a1,0(a0) ;; j label1 ;; block1: ;; ret @@ -70,19 +69,18 @@ ;; addi a3,a3,-1 ;; add a2,a2,a3 ;; ugt a3,a0,a2##ty=i64 -;; ld a2,0(a1) -;; add a2,a2,a0 -;; lui a4,1 -;; add a2,a2,a4 -;; li a4,0 -;; andi a3,a3,255 -;; sltu a5,zero,a3 -;; sub a0,zero,a5 -;; and a3,a4,a0 -;; not a4,a0 -;; and a0,a2,a4 -;; or a2,a3,a0 -;; lbu a0,0(a2) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lui a2,1 +;; add a2,a1,a2 +;; li a1,0 +;; sltu a3,zero,a3 +;; sub a4,zero,a3 +;; and a0,a1,a4 +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lbu a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index f51b54592618..400fb3f98a9b 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -46,20 +46,19 @@ ;; ult a4,a3,a0##ty=i64 ;; trap_if heap_oob##(a4 ne zero) ;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 -;; ld a3,0(a2) -;; add a3,a3,a0 -;; ld a5,[const(0)] -;; add a3,a3,a5 -;; li a5,0 -;; andi a4,a4,255 -;; sltu a0,zero,a4 -;; sub a2,zero,a0 -;; and a4,a5,a2 -;; not a5,a2 -;; and a2,a3,a5 -;; or a3,a4,a2 -;; sb a1,0(a3) +;; ugt a3,a3,a4##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; ld a4,[const(0)] +;; add a2,a2,a4 +;; li a4,0 +;; sltu a3,zero,a3 +;; sub a5,zero,a3 +;; and a3,a4,a5 +;; not a4,a5 +;; and a5,a2,a4 +;; or a2,a3,a5 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret @@ -71,20 +70,19 @@ ;; ult a3,a2,a0##ty=i64 ;; trap_if heap_oob##(a3 ne zero) ;; ld a3,8(a1) -;; ugt a4,a2,a3##ty=i64 +;; ugt a2,a2,a3##ty=i64 ;; ld a3,0(a1) ;; add a3,a3,a0 -;; ld a5,[const(0)] -;; add a3,a3,a5 -;; li a5,0 -;; andi a4,a4,255 -;; sltu a0,zero,a4 -;; sub a1,zero,a0 -;; and a4,a5,a1 -;; not a5,a1 -;; and a1,a3,a5 -;; or a3,a4,a1 -;; lbu a0,0(a3) +;; ld a4,[const(0)] +;; add a3,a3,a4 +;; li a4,0 +;; sltu a5,zero,a2 +;; sub a5,zero,a5 +;; and a1,a4,a5 +;; not a4,a5 +;; and a5,a3,a4 +;; or a1,a1,a5 +;; lbu a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index a6f0dfe87df4..489b039647cb 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -42,37 +42,35 @@ ;; function u0:0: ;; block0: ;; ld a3,8(a2) -;; ugt a4,a0,a3##ty=i64 -;; ld a3,0(a2) -;; add a3,a3,a0 -;; li a5,0 -;; andi a4,a4,255 -;; sltu a0,zero,a4 -;; sub a2,zero,a0 -;; and a4,a5,a2 -;; not a5,a2 -;; and a2,a3,a5 -;; or a3,a4,a2 -;; sw a1,0(a3) +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; li a4,0 +;; sltu a3,zero,a3 +;; sub a5,zero,a3 +;; and a3,a4,a5 +;; not a4,a5 +;; and a5,a2,a4 +;; or a2,a3,a5 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a3,8(a1) -;; ugt a4,a0,a3##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; li a5,0 -;; andi a4,a4,255 -;; sltu a0,zero,a4 -;; sub a1,zero,a0 -;; and a4,a5,a1 -;; not a5,a1 -;; and a1,a3,a5 -;; or a3,a4,a1 -;; lw a0,0(a3) +;; ld a2,8(a1) +;; ugt a3,a0,a2##ty=i64 +;; ld a2,0(a1) +;; add a2,a2,a0 +;; li a4,0 +;; sltu a3,zero,a3 +;; sub a5,zero,a3 +;; and a1,a4,a5 +;; not a3,a5 +;; and a5,a2,a3 +;; or a1,a1,a5 +;; lw a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index 3e23e55bfce9..3a0708c20d19 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -41,42 +41,40 @@ ;; function u0:0: ;; block0: -;; ld a5,8(a2) -;; ugt a3,a0,a5##ty=i64 +;; ld a4,8(a2) +;; ugt a4,a0,a4##ty=i64 ;; ld a5,0(a2) ;; add a5,a5,a0 ;; lui a0,1 ;; add a5,a5,a0 -;; li a2,0 -;; andi a0,a3,255 -;; sltu a3,zero,a0 -;; sub a3,zero,a3 -;; and a0,a2,a3 -;; not a2,a3 -;; and a3,a5,a2 -;; or a5,a0,a3 -;; sw a1,0(a5) +;; li a0,0 +;; sltu a2,zero,a4 +;; sub a2,zero,a2 +;; and a3,a0,a2 +;; not a0,a2 +;; and a2,a5,a0 +;; or a3,a3,a2 +;; sw a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a5,8(a1) -;; ugt a2,a0,a5##ty=i64 +;; ld a4,8(a1) +;; ugt a4,a0,a4##ty=i64 ;; ld a5,0(a1) ;; add a5,a5,a0 ;; lui a0,1 ;; add a5,a5,a0 -;; li a1,0 -;; andi a0,a2,255 -;; sltu a2,zero,a0 -;; sub a3,zero,a2 -;; and a0,a1,a3 -;; not a1,a3 -;; and a3,a5,a1 -;; or a5,a0,a3 -;; lw a0,0(a5) +;; li a0,0 +;; sltu a1,zero,a4 +;; sub a1,zero,a1 +;; and a3,a0,a1 +;; not a0,a1 +;; and a1,a5,a0 +;; or a3,a3,a1 +;; lw a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 67f34728d8a5..bb539d173df7 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,42 +41,40 @@ ;; function u0:0: ;; block0: -;; ld a5,8(a2) -;; ugt a3,a0,a5##ty=i64 +;; ld a4,8(a2) +;; ugt a4,a0,a4##ty=i64 ;; ld a5,0(a2) ;; add a5,a5,a0 ;; ld a0,[const(0)] ;; add a5,a5,a0 -;; li a2,0 -;; andi a0,a3,255 -;; sltu a3,zero,a0 -;; sub a3,zero,a3 -;; and a0,a2,a3 -;; not a2,a3 -;; and a3,a5,a2 -;; or a5,a0,a3 -;; sw a1,0(a5) +;; li a0,0 +;; sltu a2,zero,a4 +;; sub a2,zero,a2 +;; and a3,a0,a2 +;; not a0,a2 +;; and a2,a5,a0 +;; or a3,a3,a2 +;; sw a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a5,8(a1) -;; ugt a2,a0,a5##ty=i64 +;; ld a4,8(a1) +;; ugt a4,a0,a4##ty=i64 ;; ld a5,0(a1) ;; add a5,a5,a0 ;; ld a0,[const(0)] ;; add a5,a5,a0 -;; li a1,0 -;; andi a0,a2,255 -;; sltu a2,zero,a0 -;; sub a3,zero,a2 -;; and a0,a1,a3 -;; not a1,a3 -;; and a3,a5,a1 -;; or a5,a0,a3 -;; lw a0,0(a5) +;; li a0,0 +;; sltu a1,zero,a4 +;; sub a1,zero,a1 +;; and a3,a0,a1 +;; not a0,a1 +;; and a1,a5,a0 +;; or a3,a3,a1 +;; lw a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index c90e4738e5c4..fe719e0e93e2 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -42,37 +42,35 @@ ;; function u0:0: ;; block0: ;; ld a3,8(a2) -;; uge a4,a0,a3##ty=i64 -;; ld a3,0(a2) -;; add a3,a3,a0 -;; li a5,0 -;; andi a4,a4,255 -;; sltu a0,zero,a4 -;; sub a2,zero,a0 -;; and a4,a5,a2 -;; not a5,a2 -;; and a2,a3,a5 -;; or a3,a4,a2 -;; sb a1,0(a3) +;; uge a3,a0,a3##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; li a4,0 +;; sltu a3,zero,a3 +;; sub a5,zero,a3 +;; and a3,a4,a5 +;; not a4,a5 +;; and a5,a2,a4 +;; or a2,a3,a5 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a3,8(a1) -;; uge a4,a0,a3##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; li a5,0 -;; andi a4,a4,255 -;; sltu a0,zero,a4 -;; sub a1,zero,a0 -;; and a4,a5,a1 -;; not a5,a1 -;; and a1,a3,a5 -;; or a3,a4,a1 -;; lbu a0,0(a3) +;; ld a2,8(a1) +;; uge a3,a0,a2##ty=i64 +;; ld a2,0(a1) +;; add a2,a2,a0 +;; li a4,0 +;; sltu a3,zero,a3 +;; sub a5,zero,a3 +;; and a1,a4,a5 +;; not a3,a5 +;; and a5,a2,a3 +;; or a1,a1,a5 +;; lbu a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index 74390da34be9..c473fe49d832 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -41,42 +41,40 @@ ;; function u0:0: ;; block0: -;; ld a5,8(a2) -;; ugt a3,a0,a5##ty=i64 +;; ld a4,8(a2) +;; ugt a4,a0,a4##ty=i64 ;; ld a5,0(a2) ;; add a5,a5,a0 ;; lui a0,1 ;; add a5,a5,a0 -;; li a2,0 -;; andi a0,a3,255 -;; sltu a3,zero,a0 -;; sub a3,zero,a3 -;; and a0,a2,a3 -;; not a2,a3 -;; and a3,a5,a2 -;; or a5,a0,a3 -;; sb a1,0(a5) +;; li a0,0 +;; sltu a2,zero,a4 +;; sub a2,zero,a2 +;; and a3,a0,a2 +;; not a0,a2 +;; and a2,a5,a0 +;; or a3,a3,a2 +;; sb a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a5,8(a1) -;; ugt a2,a0,a5##ty=i64 +;; ld a4,8(a1) +;; ugt a4,a0,a4##ty=i64 ;; ld a5,0(a1) ;; add a5,a5,a0 ;; lui a0,1 ;; add a5,a5,a0 -;; li a1,0 -;; andi a0,a2,255 -;; sltu a2,zero,a0 -;; sub a3,zero,a2 -;; and a0,a1,a3 -;; not a1,a3 -;; and a3,a5,a1 -;; or a5,a0,a3 -;; lbu a0,0(a5) +;; li a0,0 +;; sltu a1,zero,a4 +;; sub a1,zero,a1 +;; and a3,a0,a1 +;; not a0,a1 +;; and a1,a5,a0 +;; or a3,a3,a1 +;; lbu a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 0b9d9f0903fc..e0ef430e2684 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -41,42 +41,40 @@ ;; function u0:0: ;; block0: -;; ld a5,8(a2) -;; ugt a3,a0,a5##ty=i64 +;; ld a4,8(a2) +;; ugt a4,a0,a4##ty=i64 ;; ld a5,0(a2) ;; add a5,a5,a0 ;; ld a0,[const(0)] ;; add a5,a5,a0 -;; li a2,0 -;; andi a0,a3,255 -;; sltu a3,zero,a0 -;; sub a3,zero,a3 -;; and a0,a2,a3 -;; not a2,a3 -;; and a3,a5,a2 -;; or a5,a0,a3 -;; sb a1,0(a5) +;; li a0,0 +;; sltu a2,zero,a4 +;; sub a2,zero,a2 +;; and a3,a0,a2 +;; not a0,a2 +;; and a2,a5,a0 +;; or a3,a3,a2 +;; sb a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a5,8(a1) -;; ugt a2,a0,a5##ty=i64 +;; ld a4,8(a1) +;; ugt a4,a0,a4##ty=i64 ;; ld a5,0(a1) ;; add a5,a5,a0 ;; ld a0,[const(0)] ;; add a5,a5,a0 -;; li a1,0 -;; andi a0,a2,255 -;; sltu a2,zero,a0 -;; sub a3,zero,a2 -;; and a0,a1,a3 -;; not a1,a3 -;; and a3,a5,a1 -;; or a5,a0,a3 -;; lbu a0,0(a5) +;; li a0,0 +;; sltu a1,zero,a4 +;; sub a1,zero,a1 +;; and a3,a0,a1 +;; not a0,a1 +;; and a1,a5,a0 +;; or a3,a3,a1 +;; lbu a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat index f8f8f2ec4288..0a75a7b3d6da 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -39,44 +39,42 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; lui a5,65536 -;; addi a4,a5,-4 -;; ugt a4,a3,a4##ty=i64 -;; ld a0,0(a2) -;; add a0,a0,a3 -;; li a2,0 -;; andi a3,a4,255 -;; sltu a3,zero,a3 -;; sub a4,zero,a3 -;; and a2,a2,a4 -;; not a3,a4 -;; and a4,a0,a3 -;; or a0,a2,a4 -;; sw a1,0(a0) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; lui a4,65536 +;; addi a3,a4,-4 +;; ugt a3,a0,a3##ty=i64 +;; ld a5,0(a2) +;; add a5,a5,a0 +;; li a0,0 +;; sltu a2,zero,a3 +;; sub a2,zero,a2 +;; and a4,a0,a2 +;; not a0,a2 +;; and a2,a5,a0 +;; or a4,a4,a2 +;; sw a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; lui a5,65536 -;; addi a3,a5,-4 -;; ugt a3,a2,a3##ty=i64 -;; ld a0,0(a1) -;; add a0,a0,a2 -;; li a1,0 -;; andi a2,a3,255 -;; sltu a2,zero,a2 -;; sub a4,zero,a2 -;; and a1,a1,a4 -;; not a2,a4 +;; slli a4,a0,32 +;; srli a0,a4,32 +;; lui a4,65536 +;; addi a2,a4,-4 +;; ugt a2,a0,a2##ty=i64 +;; ld a5,0(a1) +;; add a5,a5,a0 +;; li a0,0 +;; sltu a1,zero,a2 +;; sub a2,zero,a1 ;; and a4,a0,a2 -;; or a0,a1,a4 -;; lw a0,0(a0) +;; not a0,a2 +;; and a2,a5,a0 +;; or a4,a4,a2 +;; lw a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index ca129eb94eb9..90d1bca843f1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -39,48 +39,49 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a4,a3,32 -;; lui a3,65535 -;; addi a3,a3,-4 -;; ugt a3,a4,a3##ty=i64 -;; ld a2,0(a2) -;; add a2,a2,a4 +;; mv a4,a2 +;; slli a0,a0,32 +;; srli a3,a0,32 +;; lui a0,65535 +;; addi a2,a0,-4 +;; ugt a2,a3,a2##ty=i64 +;; ld a4,0(a4) +;; add a3,a4,a3 ;; lui a4,1 -;; add a2,a2,a4 +;; add a3,a3,a4 ;; li a4,0 -;; andi a3,a3,255 -;; sltu a5,zero,a3 -;; sub a0,zero,a5 -;; and a3,a4,a0 -;; not a4,a0 -;; and a0,a2,a4 -;; or a2,a3,a0 -;; sw a1,0(a2) +;; sltu a2,zero,a2 +;; sub a5,zero,a2 +;; and a0,a4,a5 +;; not a2,a5 +;; and a4,a3,a2 +;; or a0,a0,a4 +;; sw a1,0(a0) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a4,a2,32 -;; lui a2,65535 -;; addi a3,a2,-4 -;; ugt a3,a4,a3##ty=i64 -;; ld a2,0(a1) -;; add a2,a2,a4 -;; lui a4,1 -;; add a2,a2,a4 -;; li a4,0 -;; andi a3,a3,255 -;; sltu a5,zero,a3 -;; sub a0,zero,a5 -;; and a3,a4,a0 -;; not a4,a0 -;; and a0,a2,a4 -;; or a2,a3,a0 -;; lw a0,0(a2) +;; mv a4,a1 +;; slli a0,a0,32 +;; srli a2,a0,32 +;; lui a0,65535 +;; addi a3,a0,-4 +;; ugt a1,a2,a3##ty=i64 +;; mv a3,a4 +;; ld a3,0(a3) +;; add a2,a3,a2 +;; lui a3,1 +;; add a2,a2,a3 +;; li a3,0 +;; sltu a4,zero,a1 +;; sub a4,zero,a4 +;; and a0,a3,a4 +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lw a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat index e1532d5465fa..168a14a89d3d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -39,44 +39,42 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; lui a5,65536 -;; addi a4,a5,-1 -;; ugt a4,a3,a4##ty=i64 -;; ld a0,0(a2) -;; add a0,a0,a3 -;; li a2,0 -;; andi a3,a4,255 -;; sltu a3,zero,a3 -;; sub a4,zero,a3 -;; and a2,a2,a4 -;; not a3,a4 -;; and a4,a0,a3 -;; or a0,a2,a4 -;; sb a1,0(a0) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; lui a4,65536 +;; addi a3,a4,-1 +;; ugt a3,a0,a3##ty=i64 +;; ld a5,0(a2) +;; add a5,a5,a0 +;; li a0,0 +;; sltu a2,zero,a3 +;; sub a2,zero,a2 +;; and a4,a0,a2 +;; not a0,a2 +;; and a2,a5,a0 +;; or a4,a4,a2 +;; sb a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; lui a5,65536 -;; addi a3,a5,-1 -;; ugt a3,a2,a3##ty=i64 -;; ld a0,0(a1) -;; add a0,a0,a2 -;; li a1,0 -;; andi a2,a3,255 -;; sltu a2,zero,a2 -;; sub a4,zero,a2 -;; and a1,a1,a4 -;; not a2,a4 +;; slli a4,a0,32 +;; srli a0,a4,32 +;; lui a4,65536 +;; addi a2,a4,-1 +;; ugt a2,a0,a2##ty=i64 +;; ld a5,0(a1) +;; add a5,a5,a0 +;; li a0,0 +;; sltu a1,zero,a2 +;; sub a2,zero,a1 ;; and a4,a0,a2 -;; or a0,a1,a4 -;; lbu a0,0(a0) +;; not a0,a2 +;; and a2,a5,a0 +;; or a4,a4,a2 +;; lbu a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index 2d09800de3a3..3b7117ad7c32 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -39,48 +39,49 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a4,a3,32 -;; lui a3,65535 -;; addi a3,a3,-1 -;; ugt a3,a4,a3##ty=i64 -;; ld a2,0(a2) -;; add a2,a2,a4 +;; mv a4,a2 +;; slli a0,a0,32 +;; srli a3,a0,32 +;; lui a0,65535 +;; addi a2,a0,-1 +;; ugt a2,a3,a2##ty=i64 +;; ld a4,0(a4) +;; add a3,a4,a3 ;; lui a4,1 -;; add a2,a2,a4 +;; add a3,a3,a4 ;; li a4,0 -;; andi a3,a3,255 -;; sltu a5,zero,a3 -;; sub a0,zero,a5 -;; and a3,a4,a0 -;; not a4,a0 -;; and a0,a2,a4 -;; or a2,a3,a0 -;; sb a1,0(a2) +;; sltu a2,zero,a2 +;; sub a5,zero,a2 +;; and a0,a4,a5 +;; not a2,a5 +;; and a4,a3,a2 +;; or a0,a0,a4 +;; sb a1,0(a0) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a4,a2,32 -;; lui a2,65535 -;; addi a3,a2,-1 -;; ugt a3,a4,a3##ty=i64 -;; ld a2,0(a1) -;; add a2,a2,a4 -;; lui a4,1 -;; add a2,a2,a4 -;; li a4,0 -;; andi a3,a3,255 -;; sltu a5,zero,a3 -;; sub a0,zero,a5 -;; and a3,a4,a0 -;; not a4,a0 -;; and a0,a2,a4 -;; or a2,a3,a0 -;; lbu a0,0(a2) +;; mv a4,a1 +;; slli a0,a0,32 +;; srli a2,a0,32 +;; lui a0,65535 +;; addi a3,a0,-1 +;; ugt a1,a2,a3##ty=i64 +;; mv a3,a4 +;; ld a3,0(a3) +;; add a2,a3,a2 +;; lui a3,1 +;; add a2,a2,a3 +;; li a3,0 +;; sltu a4,zero,a1 +;; sub a4,zero,a4 +;; and a0,a3,a4 +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lbu a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat index d5a07ae68ecb..05335a7dfc54 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -40,39 +40,37 @@ ;; function u0:0: ;; block0: ;; lui a3,65536 -;; addi a5,a3,-4 -;; ugt a5,a0,a5##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a0 -;; li a0,0 -;; andi a5,a5,255 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a5,a0,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; sw a1,0(a4) +;; addi a4,a3,-4 +;; ugt a4,a0,a4##ty=i64 +;; ld a3,0(a2) +;; add a3,a3,a0 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a3,65536 -;; addi a5,a3,-4 -;; ugt a5,a0,a5##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a0 -;; li a0,0 -;; andi a5,a5,255 -;; sltu a1,zero,a5 -;; sub a2,zero,a1 -;; and a5,a0,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; lw a0,0(a4) +;; lui a2,65536 +;; addi a4,a2,-4 +;; ugt a4,a0,a4##ty=i64 +;; ld a3,0(a1) +;; add a3,a3,a0 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; lw a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index 0e16efed51ca..51822e1df936 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -39,44 +39,42 @@ ;; function u0:0: ;; block0: -;; lui a5,65535 -;; addi a3,a5,-4 -;; ugt a4,a0,a3##ty=i64 +;; lui a4,65535 +;; addi a3,a4,-4 +;; ugt a5,a0,a3##ty=i64 ;; ld a2,0(a2) ;; add a0,a2,a0 ;; lui a2,1 ;; add a0,a0,a2 -;; li a3,0 -;; andi a2,a4,255 -;; sltu a2,zero,a2 -;; sub a4,zero,a2 -;; and a2,a3,a4 -;; not a3,a4 -;; and a4,a0,a3 -;; or a0,a2,a4 -;; sw a1,0(a0) +;; li a2,0 +;; sltu a3,zero,a5 +;; sub a3,zero,a3 +;; and a4,a2,a3 +;; not a2,a3 +;; and a2,a0,a2 +;; or a4,a4,a2 +;; sw a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a5,65535 -;; addi a2,a5,-4 -;; ugt a3,a0,a2##ty=i64 +;; lui a4,65535 +;; addi a2,a4,-4 +;; ugt a5,a0,a2##ty=i64 ;; ld a1,0(a1) ;; add a0,a1,a0 ;; lui a1,1 ;; add a0,a0,a1 -;; li a2,0 -;; andi a1,a3,255 -;; sltu a3,zero,a1 -;; sub a4,zero,a3 -;; and a1,a2,a4 -;; not a2,a4 -;; and a4,a0,a2 -;; or a0,a1,a4 -;; lw a0,0(a0) +;; li a1,0 +;; sltu a2,zero,a5 +;; sub a2,zero,a2 +;; and a4,a1,a2 +;; not a1,a2 +;; and a2,a0,a1 +;; or a4,a4,a2 +;; lw a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat index b9d9b85d9634..bb9a15f71417 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -40,39 +40,37 @@ ;; function u0:0: ;; block0: ;; lui a3,65536 -;; addi a5,a3,-1 -;; ugt a5,a0,a5##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a0 -;; li a0,0 -;; andi a5,a5,255 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a5,a0,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; sb a1,0(a4) +;; addi a4,a3,-1 +;; ugt a4,a0,a4##ty=i64 +;; ld a3,0(a2) +;; add a3,a3,a0 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a3,65536 -;; addi a5,a3,-1 -;; ugt a5,a0,a5##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a0 -;; li a0,0 -;; andi a5,a5,255 -;; sltu a1,zero,a5 -;; sub a2,zero,a1 -;; and a5,a0,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; lbu a0,0(a4) +;; lui a2,65536 +;; addi a4,a2,-1 +;; ugt a4,a0,a4##ty=i64 +;; ld a3,0(a1) +;; add a3,a3,a0 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; lbu a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index dd356dcb506b..e7c33e026da5 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -39,44 +39,42 @@ ;; function u0:0: ;; block0: -;; lui a5,65535 -;; addi a3,a5,-1 -;; ugt a4,a0,a3##ty=i64 +;; lui a4,65535 +;; addi a3,a4,-1 +;; ugt a5,a0,a3##ty=i64 ;; ld a2,0(a2) ;; add a0,a2,a0 ;; lui a2,1 ;; add a0,a0,a2 -;; li a3,0 -;; andi a2,a4,255 -;; sltu a2,zero,a2 -;; sub a4,zero,a2 -;; and a2,a3,a4 -;; not a3,a4 -;; and a4,a0,a3 -;; or a0,a2,a4 -;; sb a1,0(a0) +;; li a2,0 +;; sltu a3,zero,a5 +;; sub a3,zero,a3 +;; and a4,a2,a3 +;; not a2,a3 +;; and a2,a0,a2 +;; or a4,a4,a2 +;; sb a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a5,65535 -;; addi a2,a5,-1 -;; ugt a3,a0,a2##ty=i64 +;; lui a4,65535 +;; addi a2,a4,-1 +;; ugt a5,a0,a2##ty=i64 ;; ld a1,0(a1) ;; add a0,a1,a0 ;; lui a1,1 ;; add a0,a0,a1 -;; li a2,0 -;; andi a1,a3,255 -;; sltu a3,zero,a1 -;; sub a4,zero,a3 -;; and a1,a2,a4 -;; not a2,a4 -;; and a4,a0,a2 -;; or a0,a1,a4 -;; lbu a0,0(a0) +;; li a1,0 +;; sltu a2,zero,a5 +;; sub a2,zero,a2 +;; and a4,a1,a2 +;; not a1,a2 +;; and a2,a0,a1 +;; or a4,a4,a2 +;; lbu a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index 973ba5f4bdc1..aa54e8732573 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -40,39 +40,37 @@ ;; function u0:0: ;; block0: ;; lui a3,65536 -;; addi a5,a3,-4 -;; ugt a5,a0,a5##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a0 -;; li a0,0 -;; andi a5,a5,255 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a5,a0,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; sw a1,0(a4) +;; addi a4,a3,-4 +;; ugt a4,a0,a4##ty=i64 +;; ld a3,0(a2) +;; add a3,a3,a0 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a3,65536 -;; addi a5,a3,-4 -;; ugt a5,a0,a5##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a0 -;; li a0,0 -;; andi a5,a5,255 -;; sltu a1,zero,a5 -;; sub a2,zero,a1 -;; and a5,a0,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; lw a0,0(a4) +;; lui a2,65536 +;; addi a4,a2,-4 +;; ugt a4,a0,a4##ty=i64 +;; ld a3,0(a1) +;; add a3,a3,a0 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; lw a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index d202e72373e8..da0371942a94 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -39,44 +39,42 @@ ;; function u0:0: ;; block0: -;; lui a5,65535 -;; addi a3,a5,-4 -;; ugt a4,a0,a3##ty=i64 +;; lui a4,65535 +;; addi a3,a4,-4 +;; ugt a5,a0,a3##ty=i64 ;; ld a2,0(a2) ;; add a0,a2,a0 ;; lui a2,1 ;; add a0,a0,a2 -;; li a3,0 -;; andi a2,a4,255 -;; sltu a2,zero,a2 -;; sub a4,zero,a2 -;; and a2,a3,a4 -;; not a3,a4 -;; and a4,a0,a3 -;; or a0,a2,a4 -;; sw a1,0(a0) +;; li a2,0 +;; sltu a3,zero,a5 +;; sub a3,zero,a3 +;; and a4,a2,a3 +;; not a2,a3 +;; and a2,a0,a2 +;; or a4,a4,a2 +;; sw a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a5,65535 -;; addi a2,a5,-4 -;; ugt a3,a0,a2##ty=i64 +;; lui a4,65535 +;; addi a2,a4,-4 +;; ugt a5,a0,a2##ty=i64 ;; ld a1,0(a1) ;; add a0,a1,a0 ;; lui a1,1 ;; add a0,a0,a1 -;; li a2,0 -;; andi a1,a3,255 -;; sltu a3,zero,a1 -;; sub a4,zero,a3 -;; and a1,a2,a4 -;; not a2,a4 -;; and a4,a0,a2 -;; or a0,a1,a4 -;; lw a0,0(a0) +;; li a1,0 +;; sltu a2,zero,a5 +;; sub a2,zero,a2 +;; and a4,a1,a2 +;; not a1,a2 +;; and a2,a0,a1 +;; or a4,a4,a2 +;; lw a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index ee4599cd2566..32123e5ef387 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -40,39 +40,37 @@ ;; function u0:0: ;; block0: ;; lui a3,65536 -;; addi a5,a3,-1 -;; ugt a5,a0,a5##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a0 -;; li a0,0 -;; andi a5,a5,255 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a5,a0,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; sb a1,0(a4) +;; addi a4,a3,-1 +;; ugt a4,a0,a4##ty=i64 +;; ld a3,0(a2) +;; add a3,a3,a0 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a3,65536 -;; addi a5,a3,-1 -;; ugt a5,a0,a5##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a0 -;; li a0,0 -;; andi a5,a5,255 -;; sltu a1,zero,a5 -;; sub a2,zero,a1 -;; and a5,a0,a2 -;; not a0,a2 -;; and a2,a4,a0 -;; or a4,a5,a2 -;; lbu a0,0(a4) +;; lui a2,65536 +;; addi a4,a2,-1 +;; ugt a4,a0,a4##ty=i64 +;; ld a3,0(a1) +;; add a3,a3,a0 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; lbu a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index ea258a38ff87..5c5b2fa6d655 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -39,44 +39,42 @@ ;; function u0:0: ;; block0: -;; lui a5,65535 -;; addi a3,a5,-1 -;; ugt a4,a0,a3##ty=i64 +;; lui a4,65535 +;; addi a3,a4,-1 +;; ugt a5,a0,a3##ty=i64 ;; ld a2,0(a2) ;; add a0,a2,a0 ;; lui a2,1 ;; add a0,a0,a2 -;; li a3,0 -;; andi a2,a4,255 -;; sltu a2,zero,a2 -;; sub a4,zero,a2 -;; and a2,a3,a4 -;; not a3,a4 -;; and a4,a0,a3 -;; or a0,a2,a4 -;; sb a1,0(a0) +;; li a2,0 +;; sltu a3,zero,a5 +;; sub a3,zero,a3 +;; and a4,a2,a3 +;; not a2,a3 +;; and a2,a0,a2 +;; or a4,a4,a2 +;; sb a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a5,65535 -;; addi a2,a5,-1 -;; ugt a3,a0,a2##ty=i64 +;; lui a4,65535 +;; addi a2,a4,-1 +;; ugt a5,a0,a2##ty=i64 ;; ld a1,0(a1) ;; add a0,a1,a0 ;; lui a1,1 ;; add a0,a0,a1 -;; li a2,0 -;; andi a1,a3,255 -;; sltu a3,zero,a1 -;; sub a4,zero,a3 -;; and a1,a2,a4 -;; not a2,a4 -;; and a4,a0,a2 -;; or a0,a1,a4 -;; lbu a0,0(a0) +;; li a1,0 +;; sltu a2,zero,a5 +;; sub a2,zero,a2 +;; and a4,a1,a2 +;; not a1,a2 +;; and a2,a0,a1 +;; or a4,a4,a2 +;; lbu a0,0(a4) ;; j label1 ;; block1: ;; ret From c642a56cee51d284480099dd57370ee94a2e3f75 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 Oct 2023 11:44:43 -0500 Subject: [PATCH 035/199] Fix compatibility with Rust 1.70.0 (#7128) This commit fixes a compatibility issue with Rust 1.70.0 on Windows targets. Rust 1.71.0 stabilized `AsSocket for Arc` which is used here implicitly, so to compile successfully on 1.70.0, our current MSRV, a slight code change is required. Closes #7127 --- crates/wasi/src/preview2/host/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index 185807504fd2..9df287c920bf 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -513,7 +513,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { | TcpState::ConnectReady => {} TcpState::Listening | TcpState::Connecting | TcpState::Connected => { - match rustix::net::shutdown(&dropped.inner, rustix::net::Shutdown::ReadWrite) { + match rustix::net::shutdown(&*dropped.inner, rustix::net::Shutdown::ReadWrite) { Ok(()) | Err(Errno::NOTCONN) => {} Err(err) => Err(err).unwrap(), } From 233786c257fd9f15ec6c8f69f5bccbfcf01c3fa2 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Mon, 2 Oct 2023 18:26:08 +0100 Subject: [PATCH 036/199] riscv64: Add `Zcb` extension instructions (#7123) * riscv64: Add `c.mul` * riscv64: Add `c.not` * riscv64: Add zbb and zba dependent compressed instructions * riscv64: Add `Zcb` loads and stores * riscv64: Restrict immediate encoding for halfword compressed stores and loads * riscv64: Reverse imm bits for bytewise compressed loads and stores --- cranelift/codegen/src/isa/riscv64/inst.isle | 24 ++ .../codegen/src/isa/riscv64/inst/args.rs | 82 +++++- .../codegen/src/isa/riscv64/inst/emit.rs | 197 +++++++++++++-- .../codegen/src/isa/riscv64/inst/encode.rs | 55 +++- .../codegen/src/isa/riscv64/inst/imms.rs | 28 ++ .../filetests/filetests/isa/riscv64/zcb.clif | 239 ++++++++++++++++++ 6 files changed, 591 insertions(+), 34 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/riscv64/zcb.clif diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 16545b74716b..2cf04b6032c0 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -728,6 +728,7 @@ (CSub) (CAddw) (CSubw) + (CMul) )) ;; Opcodes for the CJ compressed instruction format @@ -781,6 +782,29 @@ (CFld) )) +;; Opcodes for the CSZN compressed instruction format +(type CsznOp (enum + (CNot) + (CZextb) + (CZexth) + (CZextw) + (CSextb) + (CSexth) +)) + +;; This is a mix of all Zcb memory adressing instructions +;; +;; Technically they are split across 4 different formats. +;; But they are all very similar, so we just group them all together. +(type ZcbMemOp (enum + (CLbu) + (CLhu) + (CLh) + (CSb) + (CSh) +)) + + (type CsrRegOP (enum ;; Atomic Read/Write CSR (CsrRW) diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs index c9f8201f0249..a5acb54d0066 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/args.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs @@ -8,7 +8,7 @@ use crate::ir::condcodes::CondCode; use crate::isa::riscv64::inst::{reg_name, reg_to_gpr_num}; use crate::isa::riscv64::lower::isle::generated_code::{ - COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, CjOp, ClOp, CrOp, CsOp, CssOp, + COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, CjOp, ClOp, CrOp, CsOp, CssOp, CsznOp, ZcbMemOp, }; use crate::machinst::isle::WritableReg; @@ -1923,6 +1923,7 @@ impl CaOp { CaOp::CSub => 0b00, CaOp::CAddw => 0b01, CaOp::CSubw => 0b00, + CaOp::CMul => 0b10, } } @@ -1930,16 +1931,20 @@ impl CaOp { // https://github.com/michaeljclark/riscv-meta/blob/master/opcodes match self { CaOp::CAnd | CaOp::COr | CaOp::CXor | CaOp::CSub => 0b100_011, - CaOp::CSubw | CaOp::CAddw => 0b100_111, + CaOp::CSubw | CaOp::CAddw | CaOp::CMul => 0b100_111, } } pub fn op(&self) -> COpcodeSpace { // https://five-embeddev.com/riscv-isa-manual/latest/rvc-opcode-map.html#rvcopcodemap match self { - CaOp::CAnd | CaOp::COr | CaOp::CXor | CaOp::CSub | CaOp::CAddw | CaOp::CSubw => { - COpcodeSpace::C1 - } + CaOp::CAnd + | CaOp::COr + | CaOp::CXor + | CaOp::CSub + | CaOp::CAddw + | CaOp::CSubw + | CaOp::CMul => COpcodeSpace::C1, } } } @@ -2076,3 +2081,70 @@ impl ClOp { } } } + +impl CsznOp { + pub fn funct6(&self) -> u32 { + // https://github.com/michaeljclark/riscv-meta/blob/master/opcodes + match self { + CsznOp::CNot + | CsznOp::CZextw + | CsznOp::CZextb + | CsznOp::CZexth + | CsznOp::CSextb + | CsznOp::CSexth => 0b100_111, + } + } + + pub fn funct5(&self) -> u32 { + // https://github.com/michaeljclark/riscv-meta/blob/master/opcodes + match self { + CsznOp::CNot => 0b11_101, + CsznOp::CZextb => 0b11_000, + CsznOp::CZexth => 0b11_010, + CsznOp::CZextw => 0b11_100, + CsznOp::CSextb => 0b11_001, + CsznOp::CSexth => 0b11_011, + } + } + + pub fn op(&self) -> COpcodeSpace { + // https://five-embeddev.com/riscv-isa-manual/latest/rvc-opcode-map.html#rvcopcodemap + match self { + CsznOp::CNot + | CsznOp::CZextb + | CsznOp::CZexth + | CsznOp::CZextw + | CsznOp::CSextb + | CsznOp::CSexth => COpcodeSpace::C1, + } + } +} + +impl ZcbMemOp { + pub fn funct6(&self) -> u32 { + // https://github.com/michaeljclark/riscv-meta/blob/master/opcodes + match self { + ZcbMemOp::CLbu => 0b100_000, + // These two opcodes are differentiated in the imm field of the instruction. + ZcbMemOp::CLhu | ZcbMemOp::CLh => 0b100_001, + ZcbMemOp::CSb => 0b100_010, + ZcbMemOp::CSh => 0b100_011, + } + } + + pub fn imm_bits(&self) -> u8 { + match self { + ZcbMemOp::CLhu | ZcbMemOp::CLh | ZcbMemOp::CSh => 1, + ZcbMemOp::CLbu | ZcbMemOp::CSb => 2, + } + } + + pub fn op(&self) -> COpcodeSpace { + // https://five-embeddev.com/riscv-isa-manual/latest/rvc-opcode-map.html#rvcopcodemap + match self { + ZcbMemOp::CLbu | ZcbMemOp::CLhu | ZcbMemOp::CLh | ZcbMemOp::CSb | ZcbMemOp::CSh => { + COpcodeSpace::C0 + } + } + } +} diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs index 25b0419684bc..d6cf12d6066b 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs @@ -4,7 +4,7 @@ use crate::binemit::StackMap; use crate::ir::{self, LibCall, RelSourceLoc, TrapCode}; use crate::isa::riscv64::inst::*; use crate::isa::riscv64::lower::isle::generated_code::{ - CaOp, CbOp, CiOp, CiwOp, ClOp, CrOp, CsOp, CssOp, + CaOp, CbOp, CiOp, CiwOp, ClOp, CrOp, CsOp, CssOp, CsznOp, ZcbMemOp, }; use crate::machinst::{AllocationConsumer, Reg, Writable}; use crate::trace; @@ -465,7 +465,11 @@ impl Inst { state: &mut EmitState, start_off: &mut u32, ) -> Option<()> { + let has_m = emit_info.isa_flags.has_m(); + let has_zba = emit_info.isa_flags.has_zba(); + let has_zbb = emit_info.isa_flags.has_zbb(); let has_zca = emit_info.isa_flags.has_zca(); + let has_zcb = emit_info.isa_flags.has_zcb(); let has_zcd = emit_info.isa_flags.has_zcd(); // Currently all compressed extensions (Zcb, Zcd, Zcmp, Zcmt, etc..) require Zca @@ -513,7 +517,8 @@ impl Inst { | AluOPRRR::Xor | AluOPRRR::Sub | AluOPRRR::Addw - | AluOPRRR::Subw), + | AluOPRRR::Subw + | AluOPRRR::Mul), rd, rs1, rs2, @@ -525,7 +530,8 @@ impl Inst { AluOPRRR::Sub => CaOp::CSub, AluOPRRR::Addw => CaOp::CAddw, AluOPRRR::Subw => CaOp::CSubw, - _ => unreachable!(), + AluOPRRR::Mul if has_zcb && has_m => CaOp::CMul, + _ => return None, }; sink.put2(encode_ca_type(op, rd, rs2)); @@ -700,6 +706,22 @@ impl Inst { sink.put2(encode_cb_type(op, rd, imm6)); } + // c.zextb + // + // This is an alias for `andi rd, rd, 0xff` + Inst::AluRRImm12 { + alu_op: AluOPRRI::Andi, + rd, + rs, + imm12, + } if has_zcb + && rd.to_reg() == rs + && reg_is_compressible(rs) + && imm12.as_i16() == 0xff => + { + sink.put2(encode_cszn_type(CsznOp::CZextb, rd)); + } + // c.andi Inst::AluRRImm12 { alu_op: AluOPRRI::Andi, @@ -752,7 +774,9 @@ impl Inst { // Regular Loads Inst::Load { rd, - op: op @ (LoadOP::Lw | LoadOP::Ld | LoadOP::Fld), + op: + op + @ (LoadOP::Lw | LoadOP::Ld | LoadOP::Fld | LoadOP::Lbu | LoadOP::Lhu | LoadOP::Lh), from, flags, } if reg_is_compressible(rd.to_reg()) @@ -766,17 +790,49 @@ impl Inst { // We encode the offset in multiples of the store size. let offset = from.get_offset_with_state(state); - let imm5 = u8::try_from(offset / op.size()) - .ok() - .and_then(Uimm5::maybe_from_u8)?; + let offset = u8::try_from(offset / op.size()).ok()?; - // Floating point loads are not included in the base Zca extension - // but in a separate Zcd extension. Both of these are part of the C Extension. - let op = match op { - LoadOP::Lw => ClOp::CLw, - LoadOP::Ld => ClOp::CLd, - LoadOP::Fld if has_zcd => ClOp::CFld, - _ => return None, + // We mix two different formats here. + // + // c.lw / c.ld / c.fld instructions are available in the standard Zca + // extension using the CL format. + // + // c.lbu / c.lhu / c.lh are only available in the Zcb extension and + // are also encoded differently. Technically they each have a different + // format, but they are similar enough that we can group them. + let is_zcb_load = matches!(op, LoadOP::Lbu | LoadOP::Lhu | LoadOP::Lh); + let encoded = if is_zcb_load { + if !has_zcb { + return None; + } + + let op = match op { + LoadOP::Lbu => ZcbMemOp::CLbu, + LoadOP::Lhu => ZcbMemOp::CLhu, + LoadOP::Lh => ZcbMemOp::CLh, + _ => unreachable!(), + }; + + // Byte stores & loads have 2 bits of immediate offset. Halfword stores + // and loads only have 1 bit. + let imm2 = Uimm2::maybe_from_u8(offset)?; + if (offset & !((1 << op.imm_bits()) - 1)) != 0 { + return None; + } + + encode_zcbmem_load(op, rd, base, imm2) + } else { + // Floating point loads are not included in the base Zca extension + // but in a separate Zcd extension. Both of these are part of the C Extension. + let op = match op { + LoadOP::Lw => ClOp::CLw, + LoadOP::Ld => ClOp::CLd, + LoadOP::Fld if has_zcd => ClOp::CFld, + _ => return None, + }; + let imm5 = Uimm5::maybe_from_u8(offset)?; + + encode_cl_type(op, rd, base, imm5) }; let srcloc = state.cur_srcloc(); @@ -784,7 +840,7 @@ impl Inst { // Register the offset at which the actual load instruction starts. sink.add_trap(TrapCode::HeapOutOfBounds); } - sink.put2(encode_cl_type(op, rd, base, imm5)); + sink.put2(encoded); } // Stack Based Stores @@ -822,7 +878,7 @@ impl Inst { // Regular Stores Inst::Store { src, - op: op @ (StoreOP::Sw | StoreOP::Sd | StoreOP::Fsd), + op: op @ (StoreOP::Sw | StoreOP::Sd | StoreOP::Fsd | StoreOP::Sh | StoreOP::Sb), to, flags, } if reg_is_compressible(src) @@ -836,17 +892,47 @@ impl Inst { // We encode the offset in multiples of the store size. let offset = to.get_offset_with_state(state); - let imm5 = u8::try_from(offset / op.size()) - .ok() - .and_then(Uimm5::maybe_from_u8)?; + let offset = u8::try_from(offset / op.size()).ok()?; - // Floating point stores are not included in the base Zca extension - // but in a separate Zcd extension. Both of these are part of the C Extension. - let op = match op { - StoreOP::Sw => CsOp::CSw, - StoreOP::Sd => CsOp::CSd, - StoreOP::Fsd if has_zcd => CsOp::CFsd, - _ => return None, + // We mix two different formats here. + // + // c.sw / c.sd / c.fsd instructions are available in the standard Zca + // extension using the CL format. + // + // c.sb / c.sh are only available in the Zcb extension and are also + // encoded differently. + let is_zcb_store = matches!(op, StoreOP::Sh | StoreOP::Sb); + let encoded = if is_zcb_store { + if !has_zcb { + return None; + } + + let op = match op { + StoreOP::Sh => ZcbMemOp::CSh, + StoreOP::Sb => ZcbMemOp::CSb, + _ => unreachable!(), + }; + + // Byte stores & loads have 2 bits of immediate offset. Halfword stores + // and loads only have 1 bit. + let imm2 = Uimm2::maybe_from_u8(offset)?; + if (offset & !((1 << op.imm_bits()) - 1)) != 0 { + return None; + } + + encode_zcbmem_store(op, src, base, imm2) + } else { + // Floating point stores are not included in the base Zca extension + // but in a separate Zcd extension. Both of these are part of the C Extension. + let op = match op { + StoreOP::Sw => CsOp::CSw, + StoreOP::Sd => CsOp::CSd, + StoreOP::Fsd if has_zcd => CsOp::CFsd, + _ => return None, + }; + let imm5 = Uimm5::maybe_from_u8(offset)?; + + encode_cs_type(op, src, base, imm5) }; let srcloc = state.cur_srcloc(); @@ -854,7 +940,64 @@ impl Inst { // Register the offset at which the actual load instruction starts. sink.add_trap(TrapCode::HeapOutOfBounds); } - sink.put2(encode_cs_type(op, src, base, imm5)); + sink.put2(encoded); + } + + // c.not + // + // This is an alias for `xori rd, rd, -1` + Inst::AluRRImm12 { + alu_op: AluOPRRI::Xori, + rd, + rs, + imm12, + } if has_zcb + && rd.to_reg() == rs + && reg_is_compressible(rs) + && imm12.as_i16() == -1 => + { + sink.put2(encode_cszn_type(CsznOp::CNot, rd)); + } + + // c.sext.b / c.sext.h / c.zext.h + // + // These are all the extend instructions present in `Zcb`, they + // also require `Zbb` since they aren't available in the base ISA. + Inst::AluRRImm12 { + alu_op: alu_op @ (AluOPRRI::Sextb | AluOPRRI::Sexth | AluOPRRI::Zexth), + rd, + rs, + imm12, + } if has_zcb + && has_zbb + && rd.to_reg() == rs + && reg_is_compressible(rs) + && imm12.as_i16() == 0 => + { + let op = match alu_op { + AluOPRRI::Sextb => CsznOp::CSextb, + AluOPRRI::Sexth => CsznOp::CSexth, + AluOPRRI::Zexth => CsznOp::CZexth, + _ => unreachable!(), + }; + sink.put2(encode_cszn_type(op, rd)); + } + + // c.zext.w + // + // This is an alias for `add.uw rd, rd, zero` + Inst::AluRRR { + alu_op: AluOPRRR::Adduw, + rd, + rs1, + rs2, + } if has_zcb + && has_zba + && rd.to_reg() == rs1 + && reg_is_compressible(rs1) + && rs2 == zero_reg() => + { + sink.put2(encode_cszn_type(CsznOp::CZextw, rd)); } _ => return None, diff --git a/cranelift/codegen/src/isa/riscv64/inst/encode.rs b/cranelift/codegen/src/isa/riscv64/inst/encode.rs index e3054bafb927..fae2f929c795 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/encode.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/encode.rs @@ -9,9 +9,9 @@ use super::*; use crate::isa::riscv64::inst::reg_to_gpr_num; use crate::isa::riscv64::lower::isle::generated_code::{ - COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, CjOp, ClOp, CrOp, CsOp, CssOp, VecAluOpRImm5, + COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, CjOp, ClOp, CrOp, CsOp, CssOp, CsznOp, VecAluOpRImm5, VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, VecAluOpRRRImm5, VecAluOpRRRR, VecElementWidth, - VecOpCategory, VecOpMasking, + VecOpCategory, VecOpMasking, ZcbMemOp, }; use crate::machinst::isle::WritableReg; use crate::Reg; @@ -604,3 +604,54 @@ fn encode_cs_cl_type_bits( bits |= unsigned_field_width(funct3, 3) << 13; bits.try_into().unwrap() } + +// Encode a CSZN type instruction. +// +// This is an additional encoding format that is introduced in the Zcb extension. +// +// 0--1-2---------6-7--------9-10------15 +// |op | funct5 | rd/rs1 | funct6 | +pub fn encode_cszn_type(op: CsznOp, rd: WritableReg) -> u16 { + let mut bits = 0; + bits |= unsigned_field_width(op.op().bits(), 2); + bits |= unsigned_field_width(op.funct5(), 5) << 2; + bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7; + bits |= unsigned_field_width(op.funct6(), 6) << 10; + bits.try_into().unwrap() +} + +// Encodes the various memory operations in the Zcb extension. +// +// 0--1-2----------4-5----------6-7---------9-10-------15 +// |op | dest/src | imm(2-bit) | base | funct6 | +fn encode_zcbmem_bits(op: ZcbMemOp, dest_src: Reg, base: Reg, imm: Uimm2) -> u16 { + let imm = imm.bits(); + + // For these ops, bit 6 is part of the opcode, and bit 5 encodes the imm offset. + let imm = match op { + ZcbMemOp::CLh | ZcbMemOp::CLhu | ZcbMemOp::CSh => { + debug_assert_eq!(imm & !1, 0); + // Only c.lh has this bit as 1 + let opcode_bit = (op == ZcbMemOp::CLh) as u8; + imm | (opcode_bit << 1) + } + // In the rest of the ops the imm is reversed. + _ => ((imm & 1) << 1) | ((imm >> 1) & 1), + }; + + let mut bits = 0; + bits |= unsigned_field_width(op.op().bits(), 2); + bits |= reg_to_compressed_gpr_num(dest_src) << 2; + bits |= unsigned_field_width(imm as u32, 2) << 5; + bits |= reg_to_compressed_gpr_num(base) << 7; + bits |= unsigned_field_width(op.funct6(), 6) << 10; + bits.try_into().unwrap() +} + +pub fn encode_zcbmem_load(op: ZcbMemOp, rd: WritableReg, base: Reg, imm: Uimm2) -> u16 { + encode_zcbmem_bits(op, rd.to_reg(), base, imm) +} + +pub fn encode_zcbmem_store(op: ZcbMemOp, src: Reg, base: Reg, imm: Uimm2) -> u16 { + encode_zcbmem_bits(op, src, base, imm) +} diff --git a/cranelift/codegen/src/isa/riscv64/inst/imms.rs b/cranelift/codegen/src/isa/riscv64/inst/imms.rs index a17edcdde291..8b6854186193 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/imms.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/imms.rs @@ -263,6 +263,34 @@ impl Display for Uimm5 { } } +/// A unsigned 2-bit immediate. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Uimm2 { + value: u8, +} + +impl Uimm2 { + /// Create an unsigned 2-bit immediate from an u8 + pub fn maybe_from_u8(value: u8) -> Option { + if value <= 3 { + Some(Self { value }) + } else { + None + } + } + + /// Bits for encoding. + pub fn bits(&self) -> u8 { + self.value & 0x3 + } +} + +impl Display for Uimm2 { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.value) + } +} + impl Inst { pub(crate) fn imm_min() -> i64 { let imm20_max: i64 = (1 << 19) << 12; diff --git a/cranelift/filetests/filetests/isa/riscv64/zcb.clif b/cranelift/filetests/filetests/isa/riscv64/zcb.clif new file mode 100644 index 000000000000..76c68310c209 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/zcb.clif @@ -0,0 +1,239 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_zca has_zcb has_zbb has_zba + +function %c_mul(i64, i64) -> i64 { +block0(v0: i64, v1: i64): + v2 = imul.i64 v0, v1 + return v2 +} + +; VCode: +; block0: +; mul a0,a0,a1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x4d, 0x9d +; c.jr ra + + + +function %c_not(i64) -> i64 { +block0(v0: i64): + v1 = bnot.i64 v0 + return v1 +} + +; VCode: +; block0: +; not a0,a0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x75, 0x9d +; c.jr ra + +function %c_zextb(i8) -> i64 { +block0(v0: i8): + v1 = uextend.i64 v0 + return v1 +} + +; VCode: +; block0: +; andi a0,a0,255 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x61, 0x9d +; c.jr ra + +function %c_zexth(i16) -> i64 { +block0(v0: i16): + v1 = uextend.i64 v0 + return v1 +} + +; VCode: +; block0: +; zext.h a0,a0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x69, 0x9d +; c.jr ra + +function %c_zextw(i32) -> i64 { +block0(v0: i32): + v1 = uextend.i64 v0 + return v1 +} + +; VCode: +; block0: +; zext.w a0,a0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x71, 0x9d +; c.jr ra + +function %c_sextb(i8) -> i64 { +block0(v0: i8): + v1 = sextend.i64 v0 + return v1 +} + +; VCode: +; block0: +; sext.b a0,a0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x65, 0x9d +; c.jr ra + +function %c_sexth(i16) -> i64 { +block0(v0: i16): + v1 = sextend.i64 v0 + return v1 +} + +; VCode: +; block0: +; sext.h a0,a0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x6d, 0x9d +; c.jr ra + + +function %c_lbu(i64) -> i16, i64 { +block0(v0: i64): + v1 = uload8.i16 v0+0 + v2 = uload8.i64 v0+3 + return v1, v2 +} + +; VCode: +; block0: +; lbu a3,0(a0) +; mv a4,a3 +; lbu a1,3(a0) +; mv a0,a4 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x14, 0x81 +; c.mv a4, a3 +; .byte 0x6c, 0x81 +; c.mv a0, a4 +; c.jr ra + +function %c_lhu(i64) -> i32, i64 { +block0(v0: i64): + v1 = uload16.i32 v0+0 + v2 = uload16.i64 v0+2 + return v1, v2 +} + +; VCode: +; block0: +; lhu a3,0(a0) +; mv a4,a3 +; lhu a1,2(a0) +; mv a0,a4 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x14, 0x85 +; c.mv a4, a3 +; .byte 0x2c, 0x85 +; c.mv a0, a4 +; c.jr ra + +function %c_lh(i64) -> i16, i16 { +block0(v0: i64): + v1 = load.i16 v0+0 + v2 = load.i16 v0+2 + return v1, v2 +} + +; VCode: +; block0: +; lh a3,0(a0) +; mv a4,a3 +; lh a1,2(a0) +; mv a0,a4 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x54, 0x85 +; c.mv a4, a3 +; .byte 0x6c, 0x85 +; c.mv a0, a4 +; c.jr ra + + +function %c_sb(i64, i8) { +block0(v0: i64, v1: i8): + store.i8 v1, v0+0 + store.i8 v1, v0+1 + store.i8 v1, v0+2 + store.i8 v1, v0+3 + store.i8 v1, v0+4 + return +} + +; VCode: +; block0: +; sb a1,0(a0) +; sb a1,1(a0) +; sb a1,2(a0) +; sb a1,3(a0) +; sb a1,4(a0) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x0c, 0x89 +; .byte 0x4c, 0x89 +; .byte 0x2c, 0x89 +; .byte 0x6c, 0x89 +; sb a1, 4(a0) +; c.jr ra + +function %c_sh(i64, i16) { +block0(v0: i64, v1: i16): + store.i16 v1, v0+0 + store.i16 v1, v0+2 + store.i16 v1, v0+3 + return +} + +; VCode: +; block0: +; sh a1,0(a0) +; sh a1,2(a0) +; sh a1,3(a0) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x0c, 0x8d +; .byte 0x2c, 0x8d +; sh a1, 3(a0) +; c.jr ra + From 56e8481b3fe1ea7cb3b789ff9d7d8b4fe39c572f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 Oct 2023 16:24:06 -0500 Subject: [PATCH 037/199] riscv64: Optimize `uadd_overflow_trap` lowering (#7132) This commit removes the usage of `gen_icmp` in `uadd_overflow_trap`. The comparison previously done with an explicit comparison is now bundled directly into the conditional branch to go to the trap itself. --- cranelift/codegen/src/isa/riscv64/lower.isle | 3 +- .../isa/riscv64/uadd_overflow_trap.clif | 53 +++++--------- ...o_spectre_i32_access_0xffff0000_offset.wat | 40 +++++------ ...no_spectre_i8_access_0xffff0000_offset.wat | 40 +++++------ ...s_spectre_i32_access_0xffff0000_offset.wat | 72 +++++++++---------- ...es_spectre_i8_access_0xffff0000_offset.wat | 72 +++++++++---------- ...o_spectre_i32_access_0xffff0000_offset.wat | 24 +++---- ...no_spectre_i8_access_0xffff0000_offset.wat | 24 +++---- ...s_spectre_i32_access_0xffff0000_offset.wat | 54 +++++++------- ...es_spectre_i8_access_0xffff0000_offset.wat | 54 +++++++------- 10 files changed, 201 insertions(+), 235 deletions(-) diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index 1cbdee196f3e..956c3bd3f9b7 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -304,8 +304,7 @@ (rule 1 (lower (has_type $I64 (uadd_overflow_trap x y tc))) (let ((tmp XReg (rv_add x y)) - (test XReg (gen_icmp (IntCC.UnsignedLessThan) tmp x $I64)) - (_ InstOutput (gen_trapnz test tc))) + (_ InstOutput (gen_trapif (IntCC.UnsignedLessThan) tmp x tc))) tmp)) ;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/cranelift/filetests/filetests/isa/riscv64/uadd_overflow_trap.clif b/cranelift/filetests/filetests/isa/riscv64/uadd_overflow_trap.clif index d0c89e0c4f23..4e8e62857b55 100644 --- a/cranelift/filetests/filetests/isa/riscv64/uadd_overflow_trap.clif +++ b/cranelift/filetests/filetests/isa/riscv64/uadd_overflow_trap.clif @@ -104,22 +104,17 @@ block0(v0: i64): ; VCode: ; block0: ; mv a1,a0 -; li a4,127 -; add a0,a1,a4 -; ult a5,a0,a1##ty=i64 -; trap_if user0##(a5 ne zero) +; li a3,127 +; add a0,a1,a3 +; trap_if user0##(a0 ult a1) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; mv a1, a0 -; addi a4, zero, 0x7f -; add a0, a1, a4 -; bgeu a0, a1, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; beqz a5, 8 +; addi a3, zero, 0x7f +; add a0, a1, a3 +; bgeu a0, a1, 8 ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: user0 ; ret @@ -132,21 +127,16 @@ block0(v0: i64): ; VCode: ; block0: -; li a4,127 -; add a0,a4,a0 -; ult a5,a0,a4##ty=i64 -; trap_if user0##(a5 ne zero) +; li a3,127 +; add a0,a3,a0 +; trap_if user0##(a0 ult a3) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a4, zero, 0x7f -; add a0, a4, a0 -; bgeu a0, a4, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; beqz a5, 8 +; addi a3, zero, 0x7f +; add a0, a3, a0 +; bgeu a0, a3, 8 ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: user0 ; ret @@ -158,23 +148,16 @@ block0(v0: i64, v1: i64): ; VCode: ; block0: -; add a3,a0,a1 -; mv a1,a3 -; ult a5,a1,a0##ty=i64 -; mv a0,a1 -; trap_if user0##(a5 ne zero) +; mv a5,a0 +; add a0,a5,a1 +; trap_if user0##(a0 ult a5) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; add a3, a0, a1 -; mv a1, a3 -; bgeu a1, a0, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; mv a0, a1 -; beqz a5, 8 +; mv a5, a0 +; add a0, a5, a1 +; bgeu a0, a5, 8 ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: user0 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index f80497f60b79..2119be200106 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,21 +41,20 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a3,a3,32 +;; slli a0,a0,32 +;; srli a3,a0,32 ;; ld a4,[const(1)] ;; add a4,a3,a4 -;; ult a5,a4,a3##ty=i64 -;; trap_if heap_oob##(a5 ne zero) +;; trap_if heap_oob##(a4 ult a3) ;; ld a5,8(a2) ;; ugt a4,a4,a5##ty=i64 ;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: ;; ld a4,0(a2) -;; add a4,a4,a3 -;; ld a5,[const(0)] -;; add a4,a4,a5 -;; sw a1,0(a4) +;; add a3,a4,a3 +;; ld a4,[const(0)] +;; add a3,a3,a4 +;; sw a1,0(a3) ;; j label2 ;; block2: ;; ret @@ -64,21 +63,20 @@ ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a3,a2,32 -;; ld a2,[const(1)] -;; add a2,a3,a2 -;; ult a4,a2,a3##ty=i64 -;; trap_if heap_oob##(a4 ne zero) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a3,[const(1)] +;; add a3,a2,a3 +;; trap_if heap_oob##(a3 ult a2) ;; ld a4,8(a1) -;; ugt a4,a2,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ugt a3,a3,a4##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a1) -;; add a4,a4,a3 -;; ld a5,[const(0)] -;; add a4,a4,a5 -;; lw a0,0(a4) +;; ld a3,0(a1) +;; add a3,a3,a2 +;; ld a4,[const(0)] +;; add a3,a3,a4 +;; lw a0,0(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index eebf3e0a9c0f..4b6858ec77de 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,21 +41,20 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a3,a3,32 +;; slli a0,a0,32 +;; srli a3,a0,32 ;; ld a4,[const(1)] ;; add a4,a3,a4 -;; ult a5,a4,a3##ty=i64 -;; trap_if heap_oob##(a5 ne zero) +;; trap_if heap_oob##(a4 ult a3) ;; ld a5,8(a2) ;; ugt a4,a4,a5##ty=i64 ;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: ;; ld a4,0(a2) -;; add a4,a4,a3 -;; ld a5,[const(0)] -;; add a4,a4,a5 -;; sb a1,0(a4) +;; add a3,a4,a3 +;; ld a4,[const(0)] +;; add a3,a3,a4 +;; sb a1,0(a3) ;; j label2 ;; block2: ;; ret @@ -64,21 +63,20 @@ ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a3,a2,32 -;; ld a2,[const(1)] -;; add a2,a3,a2 -;; ult a4,a2,a3##ty=i64 -;; trap_if heap_oob##(a4 ne zero) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a3,[const(1)] +;; add a3,a2,a3 +;; trap_if heap_oob##(a3 ult a2) ;; ld a4,8(a1) -;; ugt a4,a2,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ugt a3,a3,a4##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a1) -;; add a4,a4,a3 -;; ld a5,[const(0)] -;; add a4,a4,a5 -;; lbu a0,0(a4) +;; ld a3,0(a1) +;; add a3,a3,a2 +;; ld a4,[const(0)] +;; add a3,a3,a4 +;; lbu a0,0(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index f4b0f9c1f5b7..cc82704e94cc 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -42,25 +42,24 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,[const(1)] -;; add a3,a4,a3 -;; ult a5,a3,a4##ty=i64 -;; trap_if heap_oob##(a5 ne zero) +;; srli a3,a3,32 +;; ld a4,[const(1)] +;; add a4,a3,a4 +;; trap_if heap_oob##(a4 ult a3) ;; ld a5,8(a2) -;; ugt a5,a3,a5##ty=i64 -;; ld a0,0(a2) -;; add a4,a0,a4 -;; ld a0,[const(0)] -;; add a4,a4,a0 -;; li a0,0 -;; sltu a5,zero,a5 -;; sub a2,zero,a5 -;; and a3,a0,a2 -;; not a5,a2 -;; and a2,a4,a5 -;; or a3,a3,a2 -;; sw a1,0(a3) +;; ugt a4,a4,a5##ty=i64 +;; ld a5,0(a2) +;; add a3,a5,a3 +;; ld a5,[const(0)] +;; add a3,a3,a5 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret @@ -68,25 +67,24 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,[const(1)] -;; add a2,a4,a3 -;; ult a5,a2,a4##ty=i64 -;; trap_if heap_oob##(a5 ne zero) -;; ld a5,8(a1) -;; ugt a5,a2,a5##ty=i64 -;; ld a0,0(a1) -;; add a4,a0,a4 -;; ld a0,[const(0)] -;; add a4,a4,a0 -;; li a0,0 -;; sltu a5,zero,a5 -;; sub a1,zero,a5 -;; and a3,a0,a1 -;; not a5,a1 -;; and a1,a4,a5 -;; or a3,a3,a1 -;; lw a0,0(a3) +;; srli a3,a2,32 +;; ld a2,[const(1)] +;; add a2,a3,a2 +;; trap_if heap_oob##(a2 ult a3) +;; ld a4,8(a1) +;; ugt a4,a2,a4##ty=i64 +;; ld a5,0(a1) +;; add a3,a5,a3 +;; ld a5,[const(0)] +;; add a3,a3,a5 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; lw a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 53b3403a7f5d..d1534a8d5c5c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -42,25 +42,24 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,[const(1)] -;; add a3,a4,a3 -;; ult a5,a3,a4##ty=i64 -;; trap_if heap_oob##(a5 ne zero) +;; srli a3,a3,32 +;; ld a4,[const(1)] +;; add a4,a3,a4 +;; trap_if heap_oob##(a4 ult a3) ;; ld a5,8(a2) -;; ugt a5,a3,a5##ty=i64 -;; ld a0,0(a2) -;; add a4,a0,a4 -;; ld a0,[const(0)] -;; add a4,a4,a0 -;; li a0,0 -;; sltu a5,zero,a5 -;; sub a2,zero,a5 -;; and a3,a0,a2 -;; not a5,a2 -;; and a2,a4,a5 -;; or a3,a3,a2 -;; sb a1,0(a3) +;; ugt a4,a4,a5##ty=i64 +;; ld a5,0(a2) +;; add a3,a5,a3 +;; ld a5,[const(0)] +;; add a3,a3,a5 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret @@ -68,25 +67,24 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,[const(1)] -;; add a2,a4,a3 -;; ult a5,a2,a4##ty=i64 -;; trap_if heap_oob##(a5 ne zero) -;; ld a5,8(a1) -;; ugt a5,a2,a5##ty=i64 -;; ld a0,0(a1) -;; add a4,a0,a4 -;; ld a0,[const(0)] -;; add a4,a4,a0 -;; li a0,0 -;; sltu a5,zero,a5 -;; sub a1,zero,a5 -;; and a3,a0,a1 -;; not a5,a1 -;; and a1,a4,a5 -;; or a3,a3,a1 -;; lbu a0,0(a3) +;; srli a3,a2,32 +;; ld a2,[const(1)] +;; add a2,a3,a2 +;; trap_if heap_oob##(a2 ult a3) +;; ld a4,8(a1) +;; ugt a4,a2,a4##ty=i64 +;; ld a5,0(a1) +;; add a3,a5,a3 +;; ld a5,[const(0)] +;; add a3,a3,a5 +;; li a5,0 +;; sltu a4,zero,a4 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a0,a3,a4 +;; or a2,a2,a0 +;; lbu a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index e4263aebdc52..102a618373b2 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,10 +41,9 @@ ;; function u0:0: ;; block0: -;; ld a3,[const(1)] -;; add a5,a0,a3 -;; ult a3,a5,a0##ty=i64 -;; trap_if heap_oob##(a3 ne zero) +;; ld a5,[const(1)] +;; add a5,a0,a5 +;; trap_if heap_oob##(a5 ult a0) ;; ld a3,8(a2) ;; ugt a3,a5,a3##ty=i64 ;; bne a3,zero,taken(label3),not_taken(label1) @@ -62,19 +61,18 @@ ;; ;; function u0:1: ;; block0: -;; ld a2,[const(1)] -;; add a5,a0,a2 -;; ult a2,a5,a0##ty=i64 -;; trap_if heap_oob##(a2 ne zero) +;; ld a5,[const(1)] +;; add a5,a0,a5 +;; trap_if heap_oob##(a5 ult a0) ;; ld a2,8(a1) ;; ugt a2,a5,a2##ty=i64 ;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a1) -;; add a2,a2,a0 -;; ld a3,[const(0)] -;; add a2,a2,a3 -;; lw a0,0(a2) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; ld a2,[const(0)] +;; add a1,a1,a2 +;; lw a0,0(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index c08c2c65c62e..4f6b6c50fcd1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,10 +41,9 @@ ;; function u0:0: ;; block0: -;; ld a3,[const(1)] -;; add a5,a0,a3 -;; ult a3,a5,a0##ty=i64 -;; trap_if heap_oob##(a3 ne zero) +;; ld a5,[const(1)] +;; add a5,a0,a5 +;; trap_if heap_oob##(a5 ult a0) ;; ld a3,8(a2) ;; ugt a3,a5,a3##ty=i64 ;; bne a3,zero,taken(label3),not_taken(label1) @@ -62,19 +61,18 @@ ;; ;; function u0:1: ;; block0: -;; ld a2,[const(1)] -;; add a5,a0,a2 -;; ult a2,a5,a0##ty=i64 -;; trap_if heap_oob##(a2 ne zero) +;; ld a5,[const(1)] +;; add a5,a0,a5 +;; trap_if heap_oob##(a5 ult a0) ;; ld a2,8(a1) ;; ugt a2,a5,a2##ty=i64 ;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a1) -;; add a2,a2,a0 -;; ld a3,[const(0)] -;; add a2,a2,a3 -;; lbu a0,0(a2) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; ld a2,[const(0)] +;; add a1,a1,a2 +;; lbu a0,0(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 24bf1a08723d..b40c149f61f4 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -43,22 +43,21 @@ ;; block0: ;; ld a3,[const(1)] ;; add a3,a0,a3 -;; ult a4,a3,a0##ty=i64 -;; trap_if heap_oob##(a4 ne zero) +;; trap_if heap_oob##(a3 ult a0) ;; ld a4,8(a2) -;; ugt a3,a3,a4##ty=i64 +;; ugt a4,a3,a4##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a0 -;; ld a4,[const(0)] -;; add a2,a2,a4 -;; li a4,0 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a3,a4,a5 -;; not a4,a5 -;; and a5,a2,a4 -;; or a2,a3,a5 -;; sw a1,0(a2) +;; ld a3,[const(0)] +;; add a3,a2,a3 +;; li a2,0 +;; sltu a4,zero,a4 +;; sub a4,zero,a4 +;; and a0,a2,a4 +;; not a2,a4 +;; and a4,a3,a2 +;; or a0,a0,a4 +;; sw a1,0(a0) ;; j label1 ;; block1: ;; ret @@ -67,22 +66,21 @@ ;; block0: ;; ld a2,[const(1)] ;; add a2,a0,a2 -;; ult a3,a2,a0##ty=i64 -;; trap_if heap_oob##(a3 ne zero) +;; trap_if heap_oob##(a2 ult a0) ;; ld a3,8(a1) -;; ugt a2,a2,a3##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; ld a4,[const(0)] -;; add a3,a3,a4 -;; li a4,0 -;; sltu a5,zero,a2 -;; sub a5,zero,a5 -;; and a1,a4,a5 -;; not a4,a5 -;; and a5,a3,a4 -;; or a1,a1,a5 -;; lw a0,0(a1) +;; ugt a3,a2,a3##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a0 +;; ld a2,[const(0)] +;; add a2,a1,a2 +;; li a1,0 +;; sltu a3,zero,a3 +;; sub a4,zero,a3 +;; and a0,a1,a4 +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lw a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 400fb3f98a9b..8d7247439ce2 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -43,22 +43,21 @@ ;; block0: ;; ld a3,[const(1)] ;; add a3,a0,a3 -;; ult a4,a3,a0##ty=i64 -;; trap_if heap_oob##(a4 ne zero) +;; trap_if heap_oob##(a3 ult a0) ;; ld a4,8(a2) -;; ugt a3,a3,a4##ty=i64 +;; ugt a4,a3,a4##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a0 -;; ld a4,[const(0)] -;; add a2,a2,a4 -;; li a4,0 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a3,a4,a5 -;; not a4,a5 -;; and a5,a2,a4 -;; or a2,a3,a5 -;; sb a1,0(a2) +;; ld a3,[const(0)] +;; add a3,a2,a3 +;; li a2,0 +;; sltu a4,zero,a4 +;; sub a4,zero,a4 +;; and a0,a2,a4 +;; not a2,a4 +;; and a4,a3,a2 +;; or a0,a0,a4 +;; sb a1,0(a0) ;; j label1 ;; block1: ;; ret @@ -67,22 +66,21 @@ ;; block0: ;; ld a2,[const(1)] ;; add a2,a0,a2 -;; ult a3,a2,a0##ty=i64 -;; trap_if heap_oob##(a3 ne zero) +;; trap_if heap_oob##(a2 ult a0) ;; ld a3,8(a1) -;; ugt a2,a2,a3##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; ld a4,[const(0)] -;; add a3,a3,a4 -;; li a4,0 -;; sltu a5,zero,a2 -;; sub a5,zero,a5 -;; and a1,a4,a5 -;; not a4,a5 -;; and a5,a3,a4 -;; or a1,a1,a5 -;; lbu a0,0(a1) +;; ugt a3,a2,a3##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a0 +;; ld a2,[const(0)] +;; add a2,a1,a2 +;; li a1,0 +;; sltu a3,zero,a3 +;; sub a4,zero,a3 +;; and a0,a1,a4 +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lbu a0,0(a0) ;; j label1 ;; block1: ;; ret From 1246a2466ec5a3e6809b0995746061786f6d10cf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 Oct 2023 18:21:29 -0500 Subject: [PATCH 038/199] riscv64: Add another pattern for loading constants (#7131) Currently any 32-bit constant can be materialized without a load from a constant pool on RISCV-64 but once constants start getting larger than this they're always loaded from the constant pool. This commit adds another special case for loading constants which appears to match what LLVM does which is to consider materializing a smaller constant and than shifting it left. This is done by chopping off all trailing zeros from an immediate and then testing if the immediate can be materialized as a 32-bit constant. This means that the current constant loading sequence can optionally be followed by a trailing `slli` instruction to shift the zeros back into the constant. This namely means that loading `i64::MIN` (1 << 63) no longer falls back to the constant pool. --- cranelift/codegen/src/isa/riscv64/inst.isle | 15 +- cranelift/codegen/src/isle_prelude.rs | 8 + cranelift/codegen/src/prelude.isle | 6 + .../filetests/isa/riscv64/constants.clif | 75 +- .../filetests/isa/riscv64/issue-6954.clif | 1046 ++++++++--------- .../filetests/isa/riscv64/return-call.clif | 18 +- .../filetests/isa/riscv64/simd-ceil.clif | 54 +- .../filetests/isa/riscv64/simd-floor.clif | 54 +- .../filetests/isa/riscv64/simd-fmax.clif | 27 +- .../filetests/isa/riscv64/simd-fmin.clif | 27 +- .../filetests/isa/riscv64/simd-nearest.clif | 54 +- .../filetests/isa/riscv64/simd-trunc.clif | 46 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 60 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 54 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 80 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 76 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 42 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 42 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 62 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 62 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 52 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 44 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 68 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 58 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 36 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 36 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 56 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 56 +- 28 files changed, 1205 insertions(+), 1109 deletions(-) diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 2cf04b6032c0..4ed696761e3f 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -1753,8 +1753,21 @@ (if-let (i64_generate_imm imm20 imm12) (i64_sextend_u64 ty c)) (rv_addi (rv_lui imm20) imm12)) -;; Otherwise we fall back to loading the immediate from the constant pool. +;; Combine one of the above rules with a shift-left if possible, This chops off +;; all trailing zeros from the input constant and then attempts if the resulting +;; constant can itself use one of the above rules via the `i64_generate_imm` +;; matcher. This will then recurse on the above rules to materialize a smaller +;; constant which is then shifted left to create the desired constant. (rule 0 (imm (ty_int ty) c) + (if-let c_extended (i64_sextend_u64 ty c)) ;; constant to make + (if-let shamt (i64_ctz c_extended)) ;; number of trailing zeros to chop + (if-let c_shifted (i64_shr c_extended shamt)) ;; smaller constant + (if-let (i64_generate_imm _ _) c_shifted) ;; can the smaller constant be made? + (if-let (imm12_from_i64 shimm) shamt) ;; create the shift immediate + (rv_slli (imm ty (i64_as_u64 c_shifted)) shimm)) + +;; Otherwise we fall back to loading the immediate from the constant pool. +(rule -1 (imm (ty_int ty) c) (gen_load (gen_const_amode (emit_u64_le_const c)) (LoadOP.Ld) diff --git a/cranelift/codegen/src/isle_prelude.rs b/cranelift/codegen/src/isle_prelude.rs index 043595160d4a..71c45fd329b1 100644 --- a/cranelift/codegen/src/isle_prelude.rs +++ b/cranelift/codegen/src/isle_prelude.rs @@ -169,6 +169,14 @@ macro_rules! isle_common_prelude_methods { x & 1 == 1 } + fn i64_shr(&mut self, a: i64, b: i64) -> i64 { + a >> b + } + + fn i64_ctz(&mut self, a: i64) -> i64 { + a.trailing_zeros().into() + } + #[inline] fn i64_sextend_u64(&mut self, ty: Type, x: u64) -> i64 { let shift_amt = std::cmp::max(0, 64 - ty.bits()); diff --git a/cranelift/codegen/src/prelude.isle b/cranelift/codegen/src/prelude.isle index 8dbd8044e3a3..d9f561bc2fc2 100644 --- a/cranelift/codegen/src/prelude.isle +++ b/cranelift/codegen/src/prelude.isle @@ -188,6 +188,12 @@ (decl pure u64_lt (u64 u64) bool) (extern constructor u64_lt u64_lt) +(decl pure i64_shr (i64 i64) i64) +(extern constructor i64_shr i64_shr) + +(decl pure i64_ctz (i64) i64) +(extern constructor i64_ctz i64_ctz) + ;; Sign extends a u64 from ty bits up to 64bits (decl pure i64_sextend_u64 (Type u64) i64) (extern constructor i64_sextend_u64 i64_sextend_u64) diff --git a/cranelift/filetests/filetests/isa/riscv64/constants.clif b/cranelift/filetests/filetests/isa/riscv64/constants.clif index 5ede28732228..134304fbe233 100644 --- a/cranelift/filetests/filetests/isa/riscv64/constants.clif +++ b/cranelift/filetests/filetests/isa/riscv64/constants.clif @@ -76,17 +76,17 @@ block0: ; VCode: ; block0: -; ld a0,[const(0)] +; lui a0,16 +; addi a2,a0,-1 +; slli a0,a2,16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; auipc a0, 0 -; ld a0, 0x10(a0) +; lui a0, 0x10 +; addi a2, a0, -1 +; slli a0, a2, 0x10 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0xff, 0xff -; .byte 0x00, 0x00, 0x00, 0x00 function %f() -> i64 { block0: @@ -96,17 +96,17 @@ block0: ; VCode: ; block0: -; ld a0,[const(0)] +; lui a0,16 +; addi a2,a0,-1 +; slli a0,a2,32 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; auipc a0, 0 -; ld a0, 0x10(a0) +; lui a0, 0x10 +; addi a2, a0, -1 +; slli a0, a2, 0x20 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0xff, 0xff, 0x00, 0x00 function %f() -> i64 { block0: @@ -116,17 +116,15 @@ block0: ; VCode: ; block0: -; ld a0,[const(0)] +; li a0,-1 +; slli a0,a0,48 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; auipc a0, 0 -; ld a0, 0x10(a0) +; addi a0, zero, -1 +; slli a0, a0, 0x30 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0xff, 0xff function %f() -> i64 { block0: @@ -356,18 +354,17 @@ block0: ; VCode: ; block0: -; ld a0,[const(0)] -; fmv.d.x fa0,a0 +; li a0,1023 +; slli a2,a0,52 +; fmv.d.x fa0,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; auipc a0, 0 -; ld a0, 0x10(a0) -; fmv.d.x fa0, a0 +; addi a0, zero, 0x3ff +; slli a2, a0, 0x34 +; fmv.d.x fa0, a2 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0xf0, 0x3f function %f() -> f32 { block0: @@ -395,18 +392,19 @@ block0: ; VCode: ; block0: -; ld a0,[const(0)] -; fmv.d.x fa0,a0 +; lui a0,4 +; addi a2,a0,73 +; slli a4,a2,48 +; fmv.d.x fa0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; auipc a0, 0 -; ld a0, 0x10(a0) -; fmv.d.x fa0, a0 +; lui a0, 4 +; addi a2, a0, 0x49 +; slli a4, a2, 0x30 +; fmv.d.x fa0, a4 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x49, 0x40 function %f() -> f32 { block0: @@ -470,18 +468,17 @@ block0: ; VCode: ; block0: -; ld a0,[const(0)] -; fmv.d.x fa0,a0 +; li a0,-1021 +; slli a2,a0,52 +; fmv.d.x fa0,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; auipc a0, 0 -; ld a0, 0x10(a0) -; fmv.d.x fa0, a0 +; addi a0, zero, -0x3fd +; slli a2, a0, 0x34 +; fmv.d.x fa0, a2 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x30, 0xc0 function %f() -> f32 { block0: diff --git a/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif b/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif index 44a45b85236e..26699b9e534c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif +++ b/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif @@ -129,191 +129,193 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; vle8.v v15,32(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v10,48(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v12,64(fp) #avl=16, #vtype=(e8, m1, ta, ma) +; li a2,0 ; li a3,0 ; li a4,0 ; li a0,0 -; li a2,0 -; sd a0,0(nominal_sp) -; sd a2,8(nominal_sp) -; sd a0,16(nominal_sp) -; sd a2,24(nominal_sp) -; sd a0,32(nominal_sp) -; sd a2,40(nominal_sp) -; sd a0,48(nominal_sp) -; sd a2,56(nominal_sp) -; sd a0,64(nominal_sp) -; sd a2,72(nominal_sp) -; sd a0,80(nominal_sp) -; sd a2,88(nominal_sp) -; sd a0,96(nominal_sp) -; sd a2,104(nominal_sp) -; sd a0,112(nominal_sp) -; sw a4,120(nominal_sp) -; sh a3,124(nominal_sp) -; sd a0,128(nominal_sp) -; sd a2,136(nominal_sp) -; sd a0,144(nominal_sp) -; sd a2,152(nominal_sp) -; sd a0,160(nominal_sp) -; sd a2,168(nominal_sp) -; sd a0,176(nominal_sp) -; sd a2,184(nominal_sp) -; sd a0,192(nominal_sp) -; sd a2,200(nominal_sp) -; sd a0,208(nominal_sp) -; sd a2,216(nominal_sp) -; sd a0,224(nominal_sp) -; sd a2,232(nominal_sp) -; sd a0,240(nominal_sp) -; sw a4,248(nominal_sp) -; sh a3,252(nominal_sp) -; sd a0,256(nominal_sp) -; sd a2,264(nominal_sp) -; sd a0,272(nominal_sp) -; sd a2,280(nominal_sp) -; sd a0,288(nominal_sp) -; sd a2,296(nominal_sp) -; sd a0,304(nominal_sp) -; sd a2,312(nominal_sp) -; sd a0,320(nominal_sp) -; sd a2,328(nominal_sp) -; sd a0,336(nominal_sp) -; sd a2,344(nominal_sp) -; sd a0,352(nominal_sp) -; sd a2,360(nominal_sp) -; sd a0,368(nominal_sp) -; sw a4,376(nominal_sp) -; sh a3,380(nominal_sp) -; zext.w a0,a1 -; select_i16x8 v9,v12,v12##condition=a0 -; zext.w a0,a1 -; select_i16x8 v12,v9,v9##condition=a0 -; zext.w a0,a1 -; select_i16x8 v10,v12,v12##condition=a0 -; vfsqrt.v v8,v11 #avl=2, #vtype=(e64, m1, ta, ma) -; ld a0,[const(0)] +; sd a4,0(nominal_sp) +; sd a0,8(nominal_sp) +; sd a4,16(nominal_sp) +; sd a0,24(nominal_sp) +; sd a4,32(nominal_sp) +; sd a0,40(nominal_sp) +; sd a4,48(nominal_sp) +; sd a0,56(nominal_sp) +; sd a4,64(nominal_sp) +; sd a0,72(nominal_sp) +; sd a4,80(nominal_sp) +; sd a0,88(nominal_sp) +; sd a4,96(nominal_sp) +; sd a0,104(nominal_sp) +; sd a4,112(nominal_sp) +; sw a3,120(nominal_sp) +; sh a2,124(nominal_sp) +; sd a4,128(nominal_sp) +; sd a0,136(nominal_sp) +; sd a4,144(nominal_sp) +; sd a0,152(nominal_sp) +; sd a4,160(nominal_sp) +; sd a0,168(nominal_sp) +; sd a4,176(nominal_sp) +; sd a0,184(nominal_sp) +; sd a4,192(nominal_sp) +; sd a0,200(nominal_sp) +; sd a4,208(nominal_sp) +; sd a0,216(nominal_sp) +; sd a4,224(nominal_sp) +; sd a0,232(nominal_sp) +; sd a4,240(nominal_sp) +; sw a3,248(nominal_sp) +; sh a2,252(nominal_sp) +; sd a4,256(nominal_sp) +; sd a0,264(nominal_sp) +; sd a4,272(nominal_sp) +; sd a0,280(nominal_sp) +; sd a4,288(nominal_sp) +; sd a0,296(nominal_sp) +; sd a4,304(nominal_sp) +; sd a0,312(nominal_sp) +; sd a4,320(nominal_sp) +; sd a0,328(nominal_sp) +; sd a4,336(nominal_sp) +; sd a0,344(nominal_sp) +; sd a4,352(nominal_sp) +; sd a0,360(nominal_sp) +; sd a4,368(nominal_sp) +; sw a3,376(nominal_sp) +; sh a2,380(nominal_sp) +; zext.w a3,a1 +; select_i16x8 v13,v12,v12##condition=a3 +; zext.w a3,a1 +; select_i16x8 v8,v13,v13##condition=a3 +; zext.w a3,a1 +; select_i16x8 v14,v8,v8##condition=a3 +; vfsqrt.v v12,v11 #avl=2, #vtype=(e64, m1, ta, ma) +; lui a2,1 +; addi a3,a2,-1 +; slli a0,a3,51 ; fmv.d.x fa1,a0 -; vfmv.v.f v9,fa1 #avl=2, #vtype=(e64, m1, ta, ma) -; vmfne.vv v0,v8,v8 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v11,v8,v9,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vfsqrt.v v8,v11 #avl=2, #vtype=(e64, m1, ta, ma) -; ld a0,[const(0)] +; vfmv.v.f v13,fa1 #avl=2, #vtype=(e64, m1, ta, ma) +; vmfne.vv v0,v12,v12 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v8,v12,v13,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vfsqrt.v v12,v8 #avl=2, #vtype=(e64, m1, ta, ma) +; lui a2,1 +; addi a3,a2,-1 +; slli a0,a3,51 ; fmv.d.x fa1,a0 -; vfmv.v.f v11,fa1 #avl=2, #vtype=(e64, m1, ta, ma) -; vmfne.vv v0,v8,v8 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v9,v8,v11,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a0,a1 -; select_i16x8 v11,v10,v10##condition=a0 -; zext.w a0,a1 -; select_i16x8 v10,v11,v11##condition=a0 -; zext.w a0,a1 -; select_i16x8 v11,v10,v10##condition=a0 -; zext.w a0,a1 -; select_i16x8 v10,v11,v11##condition=a0 -; zext.w a0,a1 -; select_i16x8 v11,v10,v10##condition=a0 -; zext.w a0,a1 -; select_i16x8 v10,v11,v11##condition=a0 -; zext.w a0,a1 -; select_i16x8 v11,v10,v10##condition=a0 -; zext.w a0,a1 -; select_i16x8 v10,v11,v11##condition=a0 -; add a0,a1,a1 -; zext.w a1,a0 -; select_i16x8 v11,v10,v10##condition=a1 -; zext.w a1,a0 -; select_i16x8 v10,v11,v11##condition=a1 -; zext.w a1,a0 -; select_i16x8 v11,v10,v10##condition=a1 -; zext.w a1,a0 -; select_i16x8 v10,v11,v11##condition=a1 -; zext.w a1,a0 -; select_i16x8 v11,v10,v10##condition=a1 -; vmax.vv v8,v15,v15 #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a1,a0 -; select_i16x8 v10,v11,v11##condition=a1 -; load_addr a1,3(nominal_sp) -; addi a1,a1,0 -; andi a4,a1,3 -; slli a2,a4,3 -; andi a3,a1,-4 -; atomic_rmw.i8 and a4,a5,(a3)##t0=a1 offset=a2 -; mv a1,a4 -; zext.w a4,a0 -; select_i16x8 v11,v10,v10##condition=a4 -; zext.w a4,a0 -; select_i16x8 v10,v11,v11##condition=a4 -; zext.w a4,a0 -; select_i16x8 v11,v10,v10##condition=a4 -; zext.w a4,a0 -; select_i16x8 v10,v11,v11##condition=a4 -; zext.w a4,a0 -; select_i16x8 v11,v10,v10##condition=a4 -; zext.w a4,a0 -; select_i16x8 v12,v11,v11##condition=a4 -; zext.w a4,a0 -; select_i16x8 v10,v12,v12##condition=a4 -; vse64.v v8,33(nominal_sp) #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; zext.w a5,a0 -; select_i16x8 v11,v10,v10##condition=a5 -; zext.w a5,a0 -; select_i16x8 v10,v11,v11##condition=a5 -; vse8.v v9,0(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v10,16(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v9,32(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v10,48(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v10,64(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v10,80(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v10,96(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; mv a0,a1 +; vfmv.v.f v8,fa1 #avl=2, #vtype=(e64, m1, ta, ma) +; vmfne.vv v0,v12,v12 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v13,v12,v8,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; zext.w a3,a1 +; select_i16x8 v8,v14,v14##condition=a3 +; zext.w a3,a1 +; select_i16x8 v14,v8,v8##condition=a3 +; zext.w a3,a1 +; select_i16x8 v8,v14,v14##condition=a3 +; zext.w a3,a1 +; select_i16x8 v14,v8,v8##condition=a3 +; zext.w a3,a1 +; select_i16x8 v8,v14,v14##condition=a3 +; zext.w a3,a1 +; select_i16x8 v14,v8,v8##condition=a3 +; zext.w a3,a1 +; select_i16x8 v8,v14,v14##condition=a3 +; zext.w a3,a1 +; select_i16x8 v14,v8,v8##condition=a3 +; add a4,a1,a1 +; zext.w a3,a4 +; select_i16x8 v8,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v8,v8##condition=a3 +; zext.w a3,a4 +; select_i16x8 v8,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v8,v8##condition=a3 +; zext.w a3,a4 +; select_i16x8 v8,v14,v14##condition=a3 +; vmax.vv v12,v15,v15 #avl=2, #vtype=(e64, m1, ta, ma) +; zext.w a3,a4 +; select_i16x8 v14,v8,v8##condition=a3 +; load_addr a0,3(nominal_sp) +; addi a0,a0,0 +; andi a2,a0,3 +; slli a1,a2,3 +; andi a2,a0,-4 +; atomic_rmw.i8 and a0,a5,(a2)##t0=a3 offset=a1 +; zext.w a2,a4 +; select_i16x8 v15,v14,v14##condition=a2 +; zext.w a2,a4 +; select_i16x8 v14,v15,v15##condition=a2 +; zext.w a2,a4 +; select_i16x8 v15,v14,v14##condition=a2 +; zext.w a2,a4 +; select_i16x8 v14,v15,v15##condition=a2 +; zext.w a2,a4 +; select_i16x8 v15,v14,v14##condition=a2 +; zext.w a2,a4 +; select_i16x8 v8,v15,v15##condition=a2 +; zext.w a2,a4 +; select_i16x8 v14,v8,v8##condition=a2 +; vse64.v v12,33(nominal_sp) #avl=2, #vtype=(e64, m1, ta, ma) +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; zext.w a3,a4 +; select_i16x8 v15,v14,v14##condition=a3 +; zext.w a3,a4 +; select_i16x8 v14,v15,v15##condition=a3 +; vse8.v v13,0(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v14,16(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v13,32(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v14,48(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v14,64(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v14,80(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v14,96(a6) #avl=16, #vtype=(e8, m1, ta, ma) ; add sp,+384 ; ld ra,8(sp) ; ld fp,0(sp) @@ -337,370 +339,368 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; .byte 0x07, 0x85, 0x0f, 0x02 ; addi t6, s0, 0x40 ; .byte 0x07, 0x86, 0x0f, 0x02 +; mv a2, zero ; mv a3, zero ; mv a4, zero ; mv a0, zero -; mv a2, zero -; sd a0, 0(sp) -; sd a2, 8(sp) -; sd a0, 0x10(sp) -; sd a2, 0x18(sp) -; sd a0, 0x20(sp) -; sd a2, 0x28(sp) -; sd a0, 0x30(sp) -; sd a2, 0x38(sp) -; sd a0, 0x40(sp) -; sd a2, 0x48(sp) -; sd a0, 0x50(sp) -; sd a2, 0x58(sp) -; sd a0, 0x60(sp) -; sd a2, 0x68(sp) -; sd a0, 0x70(sp) -; sw a4, 0x78(sp) -; sh a3, 0x7c(sp) -; sd a0, 0x80(sp) -; sd a2, 0x88(sp) -; sd a0, 0x90(sp) -; sd a2, 0x98(sp) -; sd a0, 0xa0(sp) -; sd a2, 0xa8(sp) -; sd a0, 0xb0(sp) -; sd a2, 0xb8(sp) -; sd a0, 0xc0(sp) -; sd a2, 0xc8(sp) -; sd a0, 0xd0(sp) -; sd a2, 0xd8(sp) -; sd a0, 0xe0(sp) -; sd a2, 0xe8(sp) -; sd a0, 0xf0(sp) -; sw a4, 0xf8(sp) -; sh a3, 0xfc(sp) -; sd a0, 0x100(sp) -; sd a2, 0x108(sp) -; sd a0, 0x110(sp) -; sd a2, 0x118(sp) -; sd a0, 0x120(sp) -; sd a2, 0x128(sp) -; sd a0, 0x130(sp) -; sd a2, 0x138(sp) -; sd a0, 0x140(sp) -; sd a2, 0x148(sp) -; sd a0, 0x150(sp) -; sd a2, 0x158(sp) -; sd a0, 0x160(sp) -; sd a2, 0x168(sp) -; sd a0, 0x170(sp) -; sw a4, 0x178(sp) -; sh a3, 0x17c(sp) -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0xd7, 0x34, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x34, 0xc0, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0x57, 0x36, 0x90, 0x9e -; j 8 -; .byte 0x57, 0x36, 0x90, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0x57, 0x35, 0xc0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xc0, 0x9e +; sd a4, 0(sp) +; sd a0, 8(sp) +; sd a4, 0x10(sp) +; sd a0, 0x18(sp) +; sd a4, 0x20(sp) +; sd a0, 0x28(sp) +; sd a4, 0x30(sp) +; sd a0, 0x38(sp) +; sd a4, 0x40(sp) +; sd a0, 0x48(sp) +; sd a4, 0x50(sp) +; sd a0, 0x58(sp) +; sd a4, 0x60(sp) +; sd a0, 0x68(sp) +; sd a4, 0x70(sp) +; sw a3, 0x78(sp) +; sh a2, 0x7c(sp) +; sd a4, 0x80(sp) +; sd a0, 0x88(sp) +; sd a4, 0x90(sp) +; sd a0, 0x98(sp) +; sd a4, 0xa0(sp) +; sd a0, 0xa8(sp) +; sd a4, 0xb0(sp) +; sd a0, 0xb8(sp) +; sd a4, 0xc0(sp) +; sd a0, 0xc8(sp) +; sd a4, 0xd0(sp) +; sd a0, 0xd8(sp) +; sd a4, 0xe0(sp) +; sd a0, 0xe8(sp) +; sd a4, 0xf0(sp) +; sw a3, 0xf8(sp) +; sh a2, 0xfc(sp) +; sd a4, 0x100(sp) +; sd a0, 0x108(sp) +; sd a4, 0x110(sp) +; sd a0, 0x118(sp) +; sd a4, 0x120(sp) +; sd a0, 0x128(sp) +; sd a4, 0x130(sp) +; sd a0, 0x138(sp) +; sd a4, 0x140(sp) +; sd a0, 0x148(sp) +; sd a4, 0x150(sp) +; sd a0, 0x158(sp) +; sd a4, 0x160(sp) +; sd a0, 0x168(sp) +; sd a4, 0x170(sp) +; sw a3, 0x178(sp) +; sh a2, 0x17c(sp) +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x34, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x34, 0xd0, 0x9e +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0x80, 0x9e +; j 8 +; .byte 0x57, 0x37, 0x80, 0x9e ; .byte 0x57, 0x70, 0x81, 0xcd -; .byte 0x57, 0x14, 0xb0, 0x4e -; auipc a0, 0 -; ld a0, 0x490(a0) +; .byte 0x57, 0x16, 0xb0, 0x4e +; lui a2, 1 +; addi a3, a2, -1 +; slli a0, a3, 0x33 ; fmv.d.x fa1, a0 -; .byte 0xd7, 0xd4, 0x05, 0x5e -; .byte 0x57, 0x10, 0x84, 0x72 -; .byte 0xd7, 0x85, 0x84, 0x5c -; .byte 0x57, 0x14, 0xb0, 0x4e -; auipc a0, 0 -; ld a0, 0x474(a0) +; .byte 0xd7, 0xd6, 0x05, 0x5e +; .byte 0x57, 0x10, 0xc6, 0x72 +; .byte 0x57, 0x84, 0xc6, 0x5c +; .byte 0x57, 0x16, 0x80, 0x4e +; lui a2, 1 +; addi a3, a2, -1 +; slli a0, a3, 0x33 ; fmv.d.x fa1, a0 -; .byte 0xd7, 0xd5, 0x05, 0x5e -; .byte 0x57, 0x10, 0x84, 0x72 -; .byte 0xd7, 0x84, 0x85, 0x5c -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0x3b, 0x85, 0x05, 0x08 -; beqz a0, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; add a0, a1, a1 -; .byte 0xbb, 0x05, 0x05, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x05, 0x05, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x05, 0x05, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x05, 0x05, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x05, 0x05, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0x57, 0x84, 0xf7, 0x1e -; .byte 0xbb, 0x05, 0x05, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; addi a1, sp, 3 -; mv a1, a1 -; andi a4, a1, 3 -; slli a2, a4, 3 -; andi a3, a1, -4 -; lr.w.aqrl a4, (a3) -; srl a4, a4, a2 -; andi a4, a4, 0xff -; and a1, a4, a5 -; lr.w.aqrl t5, (a3) +; .byte 0x57, 0xd4, 0x05, 0x5e +; .byte 0x57, 0x10, 0xc6, 0x72 +; .byte 0xd7, 0x06, 0xc4, 0x5c +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x34, 0xe0, 0x9e +; j 8 +; .byte 0x57, 0x34, 0xe0, 0x9e +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0x80, 0x9e +; j 8 +; .byte 0x57, 0x37, 0x80, 0x9e +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x34, 0xe0, 0x9e +; j 8 +; .byte 0x57, 0x34, 0xe0, 0x9e +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0x80, 0x9e +; j 8 +; .byte 0x57, 0x37, 0x80, 0x9e +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x34, 0xe0, 0x9e +; j 8 +; .byte 0x57, 0x34, 0xe0, 0x9e +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0x80, 0x9e +; j 8 +; .byte 0x57, 0x37, 0x80, 0x9e +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x34, 0xe0, 0x9e +; j 8 +; .byte 0x57, 0x34, 0xe0, 0x9e +; .byte 0xbb, 0x86, 0x05, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0x80, 0x9e +; j 8 +; .byte 0x57, 0x37, 0x80, 0x9e +; add a4, a1, a1 +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x34, 0xe0, 0x9e +; j 8 +; .byte 0x57, 0x34, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0x80, 0x9e +; j 8 +; .byte 0x57, 0x37, 0x80, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x34, 0xe0, 0x9e +; j 8 +; .byte 0x57, 0x34, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0x80, 0x9e +; j 8 +; .byte 0x57, 0x37, 0x80, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x34, 0xe0, 0x9e +; j 8 +; .byte 0x57, 0x34, 0xe0, 0x9e +; .byte 0x57, 0x86, 0xf7, 0x1e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0x80, 0x9e +; j 8 +; .byte 0x57, 0x37, 0x80, 0x9e +; addi a0, sp, 3 +; mv a0, a0 +; andi a2, a0, 3 +; slli a1, a2, 3 +; andi a2, a0, -4 +; lr.w.aqrl a0, (a2) +; srl a0, a0, a1 +; andi a0, a0, 0xff +; and a3, a0, a5 +; lr.w.aqrl t5, (a2) ; addi t6, zero, 0xff -; sll t6, t6, a2 +; sll t6, t6, a1 ; not t6, t6 ; and t5, t5, t6 -; andi t6, a1, 0xff -; sll t6, t6, a2 +; andi t6, a3, 0xff +; sll t6, t6, a1 ; or t5, t5, t6 -; sc.w.aqrl a1, t5, (a3) -; bnez a1, -0x34 -; mv a1, a4 -; .byte 0x3b, 0x07, 0x05, 0x08 -; beqz a4, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0x3b, 0x07, 0x05, 0x08 -; beqz a4, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0x3b, 0x07, 0x05, 0x08 -; beqz a4, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0x3b, 0x07, 0x05, 0x08 -; beqz a4, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0x3b, 0x07, 0x05, 0x08 -; beqz a4, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0x3b, 0x07, 0x05, 0x08 -; beqz a4, 0xc -; .byte 0x57, 0x36, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xb0, 0x9e -; .byte 0x3b, 0x07, 0x05, 0x08 -; beqz a4, 0xc -; .byte 0x57, 0x35, 0xc0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xc0, 0x9e +; sc.w.aqrl a3, t5, (a2) +; bnez a3, -0x34 +; .byte 0x3b, 0x06, 0x07, 0x08 +; beqz a2, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0x3b, 0x06, 0x07, 0x08 +; beqz a2, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0x3b, 0x06, 0x07, 0x08 +; beqz a2, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0x3b, 0x06, 0x07, 0x08 +; beqz a2, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0x3b, 0x06, 0x07, 0x08 +; beqz a2, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0x3b, 0x06, 0x07, 0x08 +; beqz a2, 0xc +; .byte 0x57, 0x34, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x34, 0xf0, 0x9e +; .byte 0x3b, 0x06, 0x07, 0x08 +; beqz a2, 0xc +; .byte 0x57, 0x37, 0x80, 0x9e +; j 8 +; .byte 0x57, 0x37, 0x80, 0x9e ; addi t6, sp, 0x21 -; .byte 0x27, 0xf4, 0x0f, 0x02 -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e -; j 8 -; .byte 0xd7, 0x35, 0xa0, 0x9e -; .byte 0xbb, 0x07, 0x05, 0x08 -; beqz a5, 0xc -; .byte 0x57, 0x35, 0xb0, 0x9e -; j 8 -; .byte 0x57, 0x35, 0xb0, 0x9e +; .byte 0x27, 0xf6, 0x0f, 0x02 +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0xd7, 0x37, 0xe0, 0x9e +; j 8 +; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x06, 0x07, 0x08 +; beqz a3, 0xc +; .byte 0x57, 0x37, 0xf0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xf0, 0x9e ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0xa7, 0x04, 0x08, 0x02 +; .byte 0xa7, 0x06, 0x08, 0x02 ; addi t6, a6, 0x10 -; .byte 0x27, 0x85, 0x0f, 0x02 +; .byte 0x27, 0x87, 0x0f, 0x02 ; addi t6, a6, 0x20 -; .byte 0xa7, 0x84, 0x0f, 0x02 +; .byte 0xa7, 0x86, 0x0f, 0x02 ; addi t6, a6, 0x30 -; .byte 0x27, 0x85, 0x0f, 0x02 +; .byte 0x27, 0x87, 0x0f, 0x02 ; addi t6, a6, 0x40 -; .byte 0x27, 0x85, 0x0f, 0x02 +; .byte 0x27, 0x87, 0x0f, 0x02 ; addi t6, a6, 0x50 -; .byte 0x27, 0x85, 0x0f, 0x02 +; .byte 0x27, 0x87, 0x0f, 0x02 ; addi t6, a6, 0x60 -; .byte 0x27, 0x85, 0x0f, 0x02 -; mv a0, a1 +; .byte 0x27, 0x87, 0x0f, 0x02 ; addi sp, sp, 0x180 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0xf8, 0x7f diff --git a/cranelift/filetests/filetests/isa/riscv64/return-call.clif b/cranelift/filetests/filetests/isa/riscv64/return-call.clif index 123f6dc0a395..4bd0b8b51dba 100644 --- a/cranelift/filetests/filetests/isa/riscv64/return-call.clif +++ b/cranelift/filetests/filetests/isa/riscv64/return-call.clif @@ -96,21 +96,19 @@ block0(v0: f64): ; VCode: ; block0: -; ld a3,[const(0)] -; fmv.d.x fa5,a3 -; fadd.d ft0,ft0,fa5 +; li a3,1027 +; slli a5,a3,52 +; fmv.d.x fa1,a5 +; fadd.d ft0,ft0,fa1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; auipc a3, 0 -; ld a3, 0x18(a3) -; fmv.d.x fa5, a3 -; fadd.d ft0, ft0, fa5 +; addi a3, zero, 0x403 +; slli a5, a3, 0x34 +; fmv.d.x fa1, a5 +; fadd.d ft0, ft0, fa1 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x30, 0x40 function %call_f64(f64) -> f64 tail { fn0 = %callee_f64(f64) -> f64 tail diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-ceil.clif b/cranelift/filetests/filetests/isa/riscv64/simd-ceil.clif index c6984a03561c..d670a1f7a7b4 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-ceil.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-ceil.clif @@ -78,18 +78,19 @@ block0(v0: f64x2): ; block0: ; vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vfabs.v v12,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; ld a1,[const(0)] -; fmv.d.x fa2,a1 -; vmflt.vf v0,v12,fa2 #avl=2, #vtype=(e64, m1, ta, ma) -; fsrmi a1,3 -; vfcvt.x.f.v v14,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; fsrm a1 -; vfcvt.f.x.v v10,v14 #avl=2, #vtype=(e64, m1, ta, ma) -; vfsgnj.vv v11,v10,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; fmv.d.x fa3,zero -; vfadd.vf v15,v9,fa3 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v9,v15,v11,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vse8.v v9,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) +; li a1,1075 +; slli a2,a1,52 +; fmv.d.x fa4,a2 +; vmflt.vf v0,v12,fa4 #avl=2, #vtype=(e64, m1, ta, ma) +; fsrmi a2,3 +; vfcvt.x.f.v v8,v9 #avl=2, #vtype=(e64, m1, ta, ma) +; fsrm a2 +; vfcvt.f.x.v v11,v8 #avl=2, #vtype=(e64, m1, ta, ma) +; vfsgnj.vv v13,v11,v9 #avl=2, #vtype=(e64, m1, ta, ma) +; fmv.d.x fa5,zero +; vfadd.vf v9,v9,fa5 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v11,v9,v13,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vse8.v v11,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -107,25 +108,22 @@ block0(v0: f64x2): ; .byte 0x87, 0x84, 0x0f, 0x02 ; .byte 0x57, 0x70, 0x81, 0xcd ; .byte 0x57, 0x96, 0x94, 0x2a -; auipc a1, 0 -; ld a1, 0x4c(a1) -; fmv.d.x fa2, a1 -; .byte 0x57, 0x50, 0xc6, 0x6e -; fsrmi a1, 3 -; .byte 0x57, 0x97, 0x90, 0x4a -; fsrm a1 -; .byte 0x57, 0x95, 0xe1, 0x4a -; .byte 0xd7, 0x95, 0xa4, 0x22 -; fmv.d.x fa3, zero -; .byte 0xd7, 0xd7, 0x96, 0x02 -; .byte 0xd7, 0x84, 0xf5, 0x5c +; addi a1, zero, 0x433 +; slli a2, a1, 0x34 +; fmv.d.x fa4, a2 +; .byte 0x57, 0x50, 0xc7, 0x6e +; fsrmi a2, 3 +; .byte 0x57, 0x94, 0x90, 0x4a +; fsrm a2 +; .byte 0xd7, 0x95, 0x81, 0x4a +; .byte 0xd7, 0x96, 0xb4, 0x22 +; fmv.d.x fa5, zero +; .byte 0xd7, 0xd4, 0x97, 0x02 +; .byte 0xd7, 0x85, 0x96, 0x5c ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0xa7, 0x04, 0x05, 0x02 +; .byte 0xa7, 0x05, 0x05, 0x02 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x30, 0x43 diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-floor.clif b/cranelift/filetests/filetests/isa/riscv64/simd-floor.clif index 3f577bcc0908..428074cfef9a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-floor.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-floor.clif @@ -78,18 +78,19 @@ block0(v0: f64x2): ; block0: ; vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vfabs.v v12,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; ld a1,[const(0)] -; fmv.d.x fa2,a1 -; vmflt.vf v0,v12,fa2 #avl=2, #vtype=(e64, m1, ta, ma) -; fsrmi a1,2 -; vfcvt.x.f.v v14,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; fsrm a1 -; vfcvt.f.x.v v10,v14 #avl=2, #vtype=(e64, m1, ta, ma) -; vfsgnj.vv v11,v10,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; fmv.d.x fa3,zero -; vfadd.vf v15,v9,fa3 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v9,v15,v11,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vse8.v v9,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) +; li a1,1075 +; slli a2,a1,52 +; fmv.d.x fa4,a2 +; vmflt.vf v0,v12,fa4 #avl=2, #vtype=(e64, m1, ta, ma) +; fsrmi a2,2 +; vfcvt.x.f.v v8,v9 #avl=2, #vtype=(e64, m1, ta, ma) +; fsrm a2 +; vfcvt.f.x.v v11,v8 #avl=2, #vtype=(e64, m1, ta, ma) +; vfsgnj.vv v13,v11,v9 #avl=2, #vtype=(e64, m1, ta, ma) +; fmv.d.x fa5,zero +; vfadd.vf v9,v9,fa5 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v11,v9,v13,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vse8.v v11,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -107,25 +108,22 @@ block0(v0: f64x2): ; .byte 0x87, 0x84, 0x0f, 0x02 ; .byte 0x57, 0x70, 0x81, 0xcd ; .byte 0x57, 0x96, 0x94, 0x2a -; auipc a1, 0 -; ld a1, 0x4c(a1) -; fmv.d.x fa2, a1 -; .byte 0x57, 0x50, 0xc6, 0x6e -; fsrmi a1, 2 -; .byte 0x57, 0x97, 0x90, 0x4a -; fsrm a1 -; .byte 0x57, 0x95, 0xe1, 0x4a -; .byte 0xd7, 0x95, 0xa4, 0x22 -; fmv.d.x fa3, zero -; .byte 0xd7, 0xd7, 0x96, 0x02 -; .byte 0xd7, 0x84, 0xf5, 0x5c +; addi a1, zero, 0x433 +; slli a2, a1, 0x34 +; fmv.d.x fa4, a2 +; .byte 0x57, 0x50, 0xc7, 0x6e +; fsrmi a2, 2 +; .byte 0x57, 0x94, 0x90, 0x4a +; fsrm a2 +; .byte 0xd7, 0x95, 0x81, 0x4a +; .byte 0xd7, 0x96, 0xb4, 0x22 +; fmv.d.x fa5, zero +; .byte 0xd7, 0xd4, 0x97, 0x02 +; .byte 0xd7, 0x85, 0x96, 0x5c ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0xa7, 0x04, 0x05, 0x02 +; .byte 0xa7, 0x05, 0x05, 0x02 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x30, 0x43 diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-fmax.clif b/cranelift/filetests/filetests/isa/riscv64/simd-fmax.clif index 8280e98eee98..59b26d22f560 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-fmax.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-fmax.clif @@ -19,11 +19,13 @@ block0(v0: f64x2, v1: f64x2): ; vmfeq.vv v14,v9,v9 #avl=2, #vtype=(e64, m1, ta, ma) ; vmfeq.vv v8,v11,v11 #avl=2, #vtype=(e64, m1, ta, ma) ; vmand.mm v0,v14,v8 #avl=2, #vtype=(e64, m1, ta, ma) -; ld a1,[const(0)] -; vmv.v.x v14,a1 #avl=2, #vtype=(e64, m1, ta, ma) -; vfmax.vv v8,v9,v11 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v10,v14,v8,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vse8.v v10,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) +; lui a1,1 +; addi a2,a1,-1 +; slli a4,a2,51 +; vmv.v.x v10,a4 #avl=2, #vtype=(e64, m1, ta, ma) +; vfmax.vv v12,v9,v11 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v14,v10,v12,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vse8.v v14,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -45,19 +47,18 @@ block0(v0: f64x2, v1: f64x2): ; .byte 0x57, 0x97, 0x94, 0x62 ; .byte 0x57, 0x94, 0xb5, 0x62 ; .byte 0x57, 0x20, 0xe4, 0x66 -; auipc a1, 0 -; ld a1, 0x2c(a1) -; .byte 0x57, 0xc7, 0x05, 0x5e -; .byte 0x57, 0x94, 0x95, 0x1a -; .byte 0x57, 0x05, 0xe4, 0x5c +; lui a1, 1 +; addi a2, a1, -1 +; slli a4, a2, 0x33 +; .byte 0x57, 0x45, 0x07, 0x5e +; .byte 0x57, 0x96, 0x95, 0x1a +; .byte 0x57, 0x07, 0xa6, 0x5c ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0x27, 0x05, 0x05, 0x02 +; .byte 0x27, 0x07, 0x05, 0x02 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0xf8, 0x7f function %fmax_f32x4(f32x4, f32x4) -> f32x4 { block0(v0: f32x4, v1: f32x4): diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-fmin.clif b/cranelift/filetests/filetests/isa/riscv64/simd-fmin.clif index c802bf396652..bfc30b597565 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-fmin.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-fmin.clif @@ -19,11 +19,13 @@ block0(v0: f64x2, v1: f64x2): ; vmfeq.vv v14,v9,v9 #avl=2, #vtype=(e64, m1, ta, ma) ; vmfeq.vv v8,v11,v11 #avl=2, #vtype=(e64, m1, ta, ma) ; vmand.mm v0,v14,v8 #avl=2, #vtype=(e64, m1, ta, ma) -; ld a1,[const(0)] -; vmv.v.x v14,a1 #avl=2, #vtype=(e64, m1, ta, ma) -; vfmin.vv v8,v9,v11 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v10,v14,v8,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vse8.v v10,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) +; lui a1,1 +; addi a2,a1,-1 +; slli a4,a2,51 +; vmv.v.x v10,a4 #avl=2, #vtype=(e64, m1, ta, ma) +; vfmin.vv v12,v9,v11 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v14,v10,v12,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vse8.v v14,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -45,19 +47,18 @@ block0(v0: f64x2, v1: f64x2): ; .byte 0x57, 0x97, 0x94, 0x62 ; .byte 0x57, 0x94, 0xb5, 0x62 ; .byte 0x57, 0x20, 0xe4, 0x66 -; auipc a1, 0 -; ld a1, 0x2c(a1) -; .byte 0x57, 0xc7, 0x05, 0x5e -; .byte 0x57, 0x94, 0x95, 0x12 -; .byte 0x57, 0x05, 0xe4, 0x5c +; lui a1, 1 +; addi a2, a1, -1 +; slli a4, a2, 0x33 +; .byte 0x57, 0x45, 0x07, 0x5e +; .byte 0x57, 0x96, 0x95, 0x12 +; .byte 0x57, 0x07, 0xa6, 0x5c ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0x27, 0x05, 0x05, 0x02 +; .byte 0x27, 0x07, 0x05, 0x02 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0xf8, 0x7f function %fmin_f32x4(f32x4, f32x4) -> f32x4 { block0(v0: f32x4, v1: f32x4): diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-nearest.clif b/cranelift/filetests/filetests/isa/riscv64/simd-nearest.clif index 63edad3aa02f..91c73441220d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-nearest.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-nearest.clif @@ -78,18 +78,19 @@ block0(v0: f64x2): ; block0: ; vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vfabs.v v12,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; ld a1,[const(0)] -; fmv.d.x fa2,a1 -; vmflt.vf v0,v12,fa2 #avl=2, #vtype=(e64, m1, ta, ma) -; fsrmi a1,0 -; vfcvt.x.f.v v14,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; fsrm a1 -; vfcvt.f.x.v v10,v14 #avl=2, #vtype=(e64, m1, ta, ma) -; vfsgnj.vv v11,v10,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; fmv.d.x fa3,zero -; vfadd.vf v15,v9,fa3 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v9,v15,v11,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vse8.v v9,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) +; li a1,1075 +; slli a2,a1,52 +; fmv.d.x fa4,a2 +; vmflt.vf v0,v12,fa4 #avl=2, #vtype=(e64, m1, ta, ma) +; fsrmi a2,0 +; vfcvt.x.f.v v8,v9 #avl=2, #vtype=(e64, m1, ta, ma) +; fsrm a2 +; vfcvt.f.x.v v11,v8 #avl=2, #vtype=(e64, m1, ta, ma) +; vfsgnj.vv v13,v11,v9 #avl=2, #vtype=(e64, m1, ta, ma) +; fmv.d.x fa5,zero +; vfadd.vf v9,v9,fa5 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v11,v9,v13,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vse8.v v11,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -107,25 +108,22 @@ block0(v0: f64x2): ; .byte 0x87, 0x84, 0x0f, 0x02 ; .byte 0x57, 0x70, 0x81, 0xcd ; .byte 0x57, 0x96, 0x94, 0x2a -; auipc a1, 0 -; ld a1, 0x4c(a1) -; fmv.d.x fa2, a1 -; .byte 0x57, 0x50, 0xc6, 0x6e -; fsrmi a1, 0 -; .byte 0x57, 0x97, 0x90, 0x4a -; fsrm a1 -; .byte 0x57, 0x95, 0xe1, 0x4a -; .byte 0xd7, 0x95, 0xa4, 0x22 -; fmv.d.x fa3, zero -; .byte 0xd7, 0xd7, 0x96, 0x02 -; .byte 0xd7, 0x84, 0xf5, 0x5c +; addi a1, zero, 0x433 +; slli a2, a1, 0x34 +; fmv.d.x fa4, a2 +; .byte 0x57, 0x50, 0xc7, 0x6e +; fsrmi a2, 0 +; .byte 0x57, 0x94, 0x90, 0x4a +; fsrm a2 +; .byte 0xd7, 0x95, 0x81, 0x4a +; .byte 0xd7, 0x96, 0xb4, 0x22 +; fmv.d.x fa5, zero +; .byte 0xd7, 0xd4, 0x97, 0x02 +; .byte 0xd7, 0x85, 0x96, 0x5c ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0xa7, 0x04, 0x05, 0x02 +; .byte 0xa7, 0x05, 0x05, 0x02 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x30, 0x43 diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-trunc.clif b/cranelift/filetests/filetests/isa/riscv64/simd-trunc.clif index 91671a7a2b2c..c01191a000dd 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-trunc.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-trunc.clif @@ -74,16 +74,17 @@ block0(v0: f64x2): ; block0: ; vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vfabs.v v12,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; ld a1,[const(0)] -; fmv.d.x fa2,a1 -; vmflt.vf v0,v12,fa2 #avl=2, #vtype=(e64, m1, ta, ma) -; vfcvt.rtz.x.f.v v12,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; vfcvt.f.x.v v14,v12 #avl=2, #vtype=(e64, m1, ta, ma) -; vfsgnj.vv v8,v14,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; fmv.d.x fa0,zero -; vfadd.vf v12,v9,fa0 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v14,v12,v8,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vse8.v v14,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) +; li a1,1075 +; slli a2,a1,52 +; fmv.d.x fa4,a2 +; vmflt.vf v0,v12,fa4 #avl=2, #vtype=(e64, m1, ta, ma) +; vfcvt.rtz.x.f.v v14,v9 #avl=2, #vtype=(e64, m1, ta, ma) +; vfcvt.f.x.v v8,v14 #avl=2, #vtype=(e64, m1, ta, ma) +; vfsgnj.vv v10,v8,v9 #avl=2, #vtype=(e64, m1, ta, ma) +; fmv.d.x fa2,zero +; vfadd.vf v14,v9,fa2 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v8,v14,v10,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vse8.v v8,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -101,23 +102,20 @@ block0(v0: f64x2): ; .byte 0x87, 0x84, 0x0f, 0x02 ; .byte 0x57, 0x70, 0x81, 0xcd ; .byte 0x57, 0x96, 0x94, 0x2a -; auipc a1, 0 -; ld a1, 0x44(a1) -; fmv.d.x fa2, a1 -; .byte 0x57, 0x50, 0xc6, 0x6e -; .byte 0x57, 0x96, 0x93, 0x4a -; .byte 0x57, 0x97, 0xc1, 0x4a -; .byte 0x57, 0x94, 0xe4, 0x22 -; fmv.d.x fa0, zero -; .byte 0x57, 0x56, 0x95, 0x02 -; .byte 0x57, 0x07, 0xc4, 0x5c +; addi a1, zero, 0x433 +; slli a2, a1, 0x34 +; fmv.d.x fa4, a2 +; .byte 0x57, 0x50, 0xc7, 0x6e +; .byte 0x57, 0x97, 0x93, 0x4a +; .byte 0x57, 0x94, 0xe1, 0x4a +; .byte 0x57, 0x95, 0x84, 0x22 +; fmv.d.x fa2, zero +; .byte 0x57, 0x57, 0x96, 0x02 +; .byte 0x57, 0x04, 0xe5, 0x5c ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0x27, 0x07, 0x05, 0x02 +; .byte 0x27, 0x04, 0x05, 0x02 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 ; ret -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x00, 0x00 -; .byte 0x00, 0x00, 0x30, 0x43 diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index 2119be200106..38145b79f095 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,20 +41,24 @@ ;; function u0:0: ;; block0: -;; slli a0,a0,32 -;; srli a3,a0,32 -;; ld a4,[const(1)] -;; add a4,a3,a4 -;; trap_if heap_oob##(a4 ult a3) -;; ld a5,8(a2) -;; ugt a4,a4,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; lui a3,262140 +;; addi a5,a3,1 +;; slli a3,a5,2 +;; add a5,a0,a3 +;; trap_if heap_oob##(a5 ult a0) +;; ld a3,8(a2) +;; ugt a3,a5,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a2) -;; add a3,a4,a3 -;; ld a4,[const(0)] -;; add a3,a3,a4 -;; sw a1,0(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; lui a5,16 +;; addi a3,a5,-1 +;; slli a3,a3,16 +;; add a2,a2,a3 +;; sw a1,0(a2) ;; j label2 ;; block2: ;; ret @@ -63,20 +67,24 @@ ;; ;; function u0:1: ;; block0: -;; slli a0,a0,32 -;; srli a2,a0,32 -;; ld a3,[const(1)] -;; add a3,a2,a3 -;; trap_if heap_oob##(a3 ult a2) -;; ld a4,8(a1) -;; ugt a3,a3,a4##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; lui a3,262140 +;; addi a5,a3,1 +;; slli a2,a5,2 +;; add a5,a0,a2 +;; trap_if heap_oob##(a5 ult a0) +;; ld a2,8(a1) +;; ugt a2,a5,a2##ty=i64 +;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a2 -;; ld a4,[const(0)] -;; add a3,a3,a4 -;; lw a0,0(a3) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lui a5,16 +;; addi a2,a5,-1 +;; slli a3,a2,16 +;; add a1,a1,a3 +;; lw a0,0(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index 4b6858ec77de..408082d57c2d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,20 +41,22 @@ ;; function u0:0: ;; block0: -;; slli a0,a0,32 -;; srli a3,a0,32 -;; ld a4,[const(1)] -;; add a4,a3,a4 -;; trap_if heap_oob##(a4 ult a3) +;; slli a3,a0,32 +;; srli a4,a3,32 +;; ld a3,[const(0)] +;; add a3,a4,a3 +;; trap_if heap_oob##(a3 ult a4) ;; ld a5,8(a2) -;; ugt a4,a4,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ugt a5,a3,a5##ty=i64 +;; bne a5,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a2) -;; add a3,a4,a3 -;; ld a4,[const(0)] -;; add a3,a3,a4 -;; sb a1,0(a3) +;; ld a5,0(a2) +;; add a5,a5,a4 +;; lui a3,16 +;; addi a0,a3,-1 +;; slli a2,a0,16 +;; add a5,a5,a2 +;; sb a1,0(a5) ;; j label2 ;; block2: ;; ret @@ -63,20 +65,22 @@ ;; ;; function u0:1: ;; block0: -;; slli a0,a0,32 -;; srli a2,a0,32 -;; ld a3,[const(1)] -;; add a3,a2,a3 -;; trap_if heap_oob##(a3 ult a2) -;; ld a4,8(a1) -;; ugt a3,a3,a4##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,[const(0)] +;; add a3,a4,a3 +;; trap_if heap_oob##(a3 ult a4) +;; ld a5,8(a1) +;; ugt a5,a3,a5##ty=i64 +;; bne a5,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a2 -;; ld a4,[const(0)] -;; add a3,a3,a4 -;; lbu a0,0(a3) +;; ld a5,0(a1) +;; add a5,a5,a4 +;; lui a3,16 +;; addi a0,a3,-1 +;; slli a1,a0,16 +;; add a5,a5,a1 +;; lbu a0,0(a5) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index cc82704e94cc..d700b9af1c0b 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,50 +41,58 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a3,a3,32 -;; ld a4,[const(1)] -;; add a4,a3,a4 -;; trap_if heap_oob##(a4 ult a3) -;; ld a5,8(a2) -;; ugt a4,a4,a5##ty=i64 -;; ld a5,0(a2) -;; add a3,a5,a3 -;; ld a5,[const(0)] -;; add a3,a3,a5 -;; li a5,0 +;; slli a5,a0,32 +;; srli a3,a5,32 +;; lui a4,262140 +;; addi a0,a4,1 +;; slli a4,a0,2 +;; add a0,a3,a4 +;; trap_if heap_oob##(a0 ult a3) +;; ld a4,8(a2) +;; ugt a4,a0,a4##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a3 +;; lui a5,16 +;; addi a3,a5,-1 +;; slli a3,a3,16 +;; add a2,a2,a3 +;; li a3,0 ;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 +;; sub a4,zero,a4 ;; and a0,a3,a4 -;; or a2,a2,a0 -;; sw a1,0(a2) +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; sw a1,0(a0) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a3,a2,32 -;; ld a2,[const(1)] -;; add a2,a3,a2 -;; trap_if heap_oob##(a2 ult a3) -;; ld a4,8(a1) -;; ugt a4,a2,a4##ty=i64 -;; ld a5,0(a1) -;; add a3,a5,a3 -;; ld a5,[const(0)] -;; add a3,a3,a5 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; lw a0,0(a2) +;; slli a5,a0,32 +;; srli a2,a5,32 +;; lui a4,262140 +;; addi a0,a4,1 +;; slli a3,a0,2 +;; add a0,a2,a3 +;; trap_if heap_oob##(a0 ult a2) +;; ld a3,8(a1) +;; ugt a3,a0,a3##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lui a5,16 +;; addi a2,a5,-1 +;; slli a4,a2,16 +;; add a1,a1,a4 +;; li a2,0 +;; sltu a3,zero,a3 +;; sub a4,zero,a3 +;; and a0,a2,a4 +;; not a2,a4 +;; and a4,a1,a2 +;; or a0,a0,a4 +;; lw a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index d1534a8d5c5c..8d7d44804966 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -42,49 +42,53 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a3,a3,32 -;; ld a4,[const(1)] -;; add a4,a3,a4 -;; trap_if heap_oob##(a4 ult a3) +;; srli a0,a3,32 +;; ld a4,[const(0)] +;; add a4,a0,a4 +;; trap_if heap_oob##(a4 ult a0) ;; ld a5,8(a2) -;; ugt a4,a4,a5##ty=i64 -;; ld a5,0(a2) -;; add a3,a5,a3 -;; ld a5,[const(0)] -;; add a3,a3,a5 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; sb a1,0(a2) +;; ugt a5,a4,a5##ty=i64 +;; ld a2,0(a2) +;; add a0,a2,a0 +;; lui a3,16 +;; addi a2,a3,-1 +;; slli a2,a2,16 +;; add a0,a0,a2 +;; li a2,0 +;; sltu a3,zero,a5 +;; sub a3,zero,a3 +;; and a4,a2,a3 +;; not a2,a3 +;; and a2,a0,a2 +;; or a4,a4,a2 +;; sb a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a3,a2,32 -;; ld a2,[const(1)] -;; add a2,a3,a2 -;; trap_if heap_oob##(a2 ult a3) -;; ld a4,8(a1) -;; ugt a4,a2,a4##ty=i64 -;; ld a5,0(a1) -;; add a3,a5,a3 -;; ld a5,[const(0)] -;; add a3,a3,a5 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; lbu a0,0(a2) +;; slli a3,a0,32 +;; srli a0,a3,32 +;; ld a4,[const(0)] +;; add a4,a0,a4 +;; trap_if heap_oob##(a4 ult a0) +;; ld a5,8(a1) +;; ugt a5,a4,a5##ty=i64 +;; ld a1,0(a1) +;; add a0,a1,a0 +;; lui a3,16 +;; addi a1,a3,-1 +;; slli a1,a1,16 +;; add a0,a0,a1 +;; li a1,0 +;; sltu a2,zero,a5 +;; sub a2,zero,a2 +;; and a4,a1,a2 +;; not a1,a2 +;; and a2,a0,a1 +;; or a4,a4,a2 +;; lbu a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index 6277594d6667..cc76ff36e440 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,17 +41,19 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a2) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; slli a0,a0,32 +;; srli a3,a0,32 +;; ld a4,8(a2) +;; ugt a4,a3,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) -;; add a0,a2,a0 -;; ld a2,[const(0)] -;; add a0,a0,a2 -;; sw a1,0(a0) +;; add a2,a2,a3 +;; lui a0,16 +;; addi a3,a0,-1 +;; slli a4,a3,16 +;; add a2,a2,a4 +;; sw a1,0(a2) ;; j label2 ;; block2: ;; ret @@ -60,17 +62,19 @@ ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a1) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a3,8(a1) +;; ugt a3,a2,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a0,a1,a0 -;; ld a1,[const(0)] -;; add a0,a0,a1 -;; lw a0,0(a0) +;; ld a3,0(a1) +;; add a2,a3,a2 +;; lui a0,16 +;; addi a3,a0,-1 +;; slli a4,a3,16 +;; add a2,a2,a4 +;; lw a0,0(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index 03fb970a0bbe..3ba5874a87e5 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,17 +41,19 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a2) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; slli a0,a0,32 +;; srli a3,a0,32 +;; ld a4,8(a2) +;; ugt a4,a3,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) -;; add a0,a2,a0 -;; ld a2,[const(0)] -;; add a0,a0,a2 -;; sb a1,0(a0) +;; add a2,a2,a3 +;; lui a0,16 +;; addi a3,a0,-1 +;; slli a4,a3,16 +;; add a2,a2,a4 +;; sb a1,0(a2) ;; j label2 ;; block2: ;; ret @@ -60,17 +62,19 @@ ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a1) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a3,8(a1) +;; ugt a3,a2,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a0,a1,a0 -;; ld a1,[const(0)] -;; add a0,a0,a1 -;; lbu a0,0(a0) +;; ld a3,0(a1) +;; add a2,a3,a2 +;; lui a0,16 +;; addi a3,a0,-1 +;; slli a4,a3,16 +;; add a2,a2,a4 +;; lbu a0,0(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 1a0c58f33706..224265ada872 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,44 +41,48 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; ld a0,8(a2) -;; ugt a0,a3,a0##ty=i64 +;; slli a3,a0,32 +;; srli a3,a3,32 +;; ld a4,8(a2) +;; ugt a4,a3,a4##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a3 -;; ld a3,[const(0)] -;; add a2,a2,a3 -;; li a3,0 -;; sltu a4,zero,a0 -;; sub a4,zero,a4 +;; lui a0,16 +;; addi a3,a0,-1 +;; slli a5,a3,16 +;; add a3,a2,a5 +;; li a2,0 +;; sltu a4,zero,a4 +;; sub a5,zero,a4 +;; and a2,a2,a5 +;; not a4,a5 ;; and a5,a3,a4 -;; not a3,a4 -;; and a3,a2,a3 -;; or a5,a5,a3 -;; sw a1,0(a5) +;; or a2,a2,a5 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; ld a0,8(a1) -;; ugt a0,a2,a0##ty=i64 -;; ld a1,0(a1) -;; add a1,a1,a2 -;; ld a2,[const(0)] -;; add a1,a1,a2 -;; li a2,0 -;; sltu a3,zero,a0 -;; sub a3,zero,a3 -;; and a5,a2,a3 -;; not a2,a3 -;; and a3,a1,a2 -;; or a5,a5,a3 -;; lw a0,0(a5) +;; slli a2,a0,32 +;; srli a3,a2,32 +;; ld a2,8(a1) +;; ugt a2,a3,a2##ty=i64 +;; ld a4,0(a1) +;; add a3,a4,a3 +;; lui a0,16 +;; addi a4,a0,-1 +;; slli a4,a4,16 +;; add a3,a3,a4 +;; li a4,0 +;; sltu a5,zero,a2 +;; sub a5,zero,a5 +;; and a1,a4,a5 +;; not a4,a5 +;; and a5,a3,a4 +;; or a1,a1,a5 +;; lw a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index e62b2b14f127..fdad050d73ef 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -41,44 +41,48 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; ld a0,8(a2) -;; ugt a0,a3,a0##ty=i64 +;; slli a3,a0,32 +;; srli a3,a3,32 +;; ld a4,8(a2) +;; ugt a4,a3,a4##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a3 -;; ld a3,[const(0)] -;; add a2,a2,a3 -;; li a3,0 -;; sltu a4,zero,a0 -;; sub a4,zero,a4 +;; lui a0,16 +;; addi a3,a0,-1 +;; slli a5,a3,16 +;; add a3,a2,a5 +;; li a2,0 +;; sltu a4,zero,a4 +;; sub a5,zero,a4 +;; and a2,a2,a5 +;; not a4,a5 ;; and a5,a3,a4 -;; not a3,a4 -;; and a3,a2,a3 -;; or a5,a5,a3 -;; sb a1,0(a5) +;; or a2,a2,a5 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; ld a0,8(a1) -;; ugt a0,a2,a0##ty=i64 -;; ld a1,0(a1) -;; add a1,a1,a2 -;; ld a2,[const(0)] -;; add a1,a1,a2 -;; li a2,0 -;; sltu a3,zero,a0 -;; sub a3,zero,a3 -;; and a5,a2,a3 -;; not a2,a3 -;; and a3,a1,a2 -;; or a5,a5,a3 -;; lbu a0,0(a5) +;; slli a2,a0,32 +;; srli a3,a2,32 +;; ld a2,8(a1) +;; ugt a2,a3,a2##ty=i64 +;; ld a4,0(a1) +;; add a3,a4,a3 +;; lui a0,16 +;; addi a4,a0,-1 +;; slli a4,a4,16 +;; add a3,a3,a4 +;; li a4,0 +;; sltu a5,zero,a2 +;; sub a5,zero,a5 +;; and a1,a4,a5 +;; not a4,a5 +;; and a5,a3,a4 +;; or a1,a1,a5 +;; lbu a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index 102a618373b2..9e4821f9c73c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,18 +41,22 @@ ;; function u0:0: ;; block0: -;; ld a5,[const(1)] -;; add a5,a0,a5 -;; trap_if heap_oob##(a5 ult a0) -;; ld a3,8(a2) -;; ugt a3,a5,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; lui a3,262140 +;; addi a3,a3,1 +;; slli a5,a3,2 +;; add a3,a0,a5 +;; trap_if heap_oob##(a3 ult a0) +;; ld a4,8(a2) +;; ugt a4,a3,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a2,a2,a0 -;; ld a3,[const(0)] -;; add a2,a2,a3 -;; sw a1,0(a2) +;; ld a5,0(a2) +;; add a5,a5,a0 +;; lui a3,16 +;; addi a0,a3,-1 +;; slli a2,a0,16 +;; add a5,a5,a2 +;; sw a1,0(a5) ;; j label2 ;; block2: ;; ret @@ -61,18 +65,22 @@ ;; ;; function u0:1: ;; block0: -;; ld a5,[const(1)] -;; add a5,a0,a5 -;; trap_if heap_oob##(a5 ult a0) -;; ld a2,8(a1) -;; ugt a2,a5,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; lui a2,262140 +;; addi a3,a2,1 +;; slli a5,a3,2 +;; add a3,a0,a5 +;; trap_if heap_oob##(a3 ult a0) +;; ld a4,8(a1) +;; ugt a4,a3,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a1,a1,a0 -;; ld a2,[const(0)] -;; add a1,a1,a2 -;; lw a0,0(a1) +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lui a3,16 +;; addi a0,a3,-1 +;; slli a1,a0,16 +;; add a5,a5,a1 +;; lw a0,0(a5) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index 4f6b6c50fcd1..de2dd33e71c2 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,18 +41,20 @@ ;; function u0:0: ;; block0: -;; ld a5,[const(1)] -;; add a5,a0,a5 -;; trap_if heap_oob##(a5 ult a0) -;; ld a3,8(a2) -;; ugt a3,a5,a3##ty=i64 +;; ld a3,[const(0)] +;; add a3,a0,a3 +;; trap_if heap_oob##(a3 ult a0) +;; ld a4,8(a2) +;; ugt a3,a3,a4##ty=i64 ;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a2,a2,a0 -;; ld a3,[const(0)] -;; add a2,a2,a3 -;; sb a1,0(a2) +;; ld a3,0(a2) +;; add a3,a3,a0 +;; lui a2,16 +;; addi a4,a2,-1 +;; slli a5,a4,16 +;; add a3,a3,a5 +;; sb a1,0(a3) ;; j label2 ;; block2: ;; ret @@ -61,18 +63,20 @@ ;; ;; function u0:1: ;; block0: -;; ld a5,[const(1)] -;; add a5,a0,a5 -;; trap_if heap_oob##(a5 ult a0) -;; ld a2,8(a1) -;; ugt a2,a5,a2##ty=i64 +;; ld a2,[const(0)] +;; add a2,a0,a2 +;; trap_if heap_oob##(a2 ult a0) +;; ld a3,8(a1) +;; ugt a2,a2,a3##ty=i64 ;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a1,a1,a0 -;; ld a2,[const(0)] -;; add a1,a1,a2 -;; lbu a0,0(a1) +;; ld a3,0(a1) +;; add a3,a3,a0 +;; lui a1,16 +;; addi a4,a1,-1 +;; slli a5,a4,16 +;; add a3,a3,a5 +;; lbu a0,0(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index b40c149f61f4..8c63b06241f6 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,46 +41,54 @@ ;; function u0:0: ;; block0: -;; ld a3,[const(1)] -;; add a3,a0,a3 -;; trap_if heap_oob##(a3 ult a0) -;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 +;; lui a3,262140 +;; addi a4,a3,1 +;; slli a3,a4,2 +;; add a4,a0,a3 +;; trap_if heap_oob##(a4 ult a0) +;; ld a5,8(a2) +;; ugt a5,a4,a5##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a0 -;; ld a3,[const(0)] -;; add a3,a2,a3 +;; add a0,a2,a0 +;; lui a3,16 +;; addi a2,a3,-1 +;; slli a2,a2,16 +;; add a0,a0,a2 ;; li a2,0 -;; sltu a4,zero,a4 -;; sub a4,zero,a4 -;; and a0,a2,a4 -;; not a2,a4 -;; and a4,a3,a2 -;; or a0,a0,a4 -;; sw a1,0(a0) +;; sltu a3,zero,a5 +;; sub a3,zero,a3 +;; and a4,a2,a3 +;; not a2,a3 +;; and a2,a0,a2 +;; or a4,a4,a2 +;; sw a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a2,[const(1)] -;; add a2,a0,a2 -;; trap_if heap_oob##(a2 ult a0) -;; ld a3,8(a1) -;; ugt a3,a2,a3##ty=i64 +;; lui a2,262140 +;; addi a4,a2,1 +;; slli a2,a4,2 +;; add a4,a0,a2 +;; trap_if heap_oob##(a4 ult a0) +;; ld a5,8(a1) +;; ugt a5,a4,a5##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a0 -;; ld a2,[const(0)] -;; add a2,a1,a2 +;; add a0,a1,a0 +;; lui a3,16 +;; addi a1,a3,-1 +;; slli a1,a1,16 +;; add a0,a0,a1 ;; li a1,0 -;; sltu a3,zero,a3 -;; sub a4,zero,a3 -;; and a0,a1,a4 -;; not a3,a4 -;; and a4,a2,a3 -;; or a0,a0,a4 -;; lw a0,0(a0) +;; sltu a2,zero,a5 +;; sub a2,zero,a2 +;; and a4,a1,a2 +;; not a1,a2 +;; and a2,a0,a1 +;; or a4,a4,a2 +;; lw a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 8d7247439ce2..a56579e418b2 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -41,46 +41,50 @@ ;; function u0:0: ;; block0: -;; ld a3,[const(1)] +;; ld a3,[const(0)] ;; add a3,a0,a3 ;; trap_if heap_oob##(a3 ult a0) ;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 -;; ld a2,0(a2) -;; add a2,a2,a0 -;; ld a3,[const(0)] -;; add a3,a2,a3 -;; li a2,0 -;; sltu a4,zero,a4 -;; sub a4,zero,a4 -;; and a0,a2,a4 -;; not a2,a4 -;; and a4,a3,a2 -;; or a0,a0,a4 -;; sb a1,0(a0) +;; ugt a3,a3,a4##ty=i64 +;; ld a4,0(a2) +;; add a4,a4,a0 +;; lui a2,16 +;; addi a5,a2,-1 +;; slli a5,a5,16 +;; add a4,a4,a5 +;; li a5,0 +;; sltu a0,zero,a3 +;; sub a0,zero,a0 +;; and a2,a5,a0 +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a2,[const(1)] +;; ld a2,[const(0)] ;; add a2,a0,a2 ;; trap_if heap_oob##(a2 ult a0) ;; ld a3,8(a1) ;; ugt a3,a2,a3##ty=i64 -;; ld a1,0(a1) -;; add a1,a1,a0 -;; ld a2,[const(0)] -;; add a2,a1,a2 -;; li a1,0 -;; sltu a3,zero,a3 -;; sub a4,zero,a3 -;; and a0,a1,a4 -;; not a3,a4 -;; and a4,a2,a3 -;; or a0,a0,a4 -;; lbu a0,0(a0) +;; ld a4,0(a1) +;; add a4,a4,a0 +;; lui a1,16 +;; addi a5,a1,-1 +;; slli a5,a5,16 +;; add a4,a4,a5 +;; li a5,0 +;; sltu a0,zero,a3 +;; sub a0,zero,a0 +;; and a2,a5,a0 +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; lbu a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index dbea624b7911..6bfb059513b1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,15 +41,17 @@ ;; function u0:0: ;; block0: -;; ld a3,8(a2) -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; ld a5,8(a2) +;; ugt a5,a0,a5##ty=i64 +;; bne a5,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a2) -;; add a4,a4,a0 -;; ld a5,[const(0)] -;; add a4,a4,a5 -;; sw a1,0(a4) +;; ld a2,0(a2) +;; add a0,a2,a0 +;; lui a4,16 +;; addi a2,a4,-1 +;; slli a2,a2,16 +;; add a0,a0,a2 +;; sw a1,0(a0) ;; j label2 ;; block2: ;; ret @@ -58,15 +60,17 @@ ;; ;; function u0:1: ;; block0: -;; ld a3,8(a1) -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; ld a5,8(a1) +;; ugt a5,a0,a5##ty=i64 +;; bne a5,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a1) -;; add a4,a4,a0 -;; ld a5,[const(0)] -;; add a4,a4,a5 -;; lw a0,0(a4) +;; ld a1,0(a1) +;; add a0,a1,a0 +;; lui a4,16 +;; addi a1,a4,-1 +;; slli a2,a1,16 +;; add a0,a0,a2 +;; lw a0,0(a0) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index 8154bb644e6a..bcccfc430359 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,15 +41,17 @@ ;; function u0:0: ;; block0: -;; ld a3,8(a2) -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; ld a5,8(a2) +;; ugt a5,a0,a5##ty=i64 +;; bne a5,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a2) -;; add a4,a4,a0 -;; ld a5,[const(0)] -;; add a4,a4,a5 -;; sb a1,0(a4) +;; ld a2,0(a2) +;; add a0,a2,a0 +;; lui a4,16 +;; addi a2,a4,-1 +;; slli a2,a2,16 +;; add a0,a0,a2 +;; sb a1,0(a0) ;; j label2 ;; block2: ;; ret @@ -58,15 +60,17 @@ ;; ;; function u0:1: ;; block0: -;; ld a3,8(a1) -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; ld a5,8(a1) +;; ugt a5,a0,a5##ty=i64 +;; bne a5,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a1) -;; add a4,a4,a0 -;; ld a5,[const(0)] -;; add a4,a4,a5 -;; lbu a0,0(a4) +;; ld a1,0(a1) +;; add a0,a1,a0 +;; lui a4,16 +;; addi a1,a4,-1 +;; slli a2,a1,16 +;; add a0,a0,a2 +;; lbu a0,0(a0) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index bb539d173df7..7cb2ebaa19f2 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,40 +41,44 @@ ;; function u0:0: ;; block0: -;; ld a4,8(a2) -;; ugt a4,a0,a4##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; ld a0,[const(0)] -;; add a5,a5,a0 +;; ld a3,8(a2) +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; lui a4,16 +;; addi a0,a4,-1 +;; slli a4,a0,16 +;; add a2,a2,a4 ;; li a0,0 -;; sltu a2,zero,a4 -;; sub a2,zero,a2 -;; and a3,a0,a2 -;; not a0,a2 -;; and a2,a5,a0 -;; or a3,a3,a2 -;; sw a1,0(a3) +;; sltu a3,zero,a3 +;; sub a3,zero,a3 +;; and a5,a0,a3 +;; not a3,a3 +;; and a3,a2,a3 +;; or a5,a5,a3 +;; sw a1,0(a5) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a4,8(a1) -;; ugt a4,a0,a4##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; ld a0,[const(0)] -;; add a5,a5,a0 +;; ld a2,8(a1) +;; ugt a2,a0,a2##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lui a4,16 +;; addi a0,a4,-1 +;; slli a3,a0,16 +;; add a1,a1,a3 ;; li a0,0 -;; sltu a1,zero,a4 -;; sub a1,zero,a1 -;; and a3,a0,a1 -;; not a0,a1 -;; and a1,a5,a0 -;; or a3,a3,a1 -;; lw a0,0(a3) +;; sltu a2,zero,a2 +;; sub a3,zero,a2 +;; and a5,a0,a3 +;; not a2,a3 +;; and a3,a1,a2 +;; or a5,a5,a3 +;; lw a0,0(a5) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index e0ef430e2684..dda4d4e639fc 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -41,40 +41,44 @@ ;; function u0:0: ;; block0: -;; ld a4,8(a2) -;; ugt a4,a0,a4##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; ld a0,[const(0)] -;; add a5,a5,a0 +;; ld a3,8(a2) +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; lui a4,16 +;; addi a0,a4,-1 +;; slli a4,a0,16 +;; add a2,a2,a4 ;; li a0,0 -;; sltu a2,zero,a4 -;; sub a2,zero,a2 -;; and a3,a0,a2 -;; not a0,a2 -;; and a2,a5,a0 -;; or a3,a3,a2 -;; sb a1,0(a3) +;; sltu a3,zero,a3 +;; sub a3,zero,a3 +;; and a5,a0,a3 +;; not a3,a3 +;; and a3,a2,a3 +;; or a5,a5,a3 +;; sb a1,0(a5) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a4,8(a1) -;; ugt a4,a0,a4##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; ld a0,[const(0)] -;; add a5,a5,a0 +;; ld a2,8(a1) +;; ugt a2,a0,a2##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lui a4,16 +;; addi a0,a4,-1 +;; slli a3,a0,16 +;; add a1,a1,a3 ;; li a0,0 -;; sltu a1,zero,a4 -;; sub a1,zero,a1 -;; and a3,a0,a1 -;; not a0,a1 -;; and a1,a5,a0 -;; or a3,a3,a1 -;; lbu a0,0(a3) +;; sltu a2,zero,a2 +;; sub a3,zero,a2 +;; and a5,a0,a3 +;; not a2,a3 +;; and a3,a1,a2 +;; or a5,a5,a3 +;; lbu a0,0(a5) ;; j label1 ;; block1: ;; ret From 83001e975298e123a66f7c1824d2d658736621c0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 Oct 2023 20:01:50 -0500 Subject: [PATCH 039/199] Refactor how `subscribe` works in WASI (#7130) This commit refactors how the `Pollable` resource is created in WASI. Previously this was created manually via two variants of a `Pollable` enum but this was somewhat error prone for a few reasons: * The more common representation, `TableEntry`, had boilerplate associated with it that had dynamic downcasts and such. This is now all hidden behind a more typed API. * Callers had to remember to use `push_child_resource` which is easy to forget. * The previous signature of the readiness check returned a `Result<()>` which made it accidentally easy for errors to be propagated into traps rather than being returned as a "last operation failed" error. This commit replaces the previous API with a single `subscribe` function which takes a `Resource` and returns a `Resource`. The `T` type must implement a new trait, `Subscribe`, which indicates the asynchronous readiness check. This readiness check additionally returns `()` so it's no longer possible to accidentally return errors (or trap). This namely required refactoring the `HostOutputStream` trait and implementations. The trait itself now has a `check_write` method corresponding to to the same WASI function. The old `write_ready` function is now a convenience helper combo between `ready` and `check_write`. Concrete implementations are refactored to store errors discovered during `ready` to get communicated through `check-write` later on. --- .../wasi-tests/src/bin/poll_oneoff_files.rs | 18 ++- crates/wasi-http/src/body.rs | 61 ++++---- crates/wasi-http/src/types.rs | 24 +-- crates/wasi-http/src/types_impl.rs | 39 ++--- crates/wasi/src/preview2/filesystem.rs | 141 ++++++++++-------- crates/wasi/src/preview2/host/clocks.rs | 57 +++---- crates/wasi/src/preview2/host/io.rs | 64 +------- crates/wasi/src/preview2/host/tcp.rs | 36 +---- crates/wasi/src/preview2/mod.rs | 30 ++-- crates/wasi/src/preview2/pipe.rs | 92 ++++++------ crates/wasi/src/preview2/poll.rs | 122 ++++++++------- crates/wasi/src/preview2/stdio.rs | 2 +- .../src/preview2/stdio/worker_thread_stdin.rs | 10 +- crates/wasi/src/preview2/stream.rs | 45 ++++-- crates/wasi/src/preview2/tcp.rs | 130 ++++++++++------ crates/wasi/src/preview2/write_stream.rs | 45 +++--- 16 files changed, 459 insertions(+), 457 deletions(-) diff --git a/crates/test-programs/wasi-tests/src/bin/poll_oneoff_files.rs b/crates/test-programs/wasi-tests/src/bin/poll_oneoff_files.rs index 03539ea35de5..7ca127865f6d 100644 --- a/crates/test-programs/wasi-tests/src/bin/poll_oneoff_files.rs +++ b/crates/test-programs/wasi-tests/src/bin/poll_oneoff_files.rs @@ -153,23 +153,29 @@ unsafe fn test_fd_readwrite(readable_fd: wasi::Fd, writable_fd: wasi::Fd, error_ ]; let out = poll_oneoff_with_retry(&r#in).unwrap(); assert_eq!(out.len(), 2, "should return 2 events, got: {:?}", out); + + let (read, write) = if out[0].userdata == 1 { + (&out[0], &out[1]) + } else { + (&out[1], &out[0]) + }; assert_eq!( - out[0].userdata, 1, + read.userdata, 1, "the event.userdata should contain fd userdata specified by the user" ); - assert_errno!(out[0].error, error_code); + assert_errno!(read.error, error_code); assert_eq!( - out[0].type_, + read.type_, wasi::EVENTTYPE_FD_READ, "the event.type_ should equal FD_READ" ); assert_eq!( - out[1].userdata, 2, + write.userdata, 2, "the event.userdata should contain fd userdata specified by the user" ); - assert_errno!(out[1].error, error_code); + assert_errno!(write.error, error_code); assert_eq!( - out[1].type_, + write.type_, wasi::EVENTTYPE_FD_WRITE, "the event.type_ should equal FD_WRITE" ); diff --git a/crates/wasi-http/src/body.rs b/crates/wasi-http/src/body.rs index fa021a3ddd61..9d27832049be 100644 --- a/crates/wasi-http/src/body.rs +++ b/crates/wasi-http/src/body.rs @@ -11,7 +11,7 @@ use std::{ use tokio::sync::{mpsc, oneshot}; use wasmtime_wasi::preview2::{ self, AbortOnDropJoinHandle, HostInputStream, HostOutputStream, OutputStreamError, - StreamRuntimeError, StreamState, + StreamRuntimeError, StreamState, Subscribe, }; pub type HyperIncomingBody = BoxBody; @@ -189,14 +189,17 @@ impl HostInputStream for HostIncomingBodyStream { } } } +} - async fn ready(&mut self) -> anyhow::Result<()> { +#[async_trait::async_trait] +impl Subscribe for HostIncomingBodyStream { + async fn ready(&mut self) { if !self.buffer.is_empty() { - return Ok(()); + return; } if !self.open { - return Ok(()); + return; } match self.receiver.recv().await { @@ -209,8 +212,6 @@ impl HostInputStream for HostIncomingBodyStream { None => self.open = false, } - - Ok(()) } } @@ -224,8 +225,9 @@ pub enum HostFutureTrailersState { Done(Result), } -impl HostFutureTrailers { - pub async fn ready(&mut self) -> anyhow::Result<()> { +#[async_trait::async_trait] +impl Subscribe for HostFutureTrailers { + async fn ready(&mut self) { if let HostFutureTrailersState::Waiting(rx) = &mut self.state { let result = match rx.await { Ok(Ok(headers)) => Ok(FieldMap::from(headers)), @@ -236,7 +238,6 @@ impl HostFutureTrailers { }; self.state = HostFutureTrailersState::Done(result); } - Ok(()) } } @@ -353,11 +354,6 @@ enum Job { Write(Bytes), } -enum WriteStatus<'a> { - Done(Result), - Pending(tokio::sync::futures::Notified<'a>), -} - impl Worker { fn new(write_budget: usize) -> Self { Self { @@ -372,17 +368,31 @@ impl Worker { write_ready_changed: tokio::sync::Notify::new(), } } - fn check_write(&self) -> WriteStatus<'_> { + async fn ready(&self) { + loop { + { + let state = self.state(); + if state.error.is_some() + || !state.alive + || (!state.flush_pending && state.write_budget > 0) + { + return; + } + } + self.write_ready_changed.notified().await; + } + } + fn check_write(&self) -> Result { let mut state = self.state(); if let Err(e) = state.check_error() { - return WriteStatus::Done(Err(e)); + return Err(e); } if state.flush_pending || state.write_budget == 0 { - return WriteStatus::Pending(self.write_ready_changed.notified()); + return Ok(0); } - WriteStatus::Done(Ok(state.write_budget)) + Ok(state.write_budget) } fn state(&self) -> std::sync::MutexGuard { self.state.lock().unwrap() @@ -496,12 +506,13 @@ impl HostOutputStream for BodyWriteStream { Ok(()) } - async fn write_ready(&mut self) -> Result { - loop { - match self.worker.check_write() { - WriteStatus::Done(r) => return r, - WriteStatus::Pending(notifier) => notifier.await, - } - } + fn check_write(&mut self) -> Result { + self.worker.check_write() + } +} +#[async_trait::async_trait] +impl Subscribe for BodyWriteStream { + async fn ready(&mut self) { + self.worker.ready().await } } diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs index 391c5d91851e..2b6ff34b1056 100644 --- a/crates/wasi-http/src/types.rs +++ b/crates/wasi-http/src/types.rs @@ -12,10 +12,8 @@ use crate::{ }, }; use std::any::Any; -use std::pin::Pin; -use std::task; use wasmtime::component::Resource; -use wasmtime_wasi::preview2::{AbortOnDropJoinHandle, Table, TableError}; +use wasmtime_wasi::preview2::{AbortOnDropJoinHandle, Subscribe, Table, TableError}; /// Capture the state necessary for use in the wasi-http API implementation. pub struct WasiHttpCtx; @@ -167,21 +165,11 @@ impl HostFutureIncomingResponse { } } -impl std::future::Future for HostFutureIncomingResponse { - type Output = anyhow::Result<()>; - - fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { - let s = self.get_mut(); - match s { - Self::Pending(ref mut handle) => match Pin::new(handle).poll(cx) { - task::Poll::Pending => task::Poll::Pending, - task::Poll::Ready(r) => { - *s = Self::Ready(r); - task::Poll::Ready(Ok(())) - } - }, - - Self::Consumed | Self::Ready(_) => task::Poll::Ready(Ok(())), +#[async_trait::async_trait] +impl Subscribe for HostFutureIncomingResponse { + async fn ready(&mut self) { + if let Self::Pending(handle) = self { + *self = Self::Ready(handle.await); } } } diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index af91caea411d..a470ece0ef3d 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -18,7 +18,7 @@ use std::any::Any; use wasmtime::component::Resource; use wasmtime_wasi::preview2::{ bindings::io::streams::{InputStream, OutputStream}, - Pollable, PollableFuture, + Pollable, }; impl crate::bindings::http::types::Host for T { @@ -352,19 +352,10 @@ impl crate::bindings::http::types::Host for T { &mut self, index: FutureTrailers, ) -> wasmtime::Result> { - // Eagerly force errors about the validity of the index. - let _ = self.table().get_future_trailers(index)?; - - fn make_future(elem: &mut dyn Any) -> PollableFuture { - Box::pin(elem.downcast_mut::().unwrap().ready()) - } - - // FIXME: this should use `push_child_resource` - let id = self - .table() - .push_resource(Pollable::TableEntry { index, make_future })?; - - Ok(id) + wasmtime_wasi::preview2::subscribe( + self.table(), + Resource::::new_borrow(index), + ) } fn future_trailers_get( @@ -480,22 +471,10 @@ impl crate::bindings::http::types::Host for T { &mut self, id: FutureIncomingResponse, ) -> wasmtime::Result> { - let _ = self.table().get_future_incoming_response(id)?; - - fn make_future<'a>(elem: &'a mut dyn Any) -> PollableFuture<'a> { - Box::pin( - elem.downcast_mut::() - .expect("parent resource is HostFutureIncomingResponse"), - ) - } - - // FIXME: this should use `push_child_resource` - let pollable = self.table().push_resource(Pollable::TableEntry { - index: id, - make_future, - })?; - - Ok(pollable) + wasmtime_wasi::preview2::subscribe( + self.table(), + Resource::::new_borrow(id), + ) } fn outgoing_body_write( diff --git a/crates/wasi/src/preview2/filesystem.rs b/crates/wasi/src/preview2/filesystem.rs index c360b874c801..e09d32df1d9c 100644 --- a/crates/wasi/src/preview2/filesystem.rs +++ b/crates/wasi/src/preview2/filesystem.rs @@ -1,10 +1,12 @@ use crate::preview2::bindings::filesystem::types; use crate::preview2::{ AbortOnDropJoinHandle, HostOutputStream, OutputStreamError, StreamRuntimeError, StreamState, + Subscribe, }; use anyhow::anyhow; use bytes::{Bytes, BytesMut}; -use futures::future::{maybe_done, MaybeDone}; +use std::io; +use std::mem; use std::sync::Arc; pub enum Descriptor { @@ -156,11 +158,11 @@ impl FileInputStream { } } -fn read_result(r: Result) -> Result<(usize, StreamState), anyhow::Error> { +fn read_result(r: io::Result) -> Result<(usize, StreamState), anyhow::Error> { match r { Ok(0) => Ok((0, StreamState::Closed)), Ok(n) => Ok((n, StreamState::Open)), - Err(e) if e.kind() == std::io::ErrorKind::Interrupted => Ok((0, StreamState::Open)), + Err(e) if e.kind() == io::ErrorKind::Interrupted => Ok((0, StreamState::Open)), Err(e) => Err(StreamRuntimeError::from(anyhow!(e)).into()), } } @@ -174,26 +176,32 @@ pub(crate) enum FileOutputMode { pub(crate) struct FileOutputStream { file: Arc, mode: FileOutputMode, - // Allows join future to be awaited in a cancellable manner. Gone variant indicates - // no task is currently outstanding. - task: MaybeDone>>, - closed: bool, + state: OutputState, } + +enum OutputState { + Ready, + /// Allows join future to be awaited in a cancellable manner. Gone variant indicates + /// no task is currently outstanding. + Waiting(AbortOnDropJoinHandle>), + /// The last I/O operation failed with this error. + Error(io::Error), + Closed, +} + impl FileOutputStream { pub fn write_at(file: Arc, position: u64) -> Self { Self { file, mode: FileOutputMode::Position(position), - task: MaybeDone::Gone, - closed: false, + state: OutputState::Ready, } } pub fn append(file: Arc) -> Self { Self { file, mode: FileOutputMode::Append, - task: MaybeDone::Gone, - closed: false, + state: OutputState::Ready, } } } @@ -201,74 +209,79 @@ impl FileOutputStream { // FIXME: configurable? determine from how much space left in file? const FILE_WRITE_CAPACITY: usize = 1024 * 1024; -#[async_trait::async_trait] impl HostOutputStream for FileOutputStream { fn write(&mut self, buf: Bytes) -> Result<(), OutputStreamError> { use system_interface::fs::FileIoExt; - - if self.closed { - return Err(OutputStreamError::Closed); - } - if !matches!(self.task, MaybeDone::Gone) { - // a write is pending - this call was not permitted - return Err(OutputStreamError::Trap(anyhow!( - "write not permitted: FileOutputStream write pending" - ))); + match self.state { + OutputState::Ready => {} + OutputState::Closed => return Err(OutputStreamError::Closed), + OutputState::Waiting(_) | OutputState::Error(_) => { + // a write is pending - this call was not permitted + return Err(OutputStreamError::Trap(anyhow!( + "write not permitted: check_write not called first" + ))); + } } + let f = Arc::clone(&self.file); let m = self.mode; - self.task = maybe_done(AbortOnDropJoinHandle::from(tokio::task::spawn_blocking( - move || match m { - FileOutputMode::Position(mut p) => { - let mut buf = buf; - while !buf.is_empty() { - let nwritten = f.write_at(buf.as_ref(), p)?; - // afterwards buf contains [nwritten, len): - let _ = buf.split_to(nwritten); - p += nwritten as u64; - } - Ok(()) + let task = AbortOnDropJoinHandle::from(tokio::task::spawn_blocking(move || match m { + FileOutputMode::Position(mut p) => { + let mut buf = buf; + while !buf.is_empty() { + let nwritten = f.write_at(buf.as_ref(), p)?; + // afterwards buf contains [nwritten, len): + let _ = buf.split_to(nwritten); + p += nwritten as u64; } - FileOutputMode::Append => { - let mut buf = buf; - while !buf.is_empty() { - let nwritten = f.append(buf.as_ref())?; - let _ = buf.split_to(nwritten); - } - Ok(()) + Ok(()) + } + FileOutputMode::Append => { + let mut buf = buf; + while !buf.is_empty() { + let nwritten = f.append(buf.as_ref())?; + let _ = buf.split_to(nwritten); } - }, - ))); + Ok(()) + } + })); + self.state = OutputState::Waiting(task); Ok(()) } fn flush(&mut self) -> Result<(), OutputStreamError> { - if self.closed { - return Err(OutputStreamError::Closed); + match self.state { + // Only userland buffering of file writes is in the blocking task, + // so there's nothing extra that needs to be done to request a + // flush. + OutputState::Ready | OutputState::Waiting(_) => Ok(()), + OutputState::Closed => Err(OutputStreamError::Closed), + OutputState::Error(_) => match mem::replace(&mut self.state, OutputState::Closed) { + OutputState::Error(e) => Err(OutputStreamError::LastOperationFailed(e.into())), + _ => unreachable!(), + }, } - // Only userland buffering of file writes is in the blocking task. - Ok(()) } - async fn write_ready(&mut self) -> Result { - if self.closed { - return Err(OutputStreamError::Closed); - } - // If there is no outstanding task, accept more input: - if matches!(self.task, MaybeDone::Gone) { - return Ok(FILE_WRITE_CAPACITY); + fn check_write(&mut self) -> Result { + match self.state { + OutputState::Ready => Ok(FILE_WRITE_CAPACITY), + OutputState::Closed => Err(OutputStreamError::Closed), + OutputState::Error(_) => match mem::replace(&mut self.state, OutputState::Closed) { + OutputState::Error(e) => Err(OutputStreamError::LastOperationFailed(e.into())), + _ => unreachable!(), + }, + OutputState::Waiting(_) => Ok(0), } - // Wait for outstanding task: - std::pin::Pin::new(&mut self.task).await; + } +} - // Mark task as finished, and handle output: - match std::pin::Pin::new(&mut self.task) - .take_output() - .expect("just awaited for MaybeDone completion") - { - Ok(()) => Ok(FILE_WRITE_CAPACITY), - Err(e) => { - self.closed = true; - Err(OutputStreamError::LastOperationFailed(e.into())) - } +#[async_trait::async_trait] +impl Subscribe for FileOutputStream { + async fn ready(&mut self) { + if let OutputState::Waiting(task) = &mut self.state { + self.state = match task.await { + Ok(()) => OutputState::Ready, + Err(e) => OutputState::Error(e), + }; } } } diff --git a/crates/wasi/src/preview2/host/clocks.rs b/crates/wasi/src/preview2/host/clocks.rs index 15adedf5770d..105f8b3ee47f 100644 --- a/crates/wasi/src/preview2/host/clocks.rs +++ b/crates/wasi/src/preview2/host/clocks.rs @@ -5,8 +5,10 @@ use crate::preview2::bindings::{ clocks::timezone::{self, TimezoneDisplay}, clocks::wall_clock::{self, Datetime}, }; +use crate::preview2::poll::{subscribe, Subscribe}; use crate::preview2::{Pollable, WasiView}; use cap_std::time::SystemTime; +use std::time::Duration; use wasmtime::component::Resource; impl TryFrom for Datetime { @@ -51,42 +53,29 @@ impl monotonic_clock::Host for T { } fn subscribe(&mut self, when: Instant, absolute: bool) -> anyhow::Result> { - use std::time::Duration; - // Calculate time relative to clock object, which may not have the same zero - // point as tokio Inst::now() let clock_now = self.ctx().monotonic_clock.now(); - if absolute && when < clock_now { - // Deadline is in the past, so pollable is always ready: - Ok(self - .table_mut() - .push_resource(Pollable::Closure(Box::new(|| Box::pin(async { Ok(()) }))))?) + let duration = if absolute { + Duration::from_nanos(when - clock_now) } else { - let duration = if absolute { - Duration::from_nanos(when - clock_now) - } else { - Duration::from_nanos(when) - }; - let deadline = tokio::time::Instant::now() - .checked_add(duration) - .ok_or_else(|| anyhow::anyhow!("time overflow: duration {duration:?}"))?; - tracing::trace!( - "deadline = {:?}, now = {:?}", - deadline, - tokio::time::Instant::now() - ); - Ok(self - .table_mut() - .push_resource(Pollable::Closure(Box::new(move || { - Box::pin(async move { - tracing::trace!( - "mkf: deadline = {:?}, now = {:?}", - deadline, - tokio::time::Instant::now() - ); - Ok(tokio::time::sleep_until(deadline).await) - }) - })))?) - } + Duration::from_nanos(when) + }; + let deadline = tokio::time::Instant::now() + .checked_add(duration) + .ok_or_else(|| anyhow::anyhow!("time overflow: duration {duration:?}"))?; + // NB: this resource created here is not actually exposed to wasm, it's + // only an internal implementation detail used to match the signature + // expected by `subscribe`. + let sleep = self.table_mut().push_resource(Sleep(deadline))?; + subscribe(self.table_mut(), sleep) + } +} + +struct Sleep(tokio::time::Instant); + +#[async_trait::async_trait] +impl Subscribe for Sleep { + async fn ready(&mut self) { + tokio::time::sleep_until(self.0).await; } } diff --git a/crates/wasi/src/preview2/host/io.rs b/crates/wasi/src/preview2/host/io.rs index 504fccac1875..a9e18de4caf3 100644 --- a/crates/wasi/src/preview2/host/io.rs +++ b/crates/wasi/src/preview2/host/io.rs @@ -1,13 +1,9 @@ use crate::preview2::{ bindings::io::streams::{self, InputStream, OutputStream}, - poll::PollableFuture, + poll::subscribe, stream::{OutputStreamError, StreamRuntimeError, StreamState}, Pollable, TableError, WasiView, }; -use std::any::Any; -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; use wasmtime::component::Resource; impl From for streams::StreamStatus { @@ -48,14 +44,8 @@ impl streams::HostOutputStream for T { } fn check_write(&mut self, stream: Resource) -> Result { - let s = self.table_mut().get_resource_mut(&stream)?; - let mut ready = s.write_ready(); - let mut task = Context::from_waker(futures::task::noop_waker_ref()); - match Pin::new(&mut ready).poll(&mut task) { - Poll::Ready(Ok(permit)) => Ok(permit as u64), - Poll::Ready(Err(e)) => Err(e.into()), - Poll::Pending => Ok(0), - } + let bytes = self.table_mut().get_resource_mut(&stream)?.check_write()?; + Ok(bytes as u64) } fn write( @@ -70,23 +60,7 @@ impl streams::HostOutputStream for T { } fn subscribe(&mut self, stream: Resource) -> anyhow::Result> { - fn output_stream_ready<'a>(stream: &'a mut dyn Any) -> PollableFuture<'a> { - let stream = stream - .downcast_mut::() - .expect("downcast to OutputStream failed"); - Box::pin(async move { - let _ = stream.write_ready().await?; - Ok(()) - }) - } - - Ok(self.table_mut().push_child_resource( - Pollable::TableEntry { - index: stream.rep(), - make_future: output_stream_ready, - }, - &stream, - )?) + subscribe(self.table_mut(), stream) } async fn blocking_write_and_flush( @@ -291,7 +265,7 @@ impl streams::HostInputStream for T { len: u64, ) -> anyhow::Result, streams::StreamStatus), ()>> { if let InputStream::Host(s) = self.table_mut().get_resource_mut(&stream)? { - s.ready().await?; + s.ready().await; } self.read(stream, len).await } @@ -341,37 +315,13 @@ impl streams::HostInputStream for T { len: u64, ) -> anyhow::Result> { if let InputStream::Host(s) = self.table_mut().get_resource_mut(&stream)? { - s.ready().await?; + s.ready().await; } self.skip(stream, len).await } fn subscribe(&mut self, stream: Resource) -> anyhow::Result> { - // Ensure that table element is an input-stream: - let pollable = match self.table_mut().get_resource(&stream)? { - InputStream::Host(_) => { - fn input_stream_ready<'a>(stream: &'a mut dyn Any) -> PollableFuture<'a> { - let stream = stream - .downcast_mut::() - .expect("downcast to InputStream failed"); - match *stream { - InputStream::Host(ref mut hs) => hs.ready(), - _ => unreachable!(), - } - } - - Pollable::TableEntry { - index: stream.rep(), - make_future: input_stream_ready, - } - } - // Files are always "ready" immediately (because we have no way to actually wait on - // readiness in epoll) - InputStream::File(_) => { - Pollable::Closure(Box::new(|| Box::pin(futures::future::ready(Ok(()))))) - } - }; - Ok(self.table_mut().push_child_resource(pollable, &stream)?) + crate::preview2::poll::subscribe(self.table_mut(), stream) } } diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index 9df287c920bf..a022f4d1ab96 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -4,13 +4,12 @@ use crate::preview2::bindings::{ sockets::tcp::{self, ShutdownType}, }; use crate::preview2::tcp::{TcpSocket, TcpState}; -use crate::preview2::{Pollable, PollableFuture, WasiView}; +use crate::preview2::{Pollable, WasiView}; use cap_net_ext::{Blocking, PoolExt, TcpListenerExt}; use cap_std::net::TcpListener; use io_lifetimes::AsSocketlike; use rustix::io::Errno; use rustix::net::sockopt; -use std::any::Any; use tokio::io::Interest; use wasmtime::component::Resource; @@ -440,38 +439,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { } fn subscribe(&mut self, this: Resource) -> anyhow::Result> { - fn make_tcp_socket_future<'a>(stream: &'a mut dyn Any) -> PollableFuture<'a> { - let socket = stream - .downcast_mut::() - .expect("downcast to TcpSocket failed"); - - // Some states are ready immediately. - match socket.tcp_state { - TcpState::BindStarted | TcpState::ListenStarted | TcpState::ConnectReady => { - return Box::pin(async { Ok(()) }) - } - _ => {} - } - - // FIXME: Add `Interest::ERROR` when we update to tokio 1.32. - let join = Box::pin(async move { - socket - .inner - .ready(Interest::READABLE | Interest::WRITABLE) - .await - .unwrap(); - Ok(()) - }); - - join - } - - let pollable = Pollable::TableEntry { - index: this.rep(), - make_future: make_tcp_socket_future, - }; - - Ok(self.table_mut().push_child_resource(pollable, &this)?) + crate::preview2::poll::subscribe(self.table_mut(), this) } fn shutdown( diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index e3f171a11973..de8dd801dcd2 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -15,6 +15,10 @@ //! `pub mod legacy` with an off-by-default feature flag, and after 2 //! releases, retire and remove that code from our tree. +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + mod clocks; pub mod command; mod ctx; @@ -37,7 +41,7 @@ pub use self::clocks::{HostMonotonicClock, HostWallClock}; pub use self::ctx::{WasiCtx, WasiCtxBuilder, WasiView}; pub use self::error::I32Exit; pub use self::filesystem::{DirPerms, FilePerms}; -pub use self::poll::{ClosureFuture, MakeFuture, Pollable, PollableFuture}; +pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe}; pub use self::random::{thread_rng, Deterministic}; pub use self::stdio::{stderr, stdin, stdout, IsATTY, Stderr, Stdin, Stdout}; pub use self::stream::{ @@ -193,14 +197,9 @@ impl From> for AbortOnDropJoinHandle { AbortOnDropJoinHandle(jh) } } -impl std::future::Future for AbortOnDropJoinHandle { +impl Future for AbortOnDropJoinHandle { type Output = T; - fn poll( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - use std::pin::Pin; - use std::task::Poll; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match Pin::new(&mut self.as_mut().0).poll(cx) { Poll::Pending => Poll::Pending, Poll::Ready(r) => Poll::Ready(r.expect("child task panicked")), @@ -210,7 +209,7 @@ impl std::future::Future for AbortOnDropJoinHandle { pub fn spawn(f: F) -> AbortOnDropJoinHandle where - F: std::future::Future + Send + 'static, + F: Future + Send + 'static, G: Send + 'static, { let j = match tokio::runtime::Handle::try_current() { @@ -223,7 +222,7 @@ where AbortOnDropJoinHandle(j) } -pub fn in_tokio(f: F) -> F::Output { +pub fn in_tokio(f: F) -> F::Output { match tokio::runtime::Handle::try_current() { Ok(h) => { let _enter = h.enter(); @@ -245,3 +244,14 @@ fn with_ambient_tokio_runtime(f: impl FnOnce() -> R) -> R { } } } + +fn poll_noop(future: Pin<&mut F>) -> Option +where + F: Future, +{ + let mut task = Context::from_waker(futures::task::noop_waker_ref()); + match future.poll(&mut task) { + Poll::Ready(result) => Some(result), + Poll::Pending => None, + } +} diff --git a/crates/wasi/src/preview2/pipe.rs b/crates/wasi/src/preview2/pipe.rs index c261760d7d93..b99e68a28524 100644 --- a/crates/wasi/src/preview2/pipe.rs +++ b/crates/wasi/src/preview2/pipe.rs @@ -7,6 +7,7 @@ //! Some convenience constructors are included for common backing types like `Vec` and `String`, //! but the virtual pipes can be instantiated with any `Read` or `Write` type. //! +use crate::preview2::poll::Subscribe; use crate::preview2::{HostInputStream, HostOutputStream, OutputStreamError, StreamState}; use anyhow::{anyhow, Error}; use bytes::Bytes; @@ -49,10 +50,11 @@ impl HostInputStream for MemoryInputPipe { }; Ok((read, state)) } +} - async fn ready(&mut self) -> Result<(), Error> { - Ok(()) - } +#[async_trait::async_trait] +impl Subscribe for MemoryInputPipe { + async fn ready(&mut self) {} } #[derive(Debug, Clone)] @@ -78,7 +80,6 @@ impl MemoryOutputPipe { } } -#[async_trait::async_trait] impl HostOutputStream for MemoryOutputPipe { fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError> { let mut buf = self.buffer.lock().unwrap(); @@ -95,7 +96,7 @@ impl HostOutputStream for MemoryOutputPipe { // This stream is always flushed Ok(()) } - async fn write_ready(&mut self) -> Result { + fn check_write(&mut self) -> Result { let consumed = self.buffer.lock().unwrap().len(); if consumed < self.capacity { Ok(self.capacity - consumed) @@ -106,6 +107,11 @@ impl HostOutputStream for MemoryOutputPipe { } } +#[async_trait::async_trait] +impl Subscribe for MemoryOutputPipe { + async fn ready(&mut self) {} +} + /// Provides a [`HostInputStream`] impl from a [`tokio::io::AsyncRead`] impl pub struct AsyncReadStream { state: StreamState, @@ -189,10 +195,12 @@ impl HostInputStream for AsyncReadStream { )), } } - - async fn ready(&mut self) -> Result<(), Error> { +} +#[async_trait::async_trait] +impl Subscribe for AsyncReadStream { + async fn ready(&mut self) { if self.buffer.is_some() || self.state == StreamState::Closed { - return Ok(()); + return; } match self.receiver.recv().await { Some(Ok((bytes, state))) => { @@ -203,12 +211,9 @@ impl HostInputStream for AsyncReadStream { } Some(Err(e)) => self.buffer = Some(Err(e)), None => { - return Err(anyhow!( - "no more sender for an open AsyncReadStream - should be impossible" - )) + panic!("no more sender for an open AsyncReadStream - should be impossible") } } - Ok(()) } } @@ -216,7 +221,6 @@ impl HostInputStream for AsyncReadStream { #[derive(Copy, Clone)] pub struct SinkOutputStream; -#[async_trait::async_trait] impl HostOutputStream for SinkOutputStream { fn write(&mut self, _buf: Bytes) -> Result<(), OutputStreamError> { Ok(()) @@ -226,12 +230,17 @@ impl HostOutputStream for SinkOutputStream { Ok(()) } - async fn write_ready(&mut self) -> Result { + fn check_write(&mut self) -> Result { // This stream is always ready for writing. Ok(usize::MAX) } } +#[async_trait::async_trait] +impl Subscribe for SinkOutputStream { + async fn ready(&mut self) {} +} + /// A stream that is ready immediately, but will always report that it's closed. #[derive(Copy, Clone)] pub struct ClosedInputStream; @@ -241,17 +250,17 @@ impl HostInputStream for ClosedInputStream { fn read(&mut self, _size: usize) -> Result<(Bytes, StreamState), Error> { Ok((Bytes::new(), StreamState::Closed)) } +} - async fn ready(&mut self) -> Result<(), Error> { - Ok(()) - } +#[async_trait::async_trait] +impl Subscribe for ClosedInputStream { + async fn ready(&mut self) {} } /// An output stream that is always closed. #[derive(Copy, Clone)] pub struct ClosedOutputStream; -#[async_trait::async_trait] impl HostOutputStream for ClosedOutputStream { fn write(&mut self, _: Bytes) -> Result<(), OutputStreamError> { Err(OutputStreamError::Closed) @@ -260,11 +269,16 @@ impl HostOutputStream for ClosedOutputStream { Err(OutputStreamError::Closed) } - async fn write_ready(&mut self) -> Result { + fn check_write(&mut self) -> Result { Err(OutputStreamError::Closed) } } +#[async_trait::async_trait] +impl Subscribe for ClosedOutputStream { + async fn ready(&mut self) {} +} + #[cfg(test)] mod test { use super::*; @@ -323,9 +337,7 @@ mod test { // The reader task hasn't run yet. Call `ready` to await and fill the buffer. StreamState::Open => { - resolves_immediately(reader.ready()) - .await - .expect("ready is ok"); + resolves_immediately(reader.ready()).await; let (bs, state) = reader.read(0).unwrap(); assert!(bs.is_empty()); assert_eq!(state, StreamState::Closed); @@ -341,9 +353,7 @@ mod test { assert_eq!(state, StreamState::Open); if bs.is_empty() { // Reader task hasn't run yet. Call `ready` to await and fill the buffer. - resolves_immediately(reader.ready()) - .await - .expect("ready is ok"); + resolves_immediately(reader.ready()).await; // Now a read should succeed let (bs, state) = reader.read(10).unwrap(); assert_eq!(bs.len(), 10); @@ -377,9 +387,7 @@ mod test { assert_eq!(state, StreamState::Open); if bs.is_empty() { // Reader task hasn't run yet. Call `ready` to await and fill the buffer. - resolves_immediately(reader.ready()) - .await - .expect("ready is ok"); + resolves_immediately(reader.ready()).await; // Now a read should succeed let (bs, state) = reader.read(123).unwrap(); assert_eq!(bs.len(), 123); @@ -396,9 +404,7 @@ mod test { StreamState::Closed => {} // Correct! StreamState::Open => { // Need to await to give this side time to catch up - resolves_immediately(reader.ready()) - .await - .expect("ready is ok"); + resolves_immediately(reader.ready()).await; // Now a read should show closed let (bs, state) = reader.read(0).unwrap(); assert_eq!(bs.len(), 0); @@ -420,9 +426,7 @@ mod test { assert_eq!(state, StreamState::Open); if bs.is_empty() { // Reader task hasn't run yet. Call `ready` to await and fill the buffer. - resolves_immediately(reader.ready()) - .await - .expect("ready is ok"); + resolves_immediately(reader.ready()).await; // Now a read should succeed let (bs, state) = reader.read(1).unwrap(); assert_eq!(*bs, [123u8]); @@ -449,9 +453,7 @@ mod test { // Wait readiness (yes we could possibly win the race and read it out faster, leaving that // out of the test for simplicity) - resolves_immediately(reader.ready()) - .await - .expect("the ready is ok"); + resolves_immediately(reader.ready()).await; // read the something else back out: let (bs, state) = reader.read(1).unwrap(); @@ -476,9 +478,7 @@ mod test { // Wait readiness (yes we could possibly win the race and read it out faster, leaving that // out of the test for simplicity) - resolves_immediately(reader.ready()) - .await - .expect("the ready is ok"); + resolves_immediately(reader.ready()).await; // empty and now closed: let (bs, state) = reader.read(1).unwrap(); @@ -500,9 +500,7 @@ mod test { w }); - resolves_immediately(reader.ready()) - .await - .expect("ready is ok"); + resolves_immediately(reader.ready()).await; // Now we expect the reader task has sent 4k from the stream to the reader. // Try to read out one bigger than the buffer available: @@ -511,9 +509,7 @@ mod test { assert_eq!(state, StreamState::Open); // Allow the crank to turn more: - resolves_immediately(reader.ready()) - .await - .expect("ready is ok"); + resolves_immediately(reader.ready()).await; // Again we expect the reader task has sent 4k from the stream to the reader. // Try to read out one bigger than the buffer available: @@ -528,9 +524,7 @@ mod test { drop(w); // Allow the crank to turn more: - resolves_immediately(reader.ready()) - .await - .expect("ready is ok"); + resolves_immediately(reader.ready()).await; // Now we expect the reader to be empty, and the stream closed: let (bs, state) = reader.read(4097).unwrap(); diff --git a/crates/wasi/src/preview2/poll.rs b/crates/wasi/src/preview2/poll.rs index 68b6ce84c14f..54962aa3672c 100644 --- a/crates/wasi/src/preview2/poll.rs +++ b/crates/wasi/src/preview2/poll.rs @@ -1,13 +1,13 @@ -use crate::preview2::{bindings::io::poll, WasiView}; +use crate::preview2::{bindings::io::poll, Table, WasiView}; use anyhow::Result; use std::any::Any; -use std::collections::{hash_map::Entry, HashMap}; +use std::collections::HashMap; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use wasmtime::component::Resource; -pub type PollableFuture<'a> = Pin> + Send + 'a>>; +pub type PollableFuture<'a> = Pin + Send + 'a>>; pub type MakeFuture = for<'a> fn(&'a mut dyn Any) -> PollableFuture<'a>; pub type ClosureFuture = Box PollableFuture<'static> + Send + Sync + 'static>; @@ -17,15 +17,50 @@ pub type ClosureFuture = Box PollableFuture<'static> + Send + Sync + /// repeatedly check for readiness of a given condition, e.g. if a stream is readable /// or writable. So, rather than containing a Future, which can only become Ready once, a /// Pollable contains a way to create a Future in each call to `poll_list`. -pub enum Pollable { - /// Create a Future by calling a fn on another resource in the table. This - /// indirection means the created Future can use a mut borrow of another - /// resource in the Table (e.g. a stream) - TableEntry { index: u32, make_future: MakeFuture }, - /// Create a future by calling an owned, static closure. This is used for - /// pollables which do not share state with another resource in the Table - /// (e.g. a timer) - Closure(ClosureFuture), +pub struct Pollable { + index: u32, + make_future: MakeFuture, + remove_index_on_delete: Option Result<()>>, +} + +#[async_trait::async_trait] +pub trait Subscribe: Send + Sync + 'static { + async fn ready(&mut self); +} + +/// Creates a `pollable` resource which is susbcribed to the provided +/// `resource`. +/// +/// If `resource` is an owned resource then it will be deleted when the returned +/// resource is deleted. Otherwise the returned resource is considered a "child" +/// of the given `resource` which means that the given resource cannot be +/// deleted while the `pollable` is still alive. +pub fn subscribe(table: &mut Table, resource: Resource) -> Result> +where + T: Subscribe, +{ + fn make_future<'a, T>(stream: &'a mut dyn Any) -> PollableFuture<'a> + where + T: Subscribe, + { + stream.downcast_mut::().unwrap().ready() + } + + let pollable = Pollable { + index: resource.rep(), + remove_index_on_delete: if resource.owned() { + Some(|table, idx| { + let resource = Resource::::new_own(idx); + table.delete_resource(resource)?; + Ok(()) + }) + } else { + None + }, + make_future: make_future::, + }; + + Ok(table.push_child_resource(pollable, &resource)?) } #[async_trait::async_trait] @@ -36,88 +71,69 @@ impl poll::Host for T { let table = self.table_mut(); let mut table_futures: HashMap)> = HashMap::new(); - let mut closure_futures: Vec<(PollableFuture<'_>, Vec)> = Vec::new(); for (ix, p) in pollables.iter().enumerate() { let ix: u32 = ix.try_into()?; - match table.get_resource_mut(&p)? { - Pollable::Closure(f) => closure_futures.push((f(), vec![ix])), - Pollable::TableEntry { index, make_future } => match table_futures.entry(*index) { - Entry::Vacant(v) => { - v.insert((*make_future, vec![ix])); - } - Entry::Occupied(mut o) => { - let (_, v) = o.get_mut(); - v.push(ix); - } - }, - } + + let pollable = table.get_resource(p)?; + let (_, list) = table_futures + .entry(pollable.index) + .or_insert((pollable.make_future, Vec::new())); + list.push(ix); } + let mut futures: Vec<(PollableFuture<'_>, Vec)> = Vec::new(); for (entry, (make_future, readylist_indices)) in table.iter_entries(table_futures) { let entry = entry?; - closure_futures.push((make_future(entry), readylist_indices)); + futures.push((make_future(entry), readylist_indices)); } struct PollList<'a> { - elems: Vec<(PollableFuture<'a>, Vec)>, + futures: Vec<(PollableFuture<'a>, Vec)>, } impl<'a> Future for PollList<'a> { - type Output = Result>; + type Output = Vec; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut any_ready = false; let mut results = Vec::new(); - for (fut, readylist_indicies) in self.elems.iter_mut() { + for (fut, readylist_indicies) in self.futures.iter_mut() { match fut.as_mut().poll(cx) { - Poll::Ready(Ok(())) => { + Poll::Ready(()) => { results.extend_from_slice(readylist_indicies); any_ready = true; } - Poll::Ready(Err(e)) => { - return Poll::Ready(Err( - e.context(format!("poll_list {readylist_indicies:?}")) - )); - } Poll::Pending => {} } } if any_ready { - Poll::Ready(Ok(results)) + Poll::Ready(results) } else { Poll::Pending } } } - Ok(PollList { - elems: closure_futures, - } - .await?) + Ok(PollList { futures }.await) } async fn poll_one(&mut self, pollable: Resource) -> Result<()> { - use anyhow::Context; - let table = self.table_mut(); - let closure_future = match table.get_resource_mut(&pollable)? { - Pollable::Closure(f) => f(), - Pollable::TableEntry { index, make_future } => { - let index = *index; - let make_future = *make_future; - make_future(table.get_as_any_mut(index)?) - } - }; - - closure_future.await.context("poll_one") + let pollable = table.get_resource(&pollable)?; + let ready = (pollable.make_future)(table.get_as_any_mut(pollable.index)?); + ready.await; + Ok(()) } } #[async_trait::async_trait] impl crate::preview2::bindings::io::poll::HostPollable for T { fn drop(&mut self, pollable: Resource) -> Result<()> { - self.table_mut().delete_resource(pollable)?; + let pollable = self.table_mut().delete_resource(pollable)?; + if let Some(delete) = pollable.remove_index_on_delete { + delete(self.table_mut(), pollable.index)?; + } Ok(()) } } diff --git a/crates/wasi/src/preview2/stdio.rs b/crates/wasi/src/preview2/stdio.rs index b13aabf5215f..0d61adc2438a 100644 --- a/crates/wasi/src/preview2/stdio.rs +++ b/crates/wasi/src/preview2/stdio.rs @@ -318,7 +318,7 @@ mod test { let mut buffer = String::new(); loop { println!("child: waiting for stdin to be ready"); - stdin.ready().await.unwrap(); + stdin.ready().await; println!("child: reading input"); let (bytes, status) = stdin.read(1024).unwrap(); diff --git a/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs b/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs index bf933ad8398e..1d1c80aabace 100644 --- a/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs +++ b/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs @@ -23,6 +23,7 @@ //! This module is one that's likely to change over time though as new systems //! are encountered along with preexisting bugs. +use crate::preview2::poll::Subscribe; use crate::preview2::stdio::StdinStream; use crate::preview2::{HostInputStream, StreamState}; use anyhow::Error; @@ -145,8 +146,11 @@ impl HostInputStream for Stdin { } } } +} - async fn ready(&mut self) -> Result<(), Error> { +#[async_trait::async_trait] +impl Subscribe for Stdin { + async fn ready(&mut self) { let g = GlobalStdin::get(); // Scope the synchronous `state.lock()` to this block which does not @@ -161,12 +165,10 @@ impl HostInputStream for Stdin { g.read_completed.notified() } StdinState::ReadRequested => g.read_completed.notified(), - StdinState::Data(_) | StdinState::Closed | StdinState::Error(_) => return Ok(()), + StdinState::Data(_) | StdinState::Closed | StdinState::Error(_) => return, } }; notified.await; - - Ok(()) } } diff --git a/crates/wasi/src/preview2/stream.rs b/crates/wasi/src/preview2/stream.rs index 03a0ac1f0d46..7300b64073a4 100644 --- a/crates/wasi/src/preview2/stream.rs +++ b/crates/wasi/src/preview2/stream.rs @@ -1,5 +1,7 @@ use crate::preview2::filesystem::FileInputStream; +use crate::preview2::poll::Subscribe; use anyhow::Error; +use anyhow::Result; use bytes::Bytes; use std::fmt; @@ -44,7 +46,7 @@ impl StreamState { /// Host trait for implementing the `wasi:io/streams.input-stream` resource: A /// bytestream which can be read from. #[async_trait::async_trait] -pub trait HostInputStream: Send + Sync { +pub trait HostInputStream: Subscribe { /// Read bytes. On success, returns a pair holding the number of bytes /// read and a flag indicating whether the end of the stream was reached. /// Important: this read must be non-blocking! @@ -69,11 +71,6 @@ pub trait HostInputStream: Send + Sync { Ok((nread, state)) } - - /// Check for read readiness: this method blocks until the stream is ready - /// for reading. - /// Returning an error will trap execution. - async fn ready(&mut self) -> Result<(), Error>; } #[derive(Debug)] @@ -103,7 +100,7 @@ impl std::error::Error for OutputStreamError { /// Host trait for implementing the `wasi:io/streams.output-stream` resource: /// A bytestream which can be written to. #[async_trait::async_trait] -pub trait HostOutputStream: Send + Sync { +pub trait HostOutputStream: Subscribe { /// Write bytes after obtaining a permit to write those bytes /// Prior to calling [`write`](Self::write) /// the caller must call [`write_ready`](Self::write_ready), @@ -141,16 +138,17 @@ pub trait HostOutputStream: Send + Sync { /// - caller performed an illegal operation (e.g. wrote more bytes than were permitted) fn flush(&mut self) -> Result<(), OutputStreamError>; - /// Returns a future, which: - /// - when pending, indicates 0 bytes are permitted for writing - /// - when ready, returns a non-zero number of bytes permitted to write + /// Returns the number of bytes that are ready to be written to this stream. + /// + /// Zero bytes indicates that this stream is not currently ready for writing + /// and `ready()` must be awaited first. /// /// # Errors /// /// Returns an [OutputStreamError] if: /// - stream is closed /// - prior operation ([`write`](Self::write) or [`flush`](Self::flush)) failed - async fn write_ready(&mut self) -> Result; + fn check_write(&mut self) -> Result; /// Repeatedly write a byte to a stream. /// Important: this write must be non-blocking! @@ -163,6 +161,20 @@ pub trait HostOutputStream: Send + Sync { self.write(bs)?; Ok(()) } + + /// Simultaneously waits for this stream to be writable and then returns how + /// much may be written or the last error that happened. + async fn write_ready(&mut self) -> Result { + self.ready().await; + self.check_write() + } +} + +#[async_trait::async_trait] +impl Subscribe for Box { + async fn ready(&mut self) { + (**self).ready().await + } } pub enum InputStream { @@ -170,4 +182,15 @@ pub enum InputStream { File(FileInputStream), } +#[async_trait::async_trait] +impl Subscribe for InputStream { + async fn ready(&mut self) { + match self { + InputStream::Host(stream) => stream.ready().await, + // Files are always ready + InputStream::File(_) => {} + } + } +} + pub type OutputStream = Box; diff --git a/crates/wasi/src/preview2/tcp.rs b/crates/wasi/src/preview2/tcp.rs index 50b8a07c2f47..61bef55dd240 100644 --- a/crates/wasi/src/preview2/tcp.rs +++ b/crates/wasi/src/preview2/tcp.rs @@ -1,11 +1,15 @@ use super::{HostInputStream, HostOutputStream, OutputStreamError}; +use crate::preview2::poll::Subscribe; use crate::preview2::stream::{InputStream, OutputStream}; use crate::preview2::{with_ambient_tokio_runtime, AbortOnDropJoinHandle, StreamState}; +use anyhow::{Error, Result}; use cap_net_ext::{AddressFamily, Blocking, TcpListenerExt}; use cap_std::net::TcpListener; use io_lifetimes::raw::{FromRawSocketlike, IntoRawSocketlike}; use std::io; +use std::mem; use std::sync::Arc; +use tokio::io::Interest; /// The state of a TCP socket. /// @@ -102,13 +106,15 @@ impl HostInputStream for TcpReadStream { buf.truncate(n); Ok((buf.freeze(), self.stream_state())) } +} - async fn ready(&mut self) -> Result<(), anyhow::Error> { +#[async_trait::async_trait] +impl Subscribe for TcpReadStream { + async fn ready(&mut self) { if self.closed { - return Ok(()); + return; } - self.stream.readable().await?; - Ok(()) + self.stream.readable().await.unwrap(); } } @@ -116,53 +122,60 @@ const SOCKET_READY_SIZE: usize = 1024 * 1024 * 1024; pub(crate) struct TcpWriteStream { stream: Arc, - write_handle: Option>>, + last_write: LastWrite, +} + +enum LastWrite { + Waiting(AbortOnDropJoinHandle>), + Error(Error), + Done, } impl TcpWriteStream { pub(crate) fn new(stream: Arc) -> Self { Self { stream, - write_handle: None, + last_write: LastWrite::Done, } } /// Write `bytes` in a background task, remembering the task handle for use in a future call to /// `write_ready` fn background_write(&mut self, mut bytes: bytes::Bytes) { - assert!(self.write_handle.is_none()); + assert!(matches!(self.last_write, LastWrite::Done)); let stream = self.stream.clone(); - self.write_handle - .replace(crate::preview2::spawn(async move { - // Note: we are not using the AsyncWrite impl here, and instead using the TcpStream - // primitive try_write, which goes directly to attempt a write with mio. This has - // two advantages: 1. this operation takes a &TcpStream instead of a &mut TcpStream - // required to AsyncWrite, and 2. it eliminates any buffering in tokio we may need - // to flush. - while !bytes.is_empty() { - stream.writable().await?; - match stream.try_write(&bytes) { - Ok(n) => { - let _ = bytes.split_to(n); - } - Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => continue, - Err(e) => return Err(e.into()), + self.last_write = LastWrite::Waiting(crate::preview2::spawn(async move { + // Note: we are not using the AsyncWrite impl here, and instead using the TcpStream + // primitive try_write, which goes directly to attempt a write with mio. This has + // two advantages: 1. this operation takes a &TcpStream instead of a &mut TcpStream + // required to AsyncWrite, and 2. it eliminates any buffering in tokio we may need + // to flush. + while !bytes.is_empty() { + stream.writable().await?; + match stream.try_write(&bytes) { + Ok(n) => { + let _ = bytes.split_to(n); } + Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => continue, + Err(e) => return Err(e.into()), } + } - Ok(()) - })); + Ok(()) + })); } } -#[async_trait::async_trait] impl HostOutputStream for TcpWriteStream { fn write(&mut self, mut bytes: bytes::Bytes) -> Result<(), OutputStreamError> { - if self.write_handle.is_some() { - return Err(OutputStreamError::Trap(anyhow::anyhow!( - "unpermitted: cannot write while background write ongoing" - ))); + match self.last_write { + LastWrite::Done => {} + LastWrite::Waiting(_) | LastWrite::Error(_) => { + return Err(OutputStreamError::Trap(anyhow::anyhow!( + "unpermitted: must call check_write first" + ))); + } } while !bytes.is_empty() { match self.stream.try_write(&bytes) { @@ -192,26 +205,40 @@ impl HostOutputStream for TcpWriteStream { Ok(()) } - async fn write_ready(&mut self) -> Result { - if let Some(handle) = &mut self.write_handle { - handle - .await - .map_err(|e| OutputStreamError::LastOperationFailed(e.into()))?; - - // Only clear out the write handle once the task has exited, to ensure that - // `write_ready` remains cancel-safe. - self.write_handle = None; + fn check_write(&mut self) -> Result { + match mem::replace(&mut self.last_write, LastWrite::Done) { + LastWrite::Waiting(task) => { + self.last_write = LastWrite::Waiting(task); + return Ok(0); + } + LastWrite::Done => {} + LastWrite::Error(e) => return Err(OutputStreamError::LastOperationFailed(e.into())), } - self.stream - .writable() - .await - .map_err(|e| OutputStreamError::LastOperationFailed(e.into()))?; - + let writable = self.stream.writable(); + futures::pin_mut!(writable); + if super::poll_noop(writable).is_none() { + return Ok(0); + } Ok(SOCKET_READY_SIZE) } } +#[async_trait::async_trait] +impl Subscribe for TcpWriteStream { + async fn ready(&mut self) { + if let LastWrite::Waiting(task) = &mut self.last_write { + self.last_write = match task.await { + Ok(()) => LastWrite::Done, + Err(e) => LastWrite::Error(e), + }; + } + if let LastWrite::Done = self.last_write { + self.stream.writable().await.unwrap(); + } + } +} + impl TcpSocket { /// Create a new socket in the given family. pub fn new(family: AddressFamily) -> io::Result { @@ -251,3 +278,20 @@ impl TcpSocket { (InputStream::Host(input), output) } } + +#[async_trait::async_trait] +impl Subscribe for TcpSocket { + async fn ready(&mut self) { + // Some states are ready immediately. + match self.tcp_state { + TcpState::BindStarted | TcpState::ListenStarted | TcpState::ConnectReady => return, + _ => {} + } + + // FIXME: Add `Interest::ERROR` when we update to tokio 1.32. + self.inner + .ready(Interest::READABLE | Interest::WRITABLE) + .await + .unwrap(); + } +} diff --git a/crates/wasi/src/preview2/write_stream.rs b/crates/wasi/src/preview2/write_stream.rs index bf154aec0720..726d0d2674bb 100644 --- a/crates/wasi/src/preview2/write_stream.rs +++ b/crates/wasi/src/preview2/write_stream.rs @@ -1,4 +1,4 @@ -use crate::preview2::{HostOutputStream, OutputStreamError}; +use crate::preview2::{HostOutputStream, OutputStreamError, Subscribe}; use anyhow::anyhow; use bytes::Bytes; use std::sync::{Arc, Mutex}; @@ -35,11 +35,6 @@ enum Job { Write(Bytes), } -enum WriteStatus<'a> { - Done(Result), - Pending(tokio::sync::futures::Notified<'a>), -} - impl Worker { fn new(write_budget: usize) -> Self { Self { @@ -54,17 +49,31 @@ impl Worker { write_ready_changed: tokio::sync::Notify::new(), } } - fn check_write(&self) -> WriteStatus<'_> { + async fn ready(&self) { + loop { + { + let state = self.state(); + if state.error.is_some() + || !state.alive + || (!state.flush_pending && state.write_budget > 0) + { + return; + } + } + self.write_ready_changed.notified().await; + } + } + fn check_write(&self) -> Result { let mut state = self.state(); if let Err(e) = state.check_error() { - return WriteStatus::Done(Err(e)); + return Err(e); } if state.flush_pending || state.write_budget == 0 { - return WriteStatus::Pending(self.write_ready_changed.notified()); + return Ok(0); } - WriteStatus::Done(Ok(state.write_budget)) + Ok(state.write_budget) } fn state(&self) -> std::sync::MutexGuard { self.state.lock().unwrap() @@ -154,7 +163,6 @@ impl AsyncWriteStream { } } -#[async_trait::async_trait] impl HostOutputStream for AsyncWriteStream { fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError> { let mut state = self.worker.state(); @@ -185,12 +193,13 @@ impl HostOutputStream for AsyncWriteStream { Ok(()) } - async fn write_ready(&mut self) -> Result { - loop { - match self.worker.check_write() { - WriteStatus::Done(r) => return r, - WriteStatus::Pending(notifier) => notifier.await, - } - } + fn check_write(&mut self) -> Result { + self.worker.check_write() + } +} +#[async_trait::async_trait] +impl Subscribe for AsyncWriteStream { + async fn ready(&mut self) { + self.worker.ready().await; } } From 9d3419741c6cca6c0b718972392d9b90453f52bd Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Mon, 2 Oct 2023 21:23:38 -0700 Subject: [PATCH 040/199] Fix a regression with `wasmtime serve`, and enable the serve feature in tests (#7137) --- ci/run-tests.sh | 1 + src/commands/serve.rs | 15 ++++++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/ci/run-tests.sh b/ci/run-tests.sh index b29b6eb71d9b..568ff4405413 100755 --- a/ci/run-tests.sh +++ b/ci/run-tests.sh @@ -5,6 +5,7 @@ cargo test \ --features wasi-threads \ --features wasi-http \ --features component-model \ + --features serve \ --workspace \ --exclude 'wasmtime-wasi-*' \ --exclude wasi-tests \ diff --git a/src/commands/serve.rs b/src/commands/serve.rs index 5cd1a8225e4c..bfca73c92cd2 100644 --- a/src/commands/serve.rs +++ b/src/commands/serve.rs @@ -14,14 +14,12 @@ struct Host { } impl Host { - fn new() -> Result { - let mut table = Table::new(); - let ctx = WasiCtxBuilder::new().build(&mut table)?; - Ok(Host { - table, - ctx, + fn new() -> Self { + Host { + table: Table::new(), + ctx: WasiCtxBuilder::new().build(), http: WasiHttpCtx, - }) + } } } @@ -168,8 +166,7 @@ impl hyper::service::Service for ProxyHandler { // TODO: need to track the join handle, but don't want to block the response on it tokio::task::spawn(async move { - let host = Host::new()?; - let mut store = Store::new(&handler.engine, host); + let mut store = Store::new(&handler.engine, Host::new()); let req = store.data_mut().new_incoming_request( req.map(|body| body.map_err(|e| anyhow::anyhow!(e)).boxed()), From c927662079ffd2212065e44d28e7a8de4f03b7d0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Oct 2023 05:21:24 -0500 Subject: [PATCH 041/199] riscv64: Refactor implementation of `{u,s}{div,rem}` (#7136) * riscv64: Refactor implementation of `{u,s}{div,rem}` This commit's goal is to remove the usage of `gen_icmp` with division-related instructions in the riscv64 backend. I've opted for a slightly more verbose approach than what was present prior to make sign/zero extensions more precise and additionally enable some immediate-related optimizations. Divison/remainder by an immediate will no longer emit checks to see if a trap needs to be emitted. Instead only the raw `div`/`rem` instructions are emitted. Additionally a few minor sign extensions are now avoided such as the dividend in 32-bit remainders/division because only the low 32-bits are inspected. Finally, while I was here, I went ahead an added `has_m` guards to all these lowering rules. The M extension is always required with riscv64 right now and won't work if it's turned off, but I figure it's not too bad to add in terms of completeness for now. As to the meat of the commit, the check for trapping value as part of division now happens without `gen_icmp` but instead `gen_trapif` which enables avoiding materializing the comparison's value into a register. * Fix tests --- cranelift/codegen/src/isa/riscv64/inst.isle | 22 +- cranelift/codegen/src/isa/riscv64/lower.isle | 203 +++++++++++------ .../codegen/src/isa/riscv64/lower/isle.rs | 20 +- .../filetests/isa/riscv64/arithmetic.clif | 210 +++++------------- 4 files changed, 199 insertions(+), 256 deletions(-) diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 4ed696761e3f..932014836de8 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -1090,6 +1090,9 @@ ;; ISA Extension helpers +(decl pure has_m () bool) +(extern constructor has_m has_m) + (decl pure has_v () bool) (extern constructor has_v has_v) @@ -2789,25 +2792,6 @@ (rule (gen_trapz test trap_code) (gen_trapif (IntCC.Equal) test (zero_reg) trap_code)) - -(decl shift_int_to_most_significant (XReg Type) XReg) -(extern constructor shift_int_to_most_significant shift_int_to_most_significant) - -;;; generate div overflow. -(decl gen_div_overflow (XReg XReg Type) InstOutput) -(rule (gen_div_overflow rs1 rs2 ty) - (let ((r_const_neg_1 XReg (imm $I64 (i64_as_u64 -1))) - (r_const_min XReg (rv_slli (imm $I64 1) (imm12_const 63))) - (tmp_rs1 XReg (shift_int_to_most_significant rs1 ty)) - (t1 XReg (gen_icmp (IntCC.Equal) r_const_neg_1 rs2 ty)) - (t2 XReg (gen_icmp (IntCC.Equal) r_const_min tmp_rs1 ty)) - (test XReg (rv_and t1 t2))) - (gen_trapnz test (TrapCode.IntegerOverflow)))) - -(decl gen_div_by_zero (XReg) InstOutput) -(rule (gen_div_by_zero r) - (gen_trapz r (TrapCode.IntegerDivisionByZero))) - ;;;; Helpers for Emitting Calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (decl gen_call (SigRef ExternalName RelocDistance ValueSlice) InstOutput) diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index 956c3bd3f9b7..1fbf014c5095 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -505,68 +505,147 @@ (rule 4 (lower (has_type (ty_vec_fits_in_register ty) (umulhi x (splat y)))) (rv_vmulhu_vx x y (unmasked) ty)) -;;;; Rules for `div` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(rule -1 (lower (has_type (fits_in_32 ty) (udiv x y))) - (let - ((y2 XReg (zext y)) - (_ InstOutput (gen_div_by_zero y2))) - (rv_divuw (zext x) y2))) - -(rule -1 (lower (has_type (fits_in_32 ty) (sdiv x y))) - (let - ((a XReg (sext x)) - (b XReg (sext y)) - (_ InstOutput (gen_div_overflow a b ty)) - (_ InstOutput (gen_div_by_zero b))) - (rv_divw a b))) - -(rule (lower (has_type $I64 (sdiv x y))) - (let - ((_ InstOutput (gen_div_overflow x y $I64)) - (_ InstOutput (gen_div_by_zero y)) ) - (rv_div x y))) - -(rule (lower (has_type $I64 (udiv x y))) - (let - ((_ InstOutput (gen_div_by_zero y))) - (rv_divu x y))) - -;;;; Rules for `rem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(rule -1 (lower (has_type (fits_in_16 ty) (urem x y))) - (let - ((y2 XReg (zext y)) - (_ InstOutput (gen_div_by_zero y2))) - (rv_remuw (zext x) y2))) - -(rule -1 (lower (has_type (fits_in_16 ty) (srem x y))) - (let - ((y2 XReg (sext y)) - (_ InstOutput (gen_div_by_zero y2))) - (rv_remw (sext x) y2))) - -(rule (lower (has_type $I32 (srem x y))) - (let - ((y2 XReg (sext y)) - (_ InstOutput (gen_div_by_zero y2))) - (rv_remw x y2))) - -(rule (lower (has_type $I32 (urem x y))) - (let - ((y2 XReg (zext y)) - (_ InstOutput (gen_div_by_zero y2))) - (rv_remuw x y2))) - -(rule (lower (has_type $I64 (srem x y))) - (let - ((_ InstOutput (gen_div_by_zero y))) - (rv_rem x y))) - -(rule (lower (has_type $I64 (urem x y))) - (let - ((_ InstOutput (gen_div_by_zero y))) - (rv_remu x y))) +;;;; Rules for `udiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule 0 (lower (has_type (fits_in_16 ty) (udiv x y))) + (if-let $true (has_m)) + (rv_divuw (zext x) (nonzero_divisor (zext y)))) + +(rule 1 (lower (has_type (fits_in_16 ty) (udiv x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 ty imm)) + (rv_divuw (zext x) (zext y))) + +(rule 2 (lower (has_type $I32 (udiv x y))) + (if-let $true (has_m)) + (rv_divuw x (nonzero_divisor (zext y)))) + +(rule 3 (lower (has_type $I32 (udiv x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 $I32 imm)) + (rv_divuw x y)) + +(rule 2 (lower (has_type $I64 (udiv x y))) + (if-let $true (has_m)) + (rv_divu x (nonzero_divisor y))) + +(rule 3 (lower (has_type $I64 (udiv x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 $I64 imm)) + (rv_divu x y)) + +;; Traps if the input register is zero, otherwise returns the same register. +(decl nonzero_divisor (XReg) XReg) +(rule (nonzero_divisor val) + (let ((_ InstOutput (gen_trapif (IntCC.Equal) val (zero_reg) (TrapCode.IntegerDivisionByZero)))) + val)) + +;;;; Rules for `sdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule 0 (lower (has_type (fits_in_16 ty) (sdiv x y))) + (if-let $true (has_m)) + (let ((x XReg (sext x))) + (rv_divw x (safe_sdiv_divisor ty x (sext y))))) + +(rule 1 (lower (has_type (fits_in_16 ty) (sdiv x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 ty imm)) + (rv_divw (sext x) y)) + +(rule 2 (lower (has_type $I32 (sdiv x y))) + (if-let $true (has_m)) + (let ((x XReg (sext x))) + (rv_divw x (safe_sdiv_divisor $I32 x (sext y))))) + +(rule 3 (lower (has_type $I32 (sdiv x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 $I32 imm)) + (rv_divw x y)) + +(rule 2 (lower (has_type $I64 (sdiv x y))) + (if-let $true (has_m)) + (rv_div x (safe_sdiv_divisor $I64 x y))) + +(rule 3 (lower (has_type $I64 (sdiv x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 $I64 imm)) + (rv_div x y)) + +;; Check for two trapping conditions: +;; +;; * the divisor is 0, or... +;; * the divisor is -1 and the dividend is $ty::MIN +(decl safe_sdiv_divisor (Type XReg XReg) XReg) +(rule (safe_sdiv_divisor ty x y) + (let ( + (y XReg (nonzero_divisor y)) + (min XReg (imm $I64 (u64_shl 0xffffffff_ffffffff (u64_sub (ty_bits ty) 1)))) + (x_is_not_min XReg (rv_xor x min)) + (y_is_not_neg_one XReg (rv_not y)) + (no_int_overflow XReg (rv_or x_is_not_min y_is_not_neg_one)) + (_ InstOutput (gen_trapif + (IntCC.Equal) + no_int_overflow (zero_reg) + (TrapCode.IntegerOverflow)))) + y)) + +;;;; Rules for `urem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule 0 (lower (has_type (fits_in_16 ty) (urem x y))) + (if-let $true (has_m)) + (rv_remuw (zext x) (nonzero_divisor (zext y)))) + +(rule 1 (lower (has_type (fits_in_16 ty) (urem x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 ty imm)) + (rv_remuw (zext x) y)) + +(rule 2 (lower (has_type $I32 (urem x y))) + (if-let $true (has_m)) + (rv_remuw x (nonzero_divisor (zext y)))) + +(rule 3 (lower (has_type $I32 (urem x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 $I32 imm)) + (rv_remuw x y)) + +(rule 2 (lower (has_type $I64 (urem x y))) + (if-let $true (has_m)) + (rv_remu x (nonzero_divisor y))) + +(rule 3 (lower (has_type $I64 (urem x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 $I64 imm)) + (rv_remu x y)) + +;;;; Rules for `srem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule 0 (lower (has_type (fits_in_16 ty) (srem x y))) + (if-let $true (has_m)) + (rv_remw (sext x) (nonzero_divisor (sext y)))) + +(rule 1 (lower (has_type (fits_in_16 ty) (srem x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 ty imm)) + (rv_remw (sext x) y)) + +(rule 2 (lower (has_type $I32 (srem x y))) + (if-let $true (has_m)) + (rv_remw x (nonzero_divisor (sext y)))) + +(rule 3 (lower (has_type $I32 (srem x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 $I32 imm)) + (rv_remw x y)) + +(rule 2 (lower (has_type $I64 (srem x y))) + (if-let $true (has_m)) + (rv_rem x (nonzero_divisor y))) + +(rule 3 (lower (has_type $I64 (srem x y @ (iconst imm)))) + (if-let $true (has_m)) + (if (safe_divisor_from_imm64 $I64 imm)) + (rv_rem x y)) ;;;; Rules for `and` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule 0 (lower (has_type (ty_int ty) (band x y))) diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs index 86a2653a631e..7432b821654c 100644 --- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs +++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs @@ -392,6 +392,10 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> self.backend.isa_flags.has_v() } + fn has_m(&mut self) -> bool { + self.backend.isa_flags.has_m() + } + fn has_zbkb(&mut self) -> bool { self.backend.isa_flags.has_zbkb() } @@ -501,22 +505,6 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> px_reg(2) } - fn shift_int_to_most_significant(&mut self, v: XReg, ty: Type) -> XReg { - assert!(ty.is_int() && ty.bits() <= 64); - if ty == I64 { - return v; - } - let tmp = self.temp_writable_reg(I64); - self.emit(&MInst::AluRRImm12 { - alu_op: AluOPRRI::Slli, - rd: tmp, - rs: v.to_reg(), - imm12: Imm12::from_i16((64 - ty.bits()) as i16), - }); - - self.xreg_new(tmp.to_reg()) - } - #[inline] fn int_compare(&mut self, kind: &IntCC, rs1: XReg, rs2: XReg) -> IntegerCompare { IntegerCompare { diff --git a/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif b/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif index a04b3b8cfefe..062a65b5d8a6 100644 --- a/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif +++ b/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif @@ -90,35 +90,27 @@ block0(v0: i64, v1: i64): ; VCode: ; block0: -; li a3,-1 -; li a5,1 -; slli a2,a5,63 -; eq a3,a3,a1##ty=i64 -; eq a5,a2,a0##ty=i64 -; and a2,a3,a5 -; trap_if int_ovf##(a2 ne zero) ; trap_if int_divz##(a1 eq zero) +; li a4,-1 +; slli a2,a4,63 +; xor a2,a0,a2 +; not a4,a1 +; or a2,a2,a4 +; trap_if int_ovf##(a2 eq zero) ; div a0,a0,a1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, -1 -; addi a5, zero, 1 -; slli a2, a5, 0x3f -; bne a3, a1, 0xc -; addi a3, zero, 1 -; j 8 -; mv a3, zero -; bne a2, a0, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; and a2, a3, a5 -; beqz a2, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf ; bnez a1, 8 ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz +; addi a4, zero, -1 +; slli a2, a4, 0x3f +; xor a2, a0, a2 +; not a4, a1 +; or a2, a2, a4 +; bnez a2, 8 +; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf ; div a0, a0, a1 ; ret @@ -132,36 +124,12 @@ block0(v0: i64): ; VCode: ; block0: ; li a3,2 -; li a4,-1 -; li a5,1 -; slli a1,a5,63 -; eq a4,a4,a3##ty=i64 -; eq a5,a1,a0##ty=i64 -; and a1,a4,a5 -; trap_if int_ovf##(a1 ne zero) -; trap_if int_divz##(a3 eq zero) ; div a0,a0,a3 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 2 -; addi a4, zero, -1 -; addi a5, zero, 1 -; slli a1, a5, 0x3f -; bne a4, a3, 0xc -; addi a4, zero, 1 -; j 8 -; mv a4, zero -; bne a1, a0, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; and a1, a4, a5 -; beqz a1, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf -; bnez a3, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz ; div a0, a0, a3 ; ret @@ -194,15 +162,12 @@ block0(v0: i64): ; VCode: ; block0: ; li a3,2 -; trap_if int_divz##(a3 eq zero) ; divu a0,a0,a3 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 2 -; bnez a3, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz ; divu a0, a0, a3 ; ret @@ -254,15 +219,12 @@ block0(v0: i32, v1: i32): ; block0: ; sext.w a3,a0 ; sext.w a5,a1 -; li a1,-1 -; li a4,1 -; slli a0,a4,63 -; slli a2,a3,32 -; eq a4,a1,a5##ty=i32 -; eq a0,a0,a2##ty=i32 -; and a1,a4,a0 -; trap_if int_ovf##(a1 ne zero) ; trap_if int_divz##(a5 eq zero) +; lui a2,-524288 +; xor a4,a3,a2 +; not a0,a5 +; or a2,a4,a0 +; trap_if int_ovf##(a2 eq zero) ; divw a0,a3,a5 ; ret ; @@ -270,23 +232,14 @@ block0(v0: i32, v1: i32): ; block0: ; offset 0x0 ; sext.w a3, a0 ; sext.w a5, a1 -; addi a1, zero, -1 -; addi a4, zero, 1 -; slli a0, a4, 0x3f -; slli a2, a3, 0x20 -; bne a1, a5, 0xc -; addi a4, zero, 1 -; j 8 -; mv a4, zero -; bne a0, a2, 0xc -; addi a0, zero, 1 -; j 8 -; mv a0, zero -; and a1, a4, a0 -; beqz a1, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf ; bnez a5, 8 ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz +; lui a2, 0x80000 +; xor a4, a3, a2 +; not a0, a5 +; or a2, a4, a0 +; bnez a2, 8 +; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf ; divw a0, a3, a5 ; ret @@ -299,46 +252,14 @@ block0(v0: i32): ; VCode: ; block0: -; mv a2,a0 -; li a0,2 -; sext.w a3,a2 -; sext.w a5,a0 -; li a1,-1 -; li a4,1 -; slli a0,a4,63 -; slli a2,a3,32 -; eq a4,a1,a5##ty=i32 -; eq a0,a0,a2##ty=i32 -; and a1,a4,a0 -; trap_if int_ovf##(a1 ne zero) -; trap_if int_divz##(a5 eq zero) -; divw a0,a3,a5 +; li a3,2 +; divw a0,a0,a3 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mv a2, a0 -; addi a0, zero, 2 -; sext.w a3, a2 -; sext.w a5, a0 -; addi a1, zero, -1 -; addi a4, zero, 1 -; slli a0, a4, 0x3f -; slli a2, a3, 0x20 -; bne a1, a5, 0xc -; addi a4, zero, 1 -; j 8 -; mv a4, zero -; bne a0, a2, 0xc -; addi a0, zero, 1 -; j 8 -; mv a0, zero -; and a1, a4, a0 -; beqz a1, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf -; bnez a5, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz -; divw a0, a3, a5 +; addi a3, zero, 2 +; divw a0, a0, a3 ; ret function %f14(i32, i32) -> i32 { @@ -352,9 +273,7 @@ block0(v0: i32, v1: i32): ; slli a3,a1,32 ; srli a5,a3,32 ; trap_if int_divz##(a5 eq zero) -; slli a2,a0,32 -; srli a4,a2,32 -; divuw a0,a4,a5 +; divuw a0,a0,a5 ; ret ; ; Disassembled: @@ -363,9 +282,7 @@ block0(v0: i32, v1: i32): ; srli a5, a3, 0x20 ; bnez a5, 8 ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz -; slli a2, a0, 0x20 -; srli a4, a2, 0x20 -; divuw a0, a4, a5 +; divuw a0, a0, a5 ; ret function %f15(i32) -> i32 { @@ -377,25 +294,14 @@ block0(v0: i32): ; VCode: ; block0: -; li a1,2 -; slli a3,a1,32 -; srli a5,a3,32 -; trap_if int_divz##(a5 eq zero) -; slli a2,a0,32 -; srli a4,a2,32 -; divuw a0,a4,a5 +; li a3,2 +; divuw a0,a0,a3 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a1, zero, 2 -; slli a3, a1, 0x20 -; srli a5, a3, 0x20 -; bnez a5, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz -; slli a2, a0, 0x20 -; srli a4, a2, 0x20 -; divuw a0, a4, a5 +; addi a3, zero, 2 +; divuw a0, a0, a3 ; ret function %f16(i32, i32) -> i32 { @@ -807,15 +713,12 @@ block0(v0: i64): ; VCode: ; block0: ; li a3,2 -; trap_if int_divz##(a3 eq zero) ; rem a0,a0,a3 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 2 -; bnez a3, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz ; rem a0, a0, a3 ; ret @@ -829,15 +732,12 @@ block0(v0: i64): ; VCode: ; block0: ; li a3,2 -; trap_if int_divz##(a3 eq zero) ; remu a0,a0,a3 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 2 -; bnez a3, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz ; remu a0, a0, a3 ; ret @@ -850,38 +750,30 @@ block0(v0: i64): ; VCode: ; block0: -; li a3,-1 +; li a2,-1 +; trap_if int_divz##(a2 eq zero) ; li a4,-1 -; li a5,1 -; slli a1,a5,63 -; eq a4,a4,a3##ty=i64 -; eq a5,a1,a0##ty=i64 -; and a1,a4,a5 -; trap_if int_ovf##(a1 ne zero) -; trap_if int_divz##(a3 eq zero) -; div a0,a0,a3 +; slli a1,a4,63 +; xor a3,a0,a1 +; not a4,a2 +; or a1,a3,a4 +; trap_if int_ovf##(a1 eq zero) +; div a0,a0,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, -1 +; addi a2, zero, -1 +; bnez a2, 8 +; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz ; addi a4, zero, -1 -; addi a5, zero, 1 -; slli a1, a5, 0x3f -; bne a4, a3, 0xc -; addi a4, zero, 1 -; j 8 -; mv a4, zero -; bne a1, a0, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; and a1, a4, a5 -; beqz a1, 8 +; slli a1, a4, 0x3f +; xor a3, a0, a1 +; not a4, a2 +; or a1, a3, a4 +; bnez a1, 8 ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf -; bnez a3, 8 -; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz -; div a0, a0, a3 +; div a0, a0, a2 ; ret function %i8_iadd_const_neg1(i8) -> i8 { From 37cf8e1e75296ea4811894e0c7e52c72b40fcb24 Mon Sep 17 00:00:00 2001 From: Tyler Rockwood Date: Tue, 3 Oct 2023 08:42:56 -0500 Subject: [PATCH 042/199] Async support in the C API (#7106) * c-api: Add a feature for async Signed-off-by: Tyler Rockwood * c-api: Add support for async config Signed-off-by: Tyler Rockwood * c-api: Add support for calling async functions Signed-off-by: Tyler Rockwood * c-api: Add ability to yield execution of Wasm in a store Signed-off-by: Tyler Rockwood * c-api: Introduce wasmtime_linker_instantiate_async Signed-off-by: Tyler Rockwood * c-api: Support defining async host functions Signed-off-by: Tyler Rockwood * gitignore: ignore cmake cache for examples Signed-off-by: Tyler Rockwood * examples: Add example of async API in C Signed-off-by: Tyler Rockwood * c-api: Consolidate async functionality into a single place Put all the async stuff in it's own header and own rust source file Also remove the wasmtime_async_continuation_new function, users can just allocate it directly. Signed-off-by: Tyler Rockwood * c-api: Make async function safe Signed-off-by: Tyler Rockwood * c-api: Remove wasmtime_call_future_get_results Signed-off-by: Tyler Rockwood * c-api: Simplify CHostCallFuture Move the result translation and hostcall_val_storage usage into an async function Signed-off-by: Tyler Rockwood * c-api: Simplify C continuation implementation Remove the caller, which means that we don't need another struct for the future implementation. Signed-off-by: Tyler Rockwood * c-api: Improve async.h documentation Signed-off-by: Tyler Rockwood * c-api: Cleanup from previous changes Signed-off-by: Tyler Rockwood * examples: Fix example Signed-off-by: Tyler Rockwood * c-api: Simplify continuation callback This gives more duality with calling an async function and also means that the implementation can pretty much mirror the sync version. Signed-off-by: Tyler Rockwood * c-api: Fix async.h documentation Signed-off-by: Tyler Rockwood * c-api: Fix documentation for async.h Signed-off-by: Tyler Rockwood * c-api: Review feedback Signed-off-by: Tyler Rockwood * examples: Downgrade async.cpp example to C++11 Signed-off-by: Tyler Rockwood * c-api: initialize continuation with a panic callback Signed-off-by: Tyler Rockwood * prtest:full Signed-off-by: Tyler Rockwood --------- Signed-off-by: Tyler Rockwood --- .gitignore | 1 + Cargo.lock | 1 + crates/c-api/Cargo.toml | 6 +- crates/c-api/include/wasmtime.h | 1 + crates/c-api/include/wasmtime/async.h | 266 ++++++++++++++++++++++ crates/c-api/src/async.rs | 305 ++++++++++++++++++++++++++ crates/c-api/src/func.rs | 4 +- crates/c-api/src/lib.rs | 5 + crates/c-api/src/linker.rs | 4 +- examples/CMakeLists.txt | 5 +- examples/async.cpp | 292 ++++++++++++++++++++++++ examples/async.wat | 18 ++ 12 files changed, 903 insertions(+), 5 deletions(-) create mode 100644 crates/c-api/include/wasmtime/async.h create mode 100644 crates/c-api/src/async.rs create mode 100644 examples/async.cpp create mode 100644 examples/async.wat diff --git a/.gitignore b/.gitignore index d9c728065301..7790b83f8725 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ foo publish vendor examples/build +examples/.cache *.coredump diff --git a/Cargo.lock b/Cargo.lock index 69d27ff848a4..cf04ee8c5ebe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3358,6 +3358,7 @@ dependencies = [ "anyhow", "cap-std", "env_logger 0.10.0", + "futures", "once_cell", "wasi-cap-std-sync", "wasi-common", diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml index 454d244d07d3..47ec57d6c8ec 100644 --- a/crates/c-api/Cargo.toml +++ b/crates/c-api/Cargo.toml @@ -32,8 +32,12 @@ wasmtime-wasi = { workspace = true, default-features = true, optional = true } cap-std = { workspace = true, optional = true } wasi-common = { workspace = true, optional = true } +# Optional dependencies for the `async` feature +futures = { workspace = true, optional = true } + [features] -default = ['jitdump', 'wat', 'wasi', 'cache', 'parallel-compilation'] +default = ['jitdump', 'wat', 'wasi', 'cache', 'parallel-compilation', 'async'] +async = ['wasmtime/async', 'futures'] jitdump = ["wasmtime/jitdump"] cache = ["wasmtime/cache"] parallel-compilation = ['wasmtime/parallel-compilation'] diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index dfe80372d70e..32c0478e5753 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -195,6 +195,7 @@ #include #include #include +#include /** * \brief Wasmtime version string. diff --git a/crates/c-api/include/wasmtime/async.h b/crates/c-api/include/wasmtime/async.h new file mode 100644 index 000000000000..0c92bf8ec31e --- /dev/null +++ b/crates/c-api/include/wasmtime/async.h @@ -0,0 +1,266 @@ +/** + * \file wasmtime/async.h + * + * \brief Wasmtime async functionality + * + * Async functionality in Wasmtime is well documented here: + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support + * + * All WebAssembly executes synchronously, but an async support enables the Wasm code + * be executed on a seperate stack, so it can be paused and resumed. There are three + * mechanisms for yielding control from wasm to the caller: fuel, epochs, and async host functions. + * + * When WebAssembly is executed, a #wasmtime_call_future_t is returned. This struct represents the + * state of the execution and each call to #wasmtime_call_future_poll will execute the WebAssembly + * code on a seperate stack until the function returns or yields control back to the caller. + * + * It's expected these futures are pulled in a loop until completed, at which point the future + * should be deleted. Functions that return a #wasmtime_call_future_t are special in that all + * parameters to that function should not be modified in any way and must be kept alive until + * the future is deleted. This includes concurrent calls for a single store - another function + * on a store should not be called while there is a #wasmtime_call_future_t alive. + * + * As for asynchronous host calls - the reverse contract is upheld. Wasmtime will keep all parameters + * to the function alive and unmodified until the #wasmtime_func_async_continuation_callback_t returns + * true. + * + */ + +#ifndef WASMTIME_ASYNC_H +#define WASMTIME_ASYNC_H + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Whether or not to enable support for asynchronous functions in Wasmtime. + * + * When enabled, the config can optionally define host functions with async. + * Instances created and functions called with this Config must be called through their asynchronous APIs, however. + * For example using wasmtime_func_call will panic when used with this config. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support + */ +WASMTIME_CONFIG_PROP(void, async_support, bool) + +/** + * \brief Configures the size of the stacks used for asynchronous execution. + * + * This setting configures the size of the stacks that are allocated for asynchronous execution. + * + * The value cannot be less than max_wasm_stack. + * + * The amount of stack space guaranteed for host functions is async_stack_size - max_wasm_stack, so take care + * not to set these two values close to one another; doing so may cause host functions to overflow the stack + * and abort the process. + * + * By default this option is 2 MiB. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_stack_size + */ +WASMTIME_CONFIG_PROP(void, async_stack_size, uint64_t) + +/** + * \brief Configures a Store to yield execution of async WebAssembly code periodically. + * + * When a Store is configured to consume fuel with `wasmtime_config_consume_fuel` + * this method will configure what happens when fuel runs out. Specifically executing + * WebAssembly will be suspended and control will be yielded back to the caller. + * + * This is only suitable with use of a store associated with an async config because + * only then are futures used and yields are possible. + */ +WASM_API_EXTERN void wasmtime_context_out_of_fuel_async_yield( + wasmtime_context_t *context, + uint64_t injection_count, + uint64_t fuel_to_inject); + +/** + * \brief Configures epoch-deadline expiration to yield to the async caller and the update the deadline. + * + * This is only suitable with use of a store associated with an async config because + * only then are futures used and yields are possible. + * + * See the Rust documentation for more: + * https://docs.wasmtime.dev/api/wasmtime/struct.Store.html#method.epoch_deadline_async_yield_and_update + */ +WASM_API_EXTERN void wasmtime_context_epoch_deadline_async_yield_and_update( + wasmtime_context_t *context, + uint64_t delta); + +/** + * The callback to determine a continuation's current state. + * + * Return true if the host call has completed, otherwise false will + * continue to yield WebAssembly execution. + */ +typedef bool (*wasmtime_func_async_continuation_callback_t)(void *env); + +/** + * A continuation for the current state of the host function's execution. + */ +typedef struct wasmtime_async_continuation_t { + /// Callback for if the async function has completed. + wasmtime_func_async_continuation_callback_t callback; + /// User-provided argument to pass to the callback. + void *env; + /// A finalizer for the user-provided *env + void (*finalizer)(void *); +} wasmtime_async_continuation_t; + +/** + * \brief Callback signature for #wasmtime_linker_define_async_func. + * + * This is a host function that returns a continuation to be called later. + * + * All the arguments to this function will be kept alive until the continuation + * returns that it has errored or has completed. + * + * \param env user-provided argument passed to #wasmtime_linker_define_async_func + * \param caller a temporary object that can only be used during this function + * call. Used to acquire #wasmtime_context_t or caller's state + * \param args the arguments provided to this function invocation + * \param nargs how many arguments are provided + * \param results where to write the results of this function + * \param nresults how many results must be produced + * \param trap_ret if assigned a not `NULL` value then the called function will + * trap with the returned error. Note that ownership of trap is transferred + * to wasmtime. + * \param continuation_ret the returned continuation that determines when the + * async function has completed executing. + * + * Only supported for async stores. + * + * See #wasmtime_func_callback_t for more information. + */ +typedef void (*wasmtime_func_async_callback_t)( + void *env, + wasmtime_caller_t *caller, + const wasmtime_val_t *args, + size_t nargs, + wasmtime_val_t *results, + size_t nresults, + wasm_trap_t** trap_ret, + wasmtime_async_continuation_t * continuation_ret); + +/** + * \brief The structure representing a asynchronously running function. + * + * This structure is always owned by the caller and must be deleted using #wasmtime_call_future_delete. + * + * Functions that return this type require that the parameters to the function are unmodified until + * this future is destroyed. + */ +typedef struct wasmtime_call_future wasmtime_call_future_t; + +/** + * \brief Executes WebAssembly in the function. + * + * Returns true if the function call has completed. After this function returns true, it should *not* be + * called again for a given future. + * + * This function returns false if execution has yielded either due to being out of fuel + * (see wasmtime_store_out_of_fuel_async_yield), or the epoch has been incremented enough + * (see wasmtime_store_epoch_deadline_async_yield_and_update). The function may also return false if + * asynchronous host functions have been called, which then calling this function will call the + * continuation from the async host function. + * + * For more see the information at + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#asynchronous-wasm + * + */ +WASM_API_EXTERN bool wasmtime_call_future_poll(wasmtime_call_future_t *future); + +/** + * /brief Frees the underlying memory for a future. + * + * All wasmtime_call_future_t are owned by the caller and should be deleted using this function. + */ +WASM_API_EXTERN void wasmtime_call_future_delete(wasmtime_call_future_t *future); + +/** + * \brief Invokes this function with the params given, returning the results asynchronously. + * + * This function is the same as wasmtime_func_call except that it is asynchronous. + * This is only compatible with stores associated with an asynchronous config. + * + * The result is a future that is owned by the caller and must be deleted via #wasmtime_call_future_delete. + * + * The `args` and `results` pointers may be `NULL` if the corresponding length is zero. + * The `trap_ret` and `error_ret` pointers may *not* be `NULL`. + * + * Does not take ownership of #wasmtime_val_t arguments or #wasmtime_val_t results, + * and all parameters to this function must be kept alive and not modified until the + * returned #wasmtime_call_future_t is deleted. This includes the context and store + * parameters. Only a single future can be alive for a given store at a single time + * (meaning only call this function after the previous call's future was deleted). + * + * See the header documentation for for more information. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/struct.Func.html#method.call_async + */ +WASM_API_EXTERN wasmtime_call_future_t* wasmtime_func_call_async( + wasmtime_context_t *context, + const wasmtime_func_t *func, + const wasmtime_val_t *args, + size_t nargs, + wasmtime_val_t *results, + size_t nresults, + wasm_trap_t** trap_ret, + wasmtime_error_t** error_ret); + +/** + * \brief Defines a new async function in this linker. + * + * This function behaves similar to #wasmtime_linker_define_func, except it supports async + * callbacks. + * + * The callback `cb` will be invoked on another stack (fiber for Windows). + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_linker_define_async_func( + wasmtime_linker_t *linker, + const char *module, + size_t module_len, + const char *name, + size_t name_len, + const wasm_functype_t *ty, + wasmtime_func_async_callback_t cb, + void *data, + void (*finalizer)(void *)); + +/** + * \brief Instantiates a #wasm_module_t with the items defined in this linker for an async store. + * + * This is the same as #wasmtime_linker_instantiate but used for async stores + * (which requires functions are called asynchronously). The returning #wasmtime_call_future_t + * must be polled using #wasmtime_call_future_poll, and is owned and must be deleted using #wasmtime_call_future_delete. + * + * The `trap_ret` and `error_ret` pointers may *not* be `NULL` and the returned memory is owned by the caller. + * + * All arguments to this function must outlive the returned future and be unmodified until the future is deleted. + */ +WASM_API_EXTERN wasmtime_call_future_t *wasmtime_linker_instantiate_async( + const wasmtime_linker_t *linker, + wasmtime_context_t *store, + const wasmtime_module_t *module, + wasmtime_instance_t *instance, + wasm_trap_t** trap_ret, + wasmtime_error_t** wasmtime_error_t); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_ASYNC_H + diff --git a/crates/c-api/src/async.rs b/crates/c-api/src/async.rs new file mode 100644 index 000000000000..99702cf26402 --- /dev/null +++ b/crates/c-api/src/async.rs @@ -0,0 +1,305 @@ +use std::ffi::c_void; +use std::future::Future; +use std::mem::{self, MaybeUninit}; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::{ptr, str}; + +use wasmtime::{AsContextMut, Caller, Func, Instance, Result, Trap, Val}; + +use crate::{ + bad_utf8, handle_result, to_str, translate_args, wasm_config_t, wasm_functype_t, wasm_trap_t, + wasmtime_caller_t, wasmtime_error_t, wasmtime_linker_t, wasmtime_module_t, wasmtime_val_t, + wasmtime_val_union, CStoreContextMut, WASMTIME_I32, +}; + +#[no_mangle] +pub extern "C" fn wasmtime_config_async_support_set(c: &mut wasm_config_t, enable: bool) { + c.config.async_support(enable); +} + +#[no_mangle] +pub extern "C" fn wasmtime_config_async_stack_size_set(c: &mut wasm_config_t, size: usize) { + c.config.async_stack_size(size); +} + +#[no_mangle] +pub extern "C" fn wasmtime_context_epoch_deadline_async_yield_and_update( + mut store: CStoreContextMut<'_>, + delta: u64, +) { + store.epoch_deadline_async_yield_and_update(delta); +} + +#[no_mangle] +pub extern "C" fn wasmtime_context_out_of_fuel_async_yield( + mut store: CStoreContextMut<'_>, + injection_count: u64, + fuel_to_inject: u64, +) { + store.out_of_fuel_async_yield(injection_count, fuel_to_inject); +} + +pub type wasmtime_func_async_callback_t = extern "C" fn( + *mut c_void, + *mut wasmtime_caller_t, + *const wasmtime_val_t, + usize, + *mut wasmtime_val_t, + usize, + &mut Option>, + &mut wasmtime_async_continuation_t, +); + +#[repr(C)] +pub struct wasmtime_async_continuation_t { + pub callback: wasmtime_func_async_continuation_callback_t, + pub env: *mut c_void, + pub finalizer: Option, +} + +unsafe impl Send for wasmtime_async_continuation_t {} +unsafe impl Sync for wasmtime_async_continuation_t {} +impl Drop for wasmtime_async_continuation_t { + fn drop(&mut self) { + if let Some(f) = self.finalizer { + f(self.env); + } + } +} +impl Future for wasmtime_async_continuation_t { + type Output = (); + fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll { + let this = self.get_mut(); + let cb = this.callback; + if cb(this.env) { + Poll::Ready(()) + } else { + Poll::Pending + } + } +} + +pub type wasmtime_func_async_continuation_callback_t = extern "C" fn(*mut c_void) -> bool; + +struct CallbackData { + env: *mut c_void, +} +unsafe impl Send for CallbackData {} + +async fn invoke_c_async_callback<'a>( + cb: wasmtime_func_async_callback_t, + data: CallbackData, + mut caller: Caller<'a, crate::StoreData>, + params: &'a [Val], + results: &'a mut [Val], +) -> Result<()> { + // Convert `params/results` to `wasmtime_val_t`. Use the previous + // storage in `hostcall_val_storage` to help avoid allocations all the + // time. + let mut hostcall_val_storage = mem::take(&mut caller.data_mut().hostcall_val_storage); + debug_assert!(hostcall_val_storage.is_empty()); + hostcall_val_storage.reserve(params.len() + results.len()); + hostcall_val_storage.extend(params.iter().cloned().map(|p| wasmtime_val_t::from_val(p))); + hostcall_val_storage.extend((0..results.len()).map(|_| wasmtime_val_t { + kind: WASMTIME_I32, + of: wasmtime_val_union { i32: 0 }, + })); + let (params, out_results) = hostcall_val_storage.split_at_mut(params.len()); + + // Invoke the C function pointer. + // The result will be a continutation which we will wrap in a Future. + let mut caller = wasmtime_caller_t { caller }; + let mut trap = None; + extern "C" fn panic_callback(_: *mut c_void) -> bool { + panic!("callback must be set") + } + let mut continuation = wasmtime_async_continuation_t { + callback: panic_callback, + env: ptr::null_mut(), + finalizer: None, + }; + cb( + data.env, + &mut caller, + params.as_ptr(), + params.len(), + out_results.as_mut_ptr(), + out_results.len(), + &mut trap, + &mut continuation, + ); + continuation.await; + + if let Some(trap) = trap { + return Err(trap.error); + } + + // Translate the `wasmtime_val_t` results into the `results` space + for (i, result) in out_results.iter().enumerate() { + unsafe { + results[i] = result.to_val(); + } + } + // Move our `vals` storage back into the store now that we no longer + // need it. This'll get picked up by the next hostcall and reuse our + // same storage. + hostcall_val_storage.truncate(0); + caller.caller.data_mut().hostcall_val_storage = hostcall_val_storage; + Ok(()) +} + +unsafe fn c_async_callback_to_rust_fn( + callback: wasmtime_func_async_callback_t, + data: *mut c_void, + finalizer: Option, +) -> impl for<'a> Fn( + Caller<'a, crate::StoreData>, + &'a [Val], + &'a mut [Val], +) -> Box> + Send + 'a> + + Send + + Sync + + 'static { + let foreign = crate::ForeignData { data, finalizer }; + move |caller, params, results| { + let _ = &foreign; // move entire foreign into this closure + let data = CallbackData { env: foreign.data }; + Box::new(invoke_c_async_callback( + callback, data, caller, params, results, + )) + } +} + +#[repr(transparent)] +pub struct wasmtime_call_future_t<'a> { + underlying: Pin + 'a>>, +} + +#[no_mangle] +pub extern "C" fn wasmtime_call_future_delete(_future: Box) {} + +#[no_mangle] +pub extern "C" fn wasmtime_call_future_poll(future: &mut wasmtime_call_future_t) -> bool { + let w = futures::task::noop_waker_ref(); + match future.underlying.as_mut().poll(&mut Context::from_waker(w)) { + Poll::Ready(()) => true, + Poll::Pending => false, + } +} + +fn handle_call_error( + err: wasmtime::Error, + trap_ret: &mut *mut wasm_trap_t, + err_ret: &mut *mut wasmtime_error_t, +) { + if err.is::() { + *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err))); + } else { + *err_ret = Box::into_raw(Box::new(wasmtime_error_t::from(err))); + } +} + +async fn do_func_call_async( + mut store: CStoreContextMut<'_>, + func: &Func, + args: impl ExactSizeIterator, + results: &mut [MaybeUninit], + trap_ret: &mut *mut wasm_trap_t, + err_ret: &mut *mut wasmtime_error_t, +) { + let mut store = store.as_context_mut(); + let mut params = mem::take(&mut store.data_mut().wasm_val_storage); + let (wt_params, wt_results) = translate_args(&mut params, args, results.len()); + let result = func.call_async(&mut store, wt_params, wt_results).await; + + match result { + Ok(()) => { + for (slot, val) in results.iter_mut().zip(wt_results.iter()) { + crate::initialize(slot, wasmtime_val_t::from_val(val.clone())); + } + params.truncate(0); + store.data_mut().wasm_val_storage = params; + } + Err(err) => handle_call_error(err, trap_ret, err_ret), + } +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_func_call_async<'a>( + store: CStoreContextMut<'a>, + func: &'a Func, + args: *const wasmtime_val_t, + nargs: usize, + results: *mut MaybeUninit, + nresults: usize, + trap_ret: &'a mut *mut wasm_trap_t, + err_ret: &'a mut *mut wasmtime_error_t, +) -> Box> { + let args = crate::slice_from_raw_parts(args, nargs) + .iter() + .map(|i| i.to_val()); + let results = crate::slice_from_raw_parts_mut(results, nresults); + let fut = Box::pin(do_func_call_async( + store, func, args, results, trap_ret, err_ret, + )); + Box::new(wasmtime_call_future_t { underlying: fut }) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_linker_define_async_func( + linker: &mut wasmtime_linker_t, + module: *const u8, + module_len: usize, + name: *const u8, + name_len: usize, + ty: &wasm_functype_t, + callback: crate::wasmtime_func_async_callback_t, + data: *mut c_void, + finalizer: Option, +) -> Option> { + let ty = ty.ty().ty.clone(); + let module = to_str!(module, module_len); + let name = to_str!(name, name_len); + let cb = c_async_callback_to_rust_fn(callback, data, finalizer); + + handle_result( + linker.linker.func_new_async(module, name, ty, cb), + |_linker| (), + ) +} + +async fn do_linker_instantiate_async( + linker: &wasmtime_linker_t, + store: CStoreContextMut<'_>, + module: &wasmtime_module_t, + instance_ptr: &mut Instance, + trap_ret: &mut *mut wasm_trap_t, + err_ret: &mut *mut wasmtime_error_t, +) { + let result = linker.linker.instantiate_async(store, &module.module).await; + match result { + Ok(instance) => *instance_ptr = instance, + Err(err) => handle_call_error(err, trap_ret, err_ret), + } +} + +#[no_mangle] +pub extern "C" fn wasmtime_linker_instantiate_async<'a>( + linker: &'a wasmtime_linker_t, + store: CStoreContextMut<'a>, + module: &'a wasmtime_module_t, + instance_ptr: &'a mut Instance, + trap_ret: &'a mut *mut wasm_trap_t, + err_ret: &'a mut *mut wasmtime_error_t, +) -> Box> { + let fut = Box::pin(do_linker_instantiate_async( + linker, + store, + module, + instance_ptr, + trap_ret, + err_ret, + )); + Box::new(crate::wasmtime_call_future_t { underlying: fut }) +} diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs index 31da871ae7c7..8fbec441da29 100644 --- a/crates/c-api/src/func.rs +++ b/crates/c-api/src/func.rs @@ -113,7 +113,7 @@ pub unsafe extern "C" fn wasm_func_new_with_env( /// Places the `args` into `dst` and additionally reserves space in `dst` for `results_size` /// returns. The params/results slices are then returned separately. -fn translate_args<'a>( +pub(crate) fn translate_args<'a>( dst: &'a mut Vec, args: impl ExactSizeIterator, results_size: usize, @@ -200,7 +200,7 @@ pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t { #[repr(C)] pub struct wasmtime_caller_t<'a> { - caller: Caller<'a, crate::StoreData>, + pub(crate) caller: Caller<'a, crate::StoreData>, } pub type wasmtime_func_callback_t = extern "C" fn( diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index 3ef450731902..ea4696e9387e 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -49,6 +49,11 @@ pub use crate::types::*; pub use crate::val::*; pub use crate::vec::*; +#[cfg(feature = "async")] +mod r#async; +#[cfg(feature = "async")] +pub use crate::r#async::*; + #[cfg(feature = "wasi")] mod wasi; #[cfg(feature = "wasi")] diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index 05c274ef9eb3..bf9cf3af4591 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -9,7 +9,7 @@ use wasmtime::{Func, Instance, Linker}; #[repr(C)] pub struct wasmtime_linker_t { - linker: Linker, + pub(crate) linker: Linker, } wasmtime_c_api_macros::declare_own!(wasmtime_linker_t); @@ -38,6 +38,8 @@ macro_rules! to_str { }; } +pub(crate) use to_str; + #[no_mangle] pub unsafe extern "C" fn wasmtime_linker_define( linker: &mut wasmtime_linker_t, diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8e61cff315c7..32343ddae679 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.10) project(wasmtime-examples) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_STANDARD 11) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../crates/c-api ${CMAKE_CURRENT_BINARY_DIR}/wasmtime) @@ -38,6 +40,7 @@ endfunction() enable_testing() # Add all examples +create_target(async async.cpp) create_target(externref externref.c) create_target(fib-debug fib-debug/main.c) create_target(fuel fuel.c) @@ -70,4 +73,4 @@ create_rust_test(multimemory) create_rust_test(serialize) create_rust_test(threads) create_rust_test(wasi) -create_rust_test(tokio wasmtime-wasi/tokio) \ No newline at end of file +create_rust_test(tokio wasmtime-wasi/tokio) diff --git a/examples/async.cpp b/examples/async.cpp new file mode 100644 index 000000000000..2656e73d1d91 --- /dev/null +++ b/examples/async.cpp @@ -0,0 +1,292 @@ +/* +Example of instantiating of the WebAssembly module and invoking its exported +function. + +You can compile and run this example on Linux with: + + cargo build --release -p wasmtime-c-api + c++ examples/async.cpp \ + -I crates/c-api/include \ + -I crates/c-api/wasm-c-api/include \ + target/release/libwasmtime.a \ + -std=c++11 \ + -lpthread -ldl -lm \ + -o async + ./async + +Note that on Windows and macOS the command will be similar, but you'll need +to tweak the `-lpthread` and such annotations. + +You can also build using cmake: + +mkdir build && cd build && cmake .. && cmake --build . --target wasmtime-async +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +template struct deleter { + void operator()(T *ptr) { fn(ptr); } +}; +template +using handle = std::unique_ptr>; + +void exit_with_error(std::string msg, wasmtime_error_t *err, + wasm_trap_t *trap) { + std::cerr << "error: " << msg << std::endl; + wasm_byte_vec_t error_message; + if (err) { + wasmtime_error_message(err, &error_message); + } else { + wasm_trap_message(trap, &error_message); + } + std::cerr << std::string(error_message.data, error_message.size) << std::endl; + wasm_byte_vec_delete(&error_message); + std::exit(1); +} + +handle create_engine() { + wasm_config_t *config = wasm_config_new(); + assert(config != nullptr); + wasmtime_config_async_support_set(config, true); + wasmtime_config_consume_fuel_set(config, true); + handle engine; + // this takes ownership of config + engine.reset(wasm_engine_new_with_config(config)); + assert(engine); + return engine; +} + +handle +create_store(wasm_engine_t *engine) { + handle store; + store.reset(wasmtime_store_new(engine, nullptr, nullptr)); + assert(store); + return store; +} + +handle +create_linker(wasm_engine_t *engine) { + handle linker; + linker.reset(wasmtime_linker_new(engine)); + assert(linker); + return linker; +} + +handle +compile_wat_module_from_file(wasm_engine_t *engine, + const std::string &filename) { + std::ifstream t(filename); + std::stringstream buffer; + buffer << t.rdbuf(); + if (t.bad()) { + std::cerr << "error reading file: " << filename << std::endl; + std::exit(1); + } + const std::string &content = buffer.str(); + wasm_byte_vec_t wasm_bytes; + handle error{ + wasmtime_wat2wasm(content.data(), content.size(), &wasm_bytes)}; + if (error) { + exit_with_error("failed to parse wat", error.get(), nullptr); + } + wasmtime_module_t *mod_ptr = nullptr; + error.reset(wasmtime_module_new(engine, + reinterpret_cast(wasm_bytes.data), + wasm_bytes.size, &mod_ptr)); + wasm_byte_vec_delete(&wasm_bytes); + handle mod{mod_ptr}; + if (!mod) { + exit_with_error("failed to compile module", error.get(), nullptr); + } + return mod; +} + +class printer_thread_state { +public: + void set_value_to_print(int32_t v) { + _print_finished_future = _print_finished.get_future(); + _value_to_print.set_value(v); + } + int32_t get_value_to_print() { return _value_to_print.get_future().get(); } + + bool print_is_pending() const { + return _print_finished_future.valid() && + _print_finished_future.wait_for(std::chrono::seconds(0)) != + std::future_status::ready; + } + void wait_for_print_result() const { _print_finished_future.wait(); } + void get_print_result() { _print_finished_future.get(); } + void set_print_success() { _print_finished.set_value(); } + +private: + std::promise _value_to_print; + std::promise _print_finished; + std::future _print_finished_future; +}; + +printer_thread_state printer_state; + +struct async_call_env { + wasm_trap_t **trap_ret; +}; + +bool poll_print_finished_state(void *env) { + std::cout << "polling async host function result" << std::endl; + auto *async_env = static_cast(env); + // Don't block, just poll the future state. + if (printer_state.print_is_pending()) { + return false; + } + try { + printer_state.get_print_result(); + } catch (const std::exception &ex) { + std::string msg = ex.what(); + *async_env->trap_ret = wasmtime_trap_new(msg.data(), msg.size()); + } + return true; +} +} // namespace + +int main() { + // A thread that will async perform host function calls. + std::thread printer_thread([]() { + int32_t value_to_print = printer_state.get_value_to_print(); + std::cout << "recieved value to print!" << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + std::cout << "printing: " << value_to_print << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + std::cout << "signaling that value is printed" << std::endl; + printer_state.set_print_success(); + }); + + handle error; + + auto engine = create_engine(); + auto store = create_store(engine.get()); + // This pointer is unowned. + auto *context = wasmtime_store_context(store.get()); + // Configure the store to periodically yield control + wasmtime_context_out_of_fuel_async_yield(context, + /*injection_count=*/10, + /*fuel_to_inject=*/10000); + + auto compiled_module = + compile_wat_module_from_file(engine.get(), "examples/async.wat"); + + auto linker = create_linker(engine.get()); + static std::string host_module_name = "host"; + static std::string host_func_name = "print"; + + // Declare our async host function's signature and definition. + wasm_valtype_vec_t arg_types; + wasm_valtype_vec_t result_types; + wasm_valtype_vec_new_uninitialized(&arg_types, 1); + arg_types.data[0] = wasm_valtype_new_i32(); + wasm_valtype_vec_new_empty(&result_types); + handle functype{ + wasm_functype_new(&arg_types, &result_types)}; + + error.reset(wasmtime_linker_define_async_func( + linker.get(), host_module_name.data(), host_module_name.size(), + host_func_name.data(), host_func_name.size(), functype.get(), + [](void *, wasmtime_caller_t *, const wasmtime_val_t *args, size_t, + wasmtime_val_t *, size_t, wasm_trap_t **trap_ret, + wasmtime_async_continuation_t *continutation_ret) { + std::cout << "invoking async host function" << std::endl; + printer_state.set_value_to_print(args[0].of.i32); + + continutation_ret->callback = &poll_print_finished_state; + continutation_ret->env = new async_call_env{trap_ret}; + continutation_ret->finalizer = [](void *env) { + std::cout << "deleting async_call_env" << std::endl; + delete static_cast(env); + }; + }, + /*env=*/nullptr, /*finalizer=*/nullptr)); + if (error) { + exit_with_error("failed to define host function", error.get(), nullptr); + } + + // Now instantiate our module using the linker. + handle call_future; + wasm_trap_t *trap_ptr = nullptr; + wasmtime_error_t *error_ptr = nullptr; + wasmtime_instance_t instance; + call_future.reset(wasmtime_linker_instantiate_async( + linker.get(), context, compiled_module.get(), &instance, &trap_ptr, + &error_ptr)); + while (!wasmtime_call_future_poll(call_future.get())) { + std::cout << "yielding instantiation!" << std::endl; + } + error.reset(error_ptr); + handle trap{trap_ptr}; + if (error || trap) { + exit_with_error("failed to instantiate module", error.get(), trap.get()); + } + // delete call future - it's no longer needed + call_future = nullptr; + // delete the linker now that we've created our instance + linker = nullptr; + + // Grab our exported function + static std::string guest_func_name = "print_fibonacci"; + wasmtime_extern_t guest_func_extern; + bool found = + wasmtime_instance_export_get(context, &instance, guest_func_name.data(), + guest_func_name.size(), &guest_func_extern); + assert(found); + assert(guest_func_extern.kind == WASMTIME_EXTERN_FUNC); + + // Now call our print_fibonacci function with n=15 + std::array args; + args[0].kind = WASMTIME_I32; + args[0].of.i32 = 15; + std::array results; + call_future.reset(wasmtime_func_call_async( + context, &guest_func_extern.of.func, args.data(), args.size(), + results.data(), results.size(), &trap_ptr, &error_ptr)); + // Poll the execution of the call. This can yield control back if there is an + // async host call or if we ran out of fuel. + while (!wasmtime_call_future_poll(call_future.get())) { + // if we have an async host call pending then wait for that future to finish + // before continuing. + if (printer_state.print_is_pending()) { + std::cout << "waiting for async host function to complete" << std::endl; + printer_state.wait_for_print_result(); + std::cout << "async host function completed" << std::endl; + continue; + } + // Otherwise we ran out of fuel and yielded. + std::cout << "yield!" << std::endl; + } + // Extract if there were failures or traps after poll returns that execution + // completed. + error.reset(error_ptr); + trap.reset(trap_ptr); + if (error || trap) { + exit_with_error("running guest function failed", error.get(), trap.get()); + } + call_future = nullptr; + // At this point, if our host function returned results they would be + // available in the `results` array. + std::cout << "async function call complete!" << std::endl; + + // Join our thread and exit. + printer_thread.join(); + return 0; +} diff --git a/examples/async.wat b/examples/async.wat new file mode 100644 index 000000000000..14f129892603 --- /dev/null +++ b/examples/async.wat @@ -0,0 +1,18 @@ +(module + (import "host" "print" (func $print (param i32))) + (func $fibonacci (param $n i32) (result i32) + (if + (i32.lt_s (local.get $n) (i32.const 2)) + (then (return (local.get $n))) + ) + (i32.add + (call $fibonacci (i32.sub (local.get $n) (i32.const 1))) + (call $fibonacci (i32.sub (local.get $n) (i32.const 2))) + ) + ) + (func $print_fibonacci (param $n i32) + (call $fibonacci (local.get $n)) + (call $print) + ) + (export "print_fibonacci" (func $print_fibonacci)) +) From 16e24fe71306106c4371d496461b0eb047eb59e0 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Tue, 3 Oct 2023 16:23:22 +0200 Subject: [PATCH 043/199] feat(preview1): implement `poll_oneoff` (#7099) Signed-off-by: Roman Volosatovs --- .../tests/wasi-preview1-host-in-preview2.rs | 3 - crates/wasi/src/preview2/preview1.rs | 431 ++++++++++++++---- 2 files changed, 334 insertions(+), 100 deletions(-) diff --git a/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs b/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs index 79f619a8a3eb..4086694d57d2 100644 --- a/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs +++ b/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs @@ -258,13 +258,10 @@ async fn path_symlink_trailing_slashes() { run("path_symlink_trailing_slashes", false).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -#[should_panic] async fn poll_oneoff_files() { run("poll_oneoff_files", false).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -// This is a known bug with the preview 2 implementation: -#[should_panic] async fn poll_oneoff_stdio() { run("poll_oneoff_stdio", true).await.unwrap() } diff --git a/crates/wasi/src/preview2/preview1.rs b/crates/wasi/src/preview2/preview1.rs index b035796cd8f3..4a51b98d0362 100644 --- a/crates/wasi/src/preview2/preview1.rs +++ b/crates/wasi/src/preview2/preview1.rs @@ -9,7 +9,7 @@ use crate::preview2::bindings::io::streams; use crate::preview2::{bindings, IsATTY, TableError, WasiView}; use anyhow::{anyhow, bail, Context}; use std::borrow::Borrow; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; use std::mem::{self, size_of, size_of_val}; use std::ops::{Deref, DerefMut}; use std::slice; @@ -82,15 +82,18 @@ impl BlockingMode { let (chunk, rest) = bytes.split_at(len); bytes = rest; - let borrow = Resource::new_borrow(output_stream.rep()); - Streams::blocking_write_and_flush(host, borrow, Vec::from(chunk)).await? + Streams::blocking_write_and_flush( + host, + output_stream.borrowed(), + Vec::from(chunk), + ) + .await? } Ok(total) } BlockingMode::NonBlocking => { - let borrow = Resource::new_borrow(output_stream.rep()); - let n = match Streams::check_write(host, borrow) { + let n = match Streams::check_write(host, output_stream.borrowed()) { Ok(n) => n, Err(e) if matches!(e.downcast_ref(), Some(streams::WriteError::Closed)) => 0, Err(e) => Err(e)?, @@ -101,8 +104,7 @@ impl BlockingMode { return Ok(0); } - let borrow = Resource::new_borrow(output_stream.rep()); - match Streams::write(host, borrow, bytes[..len].to_vec()) { + match Streams::write(host, output_stream.borrowed(), bytes[..len].to_vec()) { Ok(()) => {} Err(e) if matches!(e.downcast_ref(), Some(streams::WriteError::Closed)) => { return Ok(0) @@ -110,8 +112,7 @@ impl BlockingMode { Err(e) => Err(e)?, } - let borrow = Resource::new_borrow(output_stream.rep()); - match Streams::blocking_flush(host, borrow).await { + match Streams::blocking_flush(host, output_stream.borrowed()).await { Ok(()) => {} Err(e) if matches!(e.downcast_ref(), Some(streams::WriteError::Closed)) => { return Ok(0) @@ -127,9 +128,18 @@ impl BlockingMode { #[derive(Debug)] enum Descriptor { - Stdin { input_stream: u32, isatty: IsATTY }, - Stdout { output_stream: u32, isatty: IsATTY }, - Stderr { output_stream: u32, isatty: IsATTY }, + Stdin { + stream: Resource, + isatty: IsATTY, + }, + Stdout { + stream: Resource, + isatty: IsATTY, + }, + Stderr { + stream: Resource, + isatty: IsATTY, + }, PreopenDirectory((Resource, String)), File(File), } @@ -175,11 +185,10 @@ impl Descriptors { ) -> Result { let mut descriptors = Self::default(); descriptors.push(Descriptor::Stdin { - input_stream: host + stream: host .get_stdin() .context("failed to call `get-stdin`") - .map_err(types::Error::trap)? - .rep(), + .map_err(types::Error::trap)?, isatty: if let Some(term_in) = host .get_terminal_stdin() .context("failed to call `get-terminal-stdin`") @@ -194,11 +203,10 @@ impl Descriptors { }, })?; descriptors.push(Descriptor::Stdout { - output_stream: host + stream: host .get_stdout() .context("failed to call `get-stdout`") - .map_err(types::Error::trap)? - .rep(), + .map_err(types::Error::trap)?, isatty: if let Some(term_out) = host .get_terminal_stdout() .context("failed to call `get-terminal-stdout`") @@ -213,11 +221,10 @@ impl Descriptors { }, })?; descriptors.push(Descriptor::Stderr { - output_stream: host + stream: host .get_stderr() .context("failed to call `get-stderr`") - .map_err(types::Error::trap)? - .rep(), + .map_err(types::Error::trap)?, isatty: if let Some(term_out) = host .get_terminal_stderr() .context("failed to call `get-terminal-stderr`") @@ -1022,17 +1029,14 @@ impl< .remove(fd) .ok_or(types::Errno::Badf)?; match desc { - Descriptor::Stdin { input_stream, .. } => { - streams::HostInputStream::drop(self, Resource::new_own(input_stream)) - .context("failed to call `drop-input-stream`") - } - Descriptor::Stdout { output_stream, .. } | Descriptor::Stderr { output_stream, .. } => { - streams::HostOutputStream::drop(self, Resource::new_own(output_stream)) - .context("failed to call `drop-output-stream`") + Descriptor::Stdin { stream, .. } => streams::HostInputStream::drop(self, stream) + .context("failed to call `drop` on `input-stream`"), + Descriptor::Stdout { stream, .. } | Descriptor::Stderr { stream, .. } => { + streams::HostOutputStream::drop(self, stream) + .context("failed to call `drop` on `output-stream`") } Descriptor::File(File { fd, .. }) | Descriptor::PreopenDirectory((fd, _)) => { - filesystem::HostDescriptor::drop(self, fd) - .context("failed to call `drop-descriptor`") + filesystem::HostDescriptor::drop(self, fd).context("failed to call `drop`") } } .map_err(types::Error::trap) @@ -1349,8 +1353,8 @@ impl< (buf, read, state) } - Descriptor::Stdin { input_stream, .. } => { - let input = Resource::new_borrow(*input_stream); + Descriptor::Stdin { stream, .. } => { + let stream = stream.borrowed(); drop(t); let Some(buf) = first_non_empty_iovec(iovs)? else { return Ok(0); @@ -1358,7 +1362,7 @@ impl< let (read, state) = stream_res( streams::HostInputStream::blocking_read( self, - input, + stream, buf.len().try_into().unwrap_or(u64::MAX), ) .await, @@ -1437,53 +1441,56 @@ impl< ) -> Result { let t = self.transact()?; let desc = t.get_descriptor(fd)?; - match *desc { + match desc { Descriptor::File(File { - ref fd, + fd, blocking_mode, append, - ref position, + position, }) if t.view.table().get_resource(fd)?.is_file() => { let fd = fd.borrowed(); + let blocking_mode = *blocking_mode; let position = position.clone(); + let append = *append; drop(t); let Some(buf) = first_non_empty_ciovec(ciovs)? else { return Ok(0); }; let (stream, pos) = if append { - let stream = self.append_via_stream(fd.borrowed()).map_err(|e| { + let stream = self.append_via_stream(fd).map_err(|e| { e.try_into() .context("failed to call `append-via-stream`") .unwrap_or_else(types::Error::trap) })?; (stream, 0) } else { - let position = position.load(Ordering::Relaxed); - let stream = self - .write_via_stream(fd.borrowed(), position) - .map_err(|e| { - e.try_into() - .context("failed to call `write-via-stream`") - .unwrap_or_else(types::Error::trap) - })?; - (stream, position) + let pos = position.load(Ordering::Relaxed); + let stream = self.write_via_stream(fd, pos).map_err(|e| { + e.try_into() + .context("failed to call `write-via-stream`") + .unwrap_or_else(types::Error::trap) + })?; + (stream, pos) }; let n = blocking_mode.write(self, stream, &buf).await?; if !append { let pos = pos.checked_add(n as u64).ok_or(types::Errno::Overflow)?; position.store(pos, Ordering::Relaxed); } - Ok(n.try_into()?) + let n = n.try_into()?; + Ok(n) } - Descriptor::Stdout { output_stream, .. } | Descriptor::Stderr { output_stream, .. } => { + Descriptor::Stdout { stream, .. } | Descriptor::Stderr { stream, .. } => { + let stream = stream.borrowed(); drop(t); let Some(buf) = first_non_empty_ciovec(ciovs)? else { return Ok(0); }; - Ok(BlockingMode::Blocking - .write(self, Resource::new_borrow(output_stream), &buf) + let n = BlockingMode::Blocking + .write(self, stream, &buf) .await? - .try_into()?) + .try_into()?; + Ok(n) } _ => Err(types::Errno::Badf.into()), } @@ -1500,13 +1507,12 @@ impl< ) -> Result { let t = self.transact()?; let desc = t.get_descriptor(fd)?; - let n = match *desc { + let n = match desc { Descriptor::File(File { - ref fd, - blocking_mode, - .. + fd, blocking_mode, .. }) if t.view.table().get_resource(fd)?.is_file() => { let fd = fd.borrowed(); + let blocking_mode = *blocking_mode; drop(t); let Some(buf) = first_non_empty_ciovec(ciovs)? else { return Ok(0); @@ -1631,22 +1637,16 @@ impl< cookie: types::Dircookie, ) -> Result { let fd = self.get_dir_fd(fd)?; - let stream = self - .read_directory(Resource::new_borrow(fd.rep())) - .await - .map_err(|e| { - e.try_into() - .context("failed to call `read-directory`") - .unwrap_or_else(types::Error::trap) - })?; - let dir_metadata_hash = self - .metadata_hash(Resource::new_borrow(fd.rep())) - .await - .map_err(|e| { - e.try_into() - .context("failed to call `metadata-hash`") - .unwrap_or_else(types::Error::trap) - })?; + let stream = self.read_directory(fd.borrowed()).await.map_err(|e| { + e.try_into() + .context("failed to call `read-directory`") + .unwrap_or_else(types::Error::trap) + })?; + let dir_metadata_hash = self.metadata_hash(fd.borrowed()).await.map_err(|e| { + e.try_into() + .context("failed to call `metadata-hash`") + .unwrap_or_else(types::Error::trap) + })?; let cookie = cookie.try_into().map_err(|_| types::Errno::Overflow)?; let head = [ @@ -1684,11 +1684,7 @@ impl< .unwrap_or_else(types::Error::trap) })?; let metadata_hash = self - .metadata_hash_at( - Resource::new_borrow(fd.rep()), - filesystem::PathFlags::empty(), - name.clone(), - ) + .metadata_hash_at(fd.borrowed(), filesystem::PathFlags::empty(), name.clone()) .await .map_err(|e| { e.try_into() @@ -1754,12 +1750,13 @@ impl< ) -> Result<(), types::Error> { let dirfd = self.get_dir_fd(dirfd)?; let path = read_string(path)?; - let borrow = Resource::new_borrow(dirfd.rep()); - self.create_directory_at(borrow, path).await.map_err(|e| { - e.try_into() - .context("failed to call `create-directory-at`") - .unwrap_or_else(types::Error::trap) - }) + self.create_directory_at(dirfd.borrowed(), path) + .await + .map_err(|e| { + e.try_into() + .context("failed to call `create-directory-at`") + .unwrap_or_else(types::Error::trap) + }) } /// Return the attributes of a file or directory. @@ -1781,11 +1778,7 @@ impl< data_modification_timestamp, status_change_timestamp, } = self - .stat_at( - Resource::new_borrow(dirfd.rep()), - flags.into(), - path.clone(), - ) + .stat_at(dirfd.borrowed(), flags.into(), path.clone()) .await .map_err(|e| { e.try_into() @@ -2022,8 +2015,7 @@ impl< let dirfd = self.get_dir_fd(dirfd)?; let src_path = read_string(src_path)?; let dest_path = read_string(dest_path)?; - let borrow = Resource::new_borrow(dirfd.rep()); - self.symlink_at(borrow, src_path, dest_path) + self.symlink_at(dirfd.borrowed(), src_path, dest_path) .await .map_err(|e| { e.try_into() @@ -2040,15 +2032,15 @@ impl< ) -> Result<(), types::Error> { let dirfd = self.get_dir_fd(dirfd)?; let path = path.as_cow()?.to_string(); - let borrow = Resource::new_borrow(dirfd.rep()); - self.unlink_file_at(borrow, path).await.map_err(|e| { - e.try_into() - .context("failed to call `unlink-file-at`") - .unwrap_or_else(types::Error::trap) - }) + self.unlink_file_at(dirfd.borrowed(), path) + .await + .map_err(|e| { + e.try_into() + .context("failed to call `unlink-file-at`") + .unwrap_or_else(types::Error::trap) + }) } - #[allow(unused_variables)] #[instrument(skip(self))] async fn poll_oneoff<'a>( &mut self, @@ -2056,7 +2048,252 @@ impl< events: &GuestPtr<'a, types::Event>, nsubscriptions: types::Size, ) -> Result { - todo!("preview1 poll_oneoff is not implemented") + if nsubscriptions == 0 { + // Indefinite sleeping is not supported in preview1. + return Err(types::Errno::Inval.into()); + } + let subs = subs.as_array(nsubscriptions); + let events = events.as_array(nsubscriptions); + + let n = usize::try_from(nsubscriptions).unwrap_or(usize::MAX); + let mut pollables = Vec::with_capacity(n); + for sub in subs.iter() { + let sub = sub?.read()?; + let p = match sub.u { + types::SubscriptionU::Clock(types::SubscriptionClock { + id, + timeout, + flags, + .. + }) => { + let absolute = flags.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME); + let (timeout, absolute) = match id { + types::Clockid::Monotonic => (timeout, absolute), + types::Clockid::Realtime if !absolute => (timeout, false), + types::Clockid::Realtime => { + let now = wall_clock::Host::now(self) + .context("failed to call `wall_clock::now`") + .map_err(types::Error::trap)?; + + // Convert `timeout` to `Datetime` format. + let seconds = timeout / 1_000_000_000; + let nanoseconds = timeout % 1_000_000_000; + + let timeout = if now.seconds < seconds + || now.seconds == seconds + && u64::from(now.nanoseconds) < nanoseconds + { + // `now` is less than `timeout`, which is expressable as u64, + // substract the nanosecond counts directly + now.seconds * 1_000_000_000 + u64::from(now.nanoseconds) - timeout + } else { + 0 + }; + (timeout, false) + } + _ => return Err(types::Errno::Inval.into()), + }; + monotonic_clock::Host::subscribe(self, timeout, absolute) + .context("failed to call `monotonic_clock::subscribe`") + .map_err(types::Error::trap)? + } + types::SubscriptionU::FdRead(types::SubscriptionFdReadwrite { + file_descriptor, + }) => { + let stream = { + let t = self.transact()?; + let desc = t.get_descriptor(file_descriptor)?; + match desc { + Descriptor::Stdin { stream, .. } => stream.borrowed(), + Descriptor::File(File { fd, position, .. }) + if t.view.table().get_resource(fd)?.is_file() => + { + let pos = position.load(Ordering::Relaxed); + let fd = fd.borrowed(); + drop(t); + self.read_via_stream(fd, pos).map_err(|e| { + e.try_into() + .context("failed to call `read-via-stream`") + .unwrap_or_else(types::Error::trap) + })? + } + // TODO: Support sockets + _ => return Err(types::Errno::Badf.into()), + } + }; + streams::HostInputStream::subscribe(self, stream) + .context("failed to call `subscribe` on `input-stream`") + .map_err(types::Error::trap)? + } + types::SubscriptionU::FdWrite(types::SubscriptionFdReadwrite { + file_descriptor, + }) => { + let stream = { + let t = self.transact()?; + let desc = t.get_descriptor(file_descriptor)?; + match desc { + Descriptor::Stdout { stream, .. } + | Descriptor::Stderr { stream, .. } => stream.borrowed(), + Descriptor::File(File { + fd, + position, + append, + .. + }) if t.view.table().get_resource(fd)?.is_file() => { + let fd = fd.borrowed(); + let position = position.clone(); + let append = *append; + drop(t); + if append { + self.append_via_stream(fd).map_err(|e| { + e.try_into() + .context("failed to call `append-via-stream`") + .unwrap_or_else(types::Error::trap) + })? + } else { + let pos = position.load(Ordering::Relaxed); + self.write_via_stream(fd, pos).map_err(|e| { + e.try_into() + .context("failed to call `write-via-stream`") + .unwrap_or_else(types::Error::trap) + })? + } + } + // TODO: Support sockets + _ => return Err(types::Errno::Badf.into()), + } + }; + streams::HostOutputStream::subscribe(self, stream) + .context("failed to call `subscribe` on `output-stream`") + .map_err(types::Error::trap)? + } + }; + pollables.push(p); + } + let ready: HashSet<_> = self + .poll_list(pollables) + .await + .context("failed to call `poll-oneoff`") + .map_err(types::Error::trap)? + .into_iter() + .collect(); + + let mut count: types::Size = 0; + for (sub, event) in (0..) + .zip(subs.iter()) + .filter_map(|(idx, sub)| ready.contains(&idx).then_some(sub)) + .zip(events.iter()) + { + let sub = sub?.read()?; + let event = event?; + let e = match sub.u { + types::SubscriptionU::Clock(..) => types::Event { + userdata: sub.userdata, + error: types::Errno::Success, + type_: types::Eventtype::Clock, + fd_readwrite: types::EventFdReadwrite { + flags: types::Eventrwflags::empty(), + nbytes: 0, + }, + }, + types::SubscriptionU::FdRead(types::SubscriptionFdReadwrite { + file_descriptor, + }) => { + let t = self.transact()?; + let desc = t.get_descriptor(file_descriptor)?; + match desc { + Descriptor::Stdin { .. } => types::Event { + userdata: sub.userdata, + error: types::Errno::Success, + type_: types::Eventtype::FdRead, + fd_readwrite: types::EventFdReadwrite { + flags: types::Eventrwflags::empty(), + nbytes: 1, + }, + }, + Descriptor::File(File { fd, position, .. }) + if t.view.table().get_resource(fd)?.is_file() => + { + let fd = fd.borrowed(); + let position = position.clone(); + drop(t); + match self + .stat(fd) + .await + .map_err(|e| e.try_into().context("failed to call `stat`")) + { + Ok(filesystem::DescriptorStat { size, .. }) => { + let pos = position.load(Ordering::Relaxed); + let nbytes = size.saturating_sub(pos); + types::Event { + userdata: sub.userdata, + error: types::Errno::Success, + type_: types::Eventtype::FdRead, + fd_readwrite: types::EventFdReadwrite { + flags: if nbytes == 0 { + types::Eventrwflags::FD_READWRITE_HANGUP + } else { + types::Eventrwflags::empty() + }, + nbytes: 1, + }, + } + } + Err(Ok(error)) => types::Event { + userdata: sub.userdata, + error, + type_: types::Eventtype::FdRead, + fd_readwrite: types::EventFdReadwrite { + flags: types::Eventrwflags::empty(), + nbytes: 1, + }, + }, + Err(Err(error)) => return Err(types::Error::trap(error)), + } + } + // TODO: Support sockets + _ => return Err(types::Errno::Badf.into()), + } + } + types::SubscriptionU::FdWrite(types::SubscriptionFdReadwrite { + file_descriptor, + }) => { + let t = self.transact()?; + let desc = t.get_descriptor(file_descriptor)?; + match desc { + Descriptor::Stdout { .. } | Descriptor::Stderr { .. } => types::Event { + userdata: sub.userdata, + error: types::Errno::Success, + type_: types::Eventtype::FdWrite, + fd_readwrite: types::EventFdReadwrite { + flags: types::Eventrwflags::empty(), + nbytes: 1, + }, + }, + Descriptor::File(File { fd, .. }) + if t.view.table().get_resource(fd)?.is_file() => + { + types::Event { + userdata: sub.userdata, + error: types::Errno::Success, + type_: types::Eventtype::FdWrite, + fd_readwrite: types::EventFdReadwrite { + flags: types::Eventrwflags::empty(), + nbytes: 1, + }, + } + } + // TODO: Support sockets + _ => return Err(types::Errno::Badf.into()), + } + } + }; + event.write(e)?; + count = count + .checked_add(1) + .ok_or_else(|| types::Error::from(types::Errno::Overflow))? + } + Ok(count) } #[instrument(skip(self))] From 4a037fc06d54b2af024a21e9fb4c37928364f124 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Oct 2023 11:09:13 -0500 Subject: [PATCH 044/199] Handle `lower_branch` consistently amongst backends (#7133) * Handle `lower_branch` consistently amongst backends This commit is a refactoring to consistently implement `lower_branch` among Cranelift's backends. Previously each backend had its own means of extracting labels and shuffling along information, and now there's prelude methods for all backends to access and use. This changes a few display impls but the actual meat of what's emitted shouldn't change amongst the backends. * Fix compile --- cranelift/codegen/src/isa/aarch64/inst.isle | 38 ++++++++---------- .../codegen/src/isa/aarch64/inst/emit.rs | 17 +++----- cranelift/codegen/src/isa/aarch64/inst/mod.rs | 17 ++------ cranelift/codegen/src/isa/aarch64/lower.isle | 36 +++++++---------- .../codegen/src/isa/aarch64/lower/isle.rs | 30 +++----------- cranelift/codegen/src/isa/riscv64/inst.isle | 26 +++++------- .../codegen/src/isa/riscv64/lower/isle.rs | 16 ++------ cranelift/codegen/src/isa/s390x/inst.isle | 13 +----- cranelift/codegen/src/isa/s390x/inst/emit.rs | 5 +-- cranelift/codegen/src/isa/s390x/lower.isle | 19 ++++----- cranelift/codegen/src/isa/s390x/lower/isle.rs | 10 ----- cranelift/codegen/src/isa/x64/inst.isle | 20 ---------- cranelift/codegen/src/isa/x64/lower/isle.rs | 40 ------------------- cranelift/codegen/src/machinst/isle.rs | 36 +++++++++++++++++ cranelift/codegen/src/prelude_lower.isle | 18 +++++++++ .../filetests/filetests/isa/aarch64/bti.clif | 4 +- .../filetests/isa/aarch64/jumptable.clif | 2 +- .../filetests/isa/s390x/jumptable.clif | 2 +- winch/codegen/src/isa/x64/asm.rs | 2 +- 19 files changed, 129 insertions(+), 222 deletions(-) diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle index 624a5c9d987b..172af505ea70 100644 --- a/cranelift/codegen/src/isa/aarch64/inst.isle +++ b/cranelift/codegen/src/isa/aarch64/inst.isle @@ -883,7 +883,8 @@ ;; Jump-table sequence, as one compound instruction (see note in lower_inst.rs for rationale). (JTSequence - (info BoxJTSequenceInfo) + (default MachLabel) + (targets BoxVecMachLabel) (ridx Reg) (rtmp1 WritableReg) (rtmp2 WritableReg)) @@ -1717,18 +1718,13 @@ (decl u64_into_imm_logic (Type u64) ImmLogic) (extern constructor u64_into_imm_logic u64_into_imm_logic) -(decl branch_target (VecMachLabel u8) BranchTarget) +(decl branch_target (MachLabel) BranchTarget) (extern constructor branch_target branch_target) +(convert MachLabel BranchTarget branch_target) -(decl targets_jt_size (VecMachLabel) u32) -(extern constructor targets_jt_size targets_jt_size) - -(decl targets_jt_space (VecMachLabel) CodeOffset) +(decl targets_jt_space (BoxVecMachLabel) CodeOffset) (extern constructor targets_jt_space targets_jt_space) -(decl targets_jt_info (VecMachLabel) BoxJTSequenceInfo) -(extern constructor targets_jt_info targets_jt_info) - ;; Calculate the minimum floating-point bound for a conversion to floating ;; point from an integer type. ;; Accepts whether the output is signed, the size of the input @@ -4076,12 +4072,12 @@ ;; PC-rel offset to the jumptable would be incorrect. ;; (The alternative is to introduce a relocation pass ;; for inlined jumptables, which is much worse, IMHO.) -(decl jt_sequence (Reg BoxJTSequenceInfo) ConsumesFlags) -(rule (jt_sequence ridx info) +(decl jt_sequence (Reg MachLabel BoxVecMachLabel) ConsumesFlags) +(rule (jt_sequence ridx default targets) (let ((rtmp1 WritableReg (temp_writable_reg $I64)) (rtmp2 WritableReg (temp_writable_reg $I64))) (ConsumesFlags.ConsumesFlagsSideEffect - (MInst.JTSequence info ridx rtmp1 rtmp2)))) + (MInst.JTSequence default targets ridx rtmp1 rtmp2)))) ;; Helper for emitting `MInst.CondBr` instructions. (decl cond_br (BranchTarget BranchTarget CondBrKind) ConsumesFlags) @@ -4102,18 +4098,16 @@ (MInst.EmitIsland needed_space))) ;; Helper for emitting `br_table` sequences. -(decl br_table_impl (u64 Reg VecMachLabel) Unit) -(rule (br_table_impl (imm12_from_u64 jt_size) ridx targets) - (let ((jt_info BoxJTSequenceInfo (targets_jt_info targets))) - (emit_side_effect (with_flags_side_effect - (cmp_imm (OperandSize.Size32) ridx jt_size) - (jt_sequence ridx jt_info))))) -(rule -1 (br_table_impl jt_size ridx targets) - (let ((jt_size Reg (imm $I64 (ImmExtend.Zero) jt_size)) - (jt_info BoxJTSequenceInfo (targets_jt_info targets))) +(decl br_table_impl (u64 Reg MachLabel BoxVecMachLabel) Unit) +(rule (br_table_impl (imm12_from_u64 jt_size) ridx default targets) + (emit_side_effect (with_flags_side_effect + (cmp_imm (OperandSize.Size32) ridx jt_size) + (jt_sequence ridx default targets)))) +(rule -1 (br_table_impl jt_size ridx default targets) + (let ((jt_size Reg (imm $I64 (ImmExtend.Zero) jt_size))) (emit_side_effect (with_flags_side_effect (cmp (OperandSize.Size32) ridx jt_size) - (jt_sequence ridx jt_info))))) + (jt_sequence ridx default targets))))) ;; Helper for emitting the `uzp1` instruction (decl vec_uzp1 (Reg Reg VectorSize) Reg) diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index 43bdd4a3e981..2742134d1f0f 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -3275,7 +3275,8 @@ impl MachInstEmit for Inst { ridx, rtmp1, rtmp2, - ref info, + default, + ref targets, .. } => { let ridx = allocs.next(ridx); @@ -3287,7 +3288,7 @@ impl MachInstEmit for Inst { // Branch to default when condition code from prior comparison indicates. let br = enc_conditional_br( - info.default_target, + BranchTarget::Label(default), CondBrKind::Cond(Cond::Hs), &mut AllocationConsumer::default(), ); @@ -3296,9 +3297,7 @@ impl MachInstEmit for Inst { // will not be merged with any other branch, flipped, or elided (it is not preceded // or succeeded by any other branch). Just emit it with the label use. let default_br_offset = sink.cur_offset(); - if let BranchTarget::Label(l) = info.default_target { - sink.use_label_at_offset(default_br_offset, l, LabelUse::Branch19); - } + sink.use_label_at_offset(default_br_offset, default, LabelUse::Branch19); sink.put4(br); // Overwrite the index with a zero when the above @@ -3347,18 +3346,14 @@ impl MachInstEmit for Inst { inst.emit(&[], sink, emit_info, state); // Emit jump table (table of 32-bit offsets). let jt_off = sink.cur_offset(); - for &target in info.targets.iter() { + for &target in targets.iter() { let word_off = sink.cur_offset(); // off_into_table is an addend here embedded in the label to be later patched // at the end of codegen. The offset is initially relative to this jump table // entry; with the extra addend, it'll be relative to the jump table's start, // after patching. let off_into_table = word_off - jt_off; - sink.use_label_at_offset( - word_off, - target.as_label().unwrap(), - LabelUse::PCRel32, - ); + sink.use_label_at_offset(word_off, target, LabelUse::PCRel32); sink.put4(off_into_table); } diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs index 390ffe1d73e2..e916d9e72359 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs @@ -140,16 +140,6 @@ pub struct ReturnCallInfo { pub key: Option, } -/// Additional information for JTSequence instructions, left out of line to lower the size of the Inst -/// enum. -#[derive(Clone, Debug)] -pub struct JTSequenceInfo { - /// Possible branch targets. - pub targets: Vec, - /// Default branch target. - pub default_target: BranchTarget, -} - fn count_zero_half_words(mut value: u64, num_half_words: u8) -> usize { let mut count = 0; for _ in 0..num_half_words { @@ -2677,7 +2667,8 @@ impl Inst { &Inst::Word4 { data } => format!("data.i32 {}", data), &Inst::Word8 { data } => format!("data.i64 {}", data), &Inst::JTSequence { - ref info, + default, + ref targets, ridx, rtmp1, rtmp2, @@ -2686,7 +2677,7 @@ impl Inst { let ridx = pretty_print_reg(ridx, allocs); let rtmp1 = pretty_print_reg(rtmp1.to_reg(), allocs); let rtmp2 = pretty_print_reg(rtmp2.to_reg(), allocs); - let default_target = info.default_target.pretty_print(0, allocs); + let default_target = BranchTarget::Label(default).pretty_print(0, allocs); format!( concat!( "b.hs {} ; ", @@ -2709,7 +2700,7 @@ impl Inst { rtmp1, rtmp2, rtmp1, - info.targets + targets ) } &Inst::LoadExtName { diff --git a/cranelift/codegen/src/isa/aarch64/lower.isle b/cranelift/codegen/src/isa/aarch64/lower.isle index 80fcec000ec9..bc548ce8f18b 100644 --- a/cranelift/codegen/src/isa/aarch64/lower.isle +++ b/cranelift/codegen/src/isa/aarch64/lower.isle @@ -12,7 +12,7 @@ ;; blocks while we lower, the fallthrough in the new order is not (necessarily) ;; the same as the fallthrough in CLIF. So, we use the explicitly-provided ;; target. -(decl partial lower_branch (Inst VecMachLabel) Unit) +(decl partial lower_branch (Inst MachLabelSlice) Unit) ;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2829,11 +2829,9 @@ ;;; Rules for `brif` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `brif` following `icmp` -(rule (lower_branch (brif (maybe_uextend (icmp cc x @ (value_type ty) y)) _ _) targets) +(rule (lower_branch (brif (maybe_uextend (icmp cc x @ (value_type ty) y)) _ _) (two_targets taken not_taken)) (let ((comparison FlagsAndCC (lower_icmp_into_flags cc x y ty)) - (cond Cond (cond_code (flags_and_cc_cc comparison))) - (taken BranchTarget (branch_target targets 0)) - (not_taken BranchTarget (branch_target targets 1))) + (cond Cond (cond_code (flags_and_cc_cc comparison)))) (emit_side_effect (with_flags_side_effect (flags_and_cc_flags comparison) (cond_br taken @@ -2841,49 +2839,43 @@ (cond_br_cond cond)))))) ;; `brif` following `fcmp` -(rule (lower_branch (brif (maybe_uextend (fcmp cc x @ (value_type (ty_scalar_float ty)) y)) _ _) targets) - (let ((cond Cond (fp_cond_code cc)) - (taken BranchTarget (branch_target targets 0)) - (not_taken BranchTarget (branch_target targets 1))) +(rule (lower_branch (brif (maybe_uextend (fcmp cc x @ (value_type (ty_scalar_float ty)) y)) _ _) (two_targets taken not_taken)) + (let ((cond Cond (fp_cond_code cc))) (emit_side_effect (with_flags_side_effect (fpu_cmp (scalar_size ty) x y) (cond_br taken not_taken (cond_br_cond cond)))))) ;; standard `brif` -(rule -1 (lower_branch (brif c @ (value_type $I128) _ _) targets) +(rule -1 (lower_branch (brif c @ (value_type $I128) _ _) (two_targets taken not_taken)) (let ((flags ProducesFlags (flags_to_producesflags c)) (c ValueRegs (put_in_regs c)) (c_lo Reg (value_regs_get c 0)) (c_hi Reg (value_regs_get c 1)) - (rt Reg (orr $I64 c_lo c_hi)) - (taken BranchTarget (branch_target targets 0)) - (not_taken BranchTarget (branch_target targets 1))) + (rt Reg (orr $I64 c_lo c_hi))) (emit_side_effect (with_flags_side_effect flags (cond_br taken not_taken (cond_br_not_zero rt)))))) -(rule -2 (lower_branch (brif c @ (value_type ty) _ _) targets) +(rule -2 (lower_branch (brif c @ (value_type ty) _ _) (two_targets taken not_taken)) (if (ty_int_ref_scalar_64 ty)) (let ((flags ProducesFlags (flags_to_producesflags c)) - (rt Reg (put_in_reg_zext64 c)) - (taken BranchTarget (branch_target targets 0)) - (not_taken BranchTarget (branch_target targets 1))) + (rt Reg (put_in_reg_zext64 c))) (emit_side_effect (with_flags_side_effect flags (cond_br taken not_taken (cond_br_not_zero rt)))))) ;;; Rules for `jump` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower_branch (jump _) targets) - (emit_side_effect (aarch64_jump (branch_target targets 0)))) +(rule (lower_branch (jump _) (single_target label)) + (emit_side_effect (aarch64_jump label))) ;;; Rules for `br_table` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; `targets` contains the default target with the list of branch targets ;; concatenated. -(rule (lower_branch (br_table idx _) targets) - (let ((jt_size u32 (targets_jt_size targets)) +(rule (lower_branch (br_table idx _) (jump_table_targets default targets)) + (let ((jt_size u32 (jump_table_size targets)) (_ InstOutput (side_effect (emit_island (targets_jt_space targets)))) (ridx Reg (put_in_reg_zext32 idx))) - (br_table_impl (u32_as_u64 jt_size) ridx targets))) + (br_table_impl (u32_as_u64 jt_size) ridx default targets))) diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle.rs b/cranelift/codegen/src/isa/aarch64/lower/isle.rs index ffea0fc42595..8887ae1e2023 100644 --- a/cranelift/codegen/src/isa/aarch64/lower/isle.rs +++ b/cranelift/codegen/src/isa/aarch64/lower/isle.rs @@ -10,8 +10,8 @@ use super::{ fp_reg, lower_condcode, lower_fp_condcode, stack_reg, writable_link_reg, writable_zero_reg, zero_reg, ASIMDFPModImm, ASIMDMovModImm, BranchTarget, CallIndInfo, CallInfo, Cond, CondBrKind, ExtendOp, FPUOpRI, FPUOpRIMod, FloatCC, Imm12, ImmLogic, ImmShift, Inst as MInst, IntCC, - JTSequenceInfo, MachLabel, MemLabel, MoveWideConst, MoveWideOp, Opcode, OperandSize, Reg, - SImm9, ScalarSize, ShiftOpAndAmt, UImm12Scaled, UImm5, VecMisc2, VectorSize, NZCV, + MachLabel, MemLabel, MoveWideConst, MoveWideOp, Opcode, OperandSize, Reg, SImm9, ScalarSize, + ShiftOpAndAmt, UImm12Scaled, UImm5, VecMisc2, VectorSize, NZCV, }; use crate::ir::condcodes; use crate::isa; @@ -43,7 +43,6 @@ type BoxCallInfo = Box; type BoxCallIndInfo = Box; type BoxReturnCallInfo = Box; type VecMachLabel = Vec; -type BoxJTSequenceInfo = Box; type BoxExternalName = Box; type VecArgPair = Vec; @@ -596,32 +595,15 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> { super::regs::pinned_reg().to_real_reg().unwrap().into() } - fn branch_target(&mut self, elements: &VecMachLabel, idx: u8) -> BranchTarget { - BranchTarget::Label(elements[idx as usize]) + fn branch_target(&mut self, label: MachLabel) -> BranchTarget { + BranchTarget::Label(label) } - fn targets_jt_size(&mut self, elements: &VecMachLabel) -> u32 { - (elements.len() - 1) as u32 - } - - fn targets_jt_space(&mut self, elements: &VecMachLabel) -> CodeOffset { + fn targets_jt_space(&mut self, elements: &BoxVecMachLabel) -> CodeOffset { // calculate the number of bytes needed for the jumptable sequence: // 4 bytes per instruction, with 8 instructions base + the size of // the jumptable more. - 4 * (8 + self.targets_jt_size(elements)) - } - - fn targets_jt_info(&mut self, elements: &VecMachLabel) -> BoxJTSequenceInfo { - let targets: Vec = elements - .iter() - .skip(1) - .map(|bix| BranchTarget::Label(*bix)) - .collect(); - let default_target = BranchTarget::Label(elements[0]); - Box::new(JTSequenceInfo { - targets, - default_target, - }) + (4 * (8 + elements.len())).try_into().unwrap() } fn min_fp_value(&mut self, signed: bool, in_bits: u8, out_bits: u8) -> Reg { diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 932014836de8..f2c1d3c1ed5a 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -2641,7 +2641,7 @@ (decl int_zero_reg (Type) ValueRegs) (extern constructor int_zero_reg int_zero_reg) -(decl lower_cond_br (IntCC ValueRegs VecMachLabel Type) Unit) +(decl lower_cond_br (IntCC ValueRegs MachLabelSlice Type) Unit) (extern constructor lower_cond_br lower_cond_br) ;; Convert a truthy value, possibly of more than one register (an I128), to @@ -2660,13 +2660,11 @@ (decl label_to_br_target (MachLabel) CondBrTarget) (extern constructor label_to_br_target label_to_br_target) +(convert MachLabel CondBrTarget label_to_br_target) -(decl vec_label_get (VecMachLabel u8) MachLabel) -(extern constructor vec_label_get vec_label_get) - -(decl partial lower_branch (Inst VecMachLabel) Unit) -(rule (lower_branch (jump _) targets ) - (emit_side_effect (SideEffectNoResult.Inst (MInst.Jal (vec_label_get targets 0))))) +(decl partial lower_branch (Inst MachLabelSlice) Unit) +(rule (lower_branch (jump _) (single_target label)) + (emit_side_effect (SideEffectNoResult.Inst (MInst.Jal label)))) ;; Default behavior for branching based on an input value. (rule (lower_branch (brif v @ (value_type (fits_in_64 ty)) _ _) targets) @@ -2676,21 +2674,17 @@ ;; Branching on the result of an fcmp (rule 1 - (lower_branch (brif (maybe_uextend (fcmp cc a @ (value_type ty) b)) _ _) targets) + (lower_branch (brif (maybe_uextend (fcmp cc a @ (value_type ty) b)) _ _) (two_targets then else)) (if-let $true (floatcc_unordered cc)) - (let ((then CondBrTarget (label_to_br_target (vec_label_get targets 0))) - (else CondBrTarget (label_to_br_target (vec_label_get targets 1)))) - (emit_side_effect (cond_br (emit_fcmp (floatcc_complement cc) ty a b) else then)))) + (emit_side_effect (cond_br (emit_fcmp (floatcc_complement cc) ty a b) else then))) (rule 1 - (lower_branch (brif (maybe_uextend (fcmp cc a @ (value_type ty) b)) _ _) targets) + (lower_branch (brif (maybe_uextend (fcmp cc a @ (value_type ty) b)) _ _) (two_targets then else)) (if-let $false (floatcc_unordered cc)) - (let ((then CondBrTarget (label_to_br_target (vec_label_get targets 0))) - (else CondBrTarget (label_to_br_target (vec_label_get targets 1)))) - (emit_side_effect (cond_br (emit_fcmp cc ty a b) then else)))) + (emit_side_effect (cond_br (emit_fcmp cc ty a b) then else))) -(decl lower_br_table (Reg VecMachLabel) Unit) +(decl lower_br_table (Reg MachLabelSlice) Unit) (extern constructor lower_br_table lower_br_table) (rule (lower_branch (br_table index _) targets) diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs index 7432b821654c..1466affce463 100644 --- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs +++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs @@ -175,13 +175,7 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> } } - fn lower_cond_br( - &mut self, - cc: &IntCC, - a: ValueRegs, - targets: &VecMachLabel, - ty: Type, - ) -> Unit { + fn lower_cond_br(&mut self, cc: &IntCC, a: ValueRegs, targets: &[MachLabel], ty: Type) -> Unit { MInst::lower_br_icmp( *cc, a, @@ -216,10 +210,6 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> } } - fn vec_label_get(&mut self, val: &VecMachLabel, x: u8) -> MachLabel { - val[x as usize] - } - fn label_to_br_target(&mut self, label: MachLabel) -> CondBrTarget { CondBrTarget::Label(label) } @@ -486,14 +476,14 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> AMO::SeqCst } - fn lower_br_table(&mut self, index: Reg, targets: &VecMachLabel) -> Unit { + fn lower_br_table(&mut self, index: Reg, targets: &[MachLabel]) -> Unit { let tmp1 = self.temp_writable_reg(I64); let tmp2 = self.temp_writable_reg(I64); self.emit(&MInst::BrTable { index, tmp1, tmp2, - targets: targets.clone(), + targets: targets.to_vec(), }); } diff --git a/cranelift/codegen/src/isa/s390x/inst.isle b/cranelift/codegen/src/isa/s390x/inst.isle index 59f4003a8887..f6a5bde756c9 100644 --- a/cranelift/codegen/src/isa/s390x/inst.isle +++ b/cranelift/codegen/src/isa/s390x/inst.isle @@ -969,7 +969,7 @@ ;; for rationale). (JTSequence (ridx Reg) - (targets VecMachLabel)) + (targets BoxVecMachLabel)) ;; Load an inline symbol reference with relocation. (LoadSymbolReloc @@ -1738,15 +1738,6 @@ (extern extractor unsigned unsigned) -;; Helpers for machine label vectors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(decl vec_length_minus1 (VecMachLabel) u32) -(extern constructor vec_length_minus1 vec_length_minus1) - -(decl vec_element (VecMachLabel u8) MachLabel) -(extern constructor vec_element vec_element) - - ;; Helpers for memory arguments ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Accessors for `Offset32`. @@ -2717,7 +2708,7 @@ (ConsumesFlags.ConsumesFlagsSideEffect (MInst.OneWayCondBr dest cond))) ;; Helper for emitting `MInst.JTSequence` instructions. -(decl jt_sequence (Reg VecMachLabel) SideEffectNoResult) +(decl jt_sequence (Reg BoxVecMachLabel) SideEffectNoResult) (rule (jt_sequence ridx targets) (SideEffectNoResult.Inst (MInst.JTSequence ridx targets))) diff --git a/cranelift/codegen/src/isa/s390x/inst/emit.rs b/cranelift/codegen/src/isa/s390x/inst/emit.rs index 56dbacf2762e..7efb8b4dfc72 100644 --- a/cranelift/codegen/src/isa/s390x/inst/emit.rs +++ b/cranelift/codegen/src/isa/s390x/inst/emit.rs @@ -3656,12 +3656,9 @@ impl Inst { inst.emit(&[], sink, emit_info, state); // Emit jump table (table of 32-bit offsets). - // The first entry is the default target, which is not emitted - // into the jump table, so we skip it here. It is only in the - // list so MachTerminator will see the potential target. sink.bind_label(table_label, &mut state.ctrl_plane); let jt_off = sink.cur_offset(); - for &target in targets.iter().skip(1) { + for &target in targets.iter() { let word_off = sink.cur_offset(); let off_into_table = word_off - jt_off; sink.use_label_at_offset(word_off, target, LabelUse::PCRel32); diff --git a/cranelift/codegen/src/isa/s390x/lower.isle b/cranelift/codegen/src/isa/s390x/lower.isle index c7bfa335f8b6..6d6aa1e724e4 100644 --- a/cranelift/codegen/src/isa/s390x/lower.isle +++ b/cranelift/codegen/src/isa/s390x/lower.isle @@ -7,7 +7,7 @@ ;; A variant of the main lowering constructor term, used for branches. ;; The only difference is that it gets an extra argument holding a vector ;; of branch targets to be used. -(decl partial lower_branch (Inst VecMachLabel) Unit) +(decl partial lower_branch (Inst MachLabelSlice) Unit) ;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3734,25 +3734,24 @@ ;; Unconditional branch. The target is found as first (and only) element in ;; the list of the current block's branch targets passed as `targets`. -(rule (lower_branch (jump _) targets) - (emit_side_effect (jump_impl (vec_element targets 0)))) +(rule (lower_branch (jump _) (single_target label)) + (emit_side_effect (jump_impl label))) ;;;; Rules for `br_table` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Jump table. `targets` contains the default target followed by the ;; list of branch targets per index value. -(rule (lower_branch (br_table val_idx _) targets) +(rule (lower_branch (br_table val_idx _) (jump_table_targets default targets)) (let ((idx Reg (put_in_reg_zext64 val_idx)) ;; Bounds-check the index and branch to default. ;; This is an internal branch that is not a terminator insn. ;; Instead, the default target is listed a potential target ;; in the final JTSequence, which is the block terminator. (cond ProducesBool - (bool (icmpu_uimm32 $I64 idx (vec_length_minus1 targets)) + (bool (icmpu_uimm32 $I64 idx (jump_table_size targets)) (intcc_as_cond (IntCC.UnsignedGreaterThanOrEqual)))) - (_ Unit (emit_side_effect (oneway_cond_br_bool cond - (vec_element targets 0))))) + (_ Unit (emit_side_effect (oneway_cond_br_bool cond default)))) ;; Scale the index by the element size, and then emit the ;; compound instruction that does: ;; @@ -3775,10 +3774,8 @@ ;; Two-way conditional branch on nonzero. `targets` contains: ;; - element 0: target if the condition is true (i.e. value is nonzero) ;; - element 1: target if the condition is false (i.e. value is zero) -(rule (lower_branch (brif val_cond _ _) targets) - (emit_side_effect (cond_br_bool (value_nonzero val_cond) - (vec_element targets 0) - (vec_element targets 1)))) +(rule (lower_branch (brif val_cond _ _) (two_targets then else)) + (emit_side_effect (cond_br_bool (value_nonzero val_cond) then else))) ;;;; Rules for `trap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/cranelift/codegen/src/isa/s390x/lower/isle.rs b/cranelift/codegen/src/isa/s390x/lower/isle.rs index bb90ed1a8447..cec975d06a15 100644 --- a/cranelift/codegen/src/isa/s390x/lower/isle.rs +++ b/cranelift/codegen/src/isa/s390x/lower/isle.rs @@ -737,16 +737,6 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> { } } - #[inline] - fn vec_length_minus1(&mut self, vec: &VecMachLabel) -> u32 { - u32::try_from(vec.len()).unwrap() - 1 - } - - #[inline] - fn vec_element(&mut self, vec: &VecMachLabel, index: u8) -> MachLabel { - vec[usize::from(index)] - } - #[inline] fn zero_offset(&mut self) -> Offset32 { Offset32::new(0) diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle index a3c7bb654c1c..281b665c7eec 100644 --- a/cranelift/codegen/src/isa/x64/inst.isle +++ b/cranelift/codegen/src/isa/x64/inst.isle @@ -728,26 +728,6 @@ (type BoxCallInfo extern (enum)) (type BoxReturnCallInfo extern (enum)) -(type BoxVecMachLabel extern (enum)) - -(type MachLabelSlice extern (enum)) - -;; The size of the jump table. -(decl jump_table_size (BoxVecMachLabel) u32) -(extern constructor jump_table_size jump_table_size) - -;; Extract a the target from a MachLabelSlice with exactly one target. -(decl single_target (MachLabel) MachLabelSlice) -(extern extractor single_target single_target) - -;; Extract a the targets from a MachLabelSlice with exactly two targets. -(decl two_targets (MachLabel MachLabel) MachLabelSlice) -(extern extractor two_targets two_targets) - -;; Extract the default target and jump table from a MachLabelSlice. -(decl jump_table_targets (MachLabel BoxVecMachLabel) MachLabelSlice) -(extern extractor jump_table_targets jump_table_targets) - ;; Get the `OperandSize` for a given `Type`, rounding smaller types up to 32 bits. (decl operand_size_of_type_32_64 (Type) OperandSize) (extern constructor operand_size_of_type_32_64 operand_size_of_type_32_64) diff --git a/cranelift/codegen/src/isa/x64/lower/isle.rs b/cranelift/codegen/src/isa/x64/lower/isle.rs index 000edbe3f490..99d454a70cd6 100644 --- a/cranelift/codegen/src/isa/x64/lower/isle.rs +++ b/cranelift/codegen/src/isa/x64/lower/isle.rs @@ -36,14 +36,11 @@ use crate::{ }; use alloc::vec::Vec; use regalloc2::PReg; -use smallvec::SmallVec; use std::boxed::Box; use std::convert::TryFrom; type BoxCallInfo = Box; type BoxReturnCallInfo = Box; -type BoxVecMachLabel = Box>; -type MachLabelSlice = [MachLabel]; type VecArgPair = Vec; pub struct SinkableLoad { @@ -756,43 +753,6 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> { output_reg.to_reg() } - #[inline] - fn single_target(&mut self, targets: &MachLabelSlice) -> Option { - if targets.len() == 1 { - Some(targets[0]) - } else { - None - } - } - - #[inline] - fn two_targets(&mut self, targets: &MachLabelSlice) -> Option<(MachLabel, MachLabel)> { - if targets.len() == 2 { - Some((targets[0], targets[1])) - } else { - None - } - } - - #[inline] - fn jump_table_targets( - &mut self, - targets: &MachLabelSlice, - ) -> Option<(MachLabel, BoxVecMachLabel)> { - if targets.is_empty() { - return None; - } - - let default_label = targets[0]; - let jt_targets = Box::new(SmallVec::from(&targets[1..])); - Some((default_label, jt_targets)) - } - - #[inline] - fn jump_table_size(&mut self, targets: &BoxVecMachLabel) -> u32 { - targets.len() as u32 - } - #[inline] fn vconst_all_ones_or_all_zeros(&mut self, constant: Constant) -> Option<()> { let const_data = self.lower_ctx.get_constant_data(constant); diff --git a/cranelift/codegen/src/machinst/isle.rs b/cranelift/codegen/src/machinst/isle.rs index e06cb94e3192..51d4f3e09168 100644 --- a/cranelift/codegen/src/machinst/isle.rs +++ b/cranelift/codegen/src/machinst/isle.rs @@ -32,6 +32,8 @@ pub type InstOutput = SmallVec<[ValueRegs; 2]>; pub type InstOutputBuilder = Cell; pub type BoxExternalName = Box; pub type Range = (usize, usize); +pub type MachLabelSlice = [MachLabel]; +pub type BoxVecMachLabel = Box>; pub enum RangeView { Empty, @@ -637,6 +639,40 @@ macro_rules! isle_lower_prelude_methods { Some(bits as u64) } } + + fn single_target(&mut self, targets: &MachLabelSlice) -> Option { + if targets.len() == 1 { + Some(targets[0]) + } else { + None + } + } + + fn two_targets(&mut self, targets: &MachLabelSlice) -> Option<(MachLabel, MachLabel)> { + if targets.len() == 2 { + Some((targets[0], targets[1])) + } else { + None + } + } + + fn jump_table_targets( + &mut self, + targets: &MachLabelSlice, + ) -> Option<(MachLabel, BoxVecMachLabel)> { + use std::boxed::Box; + if targets.is_empty() { + return None; + } + + let default_label = targets[0]; + let jt_targets = Box::new(targets[1..].to_vec()); + Some((default_label, jt_targets)) + } + + fn jump_table_size(&mut self, targets: &BoxVecMachLabel) -> u32 { + targets.len() as u32 + } }; } diff --git a/cranelift/codegen/src/prelude_lower.isle b/cranelift/codegen/src/prelude_lower.isle index 133f98b01d45..4b5a3a4871ec 100644 --- a/cranelift/codegen/src/prelude_lower.isle +++ b/cranelift/codegen/src/prelude_lower.isle @@ -181,6 +181,24 @@ (type VecArgPair extern (enum)) (type VecRetPair extern (enum)) (type CallArgList extern (enum)) +(type MachLabelSlice extern (enum)) +(type BoxVecMachLabel extern (enum)) + +;; Extract a the target from a MachLabelSlice with exactly one target. +(decl single_target (MachLabel) MachLabelSlice) +(extern extractor single_target single_target) + +;; Extract a the targets from a MachLabelSlice with exactly two targets. +(decl two_targets (MachLabel MachLabel) MachLabelSlice) +(extern extractor two_targets two_targets) + +;; Extract the default target and jump table from a MachLabelSlice. +(decl jump_table_targets (MachLabel BoxVecMachLabel) MachLabelSlice) +(extern extractor jump_table_targets jump_table_targets) + +;; The size of the jump table. +(decl jump_table_size (BoxVecMachLabel) u32) +(extern constructor jump_table_size jump_table_size) ;;;; Helper Clif Extractors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/cranelift/filetests/filetests/isa/aarch64/bti.clif b/cranelift/filetests/filetests/isa/aarch64/bti.clif index e132c493c0a1..d5e7841bf0d8 100644 --- a/cranelift/filetests/filetests/isa/aarch64/bti.clif +++ b/cranelift/filetests/filetests/isa/aarch64/bti.clif @@ -32,7 +32,7 @@ block5(v5: i32): ; block0: ; emit_island 44 ; subs wzr, w0, #3 -; b.hs label4 ; csel x11, xzr, x0, hs ; csdb ; adr x10, pc+16 ; ldrsw x11, [x10, x11, uxtw #2] ; add x10, x10, x11 ; br x10 ; jt_entries [Label(MachLabel(3)), Label(MachLabel(2)), Label(MachLabel(1))] +; b.hs label4 ; csel x11, xzr, x0, hs ; csdb ; adr x10, pc+16 ; ldrsw x11, [x10, x11, uxtw #2] ; add x10, x10, x11 ; br x10 ; jt_entries [MachLabel(3), MachLabel(2), MachLabel(1)] ; block1: ; bti j ; movz w5, #3 @@ -107,7 +107,7 @@ block2: ; mov x8, x5 ; emit_island 36 ; subs wzr, w0, #1 -; b.hs label2 ; csel x7, xzr, x0, hs ; csdb ; adr x6, pc+16 ; ldrsw x7, [x6, x7, uxtw #2] ; add x6, x6, x7 ; br x6 ; jt_entries [Label(MachLabel(1))] +; b.hs label2 ; csel x7, xzr, x0, hs ; csdb ; adr x6, pc+16 ; ldrsw x7, [x6, x7, uxtw #2] ; add x6, x6, x7 ; br x6 ; jt_entries [MachLabel(1)] ; block1: ; bti j ; mov x0, x8 diff --git a/cranelift/filetests/filetests/isa/aarch64/jumptable.clif b/cranelift/filetests/filetests/isa/aarch64/jumptable.clif index 8a885e6e77d3..d400f74eda5f 100644 --- a/cranelift/filetests/filetests/isa/aarch64/jumptable.clif +++ b/cranelift/filetests/filetests/isa/aarch64/jumptable.clif @@ -31,7 +31,7 @@ block5(v5: i32): ; block0: ; emit_island 44 ; subs wzr, w0, #3 -; b.hs label4 ; csel x11, xzr, x0, hs ; csdb ; adr x10, pc+16 ; ldrsw x11, [x10, x11, uxtw #2] ; add x10, x10, x11 ; br x10 ; jt_entries [Label(MachLabel(3)), Label(MachLabel(2)), Label(MachLabel(1))] +; b.hs label4 ; csel x11, xzr, x0, hs ; csdb ; adr x10, pc+16 ; ldrsw x11, [x10, x11, uxtw #2] ; add x10, x10, x11 ; br x10 ; jt_entries [MachLabel(3), MachLabel(2), MachLabel(1)] ; block1: ; movz w5, #3 ; b label5 diff --git a/cranelift/filetests/filetests/isa/s390x/jumptable.clif b/cranelift/filetests/filetests/isa/s390x/jumptable.clif index 0e86a1eea87b..2c71a5019305 100644 --- a/cranelift/filetests/filetests/isa/s390x/jumptable.clif +++ b/cranelift/filetests/filetests/isa/s390x/jumptable.clif @@ -32,7 +32,7 @@ block5(v5: i32): ; clgfi %r3, 3 ; jghe label4 ; sllg %r3, %r3, 2 -; larl %r1, 14 ; agf %r1, 0(%r1, %r3) ; br %r1 ; jt_entries label3 label2 label1 +; larl %r1, 14 ; agf %r1, 0(%r1, %r3) ; br %r1 ; jt_entries label2 label1 ; block1: ; lhi %r5, 3 ; jg label5 diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs index 8377c3a1edda..6b94c5ad73c5 100644 --- a/winch/codegen/src/isa/x64/asm.rs +++ b/winch/codegen/src/isa/x64/asm.rs @@ -903,7 +903,7 @@ impl Assembler { tmp1: Writable::from_reg(tmp1.into()), tmp2: Writable::from_reg(tmp2.into()), default_target: default, - targets: Box::new(targets), + targets: Box::new(targets.to_vec()), }) } From 1e86063574b7e50743f30b6333c27e3c5747e150 Mon Sep 17 00:00:00 2001 From: Dave Bakker Date: Tue, 3 Oct 2023 18:59:56 +0200 Subject: [PATCH 045/199] wasi-sockets: Implement initial listen backlog (#7034) * Allow backlog size to be set before initial listen * Move set_listen_backlog_size into example_body * Format code * Let cap_net_ext handle the default value. * retrigger checks * Got lost while pulling in changes from main. --- .../wasi-sockets-tests/src/lib.rs | 2 + crates/wasi/src/preview2/host/tcp.rs | 41 +++++++++++++++---- crates/wasi/src/preview2/tcp.rs | 4 ++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/crates/test-programs/wasi-sockets-tests/src/lib.rs b/crates/test-programs/wasi-sockets-tests/src/lib.rs index 56c32d3e87b7..c7a49203bb7b 100644 --- a/crates/test-programs/wasi-sockets-tests/src/lib.rs +++ b/crates/test-programs/wasi-sockets-tests/src/lib.rs @@ -44,6 +44,8 @@ pub fn example_body(net: tcp::Network, sock: tcp::TcpSocket, family: network::Ip let sub = sock.subscribe(); + sock.set_listen_backlog_size(32).unwrap(); + sock.start_listen().unwrap(); poll::poll_one(&sub); sock.finish_listen().unwrap(); diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index a022f4d1ab96..479ae44dc63c 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -159,7 +159,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { socket .tcp_socket() .as_socketlike_view::() - .listen(None)?; + .listen(socket.listen_backlog_size)?; socket.tcp_state = TcpState::ListenStarted; @@ -317,16 +317,41 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, value: u64, ) -> Result<(), network::Error> { - let table = self.table(); - let socket = table.get_resource(&this)?; + const MIN_BACKLOG: i32 = 1; + const MAX_BACKLOG: i32 = i32::MAX; // OS'es will most likely limit it down even further. + + let table = self.table_mut(); + let socket = table.get_resource_mut(&this)?; + + // Silently clamp backlog size. This is OK for us to do, because operating systems do this too. + let value = value + .try_into() + .unwrap_or(i32::MAX) + .clamp(MIN_BACKLOG, MAX_BACKLOG); match socket.tcp_state { - TcpState::Listening => {} - _ => return Err(ErrorCode::NotInProgress.into()), - } + TcpState::Default | TcpState::BindStarted | TcpState::Bound => { + // Socket not listening yet. Stash value for first invocation to `listen`. + socket.listen_backlog_size = Some(value); - let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; - Ok(rustix::net::listen(socket.tcp_socket(), value)?) + Ok(()) + } + TcpState::Listening => { + // Try to update the backlog by calling `listen` again. + // Not all platforms support this. We'll only update our own value if the OS supports changing the backlog size after the fact. + + rustix::net::listen(socket.tcp_socket(), value) + .map_err(|_| ErrorCode::AlreadyListening)?; + + socket.listen_backlog_size = Some(value); + + Ok(()) + } + TcpState::Connected => Err(ErrorCode::AlreadyConnected.into()), + TcpState::Connecting | TcpState::ConnectReady | TcpState::ListenStarted => { + Err(ErrorCode::ConcurrencyConflict.into()) + } + } } fn keep_alive(&mut self, this: Resource) -> Result { diff --git a/crates/wasi/src/preview2/tcp.rs b/crates/wasi/src/preview2/tcp.rs index 61bef55dd240..3d5925a686b8 100644 --- a/crates/wasi/src/preview2/tcp.rs +++ b/crates/wasi/src/preview2/tcp.rs @@ -53,6 +53,9 @@ pub struct TcpSocket { /// The current state in the bind/listen/accept/connect progression. pub(crate) tcp_state: TcpState, + + /// The desired listen queue size. Set to None to use the system's default. + pub(crate) listen_backlog_size: Option, } pub(crate) struct TcpReadStream { @@ -264,6 +267,7 @@ impl TcpSocket { Ok(Self { inner: Arc::new(stream), tcp_state: TcpState::Default, + listen_backlog_size: None, }) } From 86cd2a01d12dcbf01625143dafd0d7ced8549753 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Oct 2023 12:07:18 -0500 Subject: [PATCH 046/199] Fix a flaky preview2 test (#7138) Found in CI for #7099 this commit updates the durations used in the `resolves_immediately` and `never_resolves` tests. For immediately resolving it should be ok to wait for a generous amount of time since the timeout is only a protection against tests hanging. For `never_resolves` it should be ok to wait a short amount of time and any failure there will show up as a spurious failure. --- crates/wasi/src/preview2/pipe.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/crates/wasi/src/preview2/pipe.rs b/crates/wasi/src/preview2/pipe.rs index b99e68a28524..dae3e7569a99 100644 --- a/crates/wasi/src/preview2/pipe.rs +++ b/crates/wasi/src/preview2/pipe.rs @@ -282,35 +282,33 @@ impl Subscribe for ClosedOutputStream { #[cfg(test)] mod test { use super::*; + use std::time::Duration; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; // This is a gross way to handle CI running under qemu for non-x86 architectures. #[cfg(not(target_arch = "x86_64"))] const TEST_ITERATIONS: usize = 10; - // This is a gross way to handle CI running under qemu for non-x86 architectures. - #[cfg(not(target_arch = "x86_64"))] - const REASONABLE_DURATION: std::time::Duration = std::time::Duration::from_millis(200); - #[cfg(target_arch = "x86_64")] const TEST_ITERATIONS: usize = 100; - #[cfg(target_arch = "x86_64")] - const REASONABLE_DURATION: std::time::Duration = std::time::Duration::from_millis(10); - async fn resolves_immediately(fut: F) -> O where F: futures::Future, { - tokio::time::timeout(REASONABLE_DURATION, fut) + // The input `fut` should resolve immediately, but in case it + // accidentally doesn't don't hang the test indefinitely. Provide a + // generous timeout to account for CI sensitivity and various systems. + tokio::time::timeout(Duration::from_secs(2), fut) .await .expect("operation timed out") } - // TODO: is there a way to get tokio to warp through timeouts when it knows nothing is - // happening? async fn never_resolves(fut: F) { - tokio::time::timeout(REASONABLE_DURATION, fut) + // The input `fut` should never resolve, so only give it a small window + // of budget before we time out. If `fut` is actually resolved this + // should show up as a flaky test. + tokio::time::timeout(Duration::from_millis(10), fut) .await .err() .expect("operation should time out"); From ec07c89b9b57a2e4d377b3c730f1b8cf8e31bd3a Mon Sep 17 00:00:00 2001 From: Tyler Rockwood Date: Tue, 3 Oct 2023 12:51:07 -0500 Subject: [PATCH 047/199] c-api: Support InstancePre (#7140) * c-api: Support InstancePre Signed-off-by: Tyler Rockwood * c-api: Consolidate all the instance_pre functionality Signed-off-by: Tyler Rockwood * c-api: Add async instantiate support to pre instances Signed-off-by: Tyler Rockwood * c-api: Add star to comment for doxygen prtest:full Signed-off-by: Tyler Rockwood --------- Signed-off-by: Tyler Rockwood --- crates/c-api/include/wasmtime/async.h | 26 +++++++++++++- crates/c-api/include/wasmtime/instance.h | 45 ++++++++++++++++++++++++ crates/c-api/include/wasmtime/linker.h | 18 ++++++++++ crates/c-api/src/async.rs | 36 +++++++++++++++++-- crates/c-api/src/instance.rs | 24 +++++++++++-- crates/c-api/src/linker.rs | 15 +++++++- 6 files changed, 158 insertions(+), 6 deletions(-) diff --git a/crates/c-api/include/wasmtime/async.h b/crates/c-api/include/wasmtime/async.h index 0c92bf8ec31e..dbe4312dc118 100644 --- a/crates/c-api/include/wasmtime/async.h +++ b/crates/c-api/include/wasmtime/async.h @@ -256,7 +256,31 @@ WASM_API_EXTERN wasmtime_call_future_t *wasmtime_linker_instantiate_async( const wasmtime_module_t *module, wasmtime_instance_t *instance, wasm_trap_t** trap_ret, - wasmtime_error_t** wasmtime_error_t); + wasmtime_error_t** error_ret); + +/** + * \brief Instantiates instance within the given store. + * + * This will also run the function's startup function, if there is one. + * + * For more information on async instantiation see #wasmtime_linker_instantiate_async. + * + * \param instance_pre the pre-initialized instance + * \param store the store in which to create the instance + * \param instance where to store the returned instance + * \param trap_ret where to store the returned trap + * \param error_ret where to store the returned trap + * + * The `trap_ret` and `error_ret` pointers may *not* be `NULL` and the returned memory is owned by the caller. + * + * All arguments to this function must outlive the returned future and be unmodified until the future is deleted. + */ +WASM_API_EXTERN wasmtime_call_future_t *wasmtime_instance_pre_instantiate_async( + const wasmtime_instance_pre_t* instance_pre, + wasmtime_context_t *store, + wasmtime_instance_t *instance, + wasm_trap_t** trap_ret, + wasmtime_error_t** error_ret); #ifdef __cplusplus } // extern "C" diff --git a/crates/c-api/include/wasmtime/instance.h b/crates/c-api/include/wasmtime/instance.h index 544661f9d8fe..72f398ec9cff 100644 --- a/crates/c-api/include/wasmtime/instance.h +++ b/crates/c-api/include/wasmtime/instance.h @@ -121,6 +121,51 @@ WASM_API_EXTERN bool wasmtime_instance_export_nth( wasmtime_extern_t *item ); +/** + * \brief A #wasmtime_instance_t, pre-instantiation, that is ready to be instantiated. + * + * Must be deleted using #wasmtime_instance_pre_delete. + * + * For more information see the Rust documentation: + * https://docs.wasmtime.dev/api/wasmtime/struct.InstancePre.html + */ +typedef struct wasmtime_instance_pre wasmtime_instance_pre_t; + +/** + * \brief Delete a previously created wasmtime_instance_pre_t. + */ +WASM_API_EXTERN void +wasmtime_instance_pre_delete(wasmtime_instance_pre_t *instance); + +/** + * \brief Instantiates instance within the given store. + * + * This will also run the function's startup function, if there is one. + * + * For more information on instantiation see #wasmtime_instance_new. + * + * \param instance_pre the pre-initialized instance + * \param store the store in which to create the instance + * \param instance where to store the returned instance + * \param trap_ptr where to store the returned trap + * + * \return One of three things can happen as a result of this function. First + * the module could be successfully instantiated and returned through + * `instance`, meaning the return value and `trap` are both set to `NULL`. + * Second the start function may trap, meaning the return value and `instance` + * are set to `NULL` and `trap` describes the trap that happens. Finally + * instantiation may fail for another reason, in which case an error is returned + * and `trap` and `instance` are set to `NULL`. + * + * This function does not take ownership of any of its arguments, and all return + * values are owned by the caller. + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_instance_pre_instantiate( + const wasmtime_instance_pre_t* instance_pre, + wasmtime_store_t *store, + wasmtime_instance_t* instance, + wasm_trap_t **trap_ptr); + #ifdef __cplusplus } // extern "C" #endif diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h index 453ef73d64ea..3215efe9ff6c 100644 --- a/crates/c-api/include/wasmtime/linker.h +++ b/crates/c-api/include/wasmtime/linker.h @@ -292,6 +292,24 @@ WASM_API_EXTERN bool wasmtime_linker_get( wasmtime_extern_t *item ); +/** + * \brief Preform all the checks for instantiating `module` with the linker, + * except that instantiation doesn't actually finish. + * + * \param linker the linker used to instantiate the provided module. + * \param module the module that is being instantiated. + * \param instance_pre the returned instance_pre, if successful. + * + * \return An error or `NULL` if successful. + * + * For more information see the Rust documentation at: + * https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.instantiate_pre + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate_pre( + const wasmtime_linker_t *linker, + const wasmtime_module_t *module, + wasmtime_instance_t **instance_pre); + #ifdef __cplusplus } // extern "C" #endif diff --git a/crates/c-api/src/async.rs b/crates/c-api/src/async.rs index 99702cf26402..23572db32e22 100644 --- a/crates/c-api/src/async.rs +++ b/crates/c-api/src/async.rs @@ -9,8 +9,8 @@ use wasmtime::{AsContextMut, Caller, Func, Instance, Result, Trap, Val}; use crate::{ bad_utf8, handle_result, to_str, translate_args, wasm_config_t, wasm_functype_t, wasm_trap_t, - wasmtime_caller_t, wasmtime_error_t, wasmtime_linker_t, wasmtime_module_t, wasmtime_val_t, - wasmtime_val_union, CStoreContextMut, WASMTIME_I32, + wasmtime_caller_t, wasmtime_error_t, wasmtime_instance_pre_t, wasmtime_linker_t, + wasmtime_module_t, wasmtime_val_t, wasmtime_val_union, CStoreContextMut, WASMTIME_I32, }; #[no_mangle] @@ -303,3 +303,35 @@ pub extern "C" fn wasmtime_linker_instantiate_async<'a>( )); Box::new(crate::wasmtime_call_future_t { underlying: fut }) } + +async fn do_instance_pre_instantiate_async( + instance_pre: &wasmtime_instance_pre_t, + store: CStoreContextMut<'_>, + instance_ptr: &mut Instance, + trap_ret: &mut *mut wasm_trap_t, + err_ret: &mut *mut wasmtime_error_t, +) { + let result = instance_pre.underlying.instantiate_async(store).await; + match result { + Ok(instance) => *instance_ptr = instance, + Err(err) => handle_call_error(err, trap_ret, err_ret), + } +} + +#[no_mangle] +pub extern "C" fn wasmtime_instance_pre_instantiate_async<'a>( + instance_pre: &'a wasmtime_instance_pre_t, + store: CStoreContextMut<'a>, + instance_ptr: &'a mut Instance, + trap_ret: &'a mut *mut wasm_trap_t, + err_ret: &'a mut *mut wasmtime_error_t, +) -> Box> { + let fut = Box::pin(do_instance_pre_instantiate_async( + instance_pre, + store, + instance_ptr, + trap_ret, + err_ret, + )); + Box::new(crate::wasmtime_call_future_t { underlying: fut }) +} diff --git a/crates/c-api/src/instance.rs b/crates/c-api/src/instance.rs index 13fb96a36914..b8ae5bc82341 100644 --- a/crates/c-api/src/instance.rs +++ b/crates/c-api/src/instance.rs @@ -1,9 +1,9 @@ use crate::{ wasm_extern_t, wasm_extern_vec_t, wasm_module_t, wasm_store_t, wasm_trap_t, wasmtime_error_t, - wasmtime_extern_t, wasmtime_module_t, CStoreContextMut, StoreRef, + wasmtime_extern_t, wasmtime_module_t, CStoreContextMut, StoreData, StoreRef, }; use std::mem::MaybeUninit; -use wasmtime::{Instance, Trap}; +use wasmtime::{Instance, InstancePre, Trap}; #[derive(Clone)] pub struct wasm_instance_t { @@ -150,3 +150,23 @@ pub unsafe extern "C" fn wasmtime_instance_export_nth( None => false, } } + +#[repr(transparent)] +pub struct wasmtime_instance_pre_t { + pub(crate) underlying: InstancePre, +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_instance_pre_delete(_instance_pre: Box) { +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_instance_pre_instantiate( + instance_pre: &wasmtime_instance_pre_t, + store: CStoreContextMut<'_>, + instance_ptr: &mut Instance, + trap_ptr: &mut *mut wasm_trap_t, +) -> Option> { + let result = instance_pre.underlying.instantiate(store); + handle_instantiate(result, instance_ptr, trap_ptr) +} diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs index bf9cf3af4591..7cf9391d8f8a 100644 --- a/crates/c-api/src/linker.rs +++ b/crates/c-api/src/linker.rs @@ -1,6 +1,6 @@ use crate::{ bad_utf8, handle_result, wasm_engine_t, wasm_functype_t, wasm_trap_t, wasmtime_error_t, - wasmtime_extern_t, wasmtime_module_t, CStoreContext, CStoreContextMut, + wasmtime_extern_t, wasmtime_instance_pre_t, wasmtime_module_t, CStoreContext, CStoreContextMut, }; use std::ffi::c_void; use std::mem::MaybeUninit; @@ -138,6 +138,19 @@ pub extern "C" fn wasmtime_linker_instantiate( super::instance::handle_instantiate(result, instance_ptr, trap_ptr) } +#[no_mangle] +pub unsafe extern "C" fn wasmtime_linker_instantiate_pre( + linker: &wasmtime_linker_t, + module: &wasmtime_module_t, + instance_ptr: &mut *mut wasmtime_instance_pre_t, +) -> Option> { + let linker = &linker.linker; + handle_result(linker.instantiate_pre(&module.module), |i| { + let instance_pre = Box::new(wasmtime_instance_pre_t { underlying: i }); + *instance_ptr = Box::into_raw(instance_pre) + }) +} + #[no_mangle] pub unsafe extern "C" fn wasmtime_linker_module( linker: &mut wasmtime_linker_t, From 71f650b011430041e69c17dab986f425d1b3ec58 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Oct 2023 15:23:36 -0500 Subject: [PATCH 048/199] Fix race condition in `AsyncWriteStream` (#7144) * Fix race condition in `AsyncWriteStream` This commit fixes a synchronization issue with `AsyncWriteStream` where the writer task would get wedged and not make any further progress. The underlying reason is that `notify_waiters` was used which did not buffer its notification, meaning that if a waiter wasn't actually waiting then it would miss the notification and block forever on the next call to `notified`. By using `notify_one` instead this will buffer up a single notification for the other end for when it gets to waiting for work. Additionally this removes creation of the `Notified` future ahead-of-time as that's no longer necessary. * Fix http bodies too --- crates/wasi-http/src/body.rs | 11 +++++------ crates/wasi/src/preview2/write_stream.rs | 12 +++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/crates/wasi-http/src/body.rs b/crates/wasi-http/src/body.rs index 9d27832049be..d7e3b07daced 100644 --- a/crates/wasi-http/src/body.rs +++ b/crates/wasi-http/src/body.rs @@ -416,12 +416,11 @@ impl Worker { state.error = Some(e.into()); state.flush_pending = false; } - self.write_ready_changed.notify_waiters(); + self.write_ready_changed.notify_one(); } async fn work(&self, writer: mpsc::Sender) { loop { - let notified = self.new_work.notified(); while let Some(job) = self.pop() { match job { Job::Flush => { @@ -446,10 +445,10 @@ impl Worker { } } - self.write_ready_changed.notify_waiters(); + self.write_ready_changed.notify_one(); } - notified.await; + self.new_work.notified().await; } } } @@ -493,7 +492,7 @@ impl HostOutputStream for BodyWriteStream { None => return Err(OutputStreamError::Trap(anyhow!("write exceeded budget"))), } drop(state); - self.worker.new_work.notify_waiters(); + self.worker.new_work.notify_one(); Ok(()) } fn flush(&mut self) -> Result<(), OutputStreamError> { @@ -501,7 +500,7 @@ impl HostOutputStream for BodyWriteStream { state.check_error()?; state.flush_pending = true; - self.worker.new_work.notify_waiters(); + self.worker.new_work.notify_one(); Ok(()) } diff --git a/crates/wasi/src/preview2/write_stream.rs b/crates/wasi/src/preview2/write_stream.rs index 726d0d2674bb..166192aa0506 100644 --- a/crates/wasi/src/preview2/write_stream.rs +++ b/crates/wasi/src/preview2/write_stream.rs @@ -97,12 +97,11 @@ impl Worker { state.error = Some(e.into()); state.flush_pending = false; } - self.write_ready_changed.notify_waiters(); + self.write_ready_changed.notify_one(); } async fn work(&self, mut writer: T) { use tokio::io::AsyncWriteExt; loop { - let notified = self.new_work.notified(); while let Some(job) = self.pop() { match job { Job::Flush => { @@ -130,10 +129,9 @@ impl Worker { } } - self.write_ready_changed.notify_waiters(); + self.write_ready_changed.notify_one(); } - - notified.await; + self.new_work.notified().await; } } } @@ -180,7 +178,7 @@ impl HostOutputStream for AsyncWriteStream { None => return Err(OutputStreamError::Trap(anyhow!("write exceeded budget"))), } drop(state); - self.worker.new_work.notify_waiters(); + self.worker.new_work.notify_one(); Ok(()) } fn flush(&mut self) -> Result<(), OutputStreamError> { @@ -188,7 +186,7 @@ impl HostOutputStream for AsyncWriteStream { state.check_error()?; state.flush_pending = true; - self.worker.new_work.notify_waiters(); + self.worker.new_work.notify_one(); Ok(()) } From 57a7cafe3ccb8b3c8e5c6cf37282c22392146c9a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Oct 2023 13:42:57 -0500 Subject: [PATCH 049/199] riscv64: Update `replicated_{u,}imm5` to match `vconst` (#7141) This switches the `extractor` to a `pure partial` constructor to be used in `if-let` instead of the extraction position, which while not quite as ergonomic does enable matching more constant values in more locations. --- cranelift/codegen/src/isa/aarch64/inst.isle | 11 --- .../codegen/src/isa/aarch64/lower/isle.rs | 38 ---------- cranelift/codegen/src/isa/riscv64/inst.isle | 30 +++++--- .../codegen/src/isa/riscv64/inst_vector.isle | 60 +++++++++------- cranelift/codegen/src/isa/riscv64/lower.isle | 70 +++++++++++-------- cranelift/codegen/src/isle_prelude.rs | 42 +++++++++++ cranelift/codegen/src/prelude.isle | 14 ++++ .../filetests/isa/riscv64/simd-bor.clif | 38 ++++++++++ 8 files changed, 192 insertions(+), 111 deletions(-) diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle index 172af505ea70..0e1d03334ffa 100644 --- a/cranelift/codegen/src/isa/aarch64/inst.isle +++ b/cranelift/codegen/src/isa/aarch64/inst.isle @@ -3308,17 +3308,6 @@ (rule (splat_const n size) (vec_dup (imm $I64 (ImmExtend.Zero) n) size)) -;; Each of these extractors tests whether the upper half of the input equals the -;; lower half of the input -(decl u128_replicated_u64 (u64) u128) -(extern extractor u128_replicated_u64 u128_replicated_u64) -(decl u64_replicated_u32 (u64) u64) -(extern extractor u64_replicated_u32 u64_replicated_u32) -(decl u32_replicated_u16 (u64) u64) -(extern extractor u32_replicated_u16 u32_replicated_u16) -(decl u16_replicated_u8 (u64) u64) -(extern extractor u16_replicated_u8 u16_replicated_u8) - ;; Lower a FloatCC to a Cond. (decl fp_cond_code (FloatCC) Cond) ;; TODO: Port lower_fp_condcode() to ISLE. diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle.rs b/cranelift/codegen/src/isa/aarch64/lower/isle.rs index 8887ae1e2023..0c3f151c4818 100644 --- a/cranelift/codegen/src/isa/aarch64/lower/isle.rs +++ b/cranelift/codegen/src/isa/aarch64/lower/isle.rs @@ -797,44 +797,6 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> { } } - fn u128_replicated_u64(&mut self, val: u128) -> Option { - let low64 = val as u64 as u128; - if (low64 | (low64 << 64)) == val { - Some(low64 as u64) - } else { - None - } - } - - fn u64_replicated_u32(&mut self, val: u64) -> Option { - let low32 = val as u32 as u64; - if (low32 | (low32 << 32)) == val { - Some(low32) - } else { - None - } - } - - fn u32_replicated_u16(&mut self, val: u64) -> Option { - let val = val as u32; - let low16 = val as u16 as u32; - if (low16 | (low16 << 16)) == val { - Some(low16.into()) - } else { - None - } - } - - fn u16_replicated_u8(&mut self, val: u64) -> Option { - let val = val as u16; - let low8 = val as u8 as u16; - if (low8 | (low8 << 8)) == val { - Some(low8.into()) - } else { - None - } - } - fn shift_masked_imm(&mut self, ty: Type, imm: u64) -> u8 { (imm as u8) & ((ty.lane_bits() - 1) as u8) } diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index f2c1d3c1ed5a..bba497b4cc02 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -1839,19 +1839,29 @@ (decl pure partial i8_to_imm5 (i8) Imm5) (extern constructor i8_to_imm5 i8_to_imm5) -;; Extractor that matches a `Value` equivalent to a replicated Imm5 on all lanes. -;; TODO(#6527): Try matching vconst here as well -(decl replicated_imm5 (Imm5) Value) -(extractor (replicated_imm5 n) - (def_inst (splat (i64_from_iconst (imm5_from_i64 n))))) +;; Constructor that matches a `Value` equivalent to a replicated Imm5 on all lanes. +(decl pure partial replicated_imm5 (Value) Imm5) +(rule (replicated_imm5 (splat (i64_from_iconst (imm5_from_i64 n)))) n) +(rule (replicated_imm5 (vconst (u128_from_constant n128))) + (if-let (u128_replicated_u64 n64) n128) + (if-let (u64_replicated_u32 n32) n64) + (if-let (u32_replicated_u16 n16) n32) + (if-let (u16_replicated_u8 n8) n16) + (if-let n (i8_to_imm5 (u8_as_i8 n8))) + n) ;; UImm5 Helpers -;; Extractor that matches a `Value` equivalent to a replicated UImm5 on all lanes. -;; TODO(#6527): Try matching vconst here as well -(decl replicated_uimm5 (UImm5) Value) -(extractor (replicated_uimm5 n) - (def_inst (splat (uimm5_from_value n)))) +;; Constructor that matches a `Value` equivalent to a replicated UImm5 on all lanes. +(decl pure partial replicated_uimm5 (Value) UImm5) +(rule (replicated_uimm5 (splat (uimm5_from_value n))) n) +(rule 1 (replicated_uimm5 (vconst (u128_from_constant n128))) + (if-let (u128_replicated_u64 n64) n128) + (if-let (u64_replicated_u32 n32) n64) + (if-let (u32_replicated_u16 n16) n32) + (if-let (u16_replicated_u8 n8) n16) + (if-let (uimm5_from_u8 n) n8) + n) ;; Helper to go directly from a `Value`, when it's an `iconst`, to an `UImm5`. (decl uimm5_from_value (UImm5) Value) diff --git a/cranelift/codegen/src/isa/riscv64/inst_vector.isle b/cranelift/codegen/src/isa/riscv64/inst_vector.isle index 685761e2a8d0..8c48ec991e19 100644 --- a/cranelift/codegen/src/isa/riscv64/inst_vector.isle +++ b/cranelift/codegen/src/isa/riscv64/inst_vector.isle @@ -1585,11 +1585,13 @@ (rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) (splat x) y) (rv_vmseq_vx y x (unmasked) ty)) -(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) x (replicated_imm5 y)) - (rv_vmseq_vi x y (unmasked) ty)) +(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) x y) + (if-let y_imm (replicated_imm5 y)) + (rv_vmseq_vi x y_imm (unmasked) ty)) -(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) (replicated_imm5 x) y) - (rv_vmseq_vi y x (unmasked) ty)) +(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.Equal) x y) + (if-let x_imm (replicated_imm5 x)) + (rv_vmseq_vi y x_imm (unmasked) ty)) ;; IntCC.NotEqual @@ -1602,11 +1604,13 @@ (rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) (splat x) y) (rv_vmsne_vx y x (unmasked) ty)) -(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) x (replicated_imm5 y)) - (rv_vmsne_vi x y (unmasked) ty)) +(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) x y) + (if-let y_imm (replicated_imm5 y)) + (rv_vmsne_vi x y_imm (unmasked) ty)) -(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) (replicated_imm5 x) y) - (rv_vmsne_vi y x (unmasked) ty)) +(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.NotEqual) x y) + (if-let x_imm (replicated_imm5 x)) + (rv_vmsne_vi y x_imm (unmasked) ty)) ;; IntCC.UnsignedLessThan @@ -1619,8 +1623,9 @@ (rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThan) (splat x) y) (rv_vmsgtu_vx y x (unmasked) ty)) -(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThan) (replicated_imm5 x) y) - (rv_vmsgtu_vi y x (unmasked) ty)) +(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThan) x y) + (if-let x_imm (replicated_imm5 x)) + (rv_vmsgtu_vi y x_imm (unmasked) ty)) ;; IntCC.SignedLessThan @@ -1633,8 +1638,9 @@ (rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThan) (splat x) y) (rv_vmsgt_vx y x (unmasked) ty)) -(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThan) (replicated_imm5 x) y) - (rv_vmsgt_vi y x (unmasked) ty)) +(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThan) x y) + (if-let x_imm (replicated_imm5 x)) + (rv_vmsgt_vi y x_imm (unmasked) ty)) ;; IntCC.UnsignedLessThanOrEqual @@ -1644,8 +1650,9 @@ (rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThanOrEqual) x (splat y)) (rv_vmsleu_vx x y (unmasked) ty)) -(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThanOrEqual) x (replicated_imm5 y)) - (rv_vmsleu_vi x y (unmasked) ty)) +(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedLessThanOrEqual) x y) + (if-let y_imm (replicated_imm5 y)) + (rv_vmsleu_vi x y_imm (unmasked) ty)) ;; IntCC.SignedLessThanOrEqual @@ -1655,8 +1662,9 @@ (rule 1 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThanOrEqual) x (splat y)) (rv_vmsle_vx x y (unmasked) ty)) -(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThanOrEqual) x (replicated_imm5 y)) - (rv_vmsle_vi x y (unmasked) ty)) +(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedLessThanOrEqual) x y) + (if-let y_imm (replicated_imm5 y)) + (rv_vmsle_vi x y_imm (unmasked) ty)) ;; IntCC.UnsignedGreaterThan @@ -1669,8 +1677,9 @@ (rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThan) (splat x) y) (rv_vmsltu_vx y x (unmasked) ty)) -(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThan) x (replicated_imm5 y)) - (rv_vmsgtu_vi x y (unmasked) ty)) +(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThan) x y) + (if-let y_imm (replicated_imm5 y)) + (rv_vmsgtu_vi x y_imm (unmasked) ty)) ;; IntCC.SignedGreaterThan @@ -1683,8 +1692,9 @@ (rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThan) (splat x) y) (rv_vmslt_vx y x (unmasked) ty)) -(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThan) x (replicated_imm5 y)) - (rv_vmsgt_vi x y (unmasked) ty)) +(rule 3 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThan) x y) + (if-let y_imm (replicated_imm5 y)) + (rv_vmsgt_vi x y_imm (unmasked) ty)) ;; IntCC.UnsignedGreaterThanOrEqual @@ -1694,8 +1704,9 @@ (rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThanOrEqual) (splat x) y) (rv_vmsleu_vx y x (unmasked) ty)) -(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThanOrEqual) (replicated_imm5 x) y) - (rv_vmsleu_vi y x (unmasked) ty)) +(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.UnsignedGreaterThanOrEqual) x y) + (if-let x_imm (replicated_imm5 x)) + (rv_vmsleu_vi y x_imm (unmasked) ty)) ;; IntCC.SignedGreaterThanOrEqual @@ -1705,8 +1716,9 @@ (rule 2 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThanOrEqual) (splat x) y) (rv_vmsle_vx y x (unmasked) ty)) -(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThanOrEqual) (replicated_imm5 x) y) - (rv_vmsle_vi y x (unmasked) ty)) +(rule 4 (gen_icmp_mask (ty_vec_fits_in_register ty) (IntCC.SignedGreaterThanOrEqual) x y) + (if-let x_imm (replicated_imm5 x)) + (rv_vmsle_vi y x_imm (unmasked) ty)) diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index 1fbf014c5095..13d60f3676c3 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -119,8 +119,9 @@ (if-let $true (ty_equal (lane_type half_ty) uext_ty)) (rv_vwaddu_wx x y (unmasked) (vstate_mf2 half_ty))) -(rule 11 (lower (has_type (ty_vec_fits_in_register ty) (iadd x (replicated_imm5 y)))) - (rv_vadd_vi x y (unmasked) ty)) +(rule 20 (lower (has_type (ty_vec_fits_in_register ty) (iadd x y))) + (if-let y_imm (replicated_imm5 y)) + (rv_vadd_vi x y_imm (unmasked) ty)) (rule 12 (lower (has_type (ty_vec_fits_in_register ty) (iadd (splat x) y))) @@ -136,8 +137,9 @@ (if-let $true (ty_equal (lane_type half_ty) uext_ty)) (rv_vwaddu_wx y x (unmasked) (vstate_mf2 half_ty))) -(rule 14 (lower (has_type (ty_vec_fits_in_register ty) (iadd (replicated_imm5 x) y))) - (rv_vadd_vi y x (unmasked) ty)) +(rule 21 (lower (has_type (ty_vec_fits_in_register ty) (iadd x y))) + (if-let x_imm (replicated_imm5 x)) + (rv_vadd_vi y x_imm (unmasked) ty)) ;; Signed Widening Low Additions @@ -339,8 +341,9 @@ (rule 6 (lower (has_type (ty_vec_fits_in_register ty) (isub (splat x) y))) (rv_vrsub_vx y x (unmasked) ty)) -(rule 7 (lower (has_type (ty_vec_fits_in_register ty) (isub (replicated_imm5 x) y))) - (rv_vrsub_vi y x (unmasked) ty)) +(rule 7 (lower (has_type (ty_vec_fits_in_register ty) (isub x y))) + (if-let x_imm (replicated_imm5 x)) + (rv_vrsub_vi y x_imm (unmasked) ty)) ;; Signed Widening Low Subtractions @@ -696,11 +699,13 @@ (if (ty_vector_not_float ty)) (rv_vand_vx y x (unmasked) ty)) -(rule 11 (lower (has_type (ty_vec_fits_in_register ty) (band x (replicated_imm5 y)))) - (rv_vand_vi x y (unmasked) ty)) +(rule 11 (lower (has_type (ty_vec_fits_in_register ty) (band x y))) + (if-let y_imm (replicated_imm5 y)) + (rv_vand_vi x y_imm (unmasked) ty)) -(rule 12 (lower (has_type (ty_vec_fits_in_register ty) (band (replicated_imm5 x) y))) - (rv_vand_vi y x (unmasked) ty)) +(rule 12 (lower (has_type (ty_vec_fits_in_register ty) (band x y))) + (if-let x_imm (replicated_imm5 x)) + (rv_vand_vi y x_imm (unmasked) ty)) ;;;; Rules for `or` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -752,11 +757,13 @@ (if (ty_vector_not_float ty)) (rv_vor_vx y x (unmasked) ty)) -(rule 11 (lower (has_type (ty_vec_fits_in_register ty) (bor x (replicated_imm5 y)))) - (rv_vor_vi x y (unmasked) ty)) +(rule 11 (lower (has_type (ty_vec_fits_in_register ty) (bor x y))) + (if-let y_imm (replicated_imm5 y)) + (rv_vor_vi x y_imm (unmasked) ty)) -(rule 12 (lower (has_type (ty_vec_fits_in_register ty) (bor (replicated_imm5 x) y))) - (rv_vor_vi y x (unmasked) ty)) +(rule 12 (lower (has_type (ty_vec_fits_in_register ty) (bor x y))) + (if-let x_imm (replicated_imm5 x)) + (rv_vor_vi y x_imm (unmasked) ty)) ;;;; Rules for `xor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -787,11 +794,13 @@ (if (ty_vector_not_float ty)) (rv_vxor_vx y x (unmasked) ty)) -(rule 8 (lower (has_type (ty_vec_fits_in_register ty) (bxor x (replicated_imm5 y)))) - (rv_vxor_vi x y (unmasked) ty)) +(rule 8 (lower (has_type (ty_vec_fits_in_register ty) (bxor x y))) + (if-let y_imm (replicated_imm5 y)) + (rv_vxor_vi x y_imm (unmasked) ty)) -(rule 9 (lower (has_type (ty_vec_fits_in_register ty) (bxor (replicated_imm5 x) y))) - (rv_vxor_vi y x (unmasked) ty)) +(rule 9 (lower (has_type (ty_vec_fits_in_register ty) (bxor x y))) + (if-let x_imm (replicated_imm5 x)) + (rv_vxor_vi y x_imm (unmasked) ty)) ;;;; Rules for `bnot` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2107,11 +2116,13 @@ (rule 2 (lower (has_type (ty_vec_fits_in_register ty) (uadd_sat (splat x) y))) (rv_vsaddu_vx y x (unmasked) ty)) -(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (uadd_sat x (replicated_imm5 y)))) - (rv_vsaddu_vi x y (unmasked) ty)) +(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (uadd_sat x y))) + (if-let y_imm (replicated_imm5 y)) + (rv_vsaddu_vi x y_imm (unmasked) ty)) -(rule 4 (lower (has_type (ty_vec_fits_in_register ty) (uadd_sat (replicated_imm5 x) y))) - (rv_vsaddu_vi y x (unmasked) ty)) +(rule 4 (lower (has_type (ty_vec_fits_in_register ty) (uadd_sat x y))) + (if-let x_imm (replicated_imm5 x)) + (rv_vsaddu_vi y x_imm (unmasked) ty)) ;;;; Rules for `sadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2124,11 +2135,13 @@ (rule 2 (lower (has_type (ty_vec_fits_in_register ty) (sadd_sat (splat x) y))) (rv_vsadd_vx y x (unmasked) ty)) -(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (sadd_sat x (replicated_imm5 y)))) - (rv_vsadd_vi x y (unmasked) ty)) +(rule 3 (lower (has_type (ty_vec_fits_in_register ty) (sadd_sat x y))) + (if-let y_imm (replicated_imm5 y)) + (rv_vsadd_vi x y_imm (unmasked) ty)) -(rule 4 (lower (has_type (ty_vec_fits_in_register ty) (sadd_sat (replicated_imm5 x) y))) - (rv_vsadd_vi y x (unmasked) ty)) +(rule 4 (lower (has_type (ty_vec_fits_in_register ty) (sadd_sat x y))) + (if-let x_imm (replicated_imm5 x)) + (rv_vsadd_vi y x_imm (unmasked) ty)) ;;;; Rules for `usub_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2202,8 +2215,9 @@ (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (swizzle x (splat y)))) (rv_vrgather_vx x y (unmasked) ty)) -(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (swizzle x (replicated_uimm5 y)))) - (rv_vrgather_vi x y (unmasked) ty)) +(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (swizzle x y))) + (if-let y_imm (replicated_uimm5 y)) + (rv_vrgather_vi x y_imm (unmasked) ty)) ;;;; Rules for `shuffle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/cranelift/codegen/src/isle_prelude.rs b/cranelift/codegen/src/isle_prelude.rs index 71c45fd329b1..26332de5a88b 100644 --- a/cranelift/codegen/src/isle_prelude.rs +++ b/cranelift/codegen/src/isle_prelude.rs @@ -908,5 +908,47 @@ macro_rules! isle_common_prelude_methods { fn u64_as_u32(&mut self, val: u64) -> Option { u32::try_from(val).ok() } + + fn u8_as_i8(&mut self, val: u8) -> i8 { + val as i8 + } + + fn u128_replicated_u64(&mut self, val: u128) -> Option { + let low64 = val as u64 as u128; + if (low64 | (low64 << 64)) == val { + Some(low64 as u64) + } else { + None + } + } + + fn u64_replicated_u32(&mut self, val: u64) -> Option { + let low32 = val as u32 as u64; + if (low32 | (low32 << 32)) == val { + Some(low32) + } else { + None + } + } + + fn u32_replicated_u16(&mut self, val: u64) -> Option { + let val = val as u32; + let low16 = val as u16 as u32; + if (low16 | (low16 << 16)) == val { + Some(low16.into()) + } else { + None + } + } + + fn u16_replicated_u8(&mut self, val: u64) -> Option { + let val = val as u16; + let low8 = val as u8 as u16; + if (low8 | (low8 << 8)) == val { + Some(low8 as u8) + } else { + None + } + } }; } diff --git a/cranelift/codegen/src/prelude.isle b/cranelift/codegen/src/prelude.isle index d9f561bc2fc2..da37334b1d12 100644 --- a/cranelift/codegen/src/prelude.isle +++ b/cranelift/codegen/src/prelude.isle @@ -77,6 +77,9 @@ ;;;; Primitive Type Conversions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(decl pure u8_as_i8 (u8) i8) +(extern constructor u8_as_i8 u8_as_i8) + (decl pure u8_as_u32 (u8) u32) (extern constructor u8_as_u32 u8_as_u32) (convert u8 u32 u8_as_u32) @@ -219,6 +222,17 @@ (decl pure u64_is_odd (u64) bool) (extern constructor u64_is_odd u64_is_odd) +;; Each of these extractors tests whether the upper half of the input equals the +;; lower half of the input +(decl u128_replicated_u64 (u64) u128) +(extern extractor u128_replicated_u64 u128_replicated_u64) +(decl u64_replicated_u32 (u64) u64) +(extern extractor u64_replicated_u32 u64_replicated_u32) +(decl u32_replicated_u16 (u64) u64) +(extern extractor u32_replicated_u16 u32_replicated_u16) +(decl u16_replicated_u8 (u8) u64) +(extern extractor u16_replicated_u8 u16_replicated_u8) + ;;;; `cranelift_codegen::ir::Type` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (extern const $I8 Type) diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-bor.clif b/cranelift/filetests/filetests/isa/riscv64/simd-bor.clif index c8a8572b6873..0dfec3f9a0ab 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-bor.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-bor.clif @@ -579,3 +579,41 @@ block0(v0: f64x2, v1: i64): ; addi sp, sp, 0x10 ; ret +function %vi_format_with_vconst1(i8x16) -> i8x16 { +block0(v0: i8x16): + v1 = vconst.i8x16 0x05050505050505050505050505050505 + v2 = bor v0, v1 + return v2 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; block0: +; vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) +; vor.vi v12,v9,5 #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v12,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; block1: ; offset 0x10 +; .byte 0x57, 0x70, 0x08, 0xcc +; addi t6, s0, 0x10 +; .byte 0x87, 0x84, 0x0f, 0x02 +; .byte 0x57, 0xb6, 0x92, 0x2a +; .byte 0x27, 0x06, 0x05, 0x02 +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + From aededf8197c553e7f06249b8d80ed253feacdd44 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Oct 2023 14:11:45 -0500 Subject: [PATCH 050/199] riscv64: Refactor and simplify some branches/fcmp (#7142) * riscv64: Add codegen tests for fcmp and fcmp branches * riscv64: Refactor and simplify some branches/fcmp This commit is aimed at simplifying the layers necessary to generate a branch for a `brif` statement. Previously this involved a `lower_cond_br` helper which bottomed out in emitting a `CondBr` instruction. The intention here was to cut all that out and emit a `CondBr` directly. Along the way I've additionally taken the liberty of simplifying `fcmp` as well. This moves the "prefer ordered compares" logic into `emit_fcmp` so it can benefit the `fcmp` instruction as well. This additionally trimmed some abstractions around branches which shouldn't be necessary any longer. --- cranelift/codegen/src/isa/riscv64/inst.isle | 179 ++--- cranelift/codegen/src/isa/riscv64/lower.isle | 6 +- .../codegen/src/isa/riscv64/lower/isle.rs | 12 - .../filetests/filetests/isa/riscv64/fcmp.clif | 750 +++++++++++++++++- 4 files changed, 813 insertions(+), 134 deletions(-) diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index bba497b4cc02..1001ba9453af 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -2651,9 +2651,6 @@ (decl int_zero_reg (Type) ValueRegs) (extern constructor int_zero_reg int_zero_reg) -(decl lower_cond_br (IntCC ValueRegs MachLabelSlice Type) Unit) -(extern constructor lower_cond_br lower_cond_br) - ;; Convert a truthy value, possibly of more than one register (an I128), to ;; one register. ;; @@ -2668,31 +2665,44 @@ (hi XReg (value_regs_get regs 1))) (rv_or lo hi))) +;; Consume a CmpResult, producing a branch on its result. +(decl cond_br (IntegerCompare CondBrTarget CondBrTarget) SideEffectNoResult) +(rule (cond_br cmp then else) + (SideEffectNoResult.Inst + (MInst.CondBr then else cmp))) + +;; Helper for emitting the `j` mnemonic, an unconditional jump to label. +(decl rv_j (MachLabel) SideEffectNoResult) +(rule (rv_j label) + (SideEffectNoResult.Inst (MInst.Jal label))) + +;; Construct an IntegerCompare value. +(decl int_compare (IntCC XReg XReg) IntegerCompare) +(extern constructor int_compare int_compare) + (decl label_to_br_target (MachLabel) CondBrTarget) (extern constructor label_to_br_target label_to_br_target) (convert MachLabel CondBrTarget label_to_br_target) (decl partial lower_branch (Inst MachLabelSlice) Unit) (rule (lower_branch (jump _) (single_target label)) - (emit_side_effect (SideEffectNoResult.Inst (MInst.Jal label)))) + (emit_side_effect (rv_j label))) ;; Default behavior for branching based on an input value. -(rule (lower_branch (brif v @ (value_type (fits_in_64 ty)) _ _) targets) - (lower_cond_br (IntCC.NotEqual) (zext v) targets ty)) -(rule 2 (lower_branch (brif v @ (value_type $I128)_ _) targets) - (lower_cond_br (IntCC.NotEqual) (truthy_to_reg v) targets $I64)) +(rule (lower_branch (brif v @ (value_type (fits_in_64 ty)) _ _) (two_targets then else)) + (emit_side_effect (cond_br (int_compare (IntCC.NotEqual) (zext v) (zero_reg)) then else))) +(rule 2 (lower_branch (brif v @ (value_type $I128)_ _) (two_targets then else)) + (emit_side_effect (cond_br (int_compare (IntCC.NotEqual) (truthy_to_reg v) (zero_reg)) then else))) -;; Branching on the result of an fcmp -(rule 1 - (lower_branch (brif (maybe_uextend (fcmp cc a @ (value_type ty) b)) _ _) (two_targets then else)) - (if-let $true (floatcc_unordered cc)) - (emit_side_effect (cond_br (emit_fcmp (floatcc_complement cc) ty a b) else then))) - -(rule 1 - (lower_branch (brif (maybe_uextend (fcmp cc a @ (value_type ty) b)) _ _) (two_targets then else)) - (if-let $false (floatcc_unordered cc)) +;; Branching on the result of an fcmp. +(rule 1 (lower_branch (brif (maybe_uextend (fcmp cc a @ (value_type ty) b)) _ _) (two_targets then else)) (emit_side_effect (cond_br (emit_fcmp cc ty a b) then else))) +(decl fcmp_to_compare (FCmp) IntegerCompare) +(rule (fcmp_to_compare (FCmp.One r)) (int_compare (IntCC.NotEqual) r (zero_reg))) +(rule (fcmp_to_compare (FCmp.Zero r)) (int_compare (IntCC.Equal) r (zero_reg))) +(convert FCmp IntegerCompare fcmp_to_compare) + (decl lower_br_table (Reg MachLabelSlice) Unit) (extern constructor lower_br_table lower_br_table) @@ -2857,131 +2867,62 @@ ;;;; Helpers for floating point comparisons ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(decl not (XReg) XReg) -(rule (not x) (rv_xori x (imm_from_bits 1))) - (decl is_not_nan (Type FReg) XReg) (rule (is_not_nan ty a) (rv_feq ty a a)) (decl ordered (Type FReg FReg) XReg) (rule (ordered ty a b) (rv_and (is_not_nan ty a) (is_not_nan ty b))) -(type CmpResult (enum - (Result - (result XReg) - (invert bool)))) - -;; Wrapper for the common case when constructing comparison results. It assumes -;; that the result isn't negated. -(decl cmp_result (XReg) CmpResult) -(rule (cmp_result result) (CmpResult.Result result $false)) - -;; Wrapper for the case where it's more convenient to construct the negated -;; version of the comparison. -(decl cmp_result_invert (XReg) CmpResult) -(rule (cmp_result_invert result) (CmpResult.Result result $true)) - -;; Consume a CmpResult, producing a branch on its result. -(decl cond_br (CmpResult CondBrTarget CondBrTarget) SideEffectNoResult) -(rule (cond_br cmp then else) - (SideEffectNoResult.Inst - (MInst.CondBr then else (cmp_integer_compare cmp)))) - -;; Construct an IntegerCompare value. -(decl int_compare (IntCC XReg XReg) IntegerCompare) -(extern constructor int_compare int_compare) - -;; Convert a comparison into a branch test. -(decl cmp_integer_compare (CmpResult) IntegerCompare) - -(rule - (cmp_integer_compare (CmpResult.Result res $false)) - (int_compare (IntCC.NotEqual) res (zero_reg))) - -(rule - (cmp_integer_compare (CmpResult.Result res $true)) - (int_compare (IntCC.Equal) res (zero_reg))) +(type FCmp (enum + ;; The comparison succeeded if `r` is one + (One (r XReg)) + ;; The comparison succeeded if `r` is zero + (Zero (r XReg)) +)) -;; Convert a comparison into a boolean value. -(decl cmp_value (CmpResult) XReg) -(rule (cmp_value (CmpResult.Result res $false)) res) -(rule (cmp_value (CmpResult.Result res $true)) (not res)) +(decl fcmp_invert (FCmp) FCmp) +(rule (fcmp_invert (FCmp.One r)) (FCmp.Zero r)) +(rule (fcmp_invert (FCmp.Zero r)) (FCmp.One r)) ;; Compare two floating point numbers and return a zero/non-zero result. -(decl emit_fcmp (FloatCC Type FReg FReg) CmpResult) +(decl emit_fcmp (FloatCC Type FReg FReg) FCmp) -;; a is not nan && b is not nan -(rule - (emit_fcmp (FloatCC.Ordered) ty a b) - (cmp_result (ordered ty a b))) +;; Direct codegen for unordered comparisons is not that efficient, so invert +;; the comparison to get an ordered comparison and generate that. Then invert +;; the result to produce the final fcmp result. +(rule 0 (emit_fcmp cc ty a b) + (if-let $true (floatcc_unordered cc)) + (fcmp_invert (emit_fcmp (floatcc_complement cc) ty a b))) -;; a is nan || b is nan -;; == !(a is not nan && b is not nan) -(rule - (emit_fcmp (FloatCC.Unordered) ty a b) - (cmp_result_invert (ordered ty a b))) +;; a is not nan && b is not nan +(rule 1 (emit_fcmp (FloatCC.Ordered) ty a b) + (FCmp.One (ordered ty a b))) ;; a == b -(rule - (emit_fcmp (FloatCC.Equal) ty a b) - (cmp_result (rv_feq ty a b))) +(rule 1 (emit_fcmp (FloatCC.Equal) ty a b) + (FCmp.One (rv_feq ty a b))) ;; a != b ;; == !(a == b) -(rule - (emit_fcmp (FloatCC.NotEqual) ty a b) - (cmp_result_invert (rv_feq ty a b))) +(rule 1 (emit_fcmp (FloatCC.NotEqual) ty a b) + (FCmp.Zero (rv_feq ty a b))) ;; a < b || a > b -(rule - (emit_fcmp (FloatCC.OrderedNotEqual) ty a b) - (cmp_result (rv_or (rv_flt ty a b) (rv_fgt ty a b)))) - -;; !(ordered a b) || a == b -(rule - (emit_fcmp (FloatCC.UnorderedOrEqual) ty a b) - (cmp_result (rv_or (not (ordered ty a b)) (rv_feq ty a b)))) +(rule 1 (emit_fcmp (FloatCC.OrderedNotEqual) ty a b) + (FCmp.One (rv_or (rv_flt ty a b) (rv_fgt ty a b)))) ;; a < b -(rule - (emit_fcmp (FloatCC.LessThan) ty a b) - (cmp_result (rv_flt ty a b))) +(rule 1 (emit_fcmp (FloatCC.LessThan) ty a b) + (FCmp.One (rv_flt ty a b))) ;; a <= b -(rule - (emit_fcmp (FloatCC.LessThanOrEqual) ty a b) - (cmp_result (rv_fle ty a b))) +(rule 1 (emit_fcmp (FloatCC.LessThanOrEqual) ty a b) + (FCmp.One (rv_fle ty a b))) ;; a > b -(rule - (emit_fcmp (FloatCC.GreaterThan) ty a b) - (cmp_result (rv_fgt ty a b))) +(rule 1 (emit_fcmp (FloatCC.GreaterThan) ty a b) + (FCmp.One (rv_fgt ty a b))) ;; a >= b -(rule - (emit_fcmp (FloatCC.GreaterThanOrEqual) ty a b) - (cmp_result (rv_fge ty a b))) - -;; !(ordered a b) || a < b -;; == !(ordered a b && a >= b) -(rule - (emit_fcmp (FloatCC.UnorderedOrLessThan) ty a b) - (cmp_result_invert (rv_and (ordered ty a b) (rv_fge ty a b)))) - -;; !(ordered a b) || a <= b -;; == !(ordered a b && a > b) -(rule - (emit_fcmp (FloatCC.UnorderedOrLessThanOrEqual) ty a b) - (cmp_result_invert (rv_and (ordered ty a b) (rv_fgt ty a b)))) - -;; !(ordered a b) || a > b -;; == !(ordered a b && a <= b) -(rule - (emit_fcmp (FloatCC.UnorderedOrGreaterThan) ty a b) - (cmp_result_invert (rv_and (ordered ty a b) (rv_fle ty a b)))) - -;; !(ordered a b) || a >= b -;; == !(ordered a b && a < b) -(rule - (emit_fcmp (FloatCC.UnorderedOrGreaterThanOrEqual) ty a b) - (cmp_result_invert (rv_and (ordered ty a b) (rv_flt ty a b)))) +(rule 1 (emit_fcmp (FloatCC.GreaterThanOrEqual) ty a b) + (FCmp.One (rv_fge ty a b))) diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index 13d60f3676c3..53051fa31d6e 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -1855,7 +1855,11 @@ ;;;;; Rules for `fcmp`;;;;;;;;; (rule 0 (lower (fcmp cc x @ (value_type (ty_scalar_float ty)) y)) - (cmp_value (emit_fcmp cc ty x y))) + (lower_fcmp (emit_fcmp cc ty x y))) + +(decl lower_fcmp (FCmp) XReg) +(rule (lower_fcmp (FCmp.One r)) r) +(rule (lower_fcmp (FCmp.Zero r)) (rv_seqz r)) (rule 1 (lower (fcmp cc x @ (value_type (ty_vec_fits_in_register ty)) y)) (gen_expand_mask ty (gen_fcmp_mask ty cc x y))) diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs index 1466affce463..40f700e42ac7 100644 --- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs +++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs @@ -175,18 +175,6 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> } } - fn lower_cond_br(&mut self, cc: &IntCC, a: ValueRegs, targets: &[MachLabel], ty: Type) -> Unit { - MInst::lower_br_icmp( - *cc, - a, - self.int_zero_reg(ty), - CondBrTarget::Label(targets[0]), - CondBrTarget::Label(targets[1]), - ty, - ) - .iter() - .for_each(|i| self.emit(i)); - } fn load_ra(&mut self) -> Reg { if self.backend.flags.preserve_frame_pointers() { let tmp = self.temp_writable_reg(I64); diff --git a/cranelift/filetests/filetests/isa/riscv64/fcmp.clif b/cranelift/filetests/filetests/isa/riscv64/fcmp.clif index 38217b9d398d..01b569b0fdf8 100644 --- a/cranelift/filetests/filetests/isa/riscv64/fcmp.clif +++ b/cranelift/filetests/filetests/isa/riscv64/fcmp.clif @@ -17,7 +17,7 @@ block1: ; li a1,0 ; fmv.d.x fa3,a1 ; fle.d a2,fa3,fa3 -; bne a2,zero,taken(label2),not_taken(label1) +; beq a2,zero,taken(label1),not_taken(label2) ; block1: ; j label3 ; block2: @@ -48,7 +48,7 @@ block1: ; li a1,0 ; fmv.d.x fa3,a1 ; fle.d a2,fa3,fa3 -; bne a2,zero,taken(label2),not_taken(label1) +; beq a2,zero,taken(label1),not_taken(label2) ; block1: ; j label3 ; block2: @@ -64,3 +64,749 @@ block1: ; block1: ; offset 0xc ; ret +function %ord(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp ord v0, v1 + return v2 +} + +; VCode: +; block0: +; feq.s a3,fa0,fa0 +; feq.s a5,fa1,fa1 +; and a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.s a3, fa0, fa0 +; feq.s a5, fa1, fa1 +; and a0, a3, a5 +; ret + +function %uno(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp uno v0, v1 + return v2 +} + +; VCode: +; block0: +; feq.s a3,fa0,fa0 +; feq.s a5,fa1,fa1 +; and a1,a3,a5 +; seqz a0,a1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.s a3, fa0, fa0 +; feq.s a5, fa1, fa1 +; and a1, a3, a5 +; seqz a0, a1 +; ret + +function %eq(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp eq v0, v1 + return v2 +} + +; VCode: +; block0: +; feq.s a0,fa0,fa1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.s a0, fa0, fa1 +; ret + +function %ne(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp ne v0, v1 + return v2 +} + +; VCode: +; block0: +; feq.s a3,fa0,fa1 +; seqz a0,a3 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.s a3, fa0, fa1 +; seqz a0, a3 +; ret + +function %one(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp one v0, v1 + return v2 +} + +; VCode: +; block0: +; flt.s a3,fa0,fa1 +; flt.s a5,fa1,fa0 +; or a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.s a3, fa0, fa1 +; flt.s a5, fa1, fa0 +; or a0, a3, a5 +; ret + +function %ueq(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp ueq v0, v1 + return v2 +} + +; VCode: +; block0: +; flt.s a3,fa0,fa1 +; flt.s a5,fa1,fa0 +; or a1,a3,a5 +; seqz a0,a1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.s a3, fa0, fa1 +; flt.s a5, fa1, fa0 +; or a1, a3, a5 +; seqz a0, a1 +; ret + +function %lt(f64, f64) -> i8 { +block0(v0: f64, v1: f64): + v2 = fcmp lt v0, v1 + return v2 +} + +; VCode: +; block0: +; flt.d a0,fa0,fa1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.d a0, fa0, fa1 +; ret + +function %le(f64, f64) -> i8 { +block0(v0: f64, v1: f64): + v2 = fcmp le v0, v1 + return v2 +} + +; VCode: +; block0: +; fle.d a0,fa0,fa1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; fle.d a0, fa0, fa1 +; ret + +function %gt(f64, f64) -> i8 { +block0(v0: f64, v1: f64): + v2 = fcmp gt v0, v1 + return v2 +} + +; VCode: +; block0: +; flt.d a0,fa1,fa0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.d a0, fa1, fa0 +; ret + +function %ge(f64, f64) -> i8 { +block0(v0: f64, v1: f64): + v2 = fcmp ge v0, v1 + return v2 +} + +; VCode: +; block0: +; fle.d a0,fa1,fa0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; fle.d a0, fa1, fa0 +; ret + +function %ult(f64, f64) -> i8 { +block0(v0: f64, v1: f64): + v2 = fcmp ult v0, v1 + return v2 +} + +; VCode: +; block0: +; fle.d a3,fa1,fa0 +; seqz a0,a3 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; fle.d a3, fa1, fa0 +; seqz a0, a3 +; ret + +function %ule(f64, f64) -> i8 { +block0(v0: f64, v1: f64): + v2 = fcmp ule v0, v1 + return v2 +} + +; VCode: +; block0: +; flt.d a3,fa1,fa0 +; seqz a0,a3 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.d a3, fa1, fa0 +; seqz a0, a3 +; ret + +function %ugt(f64, f64) -> i8 { +block0(v0: f64, v1: f64): + v2 = fcmp ugt v0, v1 + return v2 +} + +; VCode: +; block0: +; fle.d a3,fa0,fa1 +; seqz a0,a3 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; fle.d a3, fa0, fa1 +; seqz a0, a3 +; ret + +function %uge(f64, f64) -> i8 { +block0(v0: f64, v1: f64): + v2 = fcmp uge v0, v1 + return v2 +} + +; VCode: +; block0: +; flt.d a3,fa0,fa1 +; seqz a0,a3 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.d a3, fa0, fa1 +; seqz a0, a3 +; ret + +function %br_ord(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp ord v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; feq.s a5,fa0,fa0 +; feq.s a1,fa1,fa1 +; and a3,a5,a1 +; bne a3,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.s a5, fa0, fa0 +; feq.s a1, fa1, fa1 +; and a3, a5, a1 +; bnez a3, 0xc +; block1: ; offset 0x10 +; mv a0, zero +; ret +; block2: ; offset 0x18 +; addi a0, zero, 1 +; ret + +function %br_uno(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp uno v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; feq.s a5,fa0,fa0 +; feq.s a1,fa1,fa1 +; and a3,a5,a1 +; beq a3,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.s a5, fa0, fa0 +; feq.s a1, fa1, fa1 +; and a3, a5, a1 +; beqz a3, 0xc +; block1: ; offset 0x10 +; mv a0, zero +; ret +; block2: ; offset 0x18 +; addi a0, zero, 1 +; ret + +function %br_eq(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp eq v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; feq.s a5,fa0,fa1 +; bne a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.s a5, fa0, fa1 +; bnez a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %br_ne(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp ne v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; feq.s a5,fa0,fa1 +; beq a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.s a5, fa0, fa1 +; beqz a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %br_one(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp one v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; flt.s a5,fa0,fa1 +; flt.s a1,fa1,fa0 +; or a3,a5,a1 +; bne a3,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.s a5, fa0, fa1 +; flt.s a1, fa1, fa0 +; or a3, a5, a1 +; bnez a3, 0xc +; block1: ; offset 0x10 +; mv a0, zero +; ret +; block2: ; offset 0x18 +; addi a0, zero, 1 +; ret + +function %br_ueq(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp ueq v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; flt.s a5,fa0,fa1 +; flt.s a1,fa1,fa0 +; or a3,a5,a1 +; beq a3,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.s a5, fa0, fa1 +; flt.s a1, fa1, fa0 +; or a3, a5, a1 +; beqz a3, 0xc +; block1: ; offset 0x10 +; mv a0, zero +; ret +; block2: ; offset 0x18 +; addi a0, zero, 1 +; ret + +function %br_lt(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp lt v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; flt.s a5,fa0,fa1 +; bne a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.s a5, fa0, fa1 +; bnez a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %br_gt(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp gt v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; flt.s a5,fa1,fa0 +; bne a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.s a5, fa1, fa0 +; bnez a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %br_le(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp le v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; fle.s a5,fa0,fa1 +; bne a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; fle.s a5, fa0, fa1 +; bnez a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %br_ge(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp ge v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; fle.s a5,fa1,fa0 +; bne a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; fle.s a5, fa1, fa0 +; bnez a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %br_ult(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp ult v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; fle.s a5,fa1,fa0 +; beq a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; fle.s a5, fa1, fa0 +; beqz a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %br_ugt(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp ugt v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; fle.s a5,fa0,fa1 +; beq a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; fle.s a5, fa0, fa1 +; beqz a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %br_ule(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp ule v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; flt.s a5,fa1,fa0 +; beq a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.s a5, fa1, fa0 +; beqz a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %br_uge(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp uge v0, v1 + brif v2, block1, block2 +block1: + v3 = iconst.i8 1 + return v3 +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; flt.s a5,fa0,fa1 +; beq a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.s a5, fa0, fa1 +; beqz a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + From bf513dafdd263b207d89a307949767c7655c7f3f Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Tue, 3 Oct 2023 12:38:19 -0700 Subject: [PATCH 051/199] Resourcify the wasi-http interface (#7135) --- .../wasi-http-proxy-tests/src/lib.rs | 13 +- .../test-programs/wasi-http-tests/src/lib.rs | 37 +- crates/wasi-http/src/http_impl.rs | 15 +- crates/wasi-http/src/lib.rs | 11 + crates/wasi-http/src/types.rs | 297 +---------- crates/wasi-http/src/types_impl.rs | 498 ++++++++++-------- crates/wasi-http/wit/deps/http/types.wit | 214 ++++---- crates/wasi/wit/deps/http/types.wit | 214 ++++---- 8 files changed, 554 insertions(+), 745 deletions(-) diff --git a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs b/crates/test-programs/wasi-http-proxy-tests/src/lib.rs index c51be36d7f38..65c7db9168c6 100644 --- a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-proxy-tests/src/lib.rs @@ -16,18 +16,17 @@ struct T; impl bindings::exports::wasi::http::incoming_handler::Guest for T { fn handle(_request: IncomingRequest, outparam: ResponseOutparam) { - let hdrs = bindings::wasi::http::types::new_fields(&[]); - let resp = bindings::wasi::http::types::new_outgoing_response(200, hdrs); - let body = - bindings::wasi::http::types::outgoing_response_write(resp).expect("outgoing response"); + let hdrs = bindings::wasi::http::types::Headers::new(&[]); + let resp = bindings::wasi::http::types::OutgoingResponse::new(200, hdrs); + let body = resp.write().expect("outgoing response"); - bindings::wasi::http::types::set_response_outparam(outparam, Ok(resp)); + bindings::wasi::http::types::ResponseOutparam::set(outparam, Ok(resp)); - let out = bindings::wasi::http::types::outgoing_body_write(body).expect("outgoing stream"); + let out = body.write().expect("outgoing stream"); out.blocking_write_and_flush(b"hello, world!") .expect("writing response"); drop(out); - bindings::wasi::http::types::outgoing_body_finish(body, None); + bindings::wasi::http::types::OutgoingBody::finish(body, None); } } diff --git a/crates/test-programs/wasi-http-tests/src/lib.rs b/crates/test-programs/wasi-http-tests/src/lib.rs index 5aabc976d6b3..f432230a229d 100644 --- a/crates/test-programs/wasi-http-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-tests/src/lib.rs @@ -53,7 +53,7 @@ pub async fn request( fn header_val(v: &str) -> Vec { v.to_string().into_bytes() } - let headers = http_types::new_fields( + let headers = http_types::Headers::new( &[ &[ ("User-agent".to_string(), header_val("WASI-HTTP/0.0.1")), @@ -64,7 +64,7 @@ pub async fn request( .concat(), ); - let request = http_types::new_outgoing_request( + let request = http_types::OutgoingRequest::new( &method, Some(path_with_query), Some(&scheme), @@ -72,11 +72,13 @@ pub async fn request( headers, ); - let outgoing_body = http_types::outgoing_request_write(request) + let outgoing_body = request + .write() .map_err(|_| anyhow!("outgoing request write failed"))?; if let Some(mut buf) = body { - let request_body = http_types::outgoing_body_write(outgoing_body) + let request_body = outgoing_body + .write() .map_err(|_| anyhow!("outgoing request write failed"))?; let pollable = request_body.subscribe(); @@ -113,17 +115,15 @@ pub async fn request( let future_response = outgoing_handler::handle(request, None)?; - // TODO: The current implementation requires this drop after the request is sent. - // The ownership semantics are unclear in wasi-http we should clarify exactly what is - // supposed to happen here. - http_types::outgoing_body_finish(outgoing_body, None); + http_types::OutgoingBody::finish(outgoing_body, None); - let incoming_response = match http_types::future_incoming_response_get(future_response) { + let incoming_response = match future_response.get() { Some(result) => result.map_err(|_| anyhow!("incoming response errored"))?, None => { - let pollable = http_types::listen_to_future_incoming_response(future_response); + let pollable = future_response.subscribe(); let _ = poll::poll_list(&[&pollable]); - http_types::future_incoming_response_get(future_response) + future_response + .get() .expect("incoming response available") .map_err(|_| anyhow!("incoming response errored"))? } @@ -132,18 +132,19 @@ pub async fn request( // Error? anyway, just use its Debug here: .map_err(|e| anyhow!("{e:?}"))?; - http_types::drop_future_incoming_response(future_response); + drop(future_response); - let status = http_types::incoming_response_status(incoming_response); + let status = incoming_response.status(); - let headers_handle = http_types::incoming_response_headers(incoming_response); - let headers = http_types::fields_entries(headers_handle); - http_types::drop_fields(headers_handle); + let headers_handle = incoming_response.headers(); + let headers = headers_handle.entries(); + drop(headers_handle); - let incoming_body = http_types::incoming_response_consume(incoming_response) + let incoming_body = incoming_response + .consume() .map_err(|()| anyhow!("incoming response has no body stream"))?; - http_types::drop_incoming_response(incoming_response); + drop(incoming_response); let input_stream = incoming_body.stream().unwrap(); let input_stream_pollable = input_stream.subscribe(); diff --git a/crates/wasi-http/src/http_impl.rs b/crates/wasi-http/src/http_impl.rs index ae05c998d4ad..2df2213134de 100644 --- a/crates/wasi-http/src/http_impl.rs +++ b/crates/wasi-http/src/http_impl.rs @@ -1,8 +1,8 @@ use crate::bindings::http::{ outgoing_handler, - types::{FutureIncomingResponse, OutgoingRequest, RequestOptions, Scheme}, + types::{RequestOptions, Scheme}, }; -use crate::types::{self, HostFutureIncomingResponse, IncomingResponseInternal, TableHttpExt}; +use crate::types::{self, HostFutureIncomingResponse, IncomingResponseInternal}; use crate::WasiHttpView; use anyhow::Context; use bytes::Bytes; @@ -11,14 +11,17 @@ use hyper::Method; use std::time::Duration; use tokio::net::TcpStream; use tokio::time::timeout; +use types::HostOutgoingRequest; +use wasmtime::component::Resource; use wasmtime_wasi::preview2; impl outgoing_handler::Host for T { fn handle( &mut self, - request_id: OutgoingRequest, + request_id: Resource, options: Option, - ) -> wasmtime::Result> { + ) -> wasmtime::Result, outgoing_handler::Error>> + { let connect_timeout = Duration::from_millis( options .and_then(|opts| opts.connect_timeout_ms) @@ -37,7 +40,7 @@ impl outgoing_handler::Host for T { .unwrap_or(600 * 1000) as u64, ); - let req = types::OutgoingRequestLens::from(request_id).delete(self.table())?; + let req = self.table().delete_resource(request_id)?; let method = match req.method { crate::bindings::http::types::Method::Get => Method::GET, @@ -175,7 +178,7 @@ impl outgoing_handler::Host for T { let fut = self .table() - .push_future_incoming_response(HostFutureIncomingResponse::new(handle))?; + .push_resource(HostFutureIncomingResponse::new(handle))?; Ok(Ok(fut)) } diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs index 1fc7bc3736d7..2fac15be2ca2 100644 --- a/crates/wasi-http/src/lib.rs +++ b/crates/wasi-http/src/lib.rs @@ -19,6 +19,17 @@ pub mod bindings { with: { "wasi:io/streams": wasmtime_wasi::preview2::bindings::io::streams, "wasi:io/poll": wasmtime_wasi::preview2::bindings::io::poll, + + "wasi:http/types/outgoing-body": super::body::HostOutgoingBody, + "wasi:http/types/future-incoming-response": super::types::HostFutureIncomingResponse, + "wasi:http/types/outgoing-response": super::types::HostOutgoingResponse, + "wasi:http/types/future-trailers": super::body::HostFutureTrailers, + "wasi:http/types/incoming-body": super::body::HostIncomingBody, + "wasi:http/types/incoming-response": super::types::HostIncomingResponse, + "wasi:http/types/response-outparam": super::types::HostResponseOutparam, + "wasi:http/types/outgoing-request": super::types::HostOutgoingRequest, + "wasi:http/types/incoming-request": super::types::HostIncomingRequest, + "wasi:http/types/fields": super::types::HostFields, } }); diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs index 2b6ff34b1056..308b65283cb3 100644 --- a/crates/wasi-http/src/types.rs +++ b/crates/wasi-http/src/types.rs @@ -2,18 +2,12 @@ //! implementation of the wasi-http API. use crate::{ - bindings::http::types::{ - self, FutureTrailers, IncomingBody, IncomingRequest, Method, OutgoingBody, - OutgoingResponse, ResponseOutparam, Scheme, - }, - body::{ - HostFutureTrailers, HostIncomingBody, HostIncomingBodyBuilder, HostOutgoingBody, - HyperIncomingBody, HyperOutgoingBody, - }, + bindings::http::types::{self, Method, Scheme}, + body::{HostIncomingBodyBuilder, HyperIncomingBody, HyperOutgoingBody}, }; use std::any::Any; use wasmtime::component::Resource; -use wasmtime_wasi::preview2::{AbortOnDropJoinHandle, Subscribe, Table, TableError}; +use wasmtime_wasi::preview2::{AbortOnDropJoinHandle, Subscribe, Table}; /// Capture the state necessary for use in the wasi-http API implementation. pub struct WasiHttpCtx; @@ -25,21 +19,17 @@ pub trait WasiHttpView: Send { fn new_incoming_request( &mut self, req: hyper::Request, - ) -> wasmtime::Result { + ) -> wasmtime::Result> { let (parts, body) = req.into_parts(); let body = HostIncomingBodyBuilder { body, // TODO: this needs to be plumbed through between_bytes_timeout: std::time::Duration::from_millis(600 * 1000), }; - Ok(IncomingRequestLens::push( - self.table(), - HostIncomingRequest { - parts, - body: Some(body), - }, - )? - .id) + Ok(self.table().push_resource(HostIncomingRequest { + parts, + body: Some(body), + })?) } fn new_response_outparam( @@ -47,27 +37,24 @@ pub trait WasiHttpView: Send { result: tokio::sync::oneshot::Sender< Result, types::Error>, >, - ) -> wasmtime::Result { - Ok(ResponseOutparamLens::push(self.table(), HostResponseOutparam { result })?.id) + ) -> wasmtime::Result> { + let id = self + .table() + .push_resource(HostResponseOutparam { result })?; + Ok(id) } } -pub type IncomingRequestLens = TableLens; - pub struct HostIncomingRequest { pub parts: http::request::Parts, pub body: Option, } -pub type ResponseOutparamLens = TableLens; - pub struct HostResponseOutparam { pub result: tokio::sync::oneshot::Sender, types::Error>>, } -pub type OutgoingRequestLens = TableLens; - pub struct HostOutgoingRequest { pub method: Method, pub scheme: Option, @@ -84,8 +71,6 @@ pub struct HostIncomingResponse { pub worker: AbortOnDropJoinHandle>, } -pub type OutgoingResponseLens = TableLens; - pub struct HostOutgoingResponse { pub status: u16, pub headers: FieldMap, @@ -173,259 +158,3 @@ impl Subscribe for HostFutureIncomingResponse { } } } - -pub struct TableLens { - id: u32, - _unused: std::marker::PhantomData, -} - -impl TableLens { - pub fn from(id: u32) -> Self { - Self { - id, - _unused: std::marker::PhantomData {}, - } - } - - pub fn into(self) -> u32 { - self.id - } - - #[inline(always)] - pub fn push(table: &mut Table, val: T) -> Result { - let id = table.push(Box::new(val))?; - Ok(Self::from(id)) - } - - #[inline(always)] - pub fn get<'t>(&self, table: &'t Table) -> Result<&'t T, TableError> { - table.get(self.id) - } - - #[inline(always)] - pub fn get_mut<'t>(&self, table: &'t mut Table) -> Result<&'t mut T, TableError> { - table.get_mut(self.id) - } - - #[inline(always)] - pub fn delete(&self, table: &mut Table) -> Result { - table.delete(self.id) - } -} - -pub trait TableHttpExt { - fn push_outgoing_response( - &mut self, - resp: HostOutgoingResponse, - ) -> Result; - fn get_outgoing_response(&mut self, id: u32) -> Result<&mut HostOutgoingResponse, TableError>; - fn delete_outgoing_response(&mut self, id: u32) -> Result; - - fn push_incoming_response(&mut self, response: HostIncomingResponse) - -> Result; - fn get_incoming_response(&self, id: u32) -> Result<&HostIncomingResponse, TableError>; - fn get_incoming_response_mut( - &mut self, - id: u32, - ) -> Result<&mut HostIncomingResponse, TableError>; - fn delete_incoming_response(&mut self, id: u32) -> Result; - - fn push_fields(&mut self, fields: HostFields) -> Result; - fn get_fields(&mut self, id: u32) -> Result<&mut FieldMap, TableError>; - fn delete_fields(&mut self, id: u32) -> Result; - - fn push_future_incoming_response( - &mut self, - response: HostFutureIncomingResponse, - ) -> Result; - fn get_future_incoming_response( - &self, - id: u32, - ) -> Result<&HostFutureIncomingResponse, TableError>; - fn get_future_incoming_response_mut( - &mut self, - id: u32, - ) -> Result<&mut HostFutureIncomingResponse, TableError>; - fn delete_future_incoming_response( - &mut self, - id: u32, - ) -> Result; - - fn push_incoming_body( - &mut self, - body: HostIncomingBody, - ) -> Result, TableError>; - fn get_incoming_body( - &mut self, - id: &Resource, - ) -> Result<&mut HostIncomingBody, TableError>; - fn delete_incoming_body( - &mut self, - id: Resource, - ) -> Result; - - fn push_outgoing_body(&mut self, body: HostOutgoingBody) -> Result; - fn get_outgoing_body(&mut self, id: u32) -> Result<&mut HostOutgoingBody, TableError>; - fn delete_outgoing_body(&mut self, id: u32) -> Result; - - fn push_future_trailers( - &mut self, - trailers: HostFutureTrailers, - ) -> Result; - fn get_future_trailers( - &mut self, - id: FutureTrailers, - ) -> Result<&mut HostFutureTrailers, TableError>; - fn delete_future_trailers( - &mut self, - id: FutureTrailers, - ) -> Result; -} - -impl TableHttpExt for Table { - fn push_outgoing_response( - &mut self, - response: HostOutgoingResponse, - ) -> Result { - self.push(Box::new(response)) - } - - fn get_outgoing_response( - &mut self, - id: OutgoingResponse, - ) -> Result<&mut HostOutgoingResponse, TableError> { - self.get_mut(id) - } - - fn delete_outgoing_response( - &mut self, - id: OutgoingResponse, - ) -> Result { - self.delete(id) - } - - fn push_incoming_response( - &mut self, - response: HostIncomingResponse, - ) -> Result { - self.push(Box::new(response)) - } - fn get_incoming_response(&self, id: u32) -> Result<&HostIncomingResponse, TableError> { - self.get::(id) - } - fn get_incoming_response_mut( - &mut self, - id: u32, - ) -> Result<&mut HostIncomingResponse, TableError> { - self.get_mut::(id) - } - fn delete_incoming_response(&mut self, id: u32) -> Result { - let resp = self.delete::(id)?; - Ok(resp) - } - - fn push_fields(&mut self, fields: HostFields) -> Result { - match fields { - HostFields::Ref { parent, .. } => self.push_child(Box::new(fields), parent), - HostFields::Owned { .. } => self.push(Box::new(fields)), - } - } - fn get_fields(&mut self, id: u32) -> Result<&mut FieldMap, TableError> { - let fields = self.get_mut::(id)?; - if let HostFields::Ref { parent, get_fields } = *fields { - let entry = self.get_any_mut(parent)?; - return Ok(get_fields(entry)); - } - - match self.get_mut::(id)? { - HostFields::Owned { fields } => Ok(fields), - // NB: ideally the `if let` above would go here instead. That makes - // the borrow-checker unhappy. Unclear why. If you, dear reader, can - // refactor this to remove the `unreachable!` please do. - HostFields::Ref { .. } => unreachable!(), - } - } - fn delete_fields(&mut self, id: u32) -> Result { - let fields = self.delete::(id)?; - Ok(fields) - } - - fn push_future_incoming_response( - &mut self, - response: HostFutureIncomingResponse, - ) -> Result { - self.push(Box::new(response)) - } - fn get_future_incoming_response( - &self, - id: u32, - ) -> Result<&HostFutureIncomingResponse, TableError> { - self.get::(id) - } - fn get_future_incoming_response_mut( - &mut self, - id: u32, - ) -> Result<&mut HostFutureIncomingResponse, TableError> { - self.get_mut::(id) - } - fn delete_future_incoming_response( - &mut self, - id: u32, - ) -> Result { - self.delete(id) - } - - fn push_incoming_body( - &mut self, - body: HostIncomingBody, - ) -> Result, TableError> { - Ok(Resource::new_own(self.push(Box::new(body))?)) - } - - fn get_incoming_body( - &mut self, - id: &Resource, - ) -> Result<&mut HostIncomingBody, TableError> { - self.get_mut(id.rep()) - } - - fn delete_incoming_body( - &mut self, - id: Resource, - ) -> Result { - self.delete(id.rep()) - } - - fn push_outgoing_body(&mut self, body: HostOutgoingBody) -> Result { - Ok(self.push(Box::new(body))?) - } - - fn get_outgoing_body(&mut self, id: u32) -> Result<&mut HostOutgoingBody, TableError> { - self.get_mut(id) - } - - fn delete_outgoing_body(&mut self, id: u32) -> Result { - self.delete(id) - } - - fn push_future_trailers( - &mut self, - trailers: HostFutureTrailers, - ) -> Result { - self.push(Box::new(trailers)) - } - - fn get_future_trailers( - &mut self, - id: FutureTrailers, - ) -> Result<&mut HostFutureTrailers, TableError> { - self.get_mut(id) - } - - fn delete_future_trailers( - &mut self, - id: FutureTrailers, - ) -> Result { - self.delete(id) - } -} diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index a470ece0ef3d..bc2ae97f4ed5 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -1,16 +1,12 @@ -use crate::bindings::http::types::{ - Error, Fields, FutureIncomingResponse, FutureTrailers, Headers, IncomingBody, IncomingRequest, - IncomingResponse, Method, OutgoingBody, OutgoingRequest, OutgoingResponse, ResponseOutparam, - Scheme, StatusCode, Trailers, -}; +use crate::bindings::http::types::{Error, Headers, Method, Scheme, StatusCode, Trailers}; use crate::body::{FinishMessage, HostFutureTrailers, HostFutureTrailersState}; use crate::types::{HostIncomingRequest, HostOutgoingResponse}; use crate::WasiHttpView; use crate::{ - body::{HostIncomingBodyBuilder, HostOutgoingBody}, + body::{HostIncomingBody, HostIncomingBodyBuilder, HostOutgoingBody}, types::{ - self, FieldMap, HostFields, HostFutureIncomingResponse, HostIncomingResponse, - HostOutgoingRequest, TableHttpExt, + FieldMap, HostFields, HostFutureIncomingResponse, HostIncomingResponse, + HostOutgoingRequest, HostResponseOutparam, }, }; use anyhow::Context; @@ -18,17 +14,32 @@ use std::any::Any; use wasmtime::component::Resource; use wasmtime_wasi::preview2::{ bindings::io::streams::{InputStream, OutputStream}, - Pollable, + Pollable, Table, }; -impl crate::bindings::http::types::Host for T { - fn drop_fields(&mut self, fields: Fields) -> wasmtime::Result<()> { - self.table() - .delete_fields(fields) - .context("[drop_fields] deleting fields")?; - Ok(()) +impl crate::bindings::http::types::Host for T {} + +fn get_fields_mut<'a>( + table: &'a mut Table, + id: &Resource, +) -> wasmtime::Result<&'a mut FieldMap> { + let fields = table.get_resource(&id)?; + if let HostFields::Ref { parent, get_fields } = *fields { + let entry = table.get_any_mut(parent)?; + return Ok(get_fields(entry)); } - fn new_fields(&mut self, entries: Vec<(String, Vec)>) -> wasmtime::Result { + + match table.get_resource_mut(&id)? { + HostFields::Owned { fields } => Ok(fields), + // NB: ideally the `if let` above would go here instead. That makes + // the borrow-checker unhappy. Unclear why. If you, dear reader, can + // refactor this to remove the `unreachable!` please do. + HostFields::Ref { .. } => unreachable!(), + } +} + +impl crate::bindings::http::types::HostFields for T { + fn new(&mut self, entries: Vec<(String, Vec)>) -> wasmtime::Result> { let mut map = hyper::HeaderMap::new(); for (header, value) in entries { @@ -39,14 +50,25 @@ impl crate::bindings::http::types::Host for T { let id = self .table() - .push_fields(HostFields::Owned { fields: map }) + .push_resource(HostFields::Owned { fields: map }) .context("[new_fields] pushing fields")?; + Ok(id) } - fn fields_get(&mut self, fields: Fields, name: String) -> wasmtime::Result>> { - let res = self - .table() - .get_fields(fields) + + fn drop(&mut self, fields: Resource) -> wasmtime::Result<()> { + self.table() + .delete_resource(fields) + .context("[drop_fields] deleting fields")?; + Ok(()) + } + + fn get( + &mut self, + fields: Resource, + name: String, + ) -> wasmtime::Result>> { + let res = get_fields_mut(self.table(), &fields) .context("[fields_get] getting fields")? .get_all(hyper::header::HeaderName::from_bytes(name.as_bytes())?) .into_iter() @@ -54,13 +76,14 @@ impl crate::bindings::http::types::Host for T { .collect(); Ok(res) } - fn fields_set( + + fn set( &mut self, - fields: Fields, + fields: Resource, name: String, values: Vec>, ) -> wasmtime::Result<()> { - let m = self.table().get_fields(fields)?; + let m = get_fields_mut(self.table(), &fields)?; let header = hyper::header::HeaderName::from_bytes(name.as_bytes())?; @@ -72,61 +95,57 @@ impl crate::bindings::http::types::Host for T { Ok(()) } - fn fields_delete(&mut self, fields: Fields, name: String) -> wasmtime::Result<()> { - let m = self.table().get_fields(fields)?; + + fn delete(&mut self, fields: Resource, name: String) -> wasmtime::Result<()> { + let m = get_fields_mut(self.table(), &fields)?; let header = hyper::header::HeaderName::from_bytes(name.as_bytes())?; m.remove(header); Ok(()) } - fn fields_append( + + fn append( &mut self, - fields: Fields, + fields: Resource, name: String, value: Vec, ) -> wasmtime::Result<()> { - let m = self - .table() - .get_fields(fields) + let m = get_fields_mut(self.table(), &fields) .context("[fields_append] getting mutable fields")?; let header = hyper::header::HeaderName::from_bytes(name.as_bytes())?; let value = hyper::header::HeaderValue::from_bytes(&value)?; m.append(header, value); Ok(()) } - fn fields_entries(&mut self, fields: Fields) -> wasmtime::Result)>> { - let fields = self.table().get_fields(fields)?; + + fn entries( + &mut self, + fields: Resource, + ) -> wasmtime::Result)>> { + let fields = get_fields_mut(self.table(), &fields)?; let result = fields .iter() .map(|(name, value)| (name.as_str().to_owned(), value.as_bytes().to_owned())) .collect(); Ok(result) } - fn fields_clone(&mut self, fields: Fields) -> wasmtime::Result { - let fields = self - .table() - .get_fields(fields) + + fn clone(&mut self, fields: Resource) -> wasmtime::Result> { + let fields = get_fields_mut(self.table(), &fields) .context("[fields_clone] getting fields")? .clone(); + let id = self .table() - .push_fields(HostFields::Owned { fields }) + .push_resource(HostFields::Owned { fields }) .context("[fields_clone] pushing fields")?; + Ok(id) } - fn drop_incoming_request(&mut self, id: IncomingRequest) -> wasmtime::Result<()> { - let _ = types::IncomingRequestLens::from(id).delete(self.table())?; - Ok(()) - } - fn drop_outgoing_request(&mut self, request: OutgoingRequest) -> wasmtime::Result<()> { - types::OutgoingRequestLens::from(request).delete(self.table())?; - Ok(()) - } - fn incoming_request_method(&mut self, request: IncomingRequest) -> wasmtime::Result { - let method = types::IncomingRequestLens::from(request) - .get(self.table())? - .parts - .method - .as_ref(); +} + +impl crate::bindings::http::types::HostIncomingRequest for T { + fn method(&mut self, id: Resource) -> wasmtime::Result { + let method = self.table().get_resource(&id)?.parts.method.as_ref(); if method == hyper::Method::GET { Ok(Method::Get) @@ -150,19 +169,19 @@ impl crate::bindings::http::types::Host for T { Ok(Method::Other(method.to_owned())) } } - fn incoming_request_path_with_query( + fn path_with_query( &mut self, - id: IncomingRequest, + id: Resource, ) -> wasmtime::Result> { - let req = types::IncomingRequestLens::from(id).get(self.table())?; + let req = self.table().get_resource(&id)?; Ok(req .parts .uri .path_and_query() .map(|path_and_query| path_and_query.as_str().to_owned())) } - fn incoming_request_scheme(&mut self, id: IncomingRequest) -> wasmtime::Result> { - let req = types::IncomingRequestLens::from(id).get(self.table())?; + fn scheme(&mut self, id: Resource) -> wasmtime::Result> { + let req = self.table().get_resource(&id)?; Ok(req.parts.uri.scheme().map(|scheme| { if scheme == &http::uri::Scheme::HTTP { return Scheme::Http; @@ -175,19 +194,20 @@ impl crate::bindings::http::types::Host for T { Scheme::Other(req.parts.uri.scheme_str().unwrap().to_owned()) })) } - fn incoming_request_authority( - &mut self, - id: IncomingRequest, - ) -> wasmtime::Result> { - let req = types::IncomingRequestLens::from(id).get(self.table())?; + fn authority(&mut self, id: Resource) -> wasmtime::Result> { + let req = self.table().get_resource(&id)?; Ok(req .parts .uri .authority() .map(|auth| auth.as_str().to_owned())) } - fn incoming_request_headers(&mut self, id: IncomingRequest) -> wasmtime::Result { - let _ = types::IncomingRequestLens::from(id).get(self.table())?; + + fn headers( + &mut self, + id: Resource, + ) -> wasmtime::Result> { + let _ = self.table().get_resource(&id)?; fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { &mut elem @@ -197,56 +217,67 @@ impl crate::bindings::http::types::Host for T { .headers } - let headers = self.table().push_fields(HostFields::Ref { - parent: id, - get_fields, - })?; + let headers = self.table().push_child_resource( + HostFields::Ref { + parent: id.rep(), + get_fields, + }, + &id, + )?; Ok(headers) } - fn incoming_request_consume( + + fn consume( &mut self, - id: IncomingRequest, - ) -> wasmtime::Result, ()>> { - let req = types::IncomingRequestLens::from(id).get_mut(self.table())?; + id: Resource, + ) -> wasmtime::Result, ()>> { + let req = self.table().get_resource_mut(&id)?; match req.body.take() { Some(builder) => { - let id = self.table().push_incoming_body(builder.build())?; + let id = self.table().push_resource(builder.build())?; Ok(Ok(id)) } None => Ok(Err(())), } } - fn new_outgoing_request( + + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(id)?; + Ok(()) + } +} + +impl crate::bindings::http::types::HostOutgoingRequest for T { + fn new( &mut self, method: Method, path_with_query: Option, scheme: Option, authority: Option, - headers: Headers, - ) -> wasmtime::Result { - let headers = self.table().get_fields(headers)?.clone(); - - let req = HostOutgoingRequest { - path_with_query: path_with_query.unwrap_or("".to_string()), - authority: authority.unwrap_or("".to_string()), - method, - headers, - scheme, - body: None, - }; - let id = types::OutgoingRequestLens::push(self.table(), req) - .context("[new_outgoing_request] pushing request")? - .into(); - Ok(id) + headers: Resource, + ) -> wasmtime::Result> { + let headers = get_fields_mut(self.table(), &headers)?.clone(); + self.table() + .push_resource(HostOutgoingRequest { + path_with_query: path_with_query.unwrap_or("".to_string()), + authority: authority.unwrap_or("".to_string()), + method, + headers, + scheme, + body: None, + }) + .context("[new_outgoing_request] pushing request") } - fn outgoing_request_write( + + fn write( &mut self, - request: OutgoingRequest, - ) -> wasmtime::Result> { - let req = types::OutgoingRequestLens::from(request) - .get_mut(self.table()) + request: Resource, + ) -> wasmtime::Result, ()>> { + let req = self + .table() + .get_resource_mut(&request) .context("[outgoing_request_write] getting request")?; if req.body.is_some() { @@ -259,110 +290,122 @@ impl crate::bindings::http::types::Host for T { // The output stream will necessarily outlive the request, because we could be still // writing to the stream after `outgoing-handler.handle` is called. - let outgoing_body = self.table().push_outgoing_body(host_body)?; + let outgoing_body = self.table().push_resource(host_body)?; Ok(Ok(outgoing_body)) } - fn drop_response_outparam(&mut self, id: ResponseOutparam) -> wasmtime::Result<()> { - let _ = types::ResponseOutparamLens::from(id).delete(self.table())?; + + fn drop(&mut self, request: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(request)?; Ok(()) } - fn set_response_outparam( +} + +impl crate::bindings::http::types::HostResponseOutparam for T { + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(id)?; + Ok(()) + } + fn set( &mut self, - id: ResponseOutparam, - resp: Result, + id: Resource, + resp: Result, Error>, ) -> wasmtime::Result<()> { let val = match resp { - Ok(resp) => Ok(self.table().delete_outgoing_response(resp)?.try_into()?), + Ok(resp) => Ok(self.table().delete_resource(resp)?.try_into()?), Err(e) => Err(e), }; - types::ResponseOutparamLens::from(id) - .delete(self.table())? + self.table() + .delete_resource(id)? .result .send(val) .map_err(|_| anyhow::anyhow!("failed to initialize response")) } - fn drop_incoming_response(&mut self, response: IncomingResponse) -> wasmtime::Result<()> { - self.table() - .delete_incoming_response(response) +} + +impl crate::bindings::http::types::HostIncomingResponse for T { + fn drop(&mut self, response: Resource) -> wasmtime::Result<()> { + let _ = self + .table() + .delete_resource(response) .context("[drop_incoming_response] deleting response")?; Ok(()) } - fn drop_outgoing_response(&mut self, id: OutgoingResponse) -> wasmtime::Result<()> { - types::OutgoingResponseLens::from(id).delete(self.table())?; - Ok(()) - } - fn incoming_response_status( - &mut self, - response: IncomingResponse, - ) -> wasmtime::Result { + + fn status(&mut self, response: Resource) -> wasmtime::Result { let r = self .table() - .get_incoming_response(response) + .get_resource(&response) .context("[incoming_response_status] getting response")?; Ok(r.status) } - fn incoming_response_headers( + + fn headers( &mut self, - response: IncomingResponse, - ) -> wasmtime::Result { + response: Resource, + ) -> wasmtime::Result> { let _ = self .table() - .get_incoming_response_mut(response) + .get_resource(&response) .context("[incoming_response_headers] getting response")?; fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { &mut elem.downcast_mut::().unwrap().headers } - let id = self.table().push_fields(HostFields::Ref { - parent: response, - get_fields, - })?; + let id = self.table().push_child_resource( + HostFields::Ref { + parent: response.rep(), + get_fields, + }, + &response, + )?; Ok(id) } - fn incoming_response_consume( + + fn consume( &mut self, - response: IncomingResponse, - ) -> wasmtime::Result, ()>> { + response: Resource, + ) -> wasmtime::Result, ()>> { let table = self.table(); let r = table - .get_incoming_response_mut(response) + .get_resource_mut(&response) .context("[incoming_response_consume] getting response")?; match r.body.take() { Some(builder) => { - let id = self.table().push_incoming_body(builder.build())?; + let id = self.table().push_resource(builder.build())?; Ok(Ok(id)) } None => Ok(Err(())), } } - fn drop_future_trailers(&mut self, id: FutureTrailers) -> wasmtime::Result<()> { - self.table() - .delete_future_trailers(id) +} + +impl crate::bindings::http::types::HostFutureTrailers for T { + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self + .table() + .delete_resource(id) .context("[drop future-trailers] deleting future-trailers")?; Ok(()) } - fn future_trailers_subscribe( + fn subscribe( &mut self, - index: FutureTrailers, + index: Resource, ) -> wasmtime::Result> { - wasmtime_wasi::preview2::subscribe( - self.table(), - Resource::::new_borrow(index), - ) + wasmtime_wasi::preview2::subscribe(self.table(), index) } - fn future_trailers_get( + fn get( &mut self, - id: FutureTrailers, - ) -> wasmtime::Result>> { - let trailers = self.table().get_future_trailers(id)?; + id: Resource, + ) -> wasmtime::Result, Error>>> { + let trailers = self.table().get_resource_mut(&id)?; match &trailers.state { HostFutureTrailersState::Waiting(_) => return Ok(None), HostFutureTrailersState::Done(Err(e)) => return Ok(Some(Err(e.clone()))), @@ -377,39 +420,72 @@ impl crate::bindings::http::types::Host for T { } } - let hdrs = self.table().push_fields(HostFields::Ref { - parent: id, - get_fields, - })?; + let hdrs = self.table().push_child_resource( + HostFields::Ref { + parent: id.rep(), + get_fields, + }, + &id, + )?; Ok(Some(Ok(hdrs))) } +} - fn new_outgoing_response( +impl crate::bindings::http::types::HostIncomingBody for T { + fn stream( + &mut self, + id: Resource, + ) -> wasmtime::Result, ()>> { + let body = self.table().get_resource_mut(&id)?; + + if let Some(stream) = body.stream.take() { + let stream = InputStream::Host(Box::new(stream)); + let stream = self.table().push_child_resource(stream, &id)?; + return Ok(Ok(stream)); + } + + Ok(Err(())) + } + + fn finish( + &mut self, + id: Resource, + ) -> wasmtime::Result> { + let body = self.table().delete_resource(id)?; + let trailers = self.table().push_resource(body.into_future_trailers())?; + Ok(trailers) + } + + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(id)?; + Ok(()) + } +} + +impl crate::bindings::http::types::HostOutgoingResponse for T { + fn new( &mut self, status: StatusCode, - headers: Headers, - ) -> wasmtime::Result { - let fields = self.table().get_fields(headers)?.clone(); - self.table().delete_fields(headers)?; - - let id = types::OutgoingResponseLens::push( - self.table(), - HostOutgoingResponse { - status, - headers: fields, - body: None, - }, - )? - .into(); + headers: Resource, + ) -> wasmtime::Result> { + let fields = get_fields_mut(self.table(), &headers)?.clone(); + self.table().delete_resource(headers)?; + + let id = self.table().push_resource(HostOutgoingResponse { + status, + headers: fields, + body: None, + })?; Ok(id) } - fn outgoing_response_write( + + fn write( &mut self, - id: OutgoingResponse, - ) -> wasmtime::Result> { - let resp = types::OutgoingResponseLens::from(id).get_mut(self.table())?; + id: Resource, + ) -> wasmtime::Result, ()>> { + let resp = self.table().get_resource_mut(&id)?; if resp.body.is_some() { return Ok(Err(())); @@ -419,22 +495,28 @@ impl crate::bindings::http::types::Host for T { resp.body.replace(body); - let id = self.table().push_outgoing_body(host)?; + let id = self.table().push_resource(host)?; Ok(Ok(id)) } - fn drop_future_incoming_response( - &mut self, - id: FutureIncomingResponse, - ) -> wasmtime::Result<()> { - let _ = self.table().delete_future_incoming_response(id)?; + + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(id)?; + Ok(()) + } +} + +impl crate::bindings::http::types::HostFutureIncomingResponse for T { + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(id)?; Ok(()) } - fn future_incoming_response_get( + + fn get( &mut self, - id: FutureIncomingResponse, - ) -> wasmtime::Result, ()>>> { - let resp = self.table().get_future_incoming_response_mut(id)?; + id: Resource, + ) -> wasmtime::Result, Error>, ()>>> { + let resp = self.table().get_resource_mut(&id)?; match resp { HostFutureIncomingResponse::Pending(_) => return Ok(None), @@ -455,7 +537,7 @@ impl crate::bindings::http::types::Host for T { let (parts, body) = resp.resp.into_parts(); - let resp = self.table().push_incoming_response(HostIncomingResponse { + let resp = self.table().push_resource(HostIncomingResponse { status: parts.status.as_u16(), headers: FieldMap::from(parts.headers), body: Some(HostIncomingBodyBuilder { @@ -467,36 +549,34 @@ impl crate::bindings::http::types::Host for T { Ok(Some(Ok(Ok(resp)))) } - fn listen_to_future_incoming_response( + fn subscribe( &mut self, - id: FutureIncomingResponse, + id: Resource, ) -> wasmtime::Result> { - wasmtime_wasi::preview2::subscribe( - self.table(), - Resource::::new_borrow(id), - ) + wasmtime_wasi::preview2::subscribe(self.table(), id) } +} - fn outgoing_body_write( +impl crate::bindings::http::types::HostOutgoingBody for T { + fn write( &mut self, - id: OutgoingBody, + id: Resource, ) -> wasmtime::Result, ()>> { - let body = self.table().get_outgoing_body(id)?; + let body = self.table().get_resource_mut(&id)?; if let Some(stream) = body.body_output_stream.take() { - let dummy = Resource::::new_own(id); - let id = self.table().push_child_resource(stream, &dummy)?; + let id = self.table().push_child_resource(stream, &id)?; Ok(Ok(id)) } else { Ok(Err(())) } } - fn outgoing_body_finish( + fn finish( &mut self, - id: OutgoingBody, - ts: Option, + id: Resource, + ts: Option>, ) -> wasmtime::Result<()> { - let mut body = self.table().delete_outgoing_body(id)?; + let mut body = self.table().delete_resource(id)?; let sender = body .finish_sender @@ -504,7 +584,7 @@ impl crate::bindings::http::types::Host for T { .expect("outgoing-body trailer_sender consumed by a non-owning function"); let message = if let Some(ts) = ts { - FinishMessage::Trailers(self.table().get_fields(ts)?.clone().into()) + FinishMessage::Trailers(get_fields_mut(self.table(), &ts)?.clone().into()) } else { FinishMessage::Finished }; @@ -515,8 +595,8 @@ impl crate::bindings::http::types::Host for T { Ok(()) } - fn drop_outgoing_body(&mut self, id: OutgoingBody) -> wasmtime::Result<()> { - let mut body = self.table().delete_outgoing_body(id)?; + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let mut body = self.table().delete_resource(id)?; let sender = body .finish_sender @@ -529,33 +609,3 @@ impl crate::bindings::http::types::Host for T { Ok(()) } } - -impl crate::bindings::http::types::HostIncomingBody for T { - fn stream( - &mut self, - id: Resource, - ) -> wasmtime::Result, ()>> { - let body = self.table().get_incoming_body(&id)?; - - if let Some(stream) = body.stream.take() { - let stream = InputStream::Host(Box::new(stream)); - let stream = self.table().push_child_resource(stream, &id)?; - return Ok(Ok(stream)); - } - - Ok(Err(())) - } - - fn finish(&mut self, id: Resource) -> wasmtime::Result { - let body = self.table().delete_incoming_body(id)?; - let trailers = self - .table() - .push_future_trailers(body.into_future_trailers())?; - Ok(trailers) - } - - fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { - let _ = self.table().delete_incoming_body(id)?; - Ok(()) - } -} diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index 1abc7a1ff2c4..aa486f3c7c7c 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -30,35 +30,38 @@ interface types { // This type enumerates the different kinds of errors that may occur when // initially returning a response. variant error { - invalid-url(string), - timeout-error(string), - protocol-error(string), - unexpected-error(string) + invalid-url(string), + timeout-error(string), + protocol-error(string), + unexpected-error(string) } // This following block defines the `fields` resource which corresponds to // HTTP standard Fields. Soon, when resource types are added, the `type // fields = u32` type alias can be replaced by a proper `resource fields` // definition containing all the functions using the method syntactic sugar. - type fields = u32 - drop-fields: func(fields: /* own */ fields) - // Multiple values for a header are multiple entries in the list with the - // same key. - new-fields: func(entries: list>>) -> fields - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-get: func(fields: /* borrow */ fields, name: string) -> list> - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-set: func(fields: /* borrow */ fields, name: string, value: list>) - fields-delete: func(fields: /* borrow */ fields, name: string) - fields-append: func(fields: /* borrow */ fields, name: string, value: list) - - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-entries: func(fields: /* borrow */ fields) -> list>> - // Deep copy of all contents in a fields. - fields-clone: func(fields: /* borrow */ fields) -> fields + resource fields { + // Multiple values for a header are multiple entries in the list with the + // same key. + constructor(entries: list>>) + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + get: func(name: string) -> list> + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + set: func(name: string, value: list>) + delete: func(name: string) + append: func(name: string, value: list) + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + entries: func() -> list>> + + // Deep copy of all contents in a fields. + clone: func() -> fields + } type headers = fields type trailers = fields @@ -71,31 +74,35 @@ interface types { // a single `request` type (that uses the single `stream` type mentioned // above). The `consume` and `write` methods may only be called once (and // return failure thereafter). - type incoming-request = u32 - drop-incoming-request: func(request: /* own */ incoming-request) - incoming-request-method: func(request: /* borrow */ incoming-request) -> method - incoming-request-path-with-query: func(request: /* borrow */ incoming-request) -> option - incoming-request-scheme: func(request: /* borrow */ incoming-request) -> option - incoming-request-authority: func(request: /* borrow */ incoming-request) -> option - - incoming-request-headers: func(request: /* borrow */ incoming-request) -> /* child */ headers - // Will return the input-stream child at most once. If called more than - // once, subsequent calls will return error. - incoming-request-consume: func(request: /* borrow */ incoming-request) -> result< /* own */ incoming-body> - - type outgoing-request = u32 - drop-outgoing-request: func(request: /* own */ outgoing-request) - new-outgoing-request: func( - method: method, - path-with-query: option, - scheme: option, - authority: option, - headers: /* borrow */ headers - ) -> outgoing-request - - // Will return the outgoing-body child at most once. If called more than - // once, subsequent calls will return error. - outgoing-request-write: func(request: /* borrow */ outgoing-request) -> result< /* child */ outgoing-body> + resource incoming-request { + method: func() -> method + + path-with-query: func() -> option + + scheme: func() -> option + + authority: func() -> option + + headers: func() -> /* child */ headers + // Will return the input-stream child at most once. If called more than + // once, subsequent calls will return error. + + consume: func() -> result< /* own */ incoming-body> + } + + resource outgoing-request { + constructor( + method: method, + path-with-query: option, + scheme: option, + authority: option, + headers: /* borrow */ headers + ) + + // Will return the outgoing-body child at most once. If called more than + // once, subsequent calls will return error. + write: func() -> result< /* child */ outgoing-body> + } // Additional optional parameters that can be set when making a request. record request-options { @@ -119,9 +126,9 @@ interface types { // definition. Later, with Preview3, the need for an outparam goes away entirely // (the `wasi:http/handler` interface used for both incoming and outgoing can // simply return a `stream`). - type response-outparam = u32 - drop-response-outparam: func(response: /* own */ response-outparam) - set-response-outparam: func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) + resource response-outparam { + set: static func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) + } // This type corresponds to the HTTP standard Status Code. type status-code = u16 @@ -133,58 +140,58 @@ interface types { // Preview2 will allow both types to be merged together into a single `response` // type (that uses the single `stream` type mentioned above). The `consume` and // `write` methods may only be called once (and return failure thereafter). - type incoming-response = u32 - drop-incoming-response: func(response: /* own */ incoming-response) - incoming-response-status: func(response: /* borrow */ incoming-response) -> status-code - incoming-response-headers: func(response: /* borrow */ incoming-response) -> /* child */ headers - // May be called at most once. returns error if called additional times. - // TODO: make incoming-request-consume work the same way, giving a child - // incoming-body. - incoming-response-consume: func(response: /* borrow */ incoming-response) -> result + resource incoming-response { + status: func() -> status-code + + headers: func() -> /* child */ headers + + // May be called at most once. returns error if called additional times. + // TODO: make incoming-request-consume work the same way, giving a child + // incoming-body. + consume: func() -> result + } resource incoming-body { // returned input-stream is a child - the implementation may trap if // incoming-body is dropped (or consumed by call to // incoming-body-finish) before the input-stream is dropped. // May be called at most once. returns error if called additional times. - %stream: func() -> - result + %stream: func() -> result + // takes ownership of incoming-body. this will trap if the // incoming-body-stream child is still alive! - finish: func() -> - /* transitive child of the incoming-response of incoming-body */ future-trailers + finish: static func(this: /* own */ incoming-body) -> + /* transitive child of the incoming-response of incoming-body */ future-trailers + } + + resource future-trailers { + /// Pollable that resolves when the body has been fully read, and the trailers + /// are ready to be consumed. + subscribe: func() -> /* child */ pollable + + /// Retrieve reference to trailers, if they are ready. + get: func() -> option> } - type future-trailers = u32 - drop-future-trailers: func(this: /* own */ future-trailers) - /// Pollable that resolves when the body has been fully read, and the trailers - /// are ready to be consumed. - future-trailers-subscribe: func(this: /* borrow */ future-trailers) -> /* child */ pollable - - /// Retrieve reference to trailers, if they are ready. - future-trailers-get: func(response: /* borrow */ future-trailers) -> option> - - type outgoing-response = u32 - drop-outgoing-response: func(response: /* own */ outgoing-response) - new-outgoing-response: func( - status-code: status-code, - headers: /* borrow */ headers - ) -> outgoing-response - - /// Will give the child outgoing-response at most once. subsequent calls will - /// return an error. - outgoing-response-write: func(this: /* borrow */ outgoing-response) -> result - - type outgoing-body = u32 - drop-outgoing-body: func(this: /* own */ outgoing-body) - /// Will give the child output-stream at most once. subsequent calls will - /// return an error. - outgoing-body-write: func(this: /* borrow */ outgoing-body) -> result - /// Finalize an outgoing body, optionally providing trailers. This must be - /// called to signal that the response is complete. If the `outgoing-body` is - /// dropped without calling `outgoing-body-finalize`, the implementation - /// should treat the body as corrupted. - outgoing-body-finish: func(this: /* own */ outgoing-body, trailers: /* own */ option) + resource outgoing-response { + constructor(status-code: status-code, headers: /* borrow */ headers) + + /// Will give the child outgoing-response at most once. subsequent calls will + /// return an error. + write: func() -> result + } + + resource outgoing-body { + /// Will give the child output-stream at most once. subsequent calls will + /// return an error. + write: func() -> result + + /// Finalize an outgoing body, optionally providing trailers. This must be + /// called to signal that the response is complete. If the `outgoing-body` is + /// dropped without calling `outgoing-body-finalize`, the implementation + /// should treat the body as corrupted. + finish: static func(this: /* own */ outgoing-body, trailers: /* own */ option) + } /// The following block defines a special resource type used by the /// `wasi:http/outgoing-handler` interface to emulate @@ -193,14 +200,15 @@ interface types { /// method to get the result if it is available. If the result is not available, /// the client can call `listen` to get a `pollable` that can be passed to /// `wasi:io/poll.poll-list`. - type future-incoming-response = u32 - drop-future-incoming-response: func(f: /* own */ future-incoming-response) - /// option indicates readiness. - /// outer result indicates you are allowed to get the - /// incoming-response-or-error at most once. subsequent calls after ready - /// will return an error here. - /// inner result indicates whether the incoming-response was available, or an - /// error occured. - future-incoming-response-get: func(f: /* borrow */ future-incoming-response) -> option>> - listen-to-future-incoming-response: func(f: /* borrow */ future-incoming-response) -> /* child */ pollable + resource future-incoming-response { + /// option indicates readiness. + /// outer result indicates you are allowed to get the + /// incoming-response-or-error at most once. subsequent calls after ready + /// will return an error here. + /// inner result indicates whether the incoming-response was available, or an + /// error occured. + get: func() -> option>> + + subscribe: func() -> /* child */ pollable + } } diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index 1abc7a1ff2c4..aa486f3c7c7c 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -30,35 +30,38 @@ interface types { // This type enumerates the different kinds of errors that may occur when // initially returning a response. variant error { - invalid-url(string), - timeout-error(string), - protocol-error(string), - unexpected-error(string) + invalid-url(string), + timeout-error(string), + protocol-error(string), + unexpected-error(string) } // This following block defines the `fields` resource which corresponds to // HTTP standard Fields. Soon, when resource types are added, the `type // fields = u32` type alias can be replaced by a proper `resource fields` // definition containing all the functions using the method syntactic sugar. - type fields = u32 - drop-fields: func(fields: /* own */ fields) - // Multiple values for a header are multiple entries in the list with the - // same key. - new-fields: func(entries: list>>) -> fields - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-get: func(fields: /* borrow */ fields, name: string) -> list> - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-set: func(fields: /* borrow */ fields, name: string, value: list>) - fields-delete: func(fields: /* borrow */ fields, name: string) - fields-append: func(fields: /* borrow */ fields, name: string, value: list) - - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-entries: func(fields: /* borrow */ fields) -> list>> - // Deep copy of all contents in a fields. - fields-clone: func(fields: /* borrow */ fields) -> fields + resource fields { + // Multiple values for a header are multiple entries in the list with the + // same key. + constructor(entries: list>>) + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + get: func(name: string) -> list> + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + set: func(name: string, value: list>) + delete: func(name: string) + append: func(name: string, value: list) + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + entries: func() -> list>> + + // Deep copy of all contents in a fields. + clone: func() -> fields + } type headers = fields type trailers = fields @@ -71,31 +74,35 @@ interface types { // a single `request` type (that uses the single `stream` type mentioned // above). The `consume` and `write` methods may only be called once (and // return failure thereafter). - type incoming-request = u32 - drop-incoming-request: func(request: /* own */ incoming-request) - incoming-request-method: func(request: /* borrow */ incoming-request) -> method - incoming-request-path-with-query: func(request: /* borrow */ incoming-request) -> option - incoming-request-scheme: func(request: /* borrow */ incoming-request) -> option - incoming-request-authority: func(request: /* borrow */ incoming-request) -> option - - incoming-request-headers: func(request: /* borrow */ incoming-request) -> /* child */ headers - // Will return the input-stream child at most once. If called more than - // once, subsequent calls will return error. - incoming-request-consume: func(request: /* borrow */ incoming-request) -> result< /* own */ incoming-body> - - type outgoing-request = u32 - drop-outgoing-request: func(request: /* own */ outgoing-request) - new-outgoing-request: func( - method: method, - path-with-query: option, - scheme: option, - authority: option, - headers: /* borrow */ headers - ) -> outgoing-request - - // Will return the outgoing-body child at most once. If called more than - // once, subsequent calls will return error. - outgoing-request-write: func(request: /* borrow */ outgoing-request) -> result< /* child */ outgoing-body> + resource incoming-request { + method: func() -> method + + path-with-query: func() -> option + + scheme: func() -> option + + authority: func() -> option + + headers: func() -> /* child */ headers + // Will return the input-stream child at most once. If called more than + // once, subsequent calls will return error. + + consume: func() -> result< /* own */ incoming-body> + } + + resource outgoing-request { + constructor( + method: method, + path-with-query: option, + scheme: option, + authority: option, + headers: /* borrow */ headers + ) + + // Will return the outgoing-body child at most once. If called more than + // once, subsequent calls will return error. + write: func() -> result< /* child */ outgoing-body> + } // Additional optional parameters that can be set when making a request. record request-options { @@ -119,9 +126,9 @@ interface types { // definition. Later, with Preview3, the need for an outparam goes away entirely // (the `wasi:http/handler` interface used for both incoming and outgoing can // simply return a `stream`). - type response-outparam = u32 - drop-response-outparam: func(response: /* own */ response-outparam) - set-response-outparam: func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) + resource response-outparam { + set: static func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) + } // This type corresponds to the HTTP standard Status Code. type status-code = u16 @@ -133,58 +140,58 @@ interface types { // Preview2 will allow both types to be merged together into a single `response` // type (that uses the single `stream` type mentioned above). The `consume` and // `write` methods may only be called once (and return failure thereafter). - type incoming-response = u32 - drop-incoming-response: func(response: /* own */ incoming-response) - incoming-response-status: func(response: /* borrow */ incoming-response) -> status-code - incoming-response-headers: func(response: /* borrow */ incoming-response) -> /* child */ headers - // May be called at most once. returns error if called additional times. - // TODO: make incoming-request-consume work the same way, giving a child - // incoming-body. - incoming-response-consume: func(response: /* borrow */ incoming-response) -> result + resource incoming-response { + status: func() -> status-code + + headers: func() -> /* child */ headers + + // May be called at most once. returns error if called additional times. + // TODO: make incoming-request-consume work the same way, giving a child + // incoming-body. + consume: func() -> result + } resource incoming-body { // returned input-stream is a child - the implementation may trap if // incoming-body is dropped (or consumed by call to // incoming-body-finish) before the input-stream is dropped. // May be called at most once. returns error if called additional times. - %stream: func() -> - result + %stream: func() -> result + // takes ownership of incoming-body. this will trap if the // incoming-body-stream child is still alive! - finish: func() -> - /* transitive child of the incoming-response of incoming-body */ future-trailers + finish: static func(this: /* own */ incoming-body) -> + /* transitive child of the incoming-response of incoming-body */ future-trailers + } + + resource future-trailers { + /// Pollable that resolves when the body has been fully read, and the trailers + /// are ready to be consumed. + subscribe: func() -> /* child */ pollable + + /// Retrieve reference to trailers, if they are ready. + get: func() -> option> } - type future-trailers = u32 - drop-future-trailers: func(this: /* own */ future-trailers) - /// Pollable that resolves when the body has been fully read, and the trailers - /// are ready to be consumed. - future-trailers-subscribe: func(this: /* borrow */ future-trailers) -> /* child */ pollable - - /// Retrieve reference to trailers, if they are ready. - future-trailers-get: func(response: /* borrow */ future-trailers) -> option> - - type outgoing-response = u32 - drop-outgoing-response: func(response: /* own */ outgoing-response) - new-outgoing-response: func( - status-code: status-code, - headers: /* borrow */ headers - ) -> outgoing-response - - /// Will give the child outgoing-response at most once. subsequent calls will - /// return an error. - outgoing-response-write: func(this: /* borrow */ outgoing-response) -> result - - type outgoing-body = u32 - drop-outgoing-body: func(this: /* own */ outgoing-body) - /// Will give the child output-stream at most once. subsequent calls will - /// return an error. - outgoing-body-write: func(this: /* borrow */ outgoing-body) -> result - /// Finalize an outgoing body, optionally providing trailers. This must be - /// called to signal that the response is complete. If the `outgoing-body` is - /// dropped without calling `outgoing-body-finalize`, the implementation - /// should treat the body as corrupted. - outgoing-body-finish: func(this: /* own */ outgoing-body, trailers: /* own */ option) + resource outgoing-response { + constructor(status-code: status-code, headers: /* borrow */ headers) + + /// Will give the child outgoing-response at most once. subsequent calls will + /// return an error. + write: func() -> result + } + + resource outgoing-body { + /// Will give the child output-stream at most once. subsequent calls will + /// return an error. + write: func() -> result + + /// Finalize an outgoing body, optionally providing trailers. This must be + /// called to signal that the response is complete. If the `outgoing-body` is + /// dropped without calling `outgoing-body-finalize`, the implementation + /// should treat the body as corrupted. + finish: static func(this: /* own */ outgoing-body, trailers: /* own */ option) + } /// The following block defines a special resource type used by the /// `wasi:http/outgoing-handler` interface to emulate @@ -193,14 +200,15 @@ interface types { /// method to get the result if it is available. If the result is not available, /// the client can call `listen` to get a `pollable` that can be passed to /// `wasi:io/poll.poll-list`. - type future-incoming-response = u32 - drop-future-incoming-response: func(f: /* own */ future-incoming-response) - /// option indicates readiness. - /// outer result indicates you are allowed to get the - /// incoming-response-or-error at most once. subsequent calls after ready - /// will return an error here. - /// inner result indicates whether the incoming-response was available, or an - /// error occured. - future-incoming-response-get: func(f: /* borrow */ future-incoming-response) -> option>> - listen-to-future-incoming-response: func(f: /* borrow */ future-incoming-response) -> /* child */ pollable + resource future-incoming-response { + /// option indicates readiness. + /// outer result indicates you are allowed to get the + /// incoming-response-or-error at most once. subsequent calls after ready + /// will return an error here. + /// inner result indicates whether the incoming-response was available, or an + /// error occured. + get: func() -> option>> + + subscribe: func() -> /* child */ pollable + } } From a6d05429f681409a8f97b864d0124261869e0748 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 3 Oct 2023 13:24:26 -0700 Subject: [PATCH 052/199] Wasi input-stream: use same errors as output-stream (#7090) * streams.wit: delete stream-status, rename write-error to stream-error, transform all input-stream methods * preview2: use StreamError throughout input-stream * preview2: passes cargo test * preview1: fixes for input-stream stream-error. * wasmtime-wasi-http: fixes for HostInputStream trait changes * component adapter: fixes for input-stream changes * test programs: fixes for input-stream * component adapter: handle StreamError::Closed in fd_read * sync wit definitions to wasi-http * fix!! * preview1: handle eof and intr properly prtest:full * Fix preview1 stdin reading * Touch up stream documentation --------- Co-authored-by: Alex Crichton --- .../test-programs/wasi-http-tests/src/lib.rs | 13 +- .../wasi-sockets-tests/src/lib.rs | 45 +--- crates/wasi-http/src/body.rs | 37 ++-- crates/wasi-http/wit/deps/io/streams.wit | 62 ++---- .../src/lib.rs | 36 +-- crates/wasi/src/preview2/filesystem.rs | 54 ++--- crates/wasi/src/preview2/host/io.rs | 197 ++++++----------- crates/wasi/src/preview2/mod.rs | 9 +- crates/wasi/src/preview2/pipe.rs | 206 ++++++++---------- crates/wasi/src/preview2/preview1.rs | 108 +++++---- crates/wasi/src/preview2/stdio.rs | 10 +- .../src/preview2/stdio/worker_thread_stdin.rs | 15 +- crates/wasi/src/preview2/stream.rs | 146 +++++-------- crates/wasi/src/preview2/tcp.rs | 41 ++-- crates/wasi/src/preview2/write_stream.rs | 20 +- crates/wasi/wit/deps/io/streams.wit | 62 ++---- 16 files changed, 430 insertions(+), 631 deletions(-) diff --git a/crates/test-programs/wasi-http-tests/src/lib.rs b/crates/test-programs/wasi-http-tests/src/lib.rs index f432230a229d..90ad31b79daa 100644 --- a/crates/test-programs/wasi-http-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-tests/src/lib.rs @@ -150,15 +150,14 @@ pub async fn request( let input_stream_pollable = input_stream.subscribe(); let mut body = Vec::new(); - let mut eof = streams::StreamStatus::Open; - while eof != streams::StreamStatus::Ended { + loop { poll::poll_list(&[&input_stream_pollable]); - let (mut body_chunk, stream_status) = input_stream - .read(1024 * 1024) - .map_err(|_| anyhow!("input_stream read failed"))?; - - eof = stream_status; + let mut body_chunk = match input_stream.read(1024 * 1024) { + Ok(c) => c, + Err(streams::StreamError::Closed) => break, + Err(e) => Err(anyhow!("input_stream read failed: {e:?}"))?, + }; if !body_chunk.is_empty() { body.append(&mut body_chunk); diff --git a/crates/test-programs/wasi-sockets-tests/src/lib.rs b/crates/test-programs/wasi-sockets-tests/src/lib.rs index c7a49203bb7b..a46cff830c0f 100644 --- a/crates/test-programs/wasi-sockets-tests/src/lib.rs +++ b/crates/test-programs/wasi-sockets-tests/src/lib.rs @@ -4,38 +4,24 @@ use wasi::io::poll; use wasi::io::streams; use wasi::sockets::{network, tcp, tcp_create_socket}; -pub fn write(output: &streams::OutputStream, mut bytes: &[u8]) -> (usize, streams::StreamStatus) { - let total = bytes.len(); - let mut written = 0; - +pub fn write(output: &streams::OutputStream, mut bytes: &[u8]) -> Result<(), streams::StreamError> { let pollable = output.subscribe(); while !bytes.is_empty() { poll::poll_list(&[&pollable]); - let permit = match output.check_write() { - Ok(n) => n, - Err(_) => return (written, streams::StreamStatus::Ended), - }; + let permit = output.check_write()?; let len = bytes.len().min(permit as usize); let (chunk, rest) = bytes.split_at(len); - match output.write(chunk) { - Ok(()) => {} - Err(_) => return (written, streams::StreamStatus::Ended), - } + output.write(chunk)?; - match output.blocking_flush() { - Ok(()) => {} - Err(_) => return (written, streams::StreamStatus::Ended), - } + output.blocking_flush()?; bytes = rest; - written += len; } - - (total, streams::StreamStatus::Open) + Ok(()) } pub fn example_body(net: tcp::Network, sock: tcp::TcpSocket, family: network::IpAddressFamily) { @@ -59,13 +45,9 @@ pub fn example_body(net: tcp::Network, sock: tcp::TcpSocket, family: network::Ip poll::poll_one(&client_sub); let (client_input, client_output) = client.finish_connect().unwrap(); - let (n, status) = write(&client_output, &[]); - assert_eq!(n, 0); - assert_eq!(status, streams::StreamStatus::Open); + write(&client_output, &[]).unwrap(); - let (n, status) = write(&client_output, first_message); - assert_eq!(n, first_message.len()); - assert_eq!(status, streams::StreamStatus::Open); + write(&client_output, first_message).unwrap(); drop(client_input); drop(client_output); @@ -75,12 +57,10 @@ pub fn example_body(net: tcp::Network, sock: tcp::TcpSocket, family: network::Ip poll::poll_one(&sub); let (accepted, input, output) = sock.accept().unwrap(); - let (empty_data, status) = input.read(0).unwrap(); + let empty_data = input.read(0).unwrap(); assert!(empty_data.is_empty()); - assert_eq!(status, streams::StreamStatus::Open); - let (data, status) = input.blocking_read(first_message.len() as u64).unwrap(); - assert_eq!(status, streams::StreamStatus::Open); + let data = input.blocking_read(first_message.len() as u64).unwrap(); drop(input); drop(output); @@ -97,9 +77,7 @@ pub fn example_body(net: tcp::Network, sock: tcp::TcpSocket, family: network::Ip poll::poll_one(&client_sub); let (client_input, client_output) = client.finish_connect().unwrap(); - let (n, status) = write(&client_output, second_message); - assert_eq!(n, second_message.len()); - assert_eq!(status, streams::StreamStatus::Open); + write(&client_output, second_message).unwrap(); drop(client_input); drop(client_output); @@ -108,8 +86,7 @@ pub fn example_body(net: tcp::Network, sock: tcp::TcpSocket, family: network::Ip poll::poll_one(&sub); let (accepted, input, output) = sock.accept().unwrap(); - let (data, status) = input.blocking_read(second_message.len() as u64).unwrap(); - assert_eq!(status, streams::StreamStatus::Open); + let data = input.blocking_read(second_message.len() as u64).unwrap(); drop(input); drop(output); diff --git a/crates/wasi-http/src/body.rs b/crates/wasi-http/src/body.rs index d7e3b07daced..39bc30de05bb 100644 --- a/crates/wasi-http/src/body.rs +++ b/crates/wasi-http/src/body.rs @@ -10,8 +10,7 @@ use std::{ }; use tokio::sync::{mpsc, oneshot}; use wasmtime_wasi::preview2::{ - self, AbortOnDropJoinHandle, HostInputStream, HostOutputStream, OutputStreamError, - StreamRuntimeError, StreamState, Subscribe, + self, AbortOnDropJoinHandle, HostInputStream, HostOutputStream, StreamError, Subscribe, }; pub type HyperIncomingBody = BoxBody; @@ -146,21 +145,21 @@ impl HostIncomingBodyStream { #[async_trait::async_trait] impl HostInputStream for HostIncomingBodyStream { - fn read(&mut self, size: usize) -> anyhow::Result<(Bytes, StreamState)> { + fn read(&mut self, size: usize) -> Result { use mpsc::error::TryRecvError; if !self.buffer.is_empty() { let len = size.min(self.buffer.len()); let chunk = self.buffer.split_to(len); - return Ok((chunk, StreamState::Open)); + return Ok(chunk); } if let Some(e) = self.error.take() { - return Err(StreamRuntimeError::from(e).into()); + return Err(StreamError::LastOperationFailed(e)); } if !self.open { - return Ok((Bytes::new(), StreamState::Closed)); + return Err(StreamError::Closed); } match self.receiver.try_recv() { @@ -171,21 +170,21 @@ impl HostInputStream for HostIncomingBodyStream { self.buffer = bytes; } - return Ok((chunk, StreamState::Open)); + return Ok(chunk); } Ok(Err(e)) => { self.open = false; - return Err(StreamRuntimeError::from(e).into()); + return Err(StreamError::LastOperationFailed(e)); } Err(TryRecvError::Empty) => { - return Ok((Bytes::new(), StreamState::Open)); + return Ok(Bytes::new()); } Err(TryRecvError::Disconnected) => { self.open = false; - return Ok((Bytes::new(), StreamState::Closed)); + return Err(StreamError::Closed); } } } @@ -332,12 +331,12 @@ struct WorkerState { } impl WorkerState { - fn check_error(&mut self) -> Result<(), OutputStreamError> { + fn check_error(&mut self) -> Result<(), StreamError> { if let Some(e) = self.error.take() { - return Err(OutputStreamError::LastOperationFailed(e)); + return Err(StreamError::LastOperationFailed(e)); } if !self.alive { - return Err(OutputStreamError::Closed); + return Err(StreamError::Closed); } Ok(()) } @@ -382,7 +381,7 @@ impl Worker { self.write_ready_changed.notified().await; } } - fn check_write(&self) -> Result { + fn check_write(&self) -> Result { let mut state = self.state(); if let Err(e) = state.check_error() { return Err(e); @@ -476,11 +475,11 @@ impl BodyWriteStream { #[async_trait::async_trait] impl HostOutputStream for BodyWriteStream { - fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError> { + fn write(&mut self, bytes: Bytes) -> Result<(), StreamError> { let mut state = self.worker.state(); state.check_error()?; if state.flush_pending { - return Err(OutputStreamError::Trap(anyhow!( + return Err(StreamError::Trap(anyhow!( "write not permitted while flush pending" ))); } @@ -489,13 +488,13 @@ impl HostOutputStream for BodyWriteStream { state.write_budget = remaining_budget; state.items.push_back(bytes); } - None => return Err(OutputStreamError::Trap(anyhow!("write exceeded budget"))), + None => return Err(StreamError::Trap(anyhow!("write exceeded budget"))), } drop(state); self.worker.new_work.notify_one(); Ok(()) } - fn flush(&mut self) -> Result<(), OutputStreamError> { + fn flush(&mut self) -> Result<(), StreamError> { let mut state = self.worker.state(); state.check_error()?; @@ -505,7 +504,7 @@ impl HostOutputStream for BodyWriteStream { Ok(()) } - fn check_write(&mut self) -> Result { + fn check_write(&mut self) -> Result { self.worker.check_write() } } diff --git a/crates/wasi-http/wit/deps/io/streams.wit b/crates/wasi-http/wit/deps/io/streams.wit index eeeff505890a..8240507976f7 100644 --- a/crates/wasi-http/wit/deps/io/streams.wit +++ b/crates/wasi-http/wit/deps/io/streams.wit @@ -8,20 +8,14 @@ package wasi:io interface streams { use poll.{pollable} - /// Streams provide a sequence of data and then end; once they end, they - /// no longer provide any further data. - /// - /// For example, a stream reading from a file ends when the stream reaches - /// the end of the file. For another example, a stream reading from a - /// socket ends when the socket is closed. - enum stream-status { - /// The stream is open and may produce further data. - open, - /// When reading, this indicates that the stream will not produce - /// further data. - /// When writing, this indicates that the stream will no longer be read. - /// Further writes are still permitted. - ended, + /// An error for input-stream and output-stream operations. + enum stream-error { + /// The last operation (a write or flush) failed before completion. + last-operation-failed, + /// The stream is closed: no more input will be accepted by the + /// stream. A closed output-stream will return this error on all + /// future operations. + closed } /// An input bytestream. @@ -58,14 +52,14 @@ interface streams { read: func( /// The maximum number of bytes to read len: u64 - ) -> result, stream-status>> + ) -> result, stream-error> /// Read bytes from a stream, after blocking until at least one byte can /// be read. Except for blocking, identical to `read`. blocking-read: func( /// The maximum number of bytes to read len: u64 - ) -> result, stream-status>> + ) -> result, stream-error> /// Skip bytes from a stream. /// @@ -82,14 +76,14 @@ interface streams { skip: func( /// The maximum number of bytes to skip. len: u64, - ) -> result> + ) -> result /// Skip bytes from a stream, after blocking until at least one byte /// can be skipped. Except for blocking behavior, identical to `skip`. blocking-skip: func( /// The maximum number of bytes to skip. len: u64, - ) -> result> + ) -> result /// Create a `pollable` which will resolve once either the specified stream /// has bytes available to read or the other end of the stream has been @@ -100,18 +94,6 @@ interface streams { subscribe: func() -> pollable } - /// An error for output-stream operations. - /// - /// Contrary to input-streams, a closed output-stream is reported using - /// an error. - enum write-error { - /// The last operation (a write or flush) failed before completion. - last-operation-failed, - /// The stream is closed: no more input will be accepted by the - /// stream. A closed output-stream will return this error on all - /// future operations. - closed - } /// An output bytestream. /// @@ -131,7 +113,7 @@ interface streams { /// When this function returns 0 bytes, the `subscribe` pollable will /// become ready when this function will report at least 1 byte, or an /// error. - check-write: func() -> result + check-write: func() -> result /// Perform a write. This function never blocks. /// @@ -142,7 +124,7 @@ interface streams { /// the last call to check-write provided a permit. write: func( contents: list - ) -> result<_, write-error> + ) -> result<_, stream-error> /// Perform a write of up to 4096 bytes, and then flush the stream. Block /// until all of these operations are complete, or an error occurs. @@ -170,7 +152,7 @@ interface streams { /// ``` blocking-write-and-flush: func( contents: list - ) -> result<_, write-error> + ) -> result<_, stream-error> /// Request to flush buffered output. This function never blocks. /// @@ -182,11 +164,11 @@ interface streams { /// writes (`check-write` will return `ok(0)`) until the flush has /// completed. The `subscribe` pollable will become ready when the /// flush has completed and the stream can accept more writes. - flush: func() -> result<_, write-error> + flush: func() -> result<_, stream-error> /// Request to flush buffered output, and block until flush completes /// and stream is ready for writing again. - blocking-flush: func() -> result<_, write-error> + blocking-flush: func() -> result<_, stream-error> /// Create a `pollable` which will resolve once the output-stream /// is ready for more writing, or an error has occured. When this @@ -209,7 +191,7 @@ interface streams { write-zeroes: func( /// The number of zero-bytes to write len: u64 - ) -> result<_, write-error> + ) -> result<_, stream-error> /// Perform a write of up to 4096 zeroes, and then flush the stream. /// Block until all of these operations are complete, or an error @@ -238,7 +220,7 @@ interface streams { blocking-write-zeroes-and-flush: func( /// The number of zero-bytes to write len: u64 - ) -> result<_, write-error> + ) -> result<_, stream-error> /// Read from one stream and write to another. /// @@ -252,7 +234,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result> + ) -> result /// Read from one stream and write to another, with blocking. /// @@ -263,7 +245,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result> + ) -> result /// Forward the entire contents of an input stream to an output stream. /// @@ -280,6 +262,6 @@ interface streams { forward: func( /// The stream to read from src: input-stream - ) -> result> + ) -> result } } diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs index 46f9c887772b..acbf82eea54d 100644 --- a/crates/wasi-preview1-component-adapter/src/lib.rs +++ b/crates/wasi-preview1-component-adapter/src/lib.rs @@ -891,10 +891,17 @@ pub unsafe extern "C" fn fd_read( let read_len = u64::try_from(len).trapping_unwrap(); let wasi_stream = streams.get_read_stream()?; - let (data, stream_stat) = state + let data = match state .import_alloc .with_buffer(ptr, len, || blocking_mode.read(wasi_stream, read_len)) - .map_err(|_| ERRNO_IO)?; + { + Ok(data) => data, + Err(streams::StreamError::Closed) => { + *nread = 0; + return Ok(()); + } + Err(_) => Err(ERRNO_IO)?, + }; assert_eq!(data.as_ptr(), ptr); assert!(data.len() <= len); @@ -903,16 +910,15 @@ pub unsafe extern "C" fn fd_read( if let StreamType::File(file) = &streams.type_ { file.position .set(file.position.get() + data.len() as filesystem::Filesize); + if len == 0 { + return Err(ERRNO_INTR); + } } let len = data.len(); + *nread = len; forget(data); - if stream_stat == crate::streams::StreamStatus::Open && len == 0 { - Err(ERRNO_INTR) - } else { - *nread = len; - Ok(()) - } + Ok(()) } Descriptor::Closed(_) => Err(ERRNO_BADF), } @@ -2134,7 +2140,7 @@ impl BlockingMode { self, input_stream: &streams::InputStream, read_len: u64, - ) -> Result<(Vec, streams::StreamStatus), ()> { + ) -> Result, streams::StreamError> { match self { BlockingMode::NonBlocking => input_stream.read(read_len), BlockingMode::Blocking => input_stream.blocking_read(read_len), @@ -2163,8 +2169,8 @@ impl BlockingMode { BlockingMode::NonBlocking => { let permit = match output_stream.check_write() { Ok(n) => n, - Err(streams::WriteError::Closed) => 0, - Err(streams::WriteError::LastOperationFailed) => return Err(ERRNO_IO), + Err(streams::StreamError::Closed) => 0, + Err(streams::StreamError::LastOperationFailed) => return Err(ERRNO_IO), }; let len = bytes.len().min(permit as usize); @@ -2174,14 +2180,14 @@ impl BlockingMode { match output_stream.write(&bytes[..len]) { Ok(_) => {} - Err(streams::WriteError::Closed) => return Ok(0), - Err(streams::WriteError::LastOperationFailed) => return Err(ERRNO_IO), + Err(streams::StreamError::Closed) => return Ok(0), + Err(streams::StreamError::LastOperationFailed) => return Err(ERRNO_IO), } match output_stream.blocking_flush() { Ok(_) => {} - Err(streams::WriteError::Closed) => return Ok(0), - Err(streams::WriteError::LastOperationFailed) => return Err(ERRNO_IO), + Err(streams::StreamError::Closed) => return Ok(0), + Err(streams::StreamError::LastOperationFailed) => return Err(ERRNO_IO), } Ok(len) diff --git a/crates/wasi/src/preview2/filesystem.rs b/crates/wasi/src/preview2/filesystem.rs index e09d32df1d9c..488ec93b1f89 100644 --- a/crates/wasi/src/preview2/filesystem.rs +++ b/crates/wasi/src/preview2/filesystem.rs @@ -1,8 +1,5 @@ use crate::preview2::bindings::filesystem::types; -use crate::preview2::{ - AbortOnDropJoinHandle, HostOutputStream, OutputStreamError, StreamRuntimeError, StreamState, - Subscribe, -}; +use crate::preview2::{AbortOnDropJoinHandle, HostOutputStream, StreamError, Subscribe}; use anyhow::anyhow; use bytes::{Bytes, BytesMut}; use std::io; @@ -126,7 +123,7 @@ impl FileInputStream { Self { file, position } } - pub async fn read(&mut self, size: usize) -> anyhow::Result<(Bytes, StreamState)> { + pub async fn read(&mut self, size: usize) -> Result { use system_interface::fs::FileIoExt; let f = Arc::clone(&self.file); let p = self.position; @@ -137,33 +134,24 @@ impl FileInputStream { }) .await .unwrap(); - let (n, state) = read_result(r)?; + let n = read_result(r)?; buf.truncate(n); self.position += n as u64; - Ok((buf.freeze(), state)) + Ok(buf.freeze()) } - pub async fn skip(&mut self, nelem: usize) -> anyhow::Result<(usize, StreamState)> { - let mut nread = 0; - let mut state = StreamState::Open; - - let (bs, read_state) = self.read(nelem).await?; - // TODO: handle the case where `bs.len()` is less than `nelem` - nread += bs.len(); - if read_state.is_closed() { - state = read_state; - } - - Ok((nread, state)) + pub async fn skip(&mut self, nelem: usize) -> Result { + let bs = self.read(nelem).await?; + Ok(bs.len()) } } -fn read_result(r: io::Result) -> Result<(usize, StreamState), anyhow::Error> { +fn read_result(r: io::Result) -> Result { match r { - Ok(0) => Ok((0, StreamState::Closed)), - Ok(n) => Ok((n, StreamState::Open)), - Err(e) if e.kind() == io::ErrorKind::Interrupted => Ok((0, StreamState::Open)), - Err(e) => Err(StreamRuntimeError::from(anyhow!(e)).into()), + Ok(0) => Err(StreamError::Closed), + Ok(n) => Ok(n), + Err(e) if e.kind() == std::io::ErrorKind::Interrupted => Ok(0), + Err(e) => Err(StreamError::LastOperationFailed(e.into())), } } @@ -210,14 +198,14 @@ impl FileOutputStream { const FILE_WRITE_CAPACITY: usize = 1024 * 1024; impl HostOutputStream for FileOutputStream { - fn write(&mut self, buf: Bytes) -> Result<(), OutputStreamError> { + fn write(&mut self, buf: Bytes) -> Result<(), StreamError> { use system_interface::fs::FileIoExt; match self.state { OutputState::Ready => {} - OutputState::Closed => return Err(OutputStreamError::Closed), + OutputState::Closed => return Err(StreamError::Closed), OutputState::Waiting(_) | OutputState::Error(_) => { // a write is pending - this call was not permitted - return Err(OutputStreamError::Trap(anyhow!( + return Err(StreamError::Trap(anyhow!( "write not permitted: check_write not called first" ))); } @@ -248,25 +236,25 @@ impl HostOutputStream for FileOutputStream { self.state = OutputState::Waiting(task); Ok(()) } - fn flush(&mut self) -> Result<(), OutputStreamError> { + fn flush(&mut self) -> Result<(), StreamError> { match self.state { // Only userland buffering of file writes is in the blocking task, // so there's nothing extra that needs to be done to request a // flush. OutputState::Ready | OutputState::Waiting(_) => Ok(()), - OutputState::Closed => Err(OutputStreamError::Closed), + OutputState::Closed => Err(StreamError::Closed), OutputState::Error(_) => match mem::replace(&mut self.state, OutputState::Closed) { - OutputState::Error(e) => Err(OutputStreamError::LastOperationFailed(e.into())), + OutputState::Error(e) => Err(StreamError::LastOperationFailed(e.into())), _ => unreachable!(), }, } } - fn check_write(&mut self) -> Result { + fn check_write(&mut self) -> Result { match self.state { OutputState::Ready => Ok(FILE_WRITE_CAPACITY), - OutputState::Closed => Err(OutputStreamError::Closed), + OutputState::Closed => Err(StreamError::Closed), OutputState::Error(_) => match mem::replace(&mut self.state, OutputState::Closed) { - OutputState::Error(e) => Err(OutputStreamError::LastOperationFailed(e.into())), + OutputState::Error(e) => Err(StreamError::LastOperationFailed(e.into())), _ => unreachable!(), }, OutputState::Waiting(_) => Ok(0), diff --git a/crates/wasi/src/preview2/host/io.rs b/crates/wasi/src/preview2/host/io.rs index a9e18de4caf3..92cfa0cdb0de 100644 --- a/crates/wasi/src/preview2/host/io.rs +++ b/crates/wasi/src/preview2/host/io.rs @@ -1,34 +1,25 @@ use crate::preview2::{ bindings::io::streams::{self, InputStream, OutputStream}, poll::subscribe, - stream::{OutputStreamError, StreamRuntimeError, StreamState}, + stream::StreamError, Pollable, TableError, WasiView, }; use wasmtime::component::Resource; -impl From for streams::StreamStatus { - fn from(state: StreamState) -> Self { - match state { - StreamState::Open => Self::Open, - StreamState::Closed => Self::Ended, - } - } -} - impl From for streams::Error { fn from(e: TableError) -> streams::Error { streams::Error::trap(e.into()) } } -impl From for streams::Error { - fn from(e: OutputStreamError) -> streams::Error { +impl From for streams::Error { + fn from(e: StreamError) -> streams::Error { match e { - OutputStreamError::Closed => streams::WriteError::Closed.into(), - OutputStreamError::LastOperationFailed(e) => { - tracing::debug!("streams::WriteError::LastOperationFailed: {e:?}"); - streams::WriteError::LastOperationFailed.into() + StreamError::Closed => streams::StreamError::Closed.into(), + StreamError::LastOperationFailed(e) => { + tracing::debug!("streams::StreamError::LastOperationFailed: {e:?}"); + streams::StreamError::LastOperationFailed.into() } - OutputStreamError::Trap(e) => streams::Error::trap(e), + StreamError::Trap(e) => streams::Error::trap(e), } } } @@ -148,7 +139,7 @@ impl streams::HostOutputStream for T { _dst: Resource, _src: Resource, _len: u64, - ) -> anyhow::Result> { + ) -> Result { // TODO: We can't get two streams at the same time because they both // carry the exclusive lifetime of `ctx`. When [`get_many_mut`] is // stabilized, that could allow us to add a `get_many_stream_mut` or @@ -177,7 +168,7 @@ impl streams::HostOutputStream for T { _dst: Resource, _src: Resource, _len: u64, - ) -> anyhow::Result> { + ) -> Result { // TODO: once splice is implemented, figure out what the blocking semantics are for waiting // on src and dest here. todo!("stream splice is not implemented") @@ -187,7 +178,7 @@ impl streams::HostOutputStream for T { &mut self, _dst: Resource, _src: Resource, - ) -> anyhow::Result> { + ) -> Result { // TODO: We can't get two streams at the same time because they both // carry the exclusive lifetime of `ctx`. When [`get_many_mut`] is // stabilized, that could allow us to add a `get_many_stream_mut` or @@ -213,6 +204,11 @@ impl streams::HostOutputStream for T { } } +impl From for streams::Error { + fn from(e: std::num::TryFromIntError) -> Self { + streams::Error::trap(anyhow::anyhow!("length overflow: {e:?}")) + } +} #[async_trait::async_trait] impl streams::HostInputStream for T { fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { @@ -224,46 +220,21 @@ impl streams::HostInputStream for T { &mut self, stream: Resource, len: u64, - ) -> anyhow::Result, streams::StreamStatus), ()>> { - match self.table_mut().get_resource_mut(&stream)? { - InputStream::Host(s) => { - let (bytes, state) = match s.read(len as usize) { - Ok(a) => a, - Err(e) => { - if let Some(e) = e.downcast_ref::() { - tracing::debug!("stream runtime error: {e:?}"); - return Ok(Err(())); - } else { - return Err(e); - } - } - }; - debug_assert!(bytes.len() <= len as usize); - - Ok(Ok((bytes.into(), state.into()))) - } - InputStream::File(s) => { - let (bytes, state) = match s.read(len as usize).await { - Ok(a) => a, - Err(e) => { - if let Some(e) = e.downcast_ref::() { - tracing::debug!("stream runtime error: {e:?}"); - return Ok(Err(())); - } else { - return Err(e); - } - } - }; - Ok(Ok((bytes.into(), state.into()))) - } - } + ) -> Result, streams::Error> { + let len = len.try_into()?; + let bytes = match self.table_mut().get_resource_mut(&stream)? { + InputStream::Host(s) => s.read(len)?, + InputStream::File(s) => s.read(len).await?, + }; + debug_assert!(bytes.len() <= len as usize); + Ok(bytes.into()) } async fn blocking_read( &mut self, stream: Resource, len: u64, - ) -> anyhow::Result, streams::StreamStatus), ()>> { + ) -> Result, streams::Error> { if let InputStream::Host(s) = self.table_mut().get_resource_mut(&stream)? { s.ready().await; } @@ -274,46 +245,20 @@ impl streams::HostInputStream for T { &mut self, stream: Resource, len: u64, - ) -> anyhow::Result> { - match self.table_mut().get_resource_mut(&stream)? { - InputStream::Host(s) => { - // TODO: the cast to usize should be fallible, use `.try_into()?` - let (bytes_skipped, state) = match s.skip(len as usize) { - Ok(a) => a, - Err(e) => { - if let Some(e) = e.downcast_ref::() { - tracing::debug!("stream runtime error: {e:?}"); - return Ok(Err(())); - } else { - return Err(e); - } - } - }; - - Ok(Ok((bytes_skipped as u64, state.into()))) - } - InputStream::File(s) => { - let (bytes_skipped, state) = match s.skip(len as usize).await { - Ok(a) => a, - Err(e) => { - if let Some(e) = e.downcast_ref::() { - tracing::debug!("stream runtime error: {e:?}"); - return Ok(Err(())); - } else { - return Err(e); - } - } - }; - Ok(Ok((bytes_skipped as u64, state.into()))) - } - } + ) -> Result { + let len = len.try_into()?; + let written = match self.table_mut().get_resource_mut(&stream)? { + InputStream::Host(s) => s.skip(len)?, + InputStream::File(s) => s.skip(len).await?, + }; + Ok(written.try_into().expect("usize always fits in u64")) } async fn blocking_skip( &mut self, stream: Resource, len: u64, - ) -> anyhow::Result> { + ) -> Result { if let InputStream::Host(s) = self.table_mut().get_resource_mut(&stream)? { s.ready().await; } @@ -337,35 +282,18 @@ pub mod sync { }; use wasmtime::component::Resource; - // same boilerplate everywhere, converting between two identical types with different - // definition sites. one day wasmtime-wit-bindgen will make all this unnecessary - fn xform( - r: Result<(A, async_streams::StreamStatus), ()>, - ) -> Result<(A, streams::StreamStatus), ()> { - r.map(|(a, b)| (a, b.into())) - } - - impl From for streams::StreamStatus { - fn from(other: async_streams::StreamStatus) -> Self { - match other { - async_streams::StreamStatus::Open => Self::Open, - async_streams::StreamStatus::Ended => Self::Ended, - } - } - } - - impl From for streams::WriteError { - fn from(other: async_streams::WriteError) -> Self { + impl From for streams::StreamError { + fn from(other: async_streams::StreamError) -> Self { match other { - async_streams::WriteError::LastOperationFailed => Self::LastOperationFailed, - async_streams::WriteError::Closed => Self::Closed, + async_streams::StreamError::LastOperationFailed => Self::LastOperationFailed, + async_streams::StreamError::Closed => Self::Closed, } } } impl From for streams::Error { fn from(other: async_streams::Error) -> Self { match other.downcast() { - Ok(write_error) => streams::Error::from(streams::WriteError::from(write_error)), + Ok(write_error) => streams::Error::from(streams::StreamError::from(write_error)), Err(e) => streams::Error::trap(e), } } @@ -444,8 +372,10 @@ pub mod sync { dst: Resource, src: Resource, len: u64, - ) -> anyhow::Result> { - in_tokio(async { AsyncHostOutputStream::splice(self, dst, src, len).await }).map(xform) + ) -> Result { + Ok(in_tokio(async { + AsyncHostOutputStream::splice(self, dst, src, len).await + })?) } fn blocking_splice( @@ -453,17 +383,20 @@ pub mod sync { dst: Resource, src: Resource, len: u64, - ) -> anyhow::Result> { - in_tokio(async { AsyncHostOutputStream::blocking_splice(self, dst, src, len).await }) - .map(xform) + ) -> Result { + Ok(in_tokio(async { + AsyncHostOutputStream::blocking_splice(self, dst, src, len).await + })?) } fn forward( &mut self, dst: Resource, src: Resource, - ) -> anyhow::Result> { - in_tokio(async { AsyncHostOutputStream::forward(self, dst, src).await }).map(xform) + ) -> Result { + Ok(in_tokio(async { + AsyncHostOutputStream::forward(self, dst, src).await + })?) } } @@ -476,34 +409,36 @@ pub mod sync { &mut self, stream: Resource, len: u64, - ) -> anyhow::Result, streams::StreamStatus), ()>> { - in_tokio(async { AsyncHostInputStream::read(self, stream, len).await }).map(xform) + ) -> Result, streams::Error> { + Ok(in_tokio(async { + AsyncHostInputStream::read(self, stream, len).await + })?) } fn blocking_read( &mut self, stream: Resource, len: u64, - ) -> anyhow::Result, streams::StreamStatus), ()>> { - in_tokio(async { AsyncHostInputStream::blocking_read(self, stream, len).await }) - .map(xform) + ) -> Result, streams::Error> { + Ok(in_tokio(async { + AsyncHostInputStream::blocking_read(self, stream, len).await + })?) } - fn skip( - &mut self, - stream: Resource, - len: u64, - ) -> anyhow::Result> { - in_tokio(async { AsyncHostInputStream::skip(self, stream, len).await }).map(xform) + fn skip(&mut self, stream: Resource, len: u64) -> Result { + Ok(in_tokio(async { + AsyncHostInputStream::skip(self, stream, len).await + })?) } fn blocking_skip( &mut self, stream: Resource, len: u64, - ) -> anyhow::Result> { - in_tokio(async { AsyncHostInputStream::blocking_skip(self, stream, len).await }) - .map(xform) + ) -> Result { + Ok(in_tokio(async { + AsyncHostInputStream::blocking_skip(self, stream, len).await + })?) } fn subscribe( diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index de8dd801dcd2..78b436afe38a 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -44,10 +44,7 @@ pub use self::filesystem::{DirPerms, FilePerms}; pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe}; pub use self::random::{thread_rng, Deterministic}; pub use self::stdio::{stderr, stdin, stdout, IsATTY, Stderr, Stdin, Stdout}; -pub use self::stream::{ - HostInputStream, HostOutputStream, InputStream, OutputStream, OutputStreamError, - StreamRuntimeError, StreamState, -}; +pub use self::stream::{HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError}; pub use self::table::{Table, TableError}; pub use cap_fs_ext::SystemTimeSpec; pub use cap_rand::RngCore; @@ -68,7 +65,7 @@ pub mod bindings { ", tracing: true, trappable_error_type: { - "wasi:io/streams"::"write-error": Error, + "wasi:io/streams"::"stream-error": Error, "wasi:filesystem/types"::"error-code": Error, }, with: { @@ -146,7 +143,7 @@ pub mod bindings { ], }, trappable_error_type: { - "wasi:io/streams"::"write-error": Error, + "wasi:io/streams"::"stream-error": Error, "wasi:filesystem/types"::"error-code": Error, "wasi:sockets/network"::"error-code": Error, }, diff --git a/crates/wasi/src/preview2/pipe.rs b/crates/wasi/src/preview2/pipe.rs index dae3e7569a99..bc8d20b1de56 100644 --- a/crates/wasi/src/preview2/pipe.rs +++ b/crates/wasi/src/preview2/pipe.rs @@ -8,8 +8,8 @@ //! but the virtual pipes can be instantiated with any `Read` or `Write` type. //! use crate::preview2::poll::Subscribe; -use crate::preview2::{HostInputStream, HostOutputStream, OutputStreamError, StreamState}; -use anyhow::{anyhow, Error}; +use crate::preview2::{HostInputStream, HostOutputStream, StreamError}; +use anyhow::anyhow; use bytes::Bytes; use std::sync::{Arc, Mutex}; use tokio::sync::mpsc; @@ -35,20 +35,15 @@ impl MemoryInputPipe { #[async_trait::async_trait] impl HostInputStream for MemoryInputPipe { - fn read(&mut self, size: usize) -> Result<(Bytes, StreamState), Error> { + fn read(&mut self, size: usize) -> Result { let mut buffer = self.buffer.lock().unwrap(); if buffer.is_empty() { - return Ok((Bytes::new(), StreamState::Closed)); + return Err(StreamError::Closed); } let size = size.min(buffer.len()); let read = buffer.split_to(size); - let state = if buffer.is_empty() { - StreamState::Closed - } else { - StreamState::Open - }; - Ok((read, state)) + Ok(read) } } @@ -81,10 +76,10 @@ impl MemoryOutputPipe { } impl HostOutputStream for MemoryOutputPipe { - fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError> { + fn write(&mut self, bytes: Bytes) -> Result<(), StreamError> { let mut buf = self.buffer.lock().unwrap(); if bytes.len() > self.capacity - buf.len() { - return Err(OutputStreamError::Trap(anyhow!( + return Err(StreamError::Trap(anyhow!( "write beyond capacity of MemoryOutputPipe" ))); } @@ -92,17 +87,17 @@ impl HostOutputStream for MemoryOutputPipe { // Always ready for writing Ok(()) } - fn flush(&mut self) -> Result<(), OutputStreamError> { + fn flush(&mut self) -> Result<(), StreamError> { // This stream is always flushed Ok(()) } - fn check_write(&mut self) -> Result { + fn check_write(&mut self) -> Result { let consumed = self.buffer.lock().unwrap().len(); if consumed < self.capacity { Ok(self.capacity - consumed) } else { // Since the buffer is full, no more bytes will ever be written - Err(OutputStreamError::Closed) + Err(StreamError::Closed) } } } @@ -114,9 +109,9 @@ impl Subscribe for MemoryOutputPipe { /// Provides a [`HostInputStream`] impl from a [`tokio::io::AsyncRead`] impl pub struct AsyncReadStream { - state: StreamState, - buffer: Option>, - receiver: mpsc::Receiver>, + closed: bool, + buffer: Option>, + receiver: mpsc::Receiver>, _join_handle: crate::preview2::AbortOnDropJoinHandle<()>, } @@ -130,11 +125,13 @@ impl AsyncReadStream { use tokio::io::AsyncReadExt; let mut buf = bytes::BytesMut::with_capacity(4096); let sent = match reader.read_buf(&mut buf).await { - Ok(nbytes) if nbytes == 0 => { - sender.send(Ok((Bytes::new(), StreamState::Closed))).await + Ok(nbytes) if nbytes == 0 => sender.send(Err(StreamError::Closed)).await, + Ok(_) => sender.send(Ok(buf.freeze())).await, + Err(e) => { + sender + .send(Err(StreamError::LastOperationFailed(e.into()))) + .await } - Ok(_) => sender.send(Ok((buf.freeze(), StreamState::Open))).await, - Err(e) => sender.send(Err(e)).await, }; if sent.is_err() { // no more receiver - stop trying to read @@ -143,7 +140,7 @@ impl AsyncReadStream { } }); AsyncReadStream { - state: StreamState::Open, + closed: false, buffer: None, receiver, _join_handle: join_handle, @@ -153,7 +150,7 @@ impl AsyncReadStream { #[async_trait::async_trait] impl HostInputStream for AsyncReadStream { - fn read(&mut self, size: usize) -> Result<(Bytes, StreamState), Error> { + fn read(&mut self, size: usize) -> Result { use mpsc::error::TryRecvError; match self.buffer.take() { @@ -161,55 +158,47 @@ impl HostInputStream for AsyncReadStream { // TODO: de-duplicate the buffer management with the case below let len = bytes.len().min(size); let rest = bytes.split_off(len); - let return_state = if !rest.is_empty() { + if !rest.is_empty() { self.buffer = Some(Ok(rest)); - StreamState::Open - } else { - self.state - }; - return Ok((bytes, return_state)); + } + return Ok(bytes); + } + Some(Err(e)) => { + self.closed = true; + return Err(e); } - Some(Err(e)) => return Err(e.into()), None => {} } match self.receiver.try_recv() { - Ok(Ok((mut bytes, state))) => { - self.state = state; - + Ok(Ok(mut bytes)) => { let len = bytes.len().min(size); let rest = bytes.split_off(len); - let return_state = if !rest.is_empty() { + if !rest.is_empty() { self.buffer = Some(Ok(rest)); - StreamState::Open - } else { - self.state - }; + } - Ok((bytes, return_state)) + Ok(bytes) } - Ok(Err(e)) => Err(e.into()), - Err(TryRecvError::Empty) => Ok((Bytes::new(), self.state)), - Err(TryRecvError::Disconnected) => Err(anyhow!( + Ok(Err(e)) => { + self.closed = true; + Err(e) + } + Err(TryRecvError::Empty) => Ok(Bytes::new()), + Err(TryRecvError::Disconnected) => Err(StreamError::Trap(anyhow!( "AsyncReadStream sender died - should be impossible" - )), + ))), } } } #[async_trait::async_trait] impl Subscribe for AsyncReadStream { async fn ready(&mut self) { - if self.buffer.is_some() || self.state == StreamState::Closed { + if self.buffer.is_some() || self.closed { return; } match self.receiver.recv().await { - Some(Ok((bytes, state))) => { - if state == StreamState::Closed { - self.state = state; - } - self.buffer = Some(Ok(bytes)); - } - Some(Err(e)) => self.buffer = Some(Err(e)), + Some(res) => self.buffer = Some(res), None => { panic!("no more sender for an open AsyncReadStream - should be impossible") } @@ -222,15 +211,15 @@ impl Subscribe for AsyncReadStream { pub struct SinkOutputStream; impl HostOutputStream for SinkOutputStream { - fn write(&mut self, _buf: Bytes) -> Result<(), OutputStreamError> { + fn write(&mut self, _buf: Bytes) -> Result<(), StreamError> { Ok(()) } - fn flush(&mut self) -> Result<(), OutputStreamError> { + fn flush(&mut self) -> Result<(), StreamError> { // This stream is always flushed Ok(()) } - fn check_write(&mut self) -> Result { + fn check_write(&mut self) -> Result { // This stream is always ready for writing. Ok(usize::MAX) } @@ -247,8 +236,8 @@ pub struct ClosedInputStream; #[async_trait::async_trait] impl HostInputStream for ClosedInputStream { - fn read(&mut self, _size: usize) -> Result<(Bytes, StreamState), Error> { - Ok((Bytes::new(), StreamState::Closed)) + fn read(&mut self, _size: usize) -> Result { + Err(StreamError::Closed) } } @@ -262,15 +251,15 @@ impl Subscribe for ClosedInputStream { pub struct ClosedOutputStream; impl HostOutputStream for ClosedOutputStream { - fn write(&mut self, _: Bytes) -> Result<(), OutputStreamError> { - Err(OutputStreamError::Closed) + fn write(&mut self, _: Bytes) -> Result<(), StreamError> { + Err(StreamError::Closed) } - fn flush(&mut self) -> Result<(), OutputStreamError> { - Err(OutputStreamError::Closed) + fn flush(&mut self) -> Result<(), StreamError> { + Err(StreamError::Closed) } - fn check_write(&mut self) -> Result { - Err(OutputStreamError::Closed) + fn check_write(&mut self) -> Result { + Err(StreamError::Closed) } } @@ -324,22 +313,20 @@ mod test { #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn empty_read_stream() { let mut reader = AsyncReadStream::new(tokio::io::empty()); - let (bs, state) = reader.read(10).unwrap(); - assert!(bs.is_empty()); // In a multi-threaded context, the value of state is not deterministic -- the spawned // reader task may run on a different thread. - match state { + match reader.read(10) { // The reader task ran before we tried to read, and noticed that the input was empty. - StreamState::Closed => {} + Err(StreamError::Closed) => {} // The reader task hasn't run yet. Call `ready` to await and fill the buffer. - StreamState::Open => { - resolves_immediately(reader.ready()).await; - let (bs, state) = reader.read(0).unwrap(); + Ok(bs) => { assert!(bs.is_empty()); - assert_eq!(state, StreamState::Closed); + resolves_immediately(reader.ready()).await; + assert!(matches!(reader.read(0), Err(StreamError::Closed))); } + res => panic!("unexpected: {res:?}"), } } @@ -347,27 +334,23 @@ mod test { async fn infinite_read_stream() { let mut reader = AsyncReadStream::new(tokio::io::repeat(0)); - let (bs, state) = reader.read(10).unwrap(); - assert_eq!(state, StreamState::Open); + let bs = reader.read(10).unwrap(); if bs.is_empty() { // Reader task hasn't run yet. Call `ready` to await and fill the buffer. resolves_immediately(reader.ready()).await; // Now a read should succeed - let (bs, state) = reader.read(10).unwrap(); + let bs = reader.read(10).unwrap(); assert_eq!(bs.len(), 10); - assert_eq!(state, StreamState::Open); } else { assert_eq!(bs.len(), 10); } // Subsequent reads should succeed - let (bs, state) = reader.read(10).unwrap(); - assert_eq!(state, StreamState::Open); + let bs = reader.read(10).unwrap(); assert_eq!(bs.len(), 10); // Even 0-length reads should succeed and show its open - let (bs, state) = reader.read(0).unwrap(); - assert_eq!(state, StreamState::Open); + let bs = reader.read(0).unwrap(); assert_eq!(bs.len(), 0); } @@ -381,33 +364,29 @@ mod test { async fn finite_read_stream() { let mut reader = AsyncReadStream::new(finite_async_reader(&[1; 123]).await); - let (bs, state) = reader.read(123).unwrap(); - assert_eq!(state, StreamState::Open); + let bs = reader.read(123).unwrap(); if bs.is_empty() { // Reader task hasn't run yet. Call `ready` to await and fill the buffer. resolves_immediately(reader.ready()).await; // Now a read should succeed - let (bs, state) = reader.read(123).unwrap(); + let bs = reader.read(123).unwrap(); assert_eq!(bs.len(), 123); - assert_eq!(state, StreamState::Open); } else { assert_eq!(bs.len(), 123); } // The AsyncRead's should be empty now, but we have a race where the reader task hasn't // yet send that to the AsyncReadStream. - let (bs, state) = reader.read(0).unwrap(); - assert!(bs.is_empty()); - match state { - StreamState::Closed => {} // Correct! - StreamState::Open => { + match reader.read(0) { + Err(StreamError::Closed) => {} // Correct! + Ok(bs) => { + assert!(bs.is_empty()); // Need to await to give this side time to catch up resolves_immediately(reader.ready()).await; // Now a read should show closed - let (bs, state) = reader.read(0).unwrap(); - assert_eq!(bs.len(), 0); - assert_eq!(state, StreamState::Closed); + assert!(matches!(reader.read(0), Err(StreamError::Closed))); } + res => panic!("unexpected: {res:?}"), } } @@ -420,31 +399,27 @@ mod test { w.write_all(&[123]).await.unwrap(); - let (bs, state) = reader.read(1).unwrap(); - assert_eq!(state, StreamState::Open); + let bs = reader.read(1).unwrap(); if bs.is_empty() { // Reader task hasn't run yet. Call `ready` to await and fill the buffer. resolves_immediately(reader.ready()).await; // Now a read should succeed - let (bs, state) = reader.read(1).unwrap(); + let bs = reader.read(1).unwrap(); assert_eq!(*bs, [123u8]); - assert_eq!(state, StreamState::Open); } else { assert_eq!(*bs, [123u8]); } // The stream should be empty and open now: - let (bs, state) = reader.read(1).unwrap(); + let bs = reader.read(1).unwrap(); assert!(bs.is_empty()); - assert_eq!(state, StreamState::Open); // We can wait on readiness and it will time out: never_resolves(reader.ready()).await; // Still open and empty: - let (bs, state) = reader.read(1).unwrap(); + let bs = reader.read(1).unwrap(); assert!(bs.is_empty()); - assert_eq!(state, StreamState::Open); // Put something else in the stream: w.write_all(&[45]).await.unwrap(); @@ -454,22 +429,19 @@ mod test { resolves_immediately(reader.ready()).await; // read the something else back out: - let (bs, state) = reader.read(1).unwrap(); + let bs = reader.read(1).unwrap(); assert_eq!(*bs, [45u8]); - assert_eq!(state, StreamState::Open); // nothing else in there: - let (bs, state) = reader.read(1).unwrap(); + let bs = reader.read(1).unwrap(); assert!(bs.is_empty()); - assert_eq!(state, StreamState::Open); // We can wait on readiness and it will time out: never_resolves(reader.ready()).await; // nothing else in there: - let (bs, state) = reader.read(1).unwrap(); + let bs = reader.read(1).unwrap(); assert!(bs.is_empty()); - assert_eq!(state, StreamState::Open); // Now close the pipe: drop(w); @@ -479,9 +451,7 @@ mod test { resolves_immediately(reader.ready()).await; // empty and now closed: - let (bs, state) = reader.read(1).unwrap(); - assert!(bs.is_empty()); - assert_eq!(state, StreamState::Closed); + assert!(matches!(reader.read(1), Err(StreamError::Closed))); } #[test_log::test(tokio::test(flavor = "multi_thread"))] @@ -502,18 +472,16 @@ mod test { // Now we expect the reader task has sent 4k from the stream to the reader. // Try to read out one bigger than the buffer available: - let (bs, state) = reader.read(4097).unwrap(); + let bs = reader.read(4097).unwrap(); assert_eq!(bs.len(), 4096); - assert_eq!(state, StreamState::Open); // Allow the crank to turn more: resolves_immediately(reader.ready()).await; // Again we expect the reader task has sent 4k from the stream to the reader. // Try to read out one bigger than the buffer available: - let (bs, state) = reader.read(4097).unwrap(); + let bs = reader.read(4097).unwrap(); assert_eq!(bs.len(), 4096); - assert_eq!(state, StreamState::Open); // The writer task is now finished - join with it: let w = resolves_immediately(writer_task).await; @@ -525,9 +493,7 @@ mod test { resolves_immediately(reader.ready()).await; // Now we expect the reader to be empty, and the stream closed: - let (bs, state) = reader.read(4097).unwrap(); - assert_eq!(bs.len(), 0); - assert_eq!(state, StreamState::Closed); + assert!(matches!(reader.read(4097), Err(StreamError::Closed))); } #[test_log::test(test_log::test(tokio::test(flavor = "multi_thread")))] @@ -598,7 +564,7 @@ mod test { // worker hasn't processed write yet: Ok(1023) => {} // worker reports failure: - Err(OutputStreamError::LastOperationFailed(_)) => { + Err(StreamError::LastOperationFailed(_)) => { tracing::debug!("discovered stream failure in first write_ready"); should_be_closed = true; } @@ -612,13 +578,13 @@ mod test { let flush_res = writer.flush(); match flush_res { // worker reports failure: - Err(OutputStreamError::LastOperationFailed(_)) => { + Err(StreamError::LastOperationFailed(_)) => { tracing::debug!("discovered stream failure trying to flush"); assert!(!should_be_closed); should_be_closed = true; } // Already reported failure, now closed - Err(OutputStreamError::Closed) => { + Err(StreamError::Closed) => { assert!( should_be_closed, "expected a LastOperationFailed before we see Closed. {write_ready_res:?}" @@ -633,12 +599,12 @@ mod test { // closed. match resolves_immediately(writer.write_ready()).await { // worker reports failure: - Err(OutputStreamError::LastOperationFailed(_)) => { + Err(StreamError::LastOperationFailed(_)) => { tracing::debug!("discovered stream failure trying to flush"); assert!(!should_be_closed); } // Already reported failure, now closed - Err(OutputStreamError::Closed) => { + Err(StreamError::Closed) => { assert!(should_be_closed); } r => { diff --git a/crates/wasi/src/preview2/preview1.rs b/crates/wasi/src/preview2/preview1.rs index 4a51b98d0362..a4bdada9b7f6 100644 --- a/crates/wasi/src/preview2/preview1.rs +++ b/crates/wasi/src/preview2/preview1.rs @@ -1,12 +1,14 @@ -use crate::preview2::bindings::cli::{ - stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin, - terminal_stdout, +use crate::preview2::bindings::{ + self, + cli::{ + stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin, + terminal_stdout, + }, + clocks::{monotonic_clock, wall_clock}, + filesystem::{preopens, types as filesystem}, + io::{poll, streams}, }; -use crate::preview2::bindings::clocks::{monotonic_clock, wall_clock}; -use crate::preview2::bindings::filesystem::{preopens, types as filesystem}; -use crate::preview2::bindings::io::poll; -use crate::preview2::bindings::io::streams; -use crate::preview2::{bindings, IsATTY, TableError, WasiView}; +use crate::preview2::{IsATTY, StreamError, TableError, WasiView}; use anyhow::{anyhow, bail, Context}; use std::borrow::Borrow; use std::collections::{BTreeMap, HashSet}; @@ -54,14 +56,34 @@ impl BlockingMode { host: &mut impl streams::Host, input_stream: Resource, max_size: usize, - ) -> Result<(Vec, streams::StreamStatus), types::Error> { + ) -> Result, types::Error> { let max_size = max_size.try_into().unwrap_or(u64::MAX); match self { - BlockingMode::Blocking => stream_res( - streams::HostInputStream::blocking_read(host, input_stream, max_size).await, - ), + BlockingMode::Blocking => { + match streams::HostInputStream::blocking_read(host, input_stream, max_size).await { + Ok(r) if r.is_empty() => Err(types::Errno::Intr.into()), + Ok(r) => Ok(r), + Err(e) if matches!(e.downcast_ref(), Some(streams::StreamError::Closed)) => { + Ok(Vec::new()) + } + Err(e) => { + tracing::trace!("throwing away read error to report as Errno::Io: {e:?}"); + Err(types::Errno::Io.into()) + } + } + } + BlockingMode::NonBlocking => { - stream_res(streams::HostInputStream::read(host, input_stream, max_size).await) + match streams::HostInputStream::read(host, input_stream, max_size).await { + Ok(r) => Ok(r), + Err(e) if matches!(e.downcast_ref(), Some(streams::StreamError::Closed)) => { + Ok(Vec::new()) + } + Err(e) => { + tracing::trace!("throwing away read error to report as Errno::Io: {e:?}"); + Err(types::Errno::Io.into()) + } + } } } } @@ -70,7 +92,7 @@ impl BlockingMode { host: &mut (impl streams::Host + poll::Host), output_stream: Resource, mut bytes: &[u8], - ) -> Result { + ) -> Result { use streams::HostOutputStream as Streams; match self { @@ -95,7 +117,7 @@ impl BlockingMode { BlockingMode::NonBlocking => { let n = match Streams::check_write(host, output_stream.borrowed()) { Ok(n) => n, - Err(e) if matches!(e.downcast_ref(), Some(streams::WriteError::Closed)) => 0, + Err(e) if matches!(e.downcast_ref(), Some(streams::StreamError::Closed)) => 0, Err(e) => Err(e)?, }; @@ -106,7 +128,7 @@ impl BlockingMode { match Streams::write(host, output_stream.borrowed(), bytes[..len].to_vec()) { Ok(()) => {} - Err(e) if matches!(e.downcast_ref(), Some(streams::WriteError::Closed)) => { + Err(e) if matches!(e.downcast_ref(), Some(streams::StreamError::Closed)) => { return Ok(0) } Err(e) => Err(e)?, @@ -114,7 +136,7 @@ impl BlockingMode { match Streams::blocking_flush(host, output_stream.borrowed()).await { Ok(()) => {} - Err(e) if matches!(e.downcast_ref(), Some(streams::WriteError::Closed)) => { + Err(e) if matches!(e.downcast_ref(), Some(streams::StreamError::Closed)) => { return Ok(0) } Err(e) => Err(e)?, @@ -552,23 +574,28 @@ impl wiggle::GuestErrorType for types::Errno { } } +impl From for types::Error { + fn from(err: StreamError) -> Self { + types::Error::from(streams::Error::from(err)) + } +} + impl From for types::Error { fn from(err: streams::Error) -> Self { match err.downcast() { - Ok(streams::WriteError::Closed | streams::WriteError::LastOperationFailed) => { - types::Errno::Io.into() - } - + Ok(se) => se.into(), Err(t) => types::Error::trap(t), } } } -fn stream_res(r: anyhow::Result>) -> Result { - match r { - Ok(Ok(a)) => Ok(a), - Ok(Err(_)) => Err(types::Errno::Io.into()), - Err(trap) => Err(types::Error::trap(trap)), +impl From for types::Error { + fn from(err: streams::StreamError) -> Self { + match err { + streams::StreamError::Closed | streams::StreamError::LastOperationFailed => { + types::Errno::Io.into() + } + } } } @@ -1325,7 +1352,7 @@ impl< ) -> Result { let t = self.transact()?; let desc = t.get_descriptor(fd)?; - let (mut buf, read, state) = match desc { + let (mut buf, read) = match desc { Descriptor::File(File { fd, blocking_mode, @@ -1346,12 +1373,12 @@ impl< .context("failed to call `read-via-stream`") .unwrap_or_else(types::Error::trap) })?; - let (read, state) = blocking_mode.read(self, stream, buf.len()).await?; + let read = blocking_mode.read(self, stream, buf.len()).await?; let n = read.len().try_into()?; let pos = pos.checked_add(n).ok_or(types::Errno::Overflow)?; position.store(pos, Ordering::Relaxed); - (buf, read, state) + (buf, read) } Descriptor::Stdin { stream, .. } => { let stream = stream.borrowed(); @@ -1359,24 +1386,14 @@ impl< let Some(buf) = first_non_empty_iovec(iovs)? else { return Ok(0); }; - let (read, state) = stream_res( - streams::HostInputStream::blocking_read( - self, - stream, - buf.len().try_into().unwrap_or(u64::MAX), - ) - .await, - )?; - (buf, read, state) + let read = BlockingMode::Blocking.read(self, stream, buf.len()).await?; + (buf, read) } _ => return Err(types::Errno::Badf.into()), }; if read.len() > buf.len() { return Err(types::Errno::Range.into()); } - if state == streams::StreamStatus::Open && read.len() == 0 { - return Err(types::Errno::Intr.into()); - } let (buf, _) = buf.split_at_mut(read.len()); buf.copy_from_slice(&read); let n = read.len().try_into()?; @@ -1394,7 +1411,7 @@ impl< ) -> Result { let t = self.transact()?; let desc = t.get_descriptor(fd)?; - let (mut buf, read, state) = match desc { + let (mut buf, read) = match desc { Descriptor::File(File { fd, blocking_mode, .. }) if t.view.table().get_resource(fd)?.is_file() => { @@ -1410,8 +1427,8 @@ impl< .context("failed to call `read-via-stream`") .unwrap_or_else(types::Error::trap) })?; - let (read, state) = blocking_mode.read(self, stream, buf.len()).await?; - (buf, read, state) + let read = blocking_mode.read(self, stream, buf.len()).await?; + (buf, read) } Descriptor::Stdin { .. } => { // NOTE: legacy implementation returns SPIPE here @@ -1422,9 +1439,6 @@ impl< if read.len() > buf.len() { return Err(types::Errno::Range.into()); } - if state == streams::StreamStatus::Open && read.len() == 0 { - return Err(types::Errno::Intr.into()); - } let (buf, _) = buf.split_at_mut(read.len()); buf.copy_from_slice(&read); let n = read.len().try_into()?; diff --git a/crates/wasi/src/preview2/stdio.rs b/crates/wasi/src/preview2/stdio.rs index 0d61adc2438a..9a4e93b86f5f 100644 --- a/crates/wasi/src/preview2/stdio.rs +++ b/crates/wasi/src/preview2/stdio.rs @@ -229,7 +229,7 @@ impl terminal_stderr::Host for T { #[cfg(all(unix, test))] mod test { - use crate::preview2::{HostInputStream, StreamState}; + use crate::preview2::HostInputStream; use libc; use std::fs::File; use std::io::{BufRead, BufReader, Write}; @@ -321,12 +321,10 @@ mod test { stdin.ready().await; println!("child: reading input"); - let (bytes, status) = stdin.read(1024).unwrap(); + // We can't effectively test for the case where stdin was closed, so panic if it is... + let bytes = stdin.read(1024).unwrap(); - println!("child: {:?}, {:?}", bytes, status); - - // We can't effectively test for the case where stdin was closed. - assert_eq!(status, StreamState::Open); + println!("child got: {:?}", bytes); buffer.push_str(std::str::from_utf8(bytes.as_ref()).unwrap()); if let Some((line, rest)) = buffer.split_once('\n') { diff --git a/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs b/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs index 1d1c80aabace..cb2f50f2c61b 100644 --- a/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs +++ b/crates/wasi/src/preview2/stdio/worker_thread_stdin.rs @@ -25,8 +25,7 @@ use crate::preview2::poll::Subscribe; use crate::preview2::stdio::StdinStream; -use crate::preview2::{HostInputStream, StreamState}; -use anyhow::Error; +use crate::preview2::{HostInputStream, StreamError}; use bytes::{Bytes, BytesMut}; use std::io::{IsTerminal, Read}; use std::mem; @@ -117,15 +116,15 @@ impl StdinStream for Stdin { #[async_trait::async_trait] impl HostInputStream for Stdin { - fn read(&mut self, size: usize) -> Result<(Bytes, StreamState), Error> { + fn read(&mut self, size: usize) -> Result { let g = GlobalStdin::get(); let mut locked = g.state.lock().unwrap(); match mem::replace(&mut *locked, StdinState::ReadRequested) { StdinState::ReadNotRequested => { g.read_requested.notify_one(); - Ok((Bytes::new(), StreamState::Open)) + Ok(Bytes::new()) } - StdinState::ReadRequested => Ok((Bytes::new(), StreamState::Open)), + StdinState::ReadRequested => Ok(Bytes::new()), StdinState::Data(mut data) => { let size = data.len().min(size); let bytes = data.split_to(size); @@ -134,15 +133,15 @@ impl HostInputStream for Stdin { } else { StdinState::Data(data) }; - Ok((bytes.freeze(), StreamState::Open)) + Ok(bytes.freeze()) } StdinState::Error(e) => { *locked = StdinState::Closed; - return Err(e.into()); + Err(StreamError::LastOperationFailed(e.into())) } StdinState::Closed => { *locked = StdinState::Closed; - Ok((Bytes::new(), StreamState::Closed)) + Err(StreamError::Closed) } } } diff --git a/crates/wasi/src/preview2/stream.rs b/crates/wasi/src/preview2/stream.rs index 7300b64073a4..4143b9ad86c2 100644 --- a/crates/wasi/src/preview2/stream.rs +++ b/crates/wasi/src/preview2/stream.rs @@ -1,98 +1,56 @@ use crate::preview2::filesystem::FileInputStream; use crate::preview2::poll::Subscribe; -use anyhow::Error; use anyhow::Result; use bytes::Bytes; -use std::fmt; - -/// An error which should be reported to Wasm as a runtime error, rather than -/// an error which should trap Wasm execution. The definition for runtime -/// stream errors is the empty type, so the contents of this error will only -/// be available via a `tracing`::event` at `Level::DEBUG`. -pub struct StreamRuntimeError(anyhow::Error); -impl From for StreamRuntimeError { - fn from(e: anyhow::Error) -> Self { - StreamRuntimeError(e) - } -} -impl fmt::Debug for StreamRuntimeError { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "Stream runtime error: {:?}", self.0) - } -} -impl fmt::Display for StreamRuntimeError { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "Stream runtime error") - } -} -impl std::error::Error for StreamRuntimeError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.0.source() - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum StreamState { - Open, - Closed, -} - -impl StreamState { - pub fn is_closed(&self) -> bool { - *self == Self::Closed - } -} /// Host trait for implementing the `wasi:io/streams.input-stream` resource: A /// bytestream which can be read from. #[async_trait::async_trait] pub trait HostInputStream: Subscribe { - /// Read bytes. On success, returns a pair holding the number of bytes - /// read and a flag indicating whether the end of the stream was reached. - /// Important: this read must be non-blocking! - /// Returning an Err which downcasts to a [`StreamRuntimeError`] will be - /// reported to Wasm as the empty error result. Otherwise, errors will trap. - fn read(&mut self, size: usize) -> Result<(Bytes, StreamState), Error>; - - /// Read bytes from a stream and discard them. Important: this method must - /// be non-blocking! - /// Returning an Error which downcasts to a StreamRuntimeError will be - /// reported to Wasm as the empty error result. Otherwise, errors will trap. - fn skip(&mut self, nelem: usize) -> Result<(usize, StreamState), Error> { - let mut nread = 0; - let mut state = StreamState::Open; - - let (bs, read_state) = self.read(nelem)?; - // TODO: handle the case where `bs.len()` is less than `nelem` - nread += bs.len(); - if read_state.is_closed() { - state = read_state; - } + /// Reads up to `size` bytes, returning a buffer holding these bytes on + /// success. + /// + /// This function does not block the current thread and is the equivalent of + /// a non-blocking read. On success all bytes read are returned through + /// `Bytes`, which is no larger than the `size` provided. If the returned + /// list of `Bytes` is empty then no data is ready to be read at this time. + /// + /// # Errors + /// + /// The [`StreamError`] return value communicates when this stream is + /// closed, when a read fails, or when a trap should be generated. + fn read(&mut self, size: usize) -> Result; - Ok((nread, state)) + /// Same as the `read` method except that bytes are skipped. + /// + /// Note that this method is non-blocking like `read` and returns the same + /// errors. + fn skip(&mut self, nelem: usize) -> Result { + let bs = self.read(nelem)?; + Ok(bs.len()) } } #[derive(Debug)] -pub enum OutputStreamError { +pub enum StreamError { Closed, LastOperationFailed(anyhow::Error), Trap(anyhow::Error), } -impl std::fmt::Display for OutputStreamError { +impl std::fmt::Display for StreamError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - OutputStreamError::Closed => write!(f, "closed"), - OutputStreamError::LastOperationFailed(e) => write!(f, "last operation failed: {e}"), - OutputStreamError::Trap(e) => write!(f, "trap: {e}"), + StreamError::Closed => write!(f, "closed"), + StreamError::LastOperationFailed(e) => write!(f, "last operation failed: {e}"), + StreamError::Trap(e) => write!(f, "trap: {e}"), } } } -impl std::error::Error for OutputStreamError { +impl std::error::Error for StreamError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { - OutputStreamError::Closed => None, - OutputStreamError::LastOperationFailed(e) | OutputStreamError::Trap(e) => e.source(), + StreamError::Closed => None, + StreamError::LastOperationFailed(e) | StreamError::Trap(e) => e.source(), } } } @@ -102,59 +60,63 @@ impl std::error::Error for OutputStreamError { #[async_trait::async_trait] pub trait HostOutputStream: Subscribe { /// Write bytes after obtaining a permit to write those bytes - /// Prior to calling [`write`](Self::write) - /// the caller must call [`write_ready`](Self::write_ready), - /// which resolves to a non-zero permit /// - /// This method must never block. - /// [`write_ready`](Self::write_ready) permit indicates the maximum amount of bytes that are - /// permitted to be written in a single [`write`](Self::write) following the - /// [`write_ready`](Self::write_ready) resolution + /// Prior to calling [`write`](Self::write) the caller must call + /// [`check_write`](Self::check_write), which resolves to a non-zero permit + /// + /// This method must never block. The [`check_write`](Self::check_write) + /// permit indicates the maximum amount of bytes that are permitted to be + /// written in a single [`write`](Self::write) following the + /// [`check_write`](Self::check_write) resolution. /// /// # Errors /// - /// Returns an [OutputStreamError] if: + /// Returns a [`StreamError`] if: /// - stream is closed /// - prior operation ([`write`](Self::write) or [`flush`](Self::flush)) failed /// - caller performed an illegal operation (e.g. wrote more bytes than were permitted) - fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError>; + fn write(&mut self, bytes: Bytes) -> Result<(), StreamError>; /// Trigger a flush of any bytes buffered in this stream implementation. /// /// This method may be called at any time and must never block. /// - /// After this method is called, [`write_ready`](Self::write_ready) must pend until flush is - /// complete. - /// When [`write_ready`](Self::write_ready) becomes ready after a flush, that guarantees that - /// all prior writes have been flushed from the implementation successfully, or that any error - /// associated with those writes is reported in the return value of [`flush`](Self::flush) or - /// [`write_ready`](Self::write_ready) + /// After this method is called, [`check_write`](Self::check_write) must + /// pend until flush is complete. + /// + /// When [`check_write`](Self::check_write) becomes ready after a flush, + /// that guarantees that all prior writes have been flushed from the + /// implementation successfully, or that any error associated with those + /// writes is reported in the return value of [`flush`](Self::flush) or + /// [`check_write`](Self::check_write) /// /// # Errors /// - /// Returns an [OutputStreamError] if: + /// Returns a [`StreamError`] if: /// - stream is closed /// - prior operation ([`write`](Self::write) or [`flush`](Self::flush)) failed /// - caller performed an illegal operation (e.g. wrote more bytes than were permitted) - fn flush(&mut self) -> Result<(), OutputStreamError>; + fn flush(&mut self) -> Result<(), StreamError>; /// Returns the number of bytes that are ready to be written to this stream. /// /// Zero bytes indicates that this stream is not currently ready for writing /// and `ready()` must be awaited first. /// + /// Note that this method does not block. + /// /// # Errors /// - /// Returns an [OutputStreamError] if: + /// Returns an [`StreamError`] if: /// - stream is closed /// - prior operation ([`write`](Self::write) or [`flush`](Self::flush)) failed - fn check_write(&mut self) -> Result; + fn check_write(&mut self) -> Result; /// Repeatedly write a byte to a stream. /// Important: this write must be non-blocking! - /// Returning an Err which downcasts to a [`StreamRuntimeError`] will be + /// Returning an Err which downcasts to a [`StreamError`] will be /// reported to Wasm as the empty error result. Otherwise, errors will trap. - fn write_zeroes(&mut self, nelem: usize) -> Result<(), OutputStreamError> { + fn write_zeroes(&mut self, nelem: usize) -> Result<(), StreamError> { // TODO: We could optimize this to not allocate one big zeroed buffer, and instead write // repeatedly from a 'static buffer of zeros. let bs = Bytes::from_iter(core::iter::repeat(0 as u8).take(nelem)); @@ -164,7 +126,7 @@ pub trait HostOutputStream: Subscribe { /// Simultaneously waits for this stream to be writable and then returns how /// much may be written or the last error that happened. - async fn write_ready(&mut self) -> Result { + async fn write_ready(&mut self) -> Result { self.ready().await; self.check_write() } diff --git a/crates/wasi/src/preview2/tcp.rs b/crates/wasi/src/preview2/tcp.rs index 3d5925a686b8..434eeb3aeead 100644 --- a/crates/wasi/src/preview2/tcp.rs +++ b/crates/wasi/src/preview2/tcp.rs @@ -1,7 +1,7 @@ -use super::{HostInputStream, HostOutputStream, OutputStreamError}; -use crate::preview2::poll::Subscribe; -use crate::preview2::stream::{InputStream, OutputStream}; -use crate::preview2::{with_ambient_tokio_runtime, AbortOnDropJoinHandle, StreamState}; +use super::{HostInputStream, HostOutputStream, StreamError}; +use crate::preview2::{ + with_ambient_tokio_runtime, AbortOnDropJoinHandle, InputStream, OutputStream, Subscribe, +}; use anyhow::{Error, Result}; use cap_net_ext::{AddressFamily, Blocking, TcpListenerExt}; use cap_std::net::TcpListener; @@ -70,20 +70,16 @@ impl TcpReadStream { closed: false, } } - fn stream_state(&self) -> StreamState { - if self.closed { - StreamState::Closed - } else { - StreamState::Open - } - } } #[async_trait::async_trait] impl HostInputStream for TcpReadStream { - fn read(&mut self, size: usize) -> Result<(bytes::Bytes, StreamState), anyhow::Error> { - if size == 0 || self.closed { - return Ok((bytes::Bytes::new(), self.stream_state())); + fn read(&mut self, size: usize) -> Result { + if self.closed { + return Err(StreamError::Closed); + } + if size == 0 { + return Ok(bytes::Bytes::new()); } let mut buf = bytes::BytesMut::with_capacity(size); @@ -100,14 +96,13 @@ impl HostInputStream for TcpReadStream { Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => 0, Err(e) => { - tracing::debug!("unexpected error on TcpReadStream read: {e:?}"); self.closed = true; - 0 + return Err(StreamError::LastOperationFailed(e.into())); } }; buf.truncate(n); - Ok((buf.freeze(), self.stream_state())) + Ok(buf.freeze()) } } @@ -171,11 +166,11 @@ impl TcpWriteStream { } impl HostOutputStream for TcpWriteStream { - fn write(&mut self, mut bytes: bytes::Bytes) -> Result<(), OutputStreamError> { + fn write(&mut self, mut bytes: bytes::Bytes) -> Result<(), StreamError> { match self.last_write { LastWrite::Done => {} LastWrite::Waiting(_) | LastWrite::Error(_) => { - return Err(OutputStreamError::Trap(anyhow::anyhow!( + return Err(StreamError::Trap(anyhow::anyhow!( "unpermitted: must call check_write first" ))); } @@ -194,28 +189,28 @@ impl HostOutputStream for TcpWriteStream { return Ok(()); } - Err(e) => return Err(OutputStreamError::LastOperationFailed(e.into())), + Err(e) => return Err(StreamError::LastOperationFailed(e.into())), } } Ok(()) } - fn flush(&mut self) -> Result<(), OutputStreamError> { + fn flush(&mut self) -> Result<(), StreamError> { // `flush` is a no-op here, as we're not managing any internal buffer. Additionally, // `write_ready` will join the background write task if it's active, so following `flush` // with `write_ready` will have the desired effect. Ok(()) } - fn check_write(&mut self) -> Result { + fn check_write(&mut self) -> Result { match mem::replace(&mut self.last_write, LastWrite::Done) { LastWrite::Waiting(task) => { self.last_write = LastWrite::Waiting(task); return Ok(0); } LastWrite::Done => {} - LastWrite::Error(e) => return Err(OutputStreamError::LastOperationFailed(e.into())), + LastWrite::Error(e) => return Err(StreamError::LastOperationFailed(e.into())), } let writable = self.stream.writable(); diff --git a/crates/wasi/src/preview2/write_stream.rs b/crates/wasi/src/preview2/write_stream.rs index 166192aa0506..afc2305f007a 100644 --- a/crates/wasi/src/preview2/write_stream.rs +++ b/crates/wasi/src/preview2/write_stream.rs @@ -1,4 +1,4 @@ -use crate::preview2::{HostOutputStream, OutputStreamError, Subscribe}; +use crate::preview2::{HostOutputStream, StreamError, Subscribe}; use anyhow::anyhow; use bytes::Bytes; use std::sync::{Arc, Mutex}; @@ -13,12 +13,12 @@ struct WorkerState { } impl WorkerState { - fn check_error(&mut self) -> Result<(), OutputStreamError> { + fn check_error(&mut self) -> Result<(), StreamError> { if let Some(e) = self.error.take() { - return Err(OutputStreamError::LastOperationFailed(e)); + return Err(StreamError::LastOperationFailed(e)); } if !self.alive { - return Err(OutputStreamError::Closed); + return Err(StreamError::Closed); } Ok(()) } @@ -63,7 +63,7 @@ impl Worker { self.write_ready_changed.notified().await; } } - fn check_write(&self) -> Result { + fn check_write(&self) -> Result { let mut state = self.state(); if let Err(e) = state.check_error() { return Err(e); @@ -162,11 +162,11 @@ impl AsyncWriteStream { } impl HostOutputStream for AsyncWriteStream { - fn write(&mut self, bytes: Bytes) -> Result<(), OutputStreamError> { + fn write(&mut self, bytes: Bytes) -> Result<(), StreamError> { let mut state = self.worker.state(); state.check_error()?; if state.flush_pending { - return Err(OutputStreamError::Trap(anyhow!( + return Err(StreamError::Trap(anyhow!( "write not permitted while flush pending" ))); } @@ -175,13 +175,13 @@ impl HostOutputStream for AsyncWriteStream { state.write_budget = remaining_budget; state.items.push_back(bytes); } - None => return Err(OutputStreamError::Trap(anyhow!("write exceeded budget"))), + None => return Err(StreamError::Trap(anyhow!("write exceeded budget"))), } drop(state); self.worker.new_work.notify_one(); Ok(()) } - fn flush(&mut self) -> Result<(), OutputStreamError> { + fn flush(&mut self) -> Result<(), StreamError> { let mut state = self.worker.state(); state.check_error()?; @@ -191,7 +191,7 @@ impl HostOutputStream for AsyncWriteStream { Ok(()) } - fn check_write(&mut self) -> Result { + fn check_write(&mut self) -> Result { self.worker.check_write() } } diff --git a/crates/wasi/wit/deps/io/streams.wit b/crates/wasi/wit/deps/io/streams.wit index eeeff505890a..8240507976f7 100644 --- a/crates/wasi/wit/deps/io/streams.wit +++ b/crates/wasi/wit/deps/io/streams.wit @@ -8,20 +8,14 @@ package wasi:io interface streams { use poll.{pollable} - /// Streams provide a sequence of data and then end; once they end, they - /// no longer provide any further data. - /// - /// For example, a stream reading from a file ends when the stream reaches - /// the end of the file. For another example, a stream reading from a - /// socket ends when the socket is closed. - enum stream-status { - /// The stream is open and may produce further data. - open, - /// When reading, this indicates that the stream will not produce - /// further data. - /// When writing, this indicates that the stream will no longer be read. - /// Further writes are still permitted. - ended, + /// An error for input-stream and output-stream operations. + enum stream-error { + /// The last operation (a write or flush) failed before completion. + last-operation-failed, + /// The stream is closed: no more input will be accepted by the + /// stream. A closed output-stream will return this error on all + /// future operations. + closed } /// An input bytestream. @@ -58,14 +52,14 @@ interface streams { read: func( /// The maximum number of bytes to read len: u64 - ) -> result, stream-status>> + ) -> result, stream-error> /// Read bytes from a stream, after blocking until at least one byte can /// be read. Except for blocking, identical to `read`. blocking-read: func( /// The maximum number of bytes to read len: u64 - ) -> result, stream-status>> + ) -> result, stream-error> /// Skip bytes from a stream. /// @@ -82,14 +76,14 @@ interface streams { skip: func( /// The maximum number of bytes to skip. len: u64, - ) -> result> + ) -> result /// Skip bytes from a stream, after blocking until at least one byte /// can be skipped. Except for blocking behavior, identical to `skip`. blocking-skip: func( /// The maximum number of bytes to skip. len: u64, - ) -> result> + ) -> result /// Create a `pollable` which will resolve once either the specified stream /// has bytes available to read or the other end of the stream has been @@ -100,18 +94,6 @@ interface streams { subscribe: func() -> pollable } - /// An error for output-stream operations. - /// - /// Contrary to input-streams, a closed output-stream is reported using - /// an error. - enum write-error { - /// The last operation (a write or flush) failed before completion. - last-operation-failed, - /// The stream is closed: no more input will be accepted by the - /// stream. A closed output-stream will return this error on all - /// future operations. - closed - } /// An output bytestream. /// @@ -131,7 +113,7 @@ interface streams { /// When this function returns 0 bytes, the `subscribe` pollable will /// become ready when this function will report at least 1 byte, or an /// error. - check-write: func() -> result + check-write: func() -> result /// Perform a write. This function never blocks. /// @@ -142,7 +124,7 @@ interface streams { /// the last call to check-write provided a permit. write: func( contents: list - ) -> result<_, write-error> + ) -> result<_, stream-error> /// Perform a write of up to 4096 bytes, and then flush the stream. Block /// until all of these operations are complete, or an error occurs. @@ -170,7 +152,7 @@ interface streams { /// ``` blocking-write-and-flush: func( contents: list - ) -> result<_, write-error> + ) -> result<_, stream-error> /// Request to flush buffered output. This function never blocks. /// @@ -182,11 +164,11 @@ interface streams { /// writes (`check-write` will return `ok(0)`) until the flush has /// completed. The `subscribe` pollable will become ready when the /// flush has completed and the stream can accept more writes. - flush: func() -> result<_, write-error> + flush: func() -> result<_, stream-error> /// Request to flush buffered output, and block until flush completes /// and stream is ready for writing again. - blocking-flush: func() -> result<_, write-error> + blocking-flush: func() -> result<_, stream-error> /// Create a `pollable` which will resolve once the output-stream /// is ready for more writing, or an error has occured. When this @@ -209,7 +191,7 @@ interface streams { write-zeroes: func( /// The number of zero-bytes to write len: u64 - ) -> result<_, write-error> + ) -> result<_, stream-error> /// Perform a write of up to 4096 zeroes, and then flush the stream. /// Block until all of these operations are complete, or an error @@ -238,7 +220,7 @@ interface streams { blocking-write-zeroes-and-flush: func( /// The number of zero-bytes to write len: u64 - ) -> result<_, write-error> + ) -> result<_, stream-error> /// Read from one stream and write to another. /// @@ -252,7 +234,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result> + ) -> result /// Read from one stream and write to another, with blocking. /// @@ -263,7 +245,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result> + ) -> result /// Forward the entire contents of an input stream to an output stream. /// @@ -280,6 +262,6 @@ interface streams { forward: func( /// The stream to read from src: input-stream - ) -> result> + ) -> result } } From e2f1bdd782de03946ed607cc8a08fd2858068434 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Oct 2023 17:05:20 -0500 Subject: [PATCH 053/199] Implement the `wasi:sockets/ip-name-lookup` interface (#7109) * Implement the `wasi:sockets/ip-name-lookup` interface This commit is an initial implementation of the new `ip-name-lookup` interface from the `wasi-sockets` proposal. The intention is to get a sketch of what an implementation would look like for the preview2 release later this year. Notable features of this implementation are: * Name lookups are disabled by default and must be explicitly enabled in `WasiCtx`. This seems like a reasonable default for now while the full set of configuration around this is settled. * I've added new "typed" methods to `preview2::Table` to avoid the need for extra helpers when using resources. * A new `-Sallow-ip-name-lookup` option is added to control this on the CLI. * Implementation-wise this uses the blocking resolution in the Rust standard library, built on `getaddrinfo`. This doesn't invoke `getaddrinfo` "raw", however, so information such as error details can be lost in translation. This will probably need to change in the future. Closes #7070 * Validate the input domain name. * Use a crate-level helper for `spawn_blocking` --------- Co-authored-by: Dave Bakker --- Cargo.lock | 2 + Cargo.toml | 1 + crates/cli-flags/src/lib.rs | 2 + crates/test-programs/tests/wasi-sockets.rs | 6 + .../src/bin/ip_name_lookup.rs | 36 +++++ crates/wasi-http/wit/test.wit | 2 + crates/wasi/Cargo.toml | 2 + crates/wasi/src/preview2/command.rs | 2 + crates/wasi/src/preview2/ctx.rs | 37 +++-- crates/wasi/src/preview2/filesystem.rs | 17 ++- .../src/preview2/host/instance_network.rs | 5 +- crates/wasi/src/preview2/host/network.rs | 6 +- crates/wasi/src/preview2/host/tcp.rs | 4 +- crates/wasi/src/preview2/ip_name_lookup.rs | 137 ++++++++++++++++++ crates/wasi/src/preview2/mod.rs | 27 ++-- crates/wasi/src/preview2/network.rs | 9 +- crates/wasi/wit/test.wit | 2 + src/commands/run.rs | 3 + 18 files changed, 263 insertions(+), 37 deletions(-) create mode 100644 crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs create mode 100644 crates/wasi/src/preview2/ip_name_lookup.rs diff --git a/Cargo.lock b/Cargo.lock index cf04ee8c5ebe..f72c8cf99a7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3752,6 +3752,7 @@ dependencies = [ "io-extras", "io-lifetimes 2.0.2", "libc", + "log", "once_cell", "rustix 0.38.8", "system-interface", @@ -3760,6 +3761,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "url", "wasi-cap-std-sync", "wasi-common", "wasi-tokio", diff --git a/Cargo.toml b/Cargo.toml index ddaffc7d5889..d4cc8fc06a95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -270,6 +270,7 @@ pretty_env_logger = "0.5.0" syn = "2.0.25" test-log = { version = "0.2", default-features = false, features = ["trace"] } tracing-subscriber = { version = "0.3.1", default-features = false, features = ['fmt', 'env-filter'] } +url = "2.3.1" [features] default = [ diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index 6e53576d3ee4..db5ba3abc300 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -242,6 +242,8 @@ wasmtime_option_group! { /// Flag for WASI preview2 to inherit the host's network within the /// guest so it has full access to all addresses/ports/etc. pub inherit_network: Option, + /// Indicates whether `wasi:sockets/ip-name-lookup` is enabled or not. + pub allow_ip_name_lookup: Option, } diff --git a/crates/test-programs/tests/wasi-sockets.rs b/crates/test-programs/tests/wasi-sockets.rs index 71669817bce6..8484a4f98f77 100644 --- a/crates/test-programs/tests/wasi-sockets.rs +++ b/crates/test-programs/tests/wasi-sockets.rs @@ -52,6 +52,7 @@ async fn run(name: &str) -> anyhow::Result<()> { let wasi = WasiCtxBuilder::new() .inherit_stdio() .inherit_network(ambient_authority()) + .allow_ip_name_lookup(true) .arg(name) .build(); @@ -74,3 +75,8 @@ async fn tcp_v4() { async fn tcp_v6() { run("tcp_v6").await.unwrap(); } + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn ip_name_lookup() { + run("ip_name_lookup").await.unwrap(); +} diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs b/crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs new file mode 100644 index 000000000000..50932e057c65 --- /dev/null +++ b/crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs @@ -0,0 +1,36 @@ +use wasi_sockets_tests::wasi::clocks::*; +use wasi_sockets_tests::wasi::io::*; +use wasi_sockets_tests::wasi::sockets::*; + +fn main() { + let network = instance_network::instance_network(); + + let addresses = + ip_name_lookup::resolve_addresses(&network, "example.com", None, false).unwrap(); + let pollable = addresses.subscribe(); + poll::poll_one(&pollable); + assert!(addresses.resolve_next_address().is_ok()); + + let result = ip_name_lookup::resolve_addresses(&network, "a.b<&>", None, false); + assert!(matches!(result, Err(network::ErrorCode::InvalidName))); + + // Try resolving a valid address and ensure that it eventually terminates. + // To help prevent this test from being flaky this additionally times out + // the resolution and allows errors. + let addresses = ip_name_lookup::resolve_addresses(&network, "github.com", None, false).unwrap(); + let lookup = addresses.subscribe(); + let timeout = monotonic_clock::subscribe(1_000_000_000, false); + let ready = poll::poll_list(&[&lookup, &timeout]); + assert!(ready.len() > 0); + match ready[0] { + 0 => loop { + match addresses.resolve_next_address() { + Ok(Some(_)) => {} + Ok(None) => break, + Err(_) => break, + } + }, + 1 => {} + _ => unreachable!(), + } +} diff --git a/crates/wasi-http/wit/test.wit b/crates/wasi-http/wit/test.wit index 0b6bd28e997d..03073513f8e6 100644 --- a/crates/wasi-http/wit/test.wit +++ b/crates/wasi-http/wit/test.wit @@ -39,4 +39,6 @@ world test-command-with-sockets { import wasi:sockets/tcp-create-socket import wasi:sockets/network import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:clocks/monotonic-clock } diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 2ef0c8d67c30..1582ddfc18b1 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -21,6 +21,8 @@ wasi-tokio = { workspace = true, optional = true } wiggle = { workspace = true, optional = true } libc = { workspace = true } once_cell = { workspace = true } +log = { workspace = true } +url = { workspace = true } tokio = { workspace = true, optional = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net"] } bytes = { workspace = true } diff --git a/crates/wasi/src/preview2/command.rs b/crates/wasi/src/preview2/command.rs index f83dce6b20cf..7702e2a706da 100644 --- a/crates/wasi/src/preview2/command.rs +++ b/crates/wasi/src/preview2/command.rs @@ -54,6 +54,7 @@ pub fn add_to_linker(l: &mut wasmtime::component::Linker) -> any crate::preview2::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::instance_network::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::network::add_to_linker(l, |t| t)?; + crate::preview2::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?; Ok(()) } @@ -116,6 +117,7 @@ pub mod sync { crate::preview2::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::instance_network::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::network::add_to_linker(l, |t| t)?; + crate::preview2::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?; Ok(()) } } diff --git a/crates/wasi/src/preview2/ctx.rs b/crates/wasi/src/preview2/ctx.rs index 95c0f2eae41d..344a4a3c12da 100644 --- a/crates/wasi/src/preview2/ctx.rs +++ b/crates/wasi/src/preview2/ctx.rs @@ -27,6 +27,7 @@ pub struct WasiCtxBuilder { insecure_random_seed: u128, wall_clock: Box, monotonic_clock: Box, + allow_ip_name_lookup: bool, built: bool, } @@ -76,6 +77,7 @@ impl WasiCtxBuilder { insecure_random_seed, wall_clock: wall_clock(), monotonic_clock: monotonic_clock(), + allow_ip_name_lookup: false, built: false, } } @@ -201,21 +203,27 @@ impl WasiCtxBuilder { } /// Add network addresses to the pool. - pub fn insert_addr(&mut self, addrs: A) -> std::io::Result<()> { - self.pool.insert(addrs, ambient_authority()) + pub fn insert_addr( + &mut self, + addrs: A, + ) -> std::io::Result<&mut Self> { + self.pool.insert(addrs, ambient_authority())?; + Ok(self) } /// Add a specific [`cap_std::net::SocketAddr`] to the pool. - pub fn insert_socket_addr(&mut self, addr: cap_std::net::SocketAddr) { + pub fn insert_socket_addr(&mut self, addr: cap_std::net::SocketAddr) -> &mut Self { self.pool.insert_socket_addr(addr, ambient_authority()); + self } /// Add a range of network addresses, accepting any port, to the pool. /// /// Unlike `insert_ip_net`, this function grants access to any requested port. - pub fn insert_ip_net_port_any(&mut self, ip_net: ipnet::IpNet) { + pub fn insert_ip_net_port_any(&mut self, ip_net: ipnet::IpNet) -> &mut Self { self.pool - .insert_ip_net_port_any(ip_net, ambient_authority()) + .insert_ip_net_port_any(ip_net, ambient_authority()); + self } /// Add a range of network addresses, accepting a range of ports, to @@ -228,14 +236,22 @@ impl WasiCtxBuilder { ip_net: ipnet::IpNet, ports_start: u16, ports_end: Option, - ) { + ) -> &mut Self { self.pool - .insert_ip_net_port_range(ip_net, ports_start, ports_end, ambient_authority()) + .insert_ip_net_port_range(ip_net, ports_start, ports_end, ambient_authority()); + self } /// Add a range of network addresses with a specific port to the pool. - pub fn insert_ip_net(&mut self, ip_net: ipnet::IpNet, port: u16) { - self.pool.insert_ip_net(ip_net, port, ambient_authority()) + pub fn insert_ip_net(&mut self, ip_net: ipnet::IpNet, port: u16) -> &mut Self { + self.pool.insert_ip_net(ip_net, port, ambient_authority()); + self + } + + /// Allow usage of `wasi:sockets/ip-name-lookup` + pub fn allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self { + self.allow_ip_name_lookup = enable; + self } /// Uses the configured context so far to construct the final `WasiCtx`. @@ -264,6 +280,7 @@ impl WasiCtxBuilder { insecure_random_seed, wall_clock, monotonic_clock, + allow_ip_name_lookup, built: _, } = mem::replace(self, Self::new()); self.built = true; @@ -281,6 +298,7 @@ impl WasiCtxBuilder { insecure_random_seed, wall_clock, monotonic_clock, + allow_ip_name_lookup, } } } @@ -305,4 +323,5 @@ pub struct WasiCtx { pub(crate) stdout: Box, pub(crate) stderr: Box, pub(crate) pool: Pool, + pub(crate) allow_ip_name_lookup: bool, } diff --git a/crates/wasi/src/preview2/filesystem.rs b/crates/wasi/src/preview2/filesystem.rs index 488ec93b1f89..9b8467a86ed3 100644 --- a/crates/wasi/src/preview2/filesystem.rs +++ b/crates/wasi/src/preview2/filesystem.rs @@ -1,5 +1,7 @@ use crate::preview2::bindings::filesystem::types; -use crate::preview2::{AbortOnDropJoinHandle, HostOutputStream, StreamError, Subscribe}; +use crate::preview2::{ + spawn_blocking, AbortOnDropJoinHandle, HostOutputStream, StreamError, Subscribe, +}; use anyhow::anyhow; use bytes::{Bytes, BytesMut}; use std::io; @@ -74,7 +76,7 @@ impl File { R: Send + 'static, { let f = self.file.clone(); - tokio::task::spawn_blocking(move || body(&f)).await.unwrap() + spawn_blocking(move || body(&f)).await } } @@ -110,7 +112,7 @@ impl Dir { R: Send + 'static, { let d = self.dir.clone(); - tokio::task::spawn_blocking(move || body(&d)).await.unwrap() + spawn_blocking(move || body(&d)).await } } @@ -127,13 +129,12 @@ impl FileInputStream { use system_interface::fs::FileIoExt; let f = Arc::clone(&self.file); let p = self.position; - let (r, mut buf) = tokio::task::spawn_blocking(move || { + let (r, mut buf) = spawn_blocking(move || { let mut buf = BytesMut::zeroed(size); let r = f.read_at(&mut buf, p); (r, buf) }) - .await - .unwrap(); + .await; let n = read_result(r)?; buf.truncate(n); self.position += n as u64; @@ -213,7 +214,7 @@ impl HostOutputStream for FileOutputStream { let f = Arc::clone(&self.file); let m = self.mode; - let task = AbortOnDropJoinHandle::from(tokio::task::spawn_blocking(move || match m { + let task = spawn_blocking(move || match m { FileOutputMode::Position(mut p) => { let mut buf = buf; while !buf.is_empty() { @@ -232,7 +233,7 @@ impl HostOutputStream for FileOutputStream { } Ok(()) } - })); + }); self.state = OutputState::Waiting(task); Ok(()) } diff --git a/crates/wasi/src/preview2/host/instance_network.rs b/crates/wasi/src/preview2/host/instance_network.rs index 02bf5e8a4014..627cc359f6d2 100644 --- a/crates/wasi/src/preview2/host/instance_network.rs +++ b/crates/wasi/src/preview2/host/instance_network.rs @@ -5,7 +5,10 @@ use wasmtime::component::Resource; impl instance_network::Host for T { fn instance_network(&mut self) -> Result, anyhow::Error> { - let network = Network::new(self.ctx().pool.clone()); + let network = Network { + pool: self.ctx().pool.clone(), + allow_ip_name_lookup: self.ctx().allow_ip_name_lookup, + }; let network = self.table_mut().push_resource(network)?; Ok(network) } diff --git a/crates/wasi/src/preview2/host/network.rs b/crates/wasi/src/preview2/host/network.rs index d1aa52d7fd05..811213050e8e 100644 --- a/crates/wasi/src/preview2/host/network.rs +++ b/crates/wasi/src/preview2/host/network.rs @@ -67,7 +67,11 @@ impl From for network::Error { Some(libc::EADDRINUSE) => ErrorCode::AddressInUse, Some(_) => return Self::trap(error.into()), }, - _ => return Self::trap(error.into()), + + _ => { + log::debug!("unknown I/O error: {error}"); + ErrorCode::Unknown + } } .into() } diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index 479ae44dc63c..6565a589413a 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -31,7 +31,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { } let network = table.get_resource(&network)?; - let binder = network.0.tcp_binder(local_address)?; + let binder = network.pool.tcp_binder(local_address)?; // Perform the OS bind call. binder.bind_existing_tcp_listener( @@ -75,7 +75,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { } let network = table.get_resource(&network)?; - let connecter = network.0.tcp_connecter(remote_address)?; + let connecter = network.pool.tcp_connecter(remote_address)?; // Do an OS `connect`. Our socket is non-blocking, so it'll either... { diff --git a/crates/wasi/src/preview2/ip_name_lookup.rs b/crates/wasi/src/preview2/ip_name_lookup.rs new file mode 100644 index 000000000000..b4a02427b475 --- /dev/null +++ b/crates/wasi/src/preview2/ip_name_lookup.rs @@ -0,0 +1,137 @@ +use crate::preview2::bindings::sockets::ip_name_lookup::{Host, HostResolveAddressStream}; +use crate::preview2::bindings::sockets::network::{ + Error, ErrorCode, IpAddress, IpAddressFamily, Network, +}; +use crate::preview2::poll::{subscribe, Pollable, Subscribe}; +use crate::preview2::{spawn_blocking, AbortOnDropJoinHandle, WasiView}; +use anyhow::Result; +use std::io; +use std::mem; +use std::net::{SocketAddr, ToSocketAddrs}; +use std::pin::Pin; +use std::vec; +use wasmtime::component::Resource; + +pub enum ResolveAddressStream { + Waiting(AbortOnDropJoinHandle>>), + Done(io::Result>), +} + +#[async_trait::async_trait] +impl Host for T { + fn resolve_addresses( + &mut self, + network: Resource, + name: String, + family: Option, + include_unavailable: bool, + ) -> Result, Error> { + let network = self.table().get_resource(&network)?; + + // `Host::parse` serves us two functions: + // 1. validate the input is not an IP address, + // 2. convert unicode domains to punycode. + let name = match url::Host::parse(&name).map_err(|_| ErrorCode::InvalidName)? { + url::Host::Domain(name) => name, + url::Host::Ipv4(_) => return Err(ErrorCode::InvalidName.into()), + url::Host::Ipv6(_) => return Err(ErrorCode::InvalidName.into()), + }; + + if !network.allow_ip_name_lookup { + return Err(ErrorCode::PermanentResolverFailure.into()); + } + + // ignored for now, should probably have a future PR to actually take + // this into account. This would require invoking `getaddrinfo` directly + // rather than using the standard library to do it for us. + let _ = include_unavailable; + + // For now use the standard library to perform actual resolution through + // the usage of the `ToSocketAddrs` trait. This blocks the current + // thread, so use `spawn_blocking`. Finally note that this is only + // resolving names, not ports, so force the port to be 0. + let task = spawn_blocking(move || -> io::Result> { + let result = (name.as_str(), 0).to_socket_addrs()?; + Ok(result + .filter_map(|addr| { + // In lieu of preventing these addresses from being resolved + // in the first place, filter them out here. + match addr { + SocketAddr::V4(addr) => match family { + None | Some(IpAddressFamily::Ipv4) => { + let [a, b, c, d] = addr.ip().octets(); + Some(IpAddress::Ipv4((a, b, c, d))) + } + Some(IpAddressFamily::Ipv6) => None, + }, + SocketAddr::V6(addr) => match family { + None | Some(IpAddressFamily::Ipv6) => { + let [a, b, c, d, e, f, g, h] = addr.ip().segments(); + Some(IpAddress::Ipv6((a, b, c, d, e, f, g, h))) + } + Some(IpAddressFamily::Ipv4) => None, + }, + } + }) + .collect()) + }); + let resource = self + .table_mut() + .push_resource(ResolveAddressStream::Waiting(task))?; + Ok(resource) + } +} + +#[async_trait::async_trait] +impl HostResolveAddressStream for T { + fn resolve_next_address( + &mut self, + resource: Resource, + ) -> Result, Error> { + let stream = self.table_mut().get_resource_mut(&resource)?; + loop { + match stream { + ResolveAddressStream::Waiting(future) => { + match crate::preview2::poll_noop(Pin::new(future)) { + Some(result) => { + *stream = ResolveAddressStream::Done(result.map(|v| v.into_iter())); + } + None => return Err(ErrorCode::WouldBlock.into()), + } + } + ResolveAddressStream::Done(slot @ Err(_)) => { + // TODO: this `?` is what converts `io::Error` into `Error` + // and the conversion is not great right now. The standard + // library doesn't expose a ton of information through the + // return value of `getaddrinfo` right now so supporting a + // richer conversion here will probably require calling + // `getaddrinfo` directly. + mem::replace(slot, Ok(Vec::new().into_iter()))?; + unreachable!(); + } + ResolveAddressStream::Done(Ok(iter)) => return Ok(iter.next()), + } + } + } + + fn subscribe( + &mut self, + resource: Resource, + ) -> Result> { + subscribe(self.table_mut(), resource) + } + + fn drop(&mut self, resource: Resource) -> Result<()> { + self.table_mut().delete_resource(resource)?; + Ok(()) + } +} + +#[async_trait::async_trait] +impl Subscribe for ResolveAddressStream { + async fn ready(&mut self) { + if let ResolveAddressStream::Waiting(future) = self { + *self = ResolveAddressStream::Done(future.await.map(|v| v.into_iter())); + } + } +} diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index 78b436afe38a..851c0d1a15c4 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -25,6 +25,7 @@ mod ctx; mod error; mod filesystem; mod host; +mod ip_name_lookup; mod network; pub mod pipe; mod poll; @@ -142,6 +143,9 @@ pub mod bindings { "poll-one", ], }, + with: { + "wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream, + }, trappable_error_type: { "wasi:io/streams"::"stream-error": Error, "wasi:filesystem/types"::"error-code": Error, @@ -204,18 +208,21 @@ impl Future for AbortOnDropJoinHandle { } } -pub fn spawn(f: F) -> AbortOnDropJoinHandle +pub fn spawn(f: F) -> AbortOnDropJoinHandle where - F: Future + Send + 'static, - G: Send + 'static, + F: Future + Send + 'static, + F::Output: Send + 'static, { - let j = match tokio::runtime::Handle::try_current() { - Ok(_) => tokio::task::spawn(f), - Err(_) => { - let _enter = RUNTIME.enter(); - tokio::task::spawn(f) - } - }; + let j = with_ambient_tokio_runtime(|| tokio::task::spawn(f)); + AbortOnDropJoinHandle(j) +} + +pub fn spawn_blocking(f: F) -> AbortOnDropJoinHandle +where + F: FnOnce() -> R + Send + 'static, + R: Send + 'static, +{ + let j = with_ambient_tokio_runtime(|| tokio::task::spawn_blocking(f)); AbortOnDropJoinHandle(j) } diff --git a/crates/wasi/src/preview2/network.rs b/crates/wasi/src/preview2/network.rs index f2f7bfd7de6e..614130392d29 100644 --- a/crates/wasi/src/preview2/network.rs +++ b/crates/wasi/src/preview2/network.rs @@ -1,9 +1,6 @@ use cap_std::net::Pool; -pub struct Network(pub(crate) Pool); - -impl Network { - pub fn new(pool: Pool) -> Self { - Self(pool) - } +pub struct Network { + pub pool: Pool, + pub allow_ip_name_lookup: bool, } diff --git a/crates/wasi/wit/test.wit b/crates/wasi/wit/test.wit index 0b6bd28e997d..03073513f8e6 100644 --- a/crates/wasi/wit/test.wit +++ b/crates/wasi/wit/test.wit @@ -39,4 +39,6 @@ world test-command-with-sockets { import wasi:sockets/tcp-create-socket import wasi:sockets/network import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:clocks/monotonic-clock } diff --git a/src/commands/run.rs b/src/commands/run.rs index a75dfdb6c2a1..2af92f6ddf24 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -936,6 +936,9 @@ impl RunCommand { if self.common.wasi.inherit_network == Some(true) { builder.inherit_network(ambient_authority()); } + if let Some(enable) = self.common.wasi.allow_ip_name_lookup { + builder.allow_ip_name_lookup(enable); + } store.data_mut().preview2_ctx = Some(Arc::new(builder.build())); Ok(()) From ef868a7ae71f1c2551760316ecd01284dd824a5d Mon Sep 17 00:00:00 2001 From: Alphyr <47725341+a1phyr@users.noreply.github.com> Date: Wed, 4 Oct 2023 16:35:36 +0200 Subject: [PATCH 054/199] riscv64: Further optimize constants loading (#7139) * Further optimize RISCV constant loading * Simplify existing rule * Fix * Bless missing tests * Use right signedness for Imm20 --- cranelift/codegen/src/isa/riscv64/inst.isle | 42 +- .../codegen/src/isa/riscv64/inst/imms.rs | 12 + .../codegen/src/isa/riscv64/lower/isle.rs | 31 + .../filetests/isa/riscv64/arithmetic.clif | 16 +- .../filetests/isa/riscv64/constants.clif | 58 +- .../filetests/isa/riscv64/issue-6954.clif | 1028 ++++++++--------- .../filetests/isa/riscv64/return-call.clif | 8 +- .../filetests/isa/riscv64/simd-ceil.clif | 8 +- .../filetests/isa/riscv64/simd-floor.clif | 8 +- .../filetests/isa/riscv64/simd-fmax.clif | 26 +- .../filetests/isa/riscv64/simd-fmin.clif | 26 +- .../filetests/isa/riscv64/simd-nearest.clif | 8 +- .../filetests/isa/riscv64/simd-trunc.clif | 8 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 64 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 52 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 86 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 76 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 40 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 40 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 58 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 58 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 48 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 26 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 78 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 56 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 38 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 38 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 56 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 56 +- cranelift/isle/docs/language-reference.md | 2 +- 30 files changed, 1081 insertions(+), 1070 deletions(-) diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 1001ba9453af..5d080578ca13 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -1723,51 +1723,59 @@ (decl i64_generate_imm (Imm20 Imm12) i64) (extern extractor i64_generate_imm i64_generate_imm) +;; Helper for generating a i64 from a shift of a Imm20 constant with LUI +(decl i64_shift_for_lui (u64 Imm12) i64) +(extern extractor i64_shift_for_lui i64_shift_for_lui) + +;; Helper for generating a i64 from a shift of a Imm20 constant +(decl i64_shift (i64 Imm12) i64) +(extern extractor i64_shift i64_shift) ;; Immediate Loading rules ;; TODO: Loading the zero reg directly causes a bunch of regalloc errors, we should look into it. -;; TODO: We can load constants like 0x3ff0000000000000 by loading the bits then doing a shift left. ;; TODO: Load floats using `fld` instead of `ld` (decl imm (Type u64) Reg) ;; Refs get loaded as integers. -(rule 4 (imm $R32 c) (imm $I32 c)) -(rule 4 (imm $R64 c) (imm $I64 c)) +(rule 5 (imm $R32 c) (imm $I32 c)) +(rule 5 (imm $R64 c) (imm $I64 c)) ;; Floats get loaded as integers and then moved into an F register. -(rule 4 (imm $F32 c) (gen_bitcast (imm $I32 c) $I32 $F32)) -(rule 4 (imm $F64 c) (gen_bitcast (imm $I64 c) $I64 $F64)) +(rule 5 (imm $F32 c) (gen_bitcast (imm $I32 c) $I32 $F32)) +(rule 5 (imm $F64 c) (gen_bitcast (imm $I64 c) $I64 $F64)) ;; Try to match just an imm12 -(rule 3 (imm (ty_int ty) c) +(rule 4 (imm (ty_int ty) c) (if-let (i64_generate_imm (imm20_is_zero) imm12) (i64_sextend_u64 ty c)) (rv_addi (zero_reg) imm12)) ;; We can also try to load using a single LUI. ;; LUI takes a 20 bit immediate, places it on bits 13 to 32 of the register. ;; In RV64 this value is then sign extended to 64bits. -(rule 2 (imm (ty_int ty) c) +(rule 3 (imm (ty_int ty) c) (if-let (i64_generate_imm imm20 (imm12_is_zero)) (i64_sextend_u64 ty c)) (rv_lui imm20)) ;; We can combo addi + lui to represent all 32-bit immediates ;; And some 64-bit immediates as well. -(rule 1 (imm (ty_int ty) c) +(rule 2 (imm (ty_int ty) c) (if-let (i64_generate_imm imm20 imm12) (i64_sextend_u64 ty c)) (rv_addi (rv_lui imm20) imm12)) +;; If the non-zero bits of the immediate fit in 20 bits, we can use LUI + shift +(rule 1 (imm (ty_int ty) c) + (if-let (i64_shift_for_lui (imm20_from_u64 base) shift) (i64_sextend_u64 ty c)) + (rv_slli (rv_lui base) shift)) + ;; Combine one of the above rules with a shift-left if possible, This chops off ;; all trailing zeros from the input constant and then attempts if the resulting ;; constant can itself use one of the above rules via the `i64_generate_imm` ;; matcher. This will then recurse on the above rules to materialize a smaller ;; constant which is then shifted left to create the desired constant. (rule 0 (imm (ty_int ty) c) - (if-let c_extended (i64_sextend_u64 ty c)) ;; constant to make - (if-let shamt (i64_ctz c_extended)) ;; number of trailing zeros to chop - (if-let c_shifted (i64_shr c_extended shamt)) ;; smaller constant - (if-let (i64_generate_imm _ _) c_shifted) ;; can the smaller constant be made? - (if-let (imm12_from_i64 shimm) shamt) ;; create the shift immediate - (rv_slli (imm ty (i64_as_u64 c_shifted)) shimm)) + (if-let (i64_shift c_shifted shift) (i64_sextend_u64 ty c)) ;; constant to make + (if-let (i64_generate_imm _ _) c_shifted) ;; can the smaller constant be made? + (rv_slli (imm ty (i64_as_u64 c_shifted)) shift)) ;; Otherwise we fall back to loading the immediate from the constant pool. (rule -1 (imm (ty_int ty) c) @@ -1826,6 +1834,12 @@ (decl pure imm20_is_zero () Imm20) (extern extractor imm20_is_zero imm20_is_zero) +(decl imm20_from_u64 (Imm20) u64) +(extern extractor imm20_from_u64 imm20_from_u64) + +(decl imm20_from_i64 (Imm20) i64) +(extern extractor imm20_from_i64 imm20_from_i64) + ;; Imm5 Extractors diff --git a/cranelift/codegen/src/isa/riscv64/inst/imms.rs b/cranelift/codegen/src/isa/riscv64/inst/imms.rs index 8b6854186193..f1126fd914cb 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/imms.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/imms.rs @@ -74,6 +74,18 @@ pub struct Imm20 { impl Imm20 { pub(crate) const ZERO: Self = Self { bits: 0 }; + pub fn maybe_from_u64(val: u64) -> Option { + Self::maybe_from_i64(val as i64) + } + + pub fn maybe_from_i64(val: i64) -> Option { + if val >= -(0x7_ffff + 1) && val <= 0x7_ffff { + Some(Imm20 { bits: val as u32 }) + } else { + None + } + } + #[inline] pub fn from_i32(bits: i32) -> Self { assert!(bits >= -(0x7_ffff + 1) && bits <= 0x7_ffff); diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs index 40f700e42ac7..51886d28b8ae 100644 --- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs +++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs @@ -228,6 +228,29 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> MInst::generate_imm(imm as u64) } + fn i64_shift_for_lui(&mut self, imm: i64) -> Option<(u64, Imm12)> { + let trailing = imm.trailing_zeros(); + if trailing < 12 { + return None; + } + + let shift = Imm12::from_i16(trailing as i16 - 12); + let base = (imm as u64) >> trailing; + Some((base, shift)) + } + + fn i64_shift(&mut self, imm: i64) -> Option<(i64, Imm12)> { + let trailing = imm.trailing_zeros(); + // We can do without this condition but in this case there is no need to go further + if trailing == 0 { + return None; + } + + let shift = Imm12::from_i16(trailing as i16); + let base = imm >> trailing; + Some((base, shift)) + } + #[inline] fn emit(&mut self, arg0: &MInst) -> Unit { self.lower_ctx.emit(arg0.clone()); @@ -249,6 +272,14 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> } } + #[inline] + fn imm20_from_u64(&mut self, arg0: u64) -> Option { + Imm20::maybe_from_u64(arg0) + } + #[inline] + fn imm20_from_i64(&mut self, arg0: i64) -> Option { + Imm20::maybe_from_i64(arg0) + } #[inline] fn imm20_is_zero(&mut self, imm: Imm20) -> Option<()> { if imm.as_i32() == 0 { diff --git a/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif b/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif index 062a65b5d8a6..a34749817fd3 100644 --- a/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif +++ b/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif @@ -91,8 +91,8 @@ block0(v0: i64, v1: i64): ; VCode: ; block0: ; trap_if int_divz##(a1 eq zero) -; li a4,-1 -; slli a2,a4,63 +; lui a4,1 +; slli a2,a4,51 ; xor a2,a0,a2 ; not a4,a1 ; or a2,a2,a4 @@ -104,8 +104,8 @@ block0(v0: i64, v1: i64): ; block0: ; offset 0x0 ; bnez a1, 8 ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz -; addi a4, zero, -1 -; slli a2, a4, 0x3f +; lui a4, 1 +; slli a2, a4, 0x33 ; xor a2, a0, a2 ; not a4, a1 ; or a2, a2, a4 @@ -752,8 +752,8 @@ block0(v0: i64): ; block0: ; li a2,-1 ; trap_if int_divz##(a2 eq zero) -; li a4,-1 -; slli a1,a4,63 +; lui a4,1 +; slli a1,a4,51 ; xor a3,a0,a1 ; not a4,a2 ; or a1,a3,a4 @@ -766,8 +766,8 @@ block0(v0: i64): ; addi a2, zero, -1 ; bnez a2, 8 ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_divz -; addi a4, zero, -1 -; slli a1, a4, 0x3f +; lui a4, 1 +; slli a1, a4, 0x33 ; xor a3, a0, a1 ; not a4, a2 ; or a1, a3, a4 diff --git a/cranelift/filetests/filetests/isa/riscv64/constants.clif b/cranelift/filetests/filetests/isa/riscv64/constants.clif index 134304fbe233..3a159071e434 100644 --- a/cranelift/filetests/filetests/isa/riscv64/constants.clif +++ b/cranelift/filetests/filetests/isa/riscv64/constants.clif @@ -76,16 +76,14 @@ block0: ; VCode: ; block0: -; lui a0,16 -; addi a2,a0,-1 -; slli a0,a2,16 +; lui a0,65535 +; slli a0,a0,4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; lui a0, 0x10 -; addi a2, a0, -1 -; slli a0, a2, 0x10 +; lui a0, 0xffff +; slli a0, a0, 4 ; ret function %f() -> i64 { @@ -96,16 +94,14 @@ block0: ; VCode: ; block0: -; lui a0,16 -; addi a2,a0,-1 -; slli a0,a2,32 +; lui a0,65535 +; slli a0,a0,20 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; lui a0, 0x10 -; addi a2, a0, -1 -; slli a0, a2, 0x20 +; lui a0, 0xffff +; slli a0, a0, 0x14 ; ret function %f() -> i64 { @@ -116,14 +112,14 @@ block0: ; VCode: ; block0: -; li a0,-1 -; slli a0,a0,48 +; lui a0,65535 +; slli a0,a0,36 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a0, zero, -1 -; slli a0, a0, 0x30 +; lui a0, 0xffff +; slli a0, a0, 0x24 ; ret function %f() -> i64 { @@ -354,15 +350,15 @@ block0: ; VCode: ; block0: -; li a0,1023 -; slli a2,a0,52 +; lui a0,1023 +; slli a2,a0,40 ; fmv.d.x fa0,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a0, zero, 0x3ff -; slli a2, a0, 0x34 +; lui a0, 0x3ff +; slli a2, a0, 0x28 ; fmv.d.x fa0, a2 ; ret @@ -392,18 +388,16 @@ block0: ; VCode: ; block0: -; lui a0,4 -; addi a2,a0,73 -; slli a4,a2,48 -; fmv.d.x fa0,a4 +; lui a0,16457 +; slli a2,a0,36 +; fmv.d.x fa0,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; lui a0, 4 -; addi a2, a0, 0x49 -; slli a4, a2, 0x30 -; fmv.d.x fa0, a4 +; lui a0, 0x4049 +; slli a2, a0, 0x24 +; fmv.d.x fa0, a2 ; ret function %f() -> f32 { @@ -468,15 +462,15 @@ block0: ; VCode: ; block0: -; li a0,-1021 -; slli a2,a0,52 +; lui a0,3075 +; slli a2,a0,40 ; fmv.d.x fa0,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a0, zero, -0x3fd -; slli a2, a0, 0x34 +; lui a0, 0xc03 +; slli a2, a0, 0x28 ; fmv.d.x fa0, a2 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif b/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif index 26699b9e534c..19b7b783276a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif +++ b/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif @@ -129,193 +129,191 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; vle8.v v15,32(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v10,48(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v12,64(fp) #avl=16, #vtype=(e8, m1, ta, ma) +; li a0,0 ; li a2,0 ; li a3,0 ; li a4,0 -; li a0,0 -; sd a4,0(nominal_sp) -; sd a0,8(nominal_sp) -; sd a4,16(nominal_sp) -; sd a0,24(nominal_sp) -; sd a4,32(nominal_sp) -; sd a0,40(nominal_sp) -; sd a4,48(nominal_sp) -; sd a0,56(nominal_sp) -; sd a4,64(nominal_sp) -; sd a0,72(nominal_sp) -; sd a4,80(nominal_sp) -; sd a0,88(nominal_sp) -; sd a4,96(nominal_sp) -; sd a0,104(nominal_sp) -; sd a4,112(nominal_sp) -; sw a3,120(nominal_sp) -; sh a2,124(nominal_sp) -; sd a4,128(nominal_sp) -; sd a0,136(nominal_sp) -; sd a4,144(nominal_sp) -; sd a0,152(nominal_sp) -; sd a4,160(nominal_sp) -; sd a0,168(nominal_sp) -; sd a4,176(nominal_sp) -; sd a0,184(nominal_sp) -; sd a4,192(nominal_sp) -; sd a0,200(nominal_sp) -; sd a4,208(nominal_sp) -; sd a0,216(nominal_sp) -; sd a4,224(nominal_sp) -; sd a0,232(nominal_sp) -; sd a4,240(nominal_sp) -; sw a3,248(nominal_sp) -; sh a2,252(nominal_sp) -; sd a4,256(nominal_sp) -; sd a0,264(nominal_sp) -; sd a4,272(nominal_sp) -; sd a0,280(nominal_sp) -; sd a4,288(nominal_sp) -; sd a0,296(nominal_sp) -; sd a4,304(nominal_sp) -; sd a0,312(nominal_sp) -; sd a4,320(nominal_sp) -; sd a0,328(nominal_sp) -; sd a4,336(nominal_sp) -; sd a0,344(nominal_sp) -; sd a4,352(nominal_sp) -; sd a0,360(nominal_sp) -; sd a4,368(nominal_sp) -; sw a3,376(nominal_sp) -; sh a2,380(nominal_sp) -; zext.w a3,a1 -; select_i16x8 v13,v12,v12##condition=a3 -; zext.w a3,a1 -; select_i16x8 v8,v13,v13##condition=a3 -; zext.w a3,a1 -; select_i16x8 v14,v8,v8##condition=a3 -; vfsqrt.v v12,v11 #avl=2, #vtype=(e64, m1, ta, ma) -; lui a2,1 -; addi a3,a2,-1 -; slli a0,a3,51 -; fmv.d.x fa1,a0 -; vfmv.v.f v13,fa1 #avl=2, #vtype=(e64, m1, ta, ma) -; vmfne.vv v0,v12,v12 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v8,v12,v13,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vfsqrt.v v12,v8 #avl=2, #vtype=(e64, m1, ta, ma) -; lui a2,1 -; addi a3,a2,-1 -; slli a0,a3,51 -; fmv.d.x fa1,a0 -; vfmv.v.f v8,fa1 #avl=2, #vtype=(e64, m1, ta, ma) -; vmfne.vv v0,v12,v12 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v13,v12,v8,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a3,a1 -; select_i16x8 v8,v14,v14##condition=a3 -; zext.w a3,a1 -; select_i16x8 v14,v8,v8##condition=a3 -; zext.w a3,a1 -; select_i16x8 v8,v14,v14##condition=a3 -; zext.w a3,a1 -; select_i16x8 v14,v8,v8##condition=a3 -; zext.w a3,a1 -; select_i16x8 v8,v14,v14##condition=a3 -; zext.w a3,a1 -; select_i16x8 v14,v8,v8##condition=a3 -; zext.w a3,a1 -; select_i16x8 v8,v14,v14##condition=a3 -; zext.w a3,a1 -; select_i16x8 v14,v8,v8##condition=a3 -; add a4,a1,a1 -; zext.w a3,a4 -; select_i16x8 v8,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v8,v8##condition=a3 -; zext.w a3,a4 -; select_i16x8 v8,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v8,v8##condition=a3 -; zext.w a3,a4 -; select_i16x8 v8,v14,v14##condition=a3 -; vmax.vv v12,v15,v15 #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a3,a4 -; select_i16x8 v14,v8,v8##condition=a3 -; load_addr a0,3(nominal_sp) -; addi a0,a0,0 -; andi a2,a0,3 -; slli a1,a2,3 -; andi a2,a0,-4 -; atomic_rmw.i8 and a0,a5,(a2)##t0=a3 offset=a1 -; zext.w a2,a4 -; select_i16x8 v15,v14,v14##condition=a2 -; zext.w a2,a4 -; select_i16x8 v14,v15,v15##condition=a2 -; zext.w a2,a4 -; select_i16x8 v15,v14,v14##condition=a2 -; zext.w a2,a4 -; select_i16x8 v14,v15,v15##condition=a2 -; zext.w a2,a4 -; select_i16x8 v15,v14,v14##condition=a2 -; zext.w a2,a4 -; select_i16x8 v8,v15,v15##condition=a2 -; zext.w a2,a4 -; select_i16x8 v14,v8,v8##condition=a2 -; vse64.v v12,33(nominal_sp) #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; zext.w a3,a4 -; select_i16x8 v15,v14,v14##condition=a3 -; zext.w a3,a4 -; select_i16x8 v14,v15,v15##condition=a3 -; vse8.v v13,0(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v14,16(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v13,32(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v14,48(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v14,64(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v14,80(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v14,96(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; sd a3,0(nominal_sp) +; sd a4,8(nominal_sp) +; sd a3,16(nominal_sp) +; sd a4,24(nominal_sp) +; sd a3,32(nominal_sp) +; sd a4,40(nominal_sp) +; sd a3,48(nominal_sp) +; sd a4,56(nominal_sp) +; sd a3,64(nominal_sp) +; sd a4,72(nominal_sp) +; sd a3,80(nominal_sp) +; sd a4,88(nominal_sp) +; sd a3,96(nominal_sp) +; sd a4,104(nominal_sp) +; sd a3,112(nominal_sp) +; sw a2,120(nominal_sp) +; sh a0,124(nominal_sp) +; sd a3,128(nominal_sp) +; sd a4,136(nominal_sp) +; sd a3,144(nominal_sp) +; sd a4,152(nominal_sp) +; sd a3,160(nominal_sp) +; sd a4,168(nominal_sp) +; sd a3,176(nominal_sp) +; sd a4,184(nominal_sp) +; sd a3,192(nominal_sp) +; sd a4,200(nominal_sp) +; sd a3,208(nominal_sp) +; sd a4,216(nominal_sp) +; sd a3,224(nominal_sp) +; sd a4,232(nominal_sp) +; sd a3,240(nominal_sp) +; sw a2,248(nominal_sp) +; sh a0,252(nominal_sp) +; sd a3,256(nominal_sp) +; sd a4,264(nominal_sp) +; sd a3,272(nominal_sp) +; sd a4,280(nominal_sp) +; sd a3,288(nominal_sp) +; sd a4,296(nominal_sp) +; sd a3,304(nominal_sp) +; sd a4,312(nominal_sp) +; sd a3,320(nominal_sp) +; sd a4,328(nominal_sp) +; sd a3,336(nominal_sp) +; sd a4,344(nominal_sp) +; sd a3,352(nominal_sp) +; sd a4,360(nominal_sp) +; sd a3,368(nominal_sp) +; sw a2,376(nominal_sp) +; sh a0,380(nominal_sp) +; zext.w a2,a1 +; select_i16x8 v13,v12,v12##condition=a2 +; zext.w a2,a1 +; select_i16x8 v14,v13,v13##condition=a2 +; zext.w a2,a1 +; select_i16x8 v12,v14,v14##condition=a2 +; vfsqrt.v v10,v11 #avl=2, #vtype=(e64, m1, ta, ma) +; lui a0,4095 +; slli a2,a0,39 +; fmv.d.x fa4,a2 +; vfmv.v.f v11,fa4 #avl=2, #vtype=(e64, m1, ta, ma) +; vmfne.vv v0,v10,v10 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v13,v10,v11,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vfsqrt.v v10,v13 #avl=2, #vtype=(e64, m1, ta, ma) +; lui a0,4095 +; slli a2,a0,39 +; fmv.d.x fa4,a2 +; vfmv.v.f v13,fa4 #avl=2, #vtype=(e64, m1, ta, ma) +; vmfne.vv v0,v10,v10 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v11,v10,v13,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; zext.w a2,a1 +; select_i16x8 v13,v12,v12##condition=a2 +; zext.w a2,a1 +; select_i16x8 v12,v13,v13##condition=a2 +; zext.w a2,a1 +; select_i16x8 v13,v12,v12##condition=a2 +; zext.w a2,a1 +; select_i16x8 v12,v13,v13##condition=a2 +; zext.w a2,a1 +; select_i16x8 v13,v12,v12##condition=a2 +; zext.w a2,a1 +; select_i16x8 v12,v13,v13##condition=a2 +; zext.w a2,a1 +; select_i16x8 v13,v12,v12##condition=a2 +; zext.w a2,a1 +; select_i16x8 v12,v13,v13##condition=a2 +; add a2,a1,a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; vmax.vv v10,v15,v15 #avl=2, #vtype=(e64, m1, ta, ma) +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; load_addr a3,3(nominal_sp) +; addi a3,a3,0 +; andi a0,a3,3 +; slli a4,a0,3 +; andi a1,a3,-4 +; atomic_rmw.i8 and a0,a5,(a1)##t0=a3 offset=a4 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v14,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v14,v14##condition=a1 +; vse64.v v10,33(nominal_sp) #avl=2, #vtype=(e64, m1, ta, ma) +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; zext.w a1,a2 +; select_i16x8 v13,v12,v12##condition=a1 +; zext.w a1,a2 +; select_i16x8 v12,v13,v13##condition=a1 +; vse8.v v11,0(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v12,16(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v11,32(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v12,48(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v12,64(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v12,80(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v12,96(a6) #avl=16, #vtype=(e8, m1, ta, ma) ; add sp,+384 ; ld ra,8(sp) ; ld fp,0(sp) @@ -339,365 +337,363 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; .byte 0x07, 0x85, 0x0f, 0x02 ; addi t6, s0, 0x40 ; .byte 0x07, 0x86, 0x0f, 0x02 +; mv a0, zero ; mv a2, zero ; mv a3, zero ; mv a4, zero -; mv a0, zero -; sd a4, 0(sp) -; sd a0, 8(sp) -; sd a4, 0x10(sp) -; sd a0, 0x18(sp) -; sd a4, 0x20(sp) -; sd a0, 0x28(sp) -; sd a4, 0x30(sp) -; sd a0, 0x38(sp) -; sd a4, 0x40(sp) -; sd a0, 0x48(sp) -; sd a4, 0x50(sp) -; sd a0, 0x58(sp) -; sd a4, 0x60(sp) -; sd a0, 0x68(sp) -; sd a4, 0x70(sp) -; sw a3, 0x78(sp) -; sh a2, 0x7c(sp) -; sd a4, 0x80(sp) -; sd a0, 0x88(sp) -; sd a4, 0x90(sp) -; sd a0, 0x98(sp) -; sd a4, 0xa0(sp) -; sd a0, 0xa8(sp) -; sd a4, 0xb0(sp) -; sd a0, 0xb8(sp) -; sd a4, 0xc0(sp) -; sd a0, 0xc8(sp) -; sd a4, 0xd0(sp) -; sd a0, 0xd8(sp) -; sd a4, 0xe0(sp) -; sd a0, 0xe8(sp) -; sd a4, 0xf0(sp) -; sw a3, 0xf8(sp) -; sh a2, 0xfc(sp) -; sd a4, 0x100(sp) -; sd a0, 0x108(sp) -; sd a4, 0x110(sp) -; sd a0, 0x118(sp) -; sd a4, 0x120(sp) -; sd a0, 0x128(sp) -; sd a4, 0x130(sp) -; sd a0, 0x138(sp) -; sd a4, 0x140(sp) -; sd a0, 0x148(sp) -; sd a4, 0x150(sp) -; sd a0, 0x158(sp) -; sd a4, 0x160(sp) -; sd a0, 0x168(sp) -; sd a4, 0x170(sp) -; sw a3, 0x178(sp) -; sh a2, 0x17c(sp) -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x34, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x34, 0xd0, 0x9e -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0x80, 0x9e -; j 8 -; .byte 0x57, 0x37, 0x80, 0x9e +; sd a3, 0(sp) +; sd a4, 8(sp) +; sd a3, 0x10(sp) +; sd a4, 0x18(sp) +; sd a3, 0x20(sp) +; sd a4, 0x28(sp) +; sd a3, 0x30(sp) +; sd a4, 0x38(sp) +; sd a3, 0x40(sp) +; sd a4, 0x48(sp) +; sd a3, 0x50(sp) +; sd a4, 0x58(sp) +; sd a3, 0x60(sp) +; sd a4, 0x68(sp) +; sd a3, 0x70(sp) +; sw a2, 0x78(sp) +; sh a0, 0x7c(sp) +; sd a3, 0x80(sp) +; sd a4, 0x88(sp) +; sd a3, 0x90(sp) +; sd a4, 0x98(sp) +; sd a3, 0xa0(sp) +; sd a4, 0xa8(sp) +; sd a3, 0xb0(sp) +; sd a4, 0xb8(sp) +; sd a3, 0xc0(sp) +; sd a4, 0xc8(sp) +; sd a3, 0xd0(sp) +; sd a4, 0xd8(sp) +; sd a3, 0xe0(sp) +; sd a4, 0xe8(sp) +; sd a3, 0xf0(sp) +; sw a2, 0xf8(sp) +; sh a0, 0xfc(sp) +; sd a3, 0x100(sp) +; sd a4, 0x108(sp) +; sd a3, 0x110(sp) +; sd a4, 0x118(sp) +; sd a3, 0x120(sp) +; sd a4, 0x128(sp) +; sd a3, 0x130(sp) +; sd a4, 0x138(sp) +; sd a3, 0x140(sp) +; sd a4, 0x148(sp) +; sd a3, 0x150(sp) +; sd a4, 0x158(sp) +; sd a3, 0x160(sp) +; sd a4, 0x168(sp) +; sd a3, 0x170(sp) +; sw a2, 0x178(sp) +; sh a0, 0x17c(sp) +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0x57, 0x37, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x37, 0xd0, 0x9e +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0x57, 0x36, 0xe0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xe0, 0x9e ; .byte 0x57, 0x70, 0x81, 0xcd -; .byte 0x57, 0x16, 0xb0, 0x4e -; lui a2, 1 -; addi a3, a2, -1 -; slli a0, a3, 0x33 -; fmv.d.x fa1, a0 -; .byte 0xd7, 0xd6, 0x05, 0x5e -; .byte 0x57, 0x10, 0xc6, 0x72 -; .byte 0x57, 0x84, 0xc6, 0x5c -; .byte 0x57, 0x16, 0x80, 0x4e -; lui a2, 1 -; addi a3, a2, -1 -; slli a0, a3, 0x33 -; fmv.d.x fa1, a0 -; .byte 0x57, 0xd4, 0x05, 0x5e -; .byte 0x57, 0x10, 0xc6, 0x72 -; .byte 0xd7, 0x06, 0xc4, 0x5c -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x34, 0xe0, 0x9e -; j 8 -; .byte 0x57, 0x34, 0xe0, 0x9e -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0x80, 0x9e -; j 8 -; .byte 0x57, 0x37, 0x80, 0x9e -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x34, 0xe0, 0x9e -; j 8 -; .byte 0x57, 0x34, 0xe0, 0x9e -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0x80, 0x9e -; j 8 -; .byte 0x57, 0x37, 0x80, 0x9e -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x34, 0xe0, 0x9e -; j 8 -; .byte 0x57, 0x34, 0xe0, 0x9e -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0x80, 0x9e -; j 8 -; .byte 0x57, 0x37, 0x80, 0x9e -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x34, 0xe0, 0x9e -; j 8 -; .byte 0x57, 0x34, 0xe0, 0x9e -; .byte 0xbb, 0x86, 0x05, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0x80, 0x9e -; j 8 -; .byte 0x57, 0x37, 0x80, 0x9e -; add a4, a1, a1 -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x34, 0xe0, 0x9e -; j 8 -; .byte 0x57, 0x34, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0x80, 0x9e -; j 8 -; .byte 0x57, 0x37, 0x80, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x34, 0xe0, 0x9e -; j 8 -; .byte 0x57, 0x34, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0x80, 0x9e -; j 8 -; .byte 0x57, 0x37, 0x80, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x34, 0xe0, 0x9e -; j 8 -; .byte 0x57, 0x34, 0xe0, 0x9e -; .byte 0x57, 0x86, 0xf7, 0x1e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0x80, 0x9e -; j 8 -; .byte 0x57, 0x37, 0x80, 0x9e -; addi a0, sp, 3 -; mv a0, a0 -; andi a2, a0, 3 -; slli a1, a2, 3 -; andi a2, a0, -4 -; lr.w.aqrl a0, (a2) -; srl a0, a0, a1 +; .byte 0x57, 0x15, 0xb0, 0x4e +; lui a0, 0xfff +; slli a2, a0, 0x27 +; fmv.d.x fa4, a2 +; .byte 0xd7, 0x55, 0x07, 0x5e +; .byte 0x57, 0x10, 0xa5, 0x72 +; .byte 0xd7, 0x86, 0xa5, 0x5c +; .byte 0x57, 0x15, 0xd0, 0x4e +; lui a0, 0xfff +; slli a2, a0, 0x27 +; fmv.d.x fa4, a2 +; .byte 0xd7, 0x56, 0x07, 0x5e +; .byte 0x57, 0x10, 0xa5, 0x72 +; .byte 0xd7, 0x85, 0xa6, 0x5c +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0x3b, 0x86, 0x05, 0x08 +; beqz a2, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; add a2, a1, a1 +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0x57, 0x85, 0xf7, 0x1e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; addi a3, sp, 3 +; mv a3, a3 +; andi a0, a3, 3 +; slli a4, a0, 3 +; andi a1, a3, -4 +; lr.w.aqrl a0, (a1) +; srl a0, a0, a4 ; andi a0, a0, 0xff ; and a3, a0, a5 -; lr.w.aqrl t5, (a2) +; lr.w.aqrl t5, (a1) ; addi t6, zero, 0xff -; sll t6, t6, a1 +; sll t6, t6, a4 ; not t6, t6 ; and t5, t5, t6 ; andi t6, a3, 0xff -; sll t6, t6, a1 +; sll t6, t6, a4 ; or t5, t5, t6 -; sc.w.aqrl a3, t5, (a2) +; sc.w.aqrl a3, t5, (a1) ; bnez a3, -0x34 -; .byte 0x3b, 0x06, 0x07, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e ; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0x3b, 0x06, 0x07, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e ; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0x3b, 0x06, 0x07, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e ; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0x3b, 0x06, 0x07, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e ; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0x3b, 0x06, 0x07, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e ; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0x3b, 0x06, 0x07, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x34, 0xf0, 0x9e +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x37, 0xd0, 0x9e ; j 8 -; .byte 0x57, 0x34, 0xf0, 0x9e -; .byte 0x3b, 0x06, 0x07, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x37, 0x80, 0x9e +; .byte 0x57, 0x37, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xe0, 0x9e ; j 8 -; .byte 0x57, 0x37, 0x80, 0x9e +; .byte 0x57, 0x36, 0xe0, 0x9e ; addi t6, sp, 0x21 -; .byte 0x27, 0xf6, 0x0f, 0x02 -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0xd7, 0x37, 0xe0, 0x9e -; j 8 -; .byte 0xd7, 0x37, 0xe0, 0x9e -; .byte 0xbb, 0x06, 0x07, 0x08 -; beqz a3, 0xc -; .byte 0x57, 0x37, 0xf0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xf0, 0x9e +; .byte 0x27, 0xf5, 0x0f, 0x02 +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e +; .byte 0xbb, 0x05, 0x06, 0x08 +; beqz a1, 0xc +; .byte 0x57, 0x36, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x36, 0xd0, 0x9e ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0xa7, 0x06, 0x08, 0x02 +; .byte 0xa7, 0x05, 0x08, 0x02 ; addi t6, a6, 0x10 -; .byte 0x27, 0x87, 0x0f, 0x02 +; .byte 0x27, 0x86, 0x0f, 0x02 ; addi t6, a6, 0x20 -; .byte 0xa7, 0x86, 0x0f, 0x02 +; .byte 0xa7, 0x85, 0x0f, 0x02 ; addi t6, a6, 0x30 -; .byte 0x27, 0x87, 0x0f, 0x02 +; .byte 0x27, 0x86, 0x0f, 0x02 ; addi t6, a6, 0x40 -; .byte 0x27, 0x87, 0x0f, 0x02 +; .byte 0x27, 0x86, 0x0f, 0x02 ; addi t6, a6, 0x50 -; .byte 0x27, 0x87, 0x0f, 0x02 +; .byte 0x27, 0x86, 0x0f, 0x02 ; addi t6, a6, 0x60 -; .byte 0x27, 0x87, 0x0f, 0x02 +; .byte 0x27, 0x86, 0x0f, 0x02 ; addi sp, sp, 0x180 ; ld ra, 8(sp) ; ld s0, 0(sp) diff --git a/cranelift/filetests/filetests/isa/riscv64/return-call.clif b/cranelift/filetests/filetests/isa/riscv64/return-call.clif index 4bd0b8b51dba..ae7d8951717f 100644 --- a/cranelift/filetests/filetests/isa/riscv64/return-call.clif +++ b/cranelift/filetests/filetests/isa/riscv64/return-call.clif @@ -96,16 +96,16 @@ block0(v0: f64): ; VCode: ; block0: -; li a3,1027 -; slli a5,a3,52 +; lui a3,1027 +; slli a5,a3,40 ; fmv.d.x fa1,a5 ; fadd.d ft0,ft0,fa1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x403 -; slli a5, a3, 0x34 +; lui a3, 0x403 +; slli a5, a3, 0x28 ; fmv.d.x fa1, a5 ; fadd.d ft0, ft0, fa1 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-ceil.clif b/cranelift/filetests/filetests/isa/riscv64/simd-ceil.clif index d670a1f7a7b4..41b2b9c902c0 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-ceil.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-ceil.clif @@ -78,8 +78,8 @@ block0(v0: f64x2): ; block0: ; vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vfabs.v v12,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; li a1,1075 -; slli a2,a1,52 +; lui a1,1075 +; slli a2,a1,40 ; fmv.d.x fa4,a2 ; vmflt.vf v0,v12,fa4 #avl=2, #vtype=(e64, m1, ta, ma) ; fsrmi a2,3 @@ -108,8 +108,8 @@ block0(v0: f64x2): ; .byte 0x87, 0x84, 0x0f, 0x02 ; .byte 0x57, 0x70, 0x81, 0xcd ; .byte 0x57, 0x96, 0x94, 0x2a -; addi a1, zero, 0x433 -; slli a2, a1, 0x34 +; lui a1, 0x433 +; slli a2, a1, 0x28 ; fmv.d.x fa4, a2 ; .byte 0x57, 0x50, 0xc7, 0x6e ; fsrmi a2, 3 diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-floor.clif b/cranelift/filetests/filetests/isa/riscv64/simd-floor.clif index 428074cfef9a..50b9cbde183a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-floor.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-floor.clif @@ -78,8 +78,8 @@ block0(v0: f64x2): ; block0: ; vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vfabs.v v12,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; li a1,1075 -; slli a2,a1,52 +; lui a1,1075 +; slli a2,a1,40 ; fmv.d.x fa4,a2 ; vmflt.vf v0,v12,fa4 #avl=2, #vtype=(e64, m1, ta, ma) ; fsrmi a2,2 @@ -108,8 +108,8 @@ block0(v0: f64x2): ; .byte 0x87, 0x84, 0x0f, 0x02 ; .byte 0x57, 0x70, 0x81, 0xcd ; .byte 0x57, 0x96, 0x94, 0x2a -; addi a1, zero, 0x433 -; slli a2, a1, 0x34 +; lui a1, 0x433 +; slli a2, a1, 0x28 ; fmv.d.x fa4, a2 ; .byte 0x57, 0x50, 0xc7, 0x6e ; fsrmi a2, 2 diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-fmax.clif b/cranelift/filetests/filetests/isa/riscv64/simd-fmax.clif index 59b26d22f560..5c141df2a94c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-fmax.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-fmax.clif @@ -19,13 +19,12 @@ block0(v0: f64x2, v1: f64x2): ; vmfeq.vv v14,v9,v9 #avl=2, #vtype=(e64, m1, ta, ma) ; vmfeq.vv v8,v11,v11 #avl=2, #vtype=(e64, m1, ta, ma) ; vmand.mm v0,v14,v8 #avl=2, #vtype=(e64, m1, ta, ma) -; lui a1,1 -; addi a2,a1,-1 -; slli a4,a2,51 -; vmv.v.x v10,a4 #avl=2, #vtype=(e64, m1, ta, ma) -; vfmax.vv v12,v9,v11 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v14,v10,v12,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vse8.v v14,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) +; lui a1,4095 +; slli a2,a1,39 +; vmv.v.x v8,a2 #avl=2, #vtype=(e64, m1, ta, ma) +; vfmax.vv v10,v9,v11 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v12,v8,v10,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vse8.v v12,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -47,14 +46,13 @@ block0(v0: f64x2, v1: f64x2): ; .byte 0x57, 0x97, 0x94, 0x62 ; .byte 0x57, 0x94, 0xb5, 0x62 ; .byte 0x57, 0x20, 0xe4, 0x66 -; lui a1, 1 -; addi a2, a1, -1 -; slli a4, a2, 0x33 -; .byte 0x57, 0x45, 0x07, 0x5e -; .byte 0x57, 0x96, 0x95, 0x1a -; .byte 0x57, 0x07, 0xa6, 0x5c +; lui a1, 0xfff +; slli a2, a1, 0x27 +; .byte 0x57, 0x44, 0x06, 0x5e +; .byte 0x57, 0x95, 0x95, 0x1a +; .byte 0x57, 0x06, 0x85, 0x5c ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0x27, 0x07, 0x05, 0x02 +; .byte 0x27, 0x06, 0x05, 0x02 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-fmin.clif b/cranelift/filetests/filetests/isa/riscv64/simd-fmin.clif index bfc30b597565..94bef3ee07e9 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-fmin.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-fmin.clif @@ -19,13 +19,12 @@ block0(v0: f64x2, v1: f64x2): ; vmfeq.vv v14,v9,v9 #avl=2, #vtype=(e64, m1, ta, ma) ; vmfeq.vv v8,v11,v11 #avl=2, #vtype=(e64, m1, ta, ma) ; vmand.mm v0,v14,v8 #avl=2, #vtype=(e64, m1, ta, ma) -; lui a1,1 -; addi a2,a1,-1 -; slli a4,a2,51 -; vmv.v.x v10,a4 #avl=2, #vtype=(e64, m1, ta, ma) -; vfmin.vv v12,v9,v11 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v14,v10,v12,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vse8.v v14,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) +; lui a1,4095 +; slli a2,a1,39 +; vmv.v.x v8,a2 #avl=2, #vtype=(e64, m1, ta, ma) +; vfmin.vv v10,v9,v11 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v12,v8,v10,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vse8.v v12,0(a0) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -47,14 +46,13 @@ block0(v0: f64x2, v1: f64x2): ; .byte 0x57, 0x97, 0x94, 0x62 ; .byte 0x57, 0x94, 0xb5, 0x62 ; .byte 0x57, 0x20, 0xe4, 0x66 -; lui a1, 1 -; addi a2, a1, -1 -; slli a4, a2, 0x33 -; .byte 0x57, 0x45, 0x07, 0x5e -; .byte 0x57, 0x96, 0x95, 0x12 -; .byte 0x57, 0x07, 0xa6, 0x5c +; lui a1, 0xfff +; slli a2, a1, 0x27 +; .byte 0x57, 0x44, 0x06, 0x5e +; .byte 0x57, 0x95, 0x95, 0x12 +; .byte 0x57, 0x06, 0x85, 0x5c ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0x27, 0x07, 0x05, 0x02 +; .byte 0x27, 0x06, 0x05, 0x02 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-nearest.clif b/cranelift/filetests/filetests/isa/riscv64/simd-nearest.clif index 91c73441220d..7cd455b63bca 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-nearest.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-nearest.clif @@ -78,8 +78,8 @@ block0(v0: f64x2): ; block0: ; vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vfabs.v v12,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; li a1,1075 -; slli a2,a1,52 +; lui a1,1075 +; slli a2,a1,40 ; fmv.d.x fa4,a2 ; vmflt.vf v0,v12,fa4 #avl=2, #vtype=(e64, m1, ta, ma) ; fsrmi a2,0 @@ -108,8 +108,8 @@ block0(v0: f64x2): ; .byte 0x87, 0x84, 0x0f, 0x02 ; .byte 0x57, 0x70, 0x81, 0xcd ; .byte 0x57, 0x96, 0x94, 0x2a -; addi a1, zero, 0x433 -; slli a2, a1, 0x34 +; lui a1, 0x433 +; slli a2, a1, 0x28 ; fmv.d.x fa4, a2 ; .byte 0x57, 0x50, 0xc7, 0x6e ; fsrmi a2, 0 diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-trunc.clif b/cranelift/filetests/filetests/isa/riscv64/simd-trunc.clif index c01191a000dd..723c07e55de3 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-trunc.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-trunc.clif @@ -74,8 +74,8 @@ block0(v0: f64x2): ; block0: ; vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vfabs.v v12,v9 #avl=2, #vtype=(e64, m1, ta, ma) -; li a1,1075 -; slli a2,a1,52 +; lui a1,1075 +; slli a2,a1,40 ; fmv.d.x fa4,a2 ; vmflt.vf v0,v12,fa4 #avl=2, #vtype=(e64, m1, ta, ma) ; vfcvt.rtz.x.f.v v14,v9 #avl=2, #vtype=(e64, m1, ta, ma) @@ -102,8 +102,8 @@ block0(v0: f64x2): ; .byte 0x87, 0x84, 0x0f, 0x02 ; .byte 0x57, 0x70, 0x81, 0xcd ; .byte 0x57, 0x96, 0x94, 0x2a -; addi a1, zero, 0x433 -; slli a2, a1, 0x34 +; lui a1, 0x433 +; slli a2, a1, 0x28 ; fmv.d.x fa4, a2 ; .byte 0x57, 0x50, 0xc7, 0x6e ; .byte 0x57, 0x97, 0x93, 0x4a diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index 38145b79f095..e99ca727f9b8 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,24 +41,23 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 +;; slli a3,a0,32 +;; srli a5,a3,32 ;; lui a3,262140 -;; addi a5,a3,1 -;; slli a3,a5,2 -;; add a5,a0,a3 -;; trap_if heap_oob##(a5 ult a0) -;; ld a3,8(a2) -;; ugt a3,a5,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; addi a4,a3,1 +;; slli a0,a4,2 +;; add a4,a5,a0 +;; trap_if heap_oob##(a4 ult a5) +;; ld a0,8(a2) +;; ugt a0,a4,a0##ty=i64 +;; bne a0,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a2,a2,a0 -;; lui a5,16 -;; addi a3,a5,-1 -;; slli a3,a3,16 -;; add a2,a2,a3 -;; sw a1,0(a2) +;; ld a0,0(a2) +;; add a0,a0,a5 +;; lui a5,65535 +;; slli a2,a5,4 +;; add a0,a0,a2 +;; sw a1,0(a0) ;; j label2 ;; block2: ;; ret @@ -67,24 +66,23 @@ ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; lui a3,262140 -;; addi a5,a3,1 -;; slli a2,a5,2 -;; add a5,a0,a2 -;; trap_if heap_oob##(a5 ult a0) -;; ld a2,8(a1) -;; ugt a2,a5,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; slli a3,a0,32 +;; srli a5,a3,32 +;; lui a2,262140 +;; addi a4,a2,1 +;; slli a0,a4,2 +;; add a4,a5,a0 +;; trap_if heap_oob##(a4 ult a5) +;; ld a0,8(a1) +;; ugt a0,a4,a0##ty=i64 +;; bne a0,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a1,a1,a0 -;; lui a5,16 -;; addi a2,a5,-1 -;; slli a3,a2,16 -;; add a1,a1,a3 -;; lw a0,0(a1) +;; ld a0,0(a1) +;; add a0,a0,a5 +;; lui a5,65535 +;; slli a1,a5,4 +;; add a0,a0,a1 +;; lw a0,0(a0) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index 408082d57c2d..15ca642762a1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -42,21 +42,20 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,[const(0)] -;; add a3,a4,a3 -;; trap_if heap_oob##(a3 ult a4) +;; srli a3,a3,32 +;; ld a4,[const(0)] +;; add a4,a3,a4 +;; trap_if heap_oob##(a4 ult a3) ;; ld a5,8(a2) -;; ugt a5,a3,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; ugt a4,a4,a5##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a5,a5,a4 -;; lui a3,16 -;; addi a0,a3,-1 -;; slli a2,a0,16 -;; add a5,a5,a2 -;; sb a1,0(a5) +;; ld a4,0(a2) +;; add a4,a4,a3 +;; lui a3,65535 +;; slli a5,a3,4 +;; add a4,a4,a5 +;; sb a1,0(a4) ;; j label2 ;; block2: ;; ret @@ -66,21 +65,20 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,[const(0)] -;; add a3,a4,a3 -;; trap_if heap_oob##(a3 ult a4) -;; ld a5,8(a1) -;; ugt a5,a3,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; srli a3,a2,32 +;; ld a2,[const(0)] +;; add a2,a3,a2 +;; trap_if heap_oob##(a2 ult a3) +;; ld a4,8(a1) +;; ugt a4,a2,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a5,a5,a4 -;; lui a3,16 -;; addi a0,a3,-1 -;; slli a1,a0,16 -;; add a5,a5,a1 -;; lbu a0,0(a5) +;; ld a4,0(a1) +;; add a4,a4,a3 +;; lui a3,65535 +;; slli a5,a3,4 +;; add a4,a4,a5 +;; lbu a0,0(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index d700b9af1c0b..8e2d5c2cc84a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,58 +41,56 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; lui a4,262140 -;; addi a0,a4,1 -;; slli a4,a0,2 -;; add a0,a3,a4 -;; trap_if heap_oob##(a0 ult a3) -;; ld a4,8(a2) -;; ugt a4,a0,a4##ty=i64 +;; slli a4,a0,32 +;; srli a0,a4,32 +;; lui a3,262140 +;; addi a5,a3,1 +;; slli a3,a5,2 +;; add a5,a0,a3 +;; trap_if heap_oob##(a5 ult a0) +;; ld a3,8(a2) +;; ugt a3,a5,a3##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a3 -;; lui a5,16 -;; addi a3,a5,-1 -;; slli a3,a3,16 -;; add a2,a2,a3 -;; li a3,0 -;; sltu a4,zero,a4 -;; sub a4,zero,a4 -;; and a0,a3,a4 -;; not a3,a4 -;; and a4,a2,a3 -;; or a0,a0,a4 -;; sw a1,0(a0) +;; add a0,a2,a0 +;; lui a5,65535 +;; slli a2,a5,4 +;; add a0,a0,a2 +;; li a2,0 +;; sltu a3,zero,a3 +;; sub a3,zero,a3 +;; and a5,a2,a3 +;; not a2,a3 +;; and a3,a0,a2 +;; or a5,a5,a3 +;; sw a1,0(a5) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; lui a4,262140 -;; addi a0,a4,1 -;; slli a3,a0,2 -;; add a0,a2,a3 -;; trap_if heap_oob##(a0 ult a2) -;; ld a3,8(a1) -;; ugt a3,a0,a3##ty=i64 +;; slli a4,a0,32 +;; srli a0,a4,32 +;; lui a3,262140 +;; addi a5,a3,1 +;; slli a2,a5,2 +;; add a5,a0,a2 +;; trap_if heap_oob##(a5 ult a0) +;; ld a2,8(a1) +;; ugt a2,a5,a2##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a2 -;; lui a5,16 -;; addi a2,a5,-1 -;; slli a4,a2,16 -;; add a1,a1,a4 -;; li a2,0 -;; sltu a3,zero,a3 -;; sub a4,zero,a3 -;; and a0,a2,a4 -;; not a2,a4 -;; and a4,a1,a2 -;; or a0,a0,a4 -;; lw a0,0(a0) +;; add a0,a1,a0 +;; lui a5,65535 +;; slli a1,a5,4 +;; add a0,a0,a1 +;; li a1,0 +;; sltu a2,zero,a2 +;; sub a3,zero,a2 +;; and a5,a1,a3 +;; not a1,a3 +;; and a3,a0,a1 +;; or a5,a5,a3 +;; lw a0,0(a5) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 8d7d44804966..bc39f65d29da 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -42,53 +42,51 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a0,a3,32 -;; ld a4,[const(0)] -;; add a4,a0,a4 -;; trap_if heap_oob##(a4 ult a0) +;; srli a4,a3,32 +;; ld a3,[const(0)] +;; add a3,a4,a3 +;; trap_if heap_oob##(a3 ult a4) ;; ld a5,8(a2) -;; ugt a5,a4,a5##ty=i64 -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a3,16 -;; addi a2,a3,-1 -;; slli a2,a2,16 -;; add a0,a0,a2 -;; li a2,0 -;; sltu a3,zero,a5 -;; sub a3,zero,a3 -;; and a4,a2,a3 -;; not a2,a3 -;; and a2,a0,a2 -;; or a4,a4,a2 -;; sb a1,0(a4) +;; ugt a5,a3,a5##ty=i64 +;; ld a0,0(a2) +;; add a4,a0,a4 +;; lui a3,65535 +;; slli a0,a3,4 +;; add a4,a4,a0 +;; li a0,0 +;; sltu a5,zero,a5 +;; sub a2,zero,a5 +;; and a3,a0,a2 +;; not a5,a2 +;; and a2,a4,a5 +;; or a3,a3,a2 +;; sb a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a0,a3,32 -;; ld a4,[const(0)] -;; add a4,a0,a4 -;; trap_if heap_oob##(a4 ult a0) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,[const(0)] +;; add a3,a4,a3 +;; trap_if heap_oob##(a3 ult a4) ;; ld a5,8(a1) -;; ugt a5,a4,a5##ty=i64 -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a3,16 -;; addi a1,a3,-1 -;; slli a1,a1,16 -;; add a0,a0,a1 -;; li a1,0 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a4,a1,a2 -;; not a1,a2 -;; and a2,a0,a1 -;; or a4,a4,a2 -;; lbu a0,0(a4) +;; ugt a5,a3,a5##ty=i64 +;; ld a0,0(a1) +;; add a4,a0,a4 +;; lui a3,65535 +;; slli a0,a3,4 +;; add a4,a4,a0 +;; li a0,0 +;; sltu a5,zero,a5 +;; sub a1,zero,a5 +;; and a3,a0,a1 +;; not a5,a1 +;; and a1,a4,a5 +;; or a3,a3,a1 +;; lbu a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index cc76ff36e440..a9fbcf7ce77e 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,18 +41,17 @@ ;; function u0:0: ;; block0: -;; slli a0,a0,32 -;; srli a3,a0,32 -;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; slli a5,a0,32 +;; srli a3,a5,32 +;; ld a0,8(a2) +;; ugt a0,a3,a0##ty=i64 +;; bne a0,zero,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) ;; add a2,a2,a3 -;; lui a0,16 -;; addi a3,a0,-1 -;; slli a4,a3,16 -;; add a2,a2,a4 +;; lui a0,65535 +;; slli a3,a0,4 +;; add a2,a2,a3 ;; sw a1,0(a2) ;; j label2 ;; block2: @@ -62,19 +61,18 @@ ;; ;; function u0:1: ;; block0: -;; slli a0,a0,32 -;; srli a2,a0,32 -;; ld a3,8(a1) -;; ugt a3,a2,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; slli a5,a0,32 +;; srli a2,a5,32 +;; ld a0,8(a1) +;; ugt a0,a2,a0##ty=i64 +;; bne a0,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a2,a3,a2 -;; lui a0,16 -;; addi a3,a0,-1 -;; slli a4,a3,16 -;; add a2,a2,a4 -;; lw a0,0(a2) +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lui a0,65535 +;; slli a2,a0,4 +;; add a1,a1,a2 +;; lw a0,0(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index 3ba5874a87e5..b34ee036abbd 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,18 +41,17 @@ ;; function u0:0: ;; block0: -;; slli a0,a0,32 -;; srli a3,a0,32 -;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; slli a5,a0,32 +;; srli a3,a5,32 +;; ld a0,8(a2) +;; ugt a0,a3,a0##ty=i64 +;; bne a0,zero,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) ;; add a2,a2,a3 -;; lui a0,16 -;; addi a3,a0,-1 -;; slli a4,a3,16 -;; add a2,a2,a4 +;; lui a0,65535 +;; slli a3,a0,4 +;; add a2,a2,a3 ;; sb a1,0(a2) ;; j label2 ;; block2: @@ -62,19 +61,18 @@ ;; ;; function u0:1: ;; block0: -;; slli a0,a0,32 -;; srli a2,a0,32 -;; ld a3,8(a1) -;; ugt a3,a2,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; slli a5,a0,32 +;; srli a2,a5,32 +;; ld a0,8(a1) +;; ugt a0,a2,a0##ty=i64 +;; bne a0,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a2,a3,a2 -;; lui a0,16 -;; addi a3,a0,-1 -;; slli a4,a3,16 -;; add a2,a2,a4 -;; lbu a0,0(a2) +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lui a0,65535 +;; slli a2,a0,4 +;; add a1,a1,a2 +;; lbu a0,0(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 224265ada872..6ed2f33eb810 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,48 +41,46 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a3,a3,32 +;; slli a0,a0,32 +;; srli a3,a0,32 ;; ld a4,8(a2) ;; ugt a4,a3,a4##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a3 -;; lui a0,16 -;; addi a3,a0,-1 -;; slli a5,a3,16 -;; add a3,a2,a5 +;; lui a0,65535 +;; slli a3,a0,4 +;; add a3,a2,a3 ;; li a2,0 ;; sltu a4,zero,a4 -;; sub a5,zero,a4 -;; and a2,a2,a5 -;; not a4,a5 -;; and a5,a3,a4 -;; or a2,a2,a5 -;; sw a1,0(a2) +;; sub a4,zero,a4 +;; and a0,a2,a4 +;; not a2,a4 +;; and a4,a3,a2 +;; or a0,a0,a4 +;; sw a1,0(a0) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a3,a2,32 -;; ld a2,8(a1) -;; ugt a2,a3,a2##ty=i64 -;; ld a4,0(a1) -;; add a3,a4,a3 -;; lui a0,16 -;; addi a4,a0,-1 -;; slli a4,a4,16 -;; add a3,a3,a4 -;; li a4,0 -;; sltu a5,zero,a2 -;; sub a5,zero,a5 -;; and a1,a4,a5 -;; not a4,a5 -;; and a5,a3,a4 -;; or a1,a1,a5 -;; lw a0,0(a1) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a3,8(a1) +;; ugt a3,a2,a3##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lui a0,65535 +;; slli a2,a0,4 +;; add a2,a1,a2 +;; li a1,0 +;; sltu a3,zero,a3 +;; sub a4,zero,a3 +;; and a0,a1,a4 +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lw a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index fdad050d73ef..579c3e1c7312 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -41,48 +41,46 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a3,a3,32 +;; slli a0,a0,32 +;; srli a3,a0,32 ;; ld a4,8(a2) ;; ugt a4,a3,a4##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a3 -;; lui a0,16 -;; addi a3,a0,-1 -;; slli a5,a3,16 -;; add a3,a2,a5 +;; lui a0,65535 +;; slli a3,a0,4 +;; add a3,a2,a3 ;; li a2,0 ;; sltu a4,zero,a4 -;; sub a5,zero,a4 -;; and a2,a2,a5 -;; not a4,a5 -;; and a5,a3,a4 -;; or a2,a2,a5 -;; sb a1,0(a2) +;; sub a4,zero,a4 +;; and a0,a2,a4 +;; not a2,a4 +;; and a4,a3,a2 +;; or a0,a0,a4 +;; sb a1,0(a0) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a3,a2,32 -;; ld a2,8(a1) -;; ugt a2,a3,a2##ty=i64 -;; ld a4,0(a1) -;; add a3,a4,a3 -;; lui a0,16 -;; addi a4,a0,-1 -;; slli a4,a4,16 -;; add a3,a3,a4 -;; li a4,0 -;; sltu a5,zero,a2 -;; sub a5,zero,a5 -;; and a1,a4,a5 -;; not a4,a5 -;; and a5,a3,a4 -;; or a1,a1,a5 -;; lbu a0,0(a1) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a3,8(a1) +;; ugt a3,a2,a3##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lui a0,65535 +;; slli a2,a0,4 +;; add a2,a1,a2 +;; li a1,0 +;; sltu a3,zero,a3 +;; sub a4,zero,a3 +;; and a0,a1,a4 +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lbu a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index 9e4821f9c73c..d374fc15791c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -43,20 +43,19 @@ ;; block0: ;; lui a3,262140 ;; addi a3,a3,1 -;; slli a5,a3,2 -;; add a3,a0,a5 +;; slli a4,a3,2 +;; add a3,a0,a4 ;; trap_if heap_oob##(a3 ult a0) ;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ugt a3,a3,a4##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a5,a5,a0 -;; lui a3,16 -;; addi a0,a3,-1 -;; slli a2,a0,16 -;; add a5,a5,a2 -;; sw a1,0(a5) +;; ld a4,0(a2) +;; add a4,a4,a0 +;; lui a3,65535 +;; slli a5,a3,4 +;; add a4,a4,a5 +;; sw a1,0(a4) ;; j label2 ;; block2: ;; ret @@ -66,21 +65,20 @@ ;; function u0:1: ;; block0: ;; lui a2,262140 -;; addi a3,a2,1 -;; slli a5,a3,2 -;; add a3,a0,a5 -;; trap_if heap_oob##(a3 ult a0) -;; ld a4,8(a1) -;; ugt a4,a3,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a2,a2,1 +;; slli a4,a2,2 +;; add a2,a0,a4 +;; trap_if heap_oob##(a2 ult a0) +;; ld a3,8(a1) +;; ugt a3,a2,a3##ty=i64 +;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lui a3,16 -;; addi a0,a3,-1 -;; slli a1,a0,16 -;; add a5,a5,a1 -;; lw a0,0(a5) +;; ld a4,0(a1) +;; add a4,a4,a0 +;; lui a3,65535 +;; slli a5,a3,4 +;; add a4,a4,a5 +;; lw a0,0(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index de2dd33e71c2..9127fa86c181 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -48,13 +48,12 @@ ;; ugt a3,a3,a4##ty=i64 ;; bne a3,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a2) -;; add a3,a3,a0 -;; lui a2,16 -;; addi a4,a2,-1 -;; slli a5,a4,16 -;; add a3,a3,a5 -;; sb a1,0(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; lui a3,65535 +;; slli a3,a3,4 +;; add a2,a2,a3 +;; sb a1,0(a2) ;; j label2 ;; block2: ;; ret @@ -70,13 +69,12 @@ ;; ugt a2,a2,a3##ty=i64 ;; bne a2,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lui a1,16 -;; addi a4,a1,-1 -;; slli a5,a4,16 -;; add a3,a3,a5 -;; lbu a0,0(a3) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lui a1,65535 +;; slli a3,a1,4 +;; add a2,a2,a3 +;; lbu a0,0(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 8c63b06241f6..f49466b1499c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -42,26 +42,25 @@ ;; function u0:0: ;; block0: ;; lui a3,262140 -;; addi a4,a3,1 -;; slli a3,a4,2 -;; add a4,a0,a3 -;; trap_if heap_oob##(a4 ult a0) -;; ld a5,8(a2) -;; ugt a5,a4,a5##ty=i64 -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a3,16 -;; addi a2,a3,-1 -;; slli a2,a2,16 -;; add a0,a0,a2 -;; li a2,0 -;; sltu a3,zero,a5 -;; sub a3,zero,a3 -;; and a4,a2,a3 -;; not a2,a3 -;; and a2,a0,a2 -;; or a4,a4,a2 -;; sw a1,0(a4) +;; addi a3,a3,1 +;; slli a5,a3,2 +;; add a3,a0,a5 +;; trap_if heap_oob##(a3 ult a0) +;; ld a4,8(a2) +;; ugt a4,a3,a4##ty=i64 +;; ld a5,0(a2) +;; add a5,a5,a0 +;; lui a3,65535 +;; slli a0,a3,4 +;; add a5,a5,a0 +;; li a0,0 +;; sltu a2,zero,a4 +;; sub a2,zero,a2 +;; and a3,a0,a2 +;; not a0,a2 +;; and a2,a5,a0 +;; or a3,a3,a2 +;; sw a1,0(a3) ;; j label1 ;; block1: ;; ret @@ -69,26 +68,25 @@ ;; function u0:1: ;; block0: ;; lui a2,262140 -;; addi a4,a2,1 -;; slli a2,a4,2 -;; add a4,a0,a2 -;; trap_if heap_oob##(a4 ult a0) -;; ld a5,8(a1) -;; ugt a5,a4,a5##ty=i64 -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a3,16 -;; addi a1,a3,-1 -;; slli a1,a1,16 -;; add a0,a0,a1 -;; li a1,0 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a4,a1,a2 -;; not a1,a2 -;; and a2,a0,a1 -;; or a4,a4,a2 -;; lw a0,0(a4) +;; addi a3,a2,1 +;; slli a5,a3,2 +;; add a3,a0,a5 +;; trap_if heap_oob##(a3 ult a0) +;; ld a4,8(a1) +;; ugt a4,a3,a4##ty=i64 +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lui a3,65535 +;; slli a0,a3,4 +;; add a5,a5,a0 +;; li a0,0 +;; sltu a1,zero,a4 +;; sub a1,zero,a1 +;; and a3,a0,a1 +;; not a0,a1 +;; and a1,a5,a0 +;; or a3,a3,a1 +;; lw a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index a56579e418b2..26e0d05f0b4d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -45,20 +45,19 @@ ;; add a3,a0,a3 ;; trap_if heap_oob##(a3 ult a0) ;; ld a4,8(a2) -;; ugt a3,a3,a4##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a0 -;; lui a2,16 -;; addi a5,a2,-1 -;; slli a5,a5,16 -;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a3 -;; sub a0,zero,a0 -;; and a2,a5,a0 -;; not a5,a0 -;; and a0,a4,a5 -;; or a2,a2,a0 +;; ugt a4,a3,a4##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; lui a3,65535 +;; slli a3,a3,4 +;; add a3,a2,a3 +;; li a2,0 +;; sltu a4,zero,a4 +;; sub a5,zero,a4 +;; and a2,a2,a5 +;; not a4,a5 +;; and a5,a3,a4 +;; or a2,a2,a5 ;; sb a1,0(a2) ;; j label1 ;; block1: @@ -70,21 +69,20 @@ ;; add a2,a0,a2 ;; trap_if heap_oob##(a2 ult a0) ;; ld a3,8(a1) -;; ugt a3,a2,a3##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a0 -;; lui a1,16 -;; addi a5,a1,-1 -;; slli a5,a5,16 -;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a3 -;; sub a0,zero,a0 -;; and a2,a5,a0 -;; not a5,a0 -;; and a0,a4,a5 -;; or a2,a2,a0 -;; lbu a0,0(a2) +;; ugt a2,a2,a3##ty=i64 +;; ld a3,0(a1) +;; add a3,a3,a0 +;; lui a1,65535 +;; slli a4,a1,4 +;; add a3,a3,a4 +;; li a4,0 +;; sltu a5,zero,a2 +;; sub a5,zero,a5 +;; and a1,a4,a5 +;; not a4,a5 +;; and a5,a3,a4 +;; or a1,a1,a5 +;; lbu a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index 6bfb059513b1..bc0a82b1a9fa 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,17 +41,16 @@ ;; function u0:0: ;; block0: -;; ld a5,8(a2) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; ld a4,8(a2) +;; ugt a4,a0,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a4,16 -;; addi a2,a4,-1 -;; slli a2,a2,16 -;; add a0,a0,a2 -;; sw a1,0(a0) +;; ld a5,0(a2) +;; add a5,a5,a0 +;; lui a4,65535 +;; slli a0,a4,4 +;; add a5,a5,a0 +;; sw a1,0(a5) ;; j label2 ;; block2: ;; ret @@ -60,17 +59,16 @@ ;; ;; function u0:1: ;; block0: -;; ld a5,8(a1) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; ld a4,8(a1) +;; ugt a4,a0,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a4,16 -;; addi a1,a4,-1 -;; slli a2,a1,16 -;; add a0,a0,a2 -;; lw a0,0(a0) +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lui a4,65535 +;; slli a0,a4,4 +;; add a5,a5,a0 +;; lw a0,0(a5) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index bcccfc430359..f2e476f13c60 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,17 +41,16 @@ ;; function u0:0: ;; block0: -;; ld a5,8(a2) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; ld a4,8(a2) +;; ugt a4,a0,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a4,16 -;; addi a2,a4,-1 -;; slli a2,a2,16 -;; add a0,a0,a2 -;; sb a1,0(a0) +;; ld a5,0(a2) +;; add a5,a5,a0 +;; lui a4,65535 +;; slli a0,a4,4 +;; add a5,a5,a0 +;; sb a1,0(a5) ;; j label2 ;; block2: ;; ret @@ -60,17 +59,16 @@ ;; ;; function u0:1: ;; block0: -;; ld a5,8(a1) -;; ugt a5,a0,a5##ty=i64 -;; bne a5,zero,taken(label3),not_taken(label1) +;; ld a4,8(a1) +;; ugt a4,a0,a4##ty=i64 +;; bne a4,zero,taken(label3),not_taken(label1) ;; block1: -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a4,16 -;; addi a1,a4,-1 -;; slli a2,a1,16 -;; add a0,a0,a2 -;; lbu a0,0(a0) +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lui a4,65535 +;; slli a0,a4,4 +;; add a5,a5,a0 +;; lbu a0,0(a5) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 7cb2ebaa19f2..9efd0648bb0e 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,44 +41,42 @@ ;; function u0:0: ;; block0: -;; ld a3,8(a2) -;; ugt a3,a0,a3##ty=i64 +;; ld a5,8(a2) +;; ugt a5,a0,a5##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a0 -;; lui a4,16 -;; addi a0,a4,-1 -;; slli a4,a0,16 -;; add a2,a2,a4 -;; li a0,0 -;; sltu a3,zero,a3 +;; add a0,a2,a0 +;; lui a4,65535 +;; slli a2,a4,4 +;; add a0,a0,a2 +;; li a2,0 +;; sltu a3,zero,a5 ;; sub a3,zero,a3 -;; and a5,a0,a3 -;; not a3,a3 -;; and a3,a2,a3 -;; or a5,a5,a3 -;; sw a1,0(a5) +;; and a4,a2,a3 +;; not a2,a3 +;; and a2,a0,a2 +;; or a4,a4,a2 +;; sw a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a2,8(a1) -;; ugt a2,a0,a2##ty=i64 +;; ld a5,8(a1) +;; ugt a5,a0,a5##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a0 -;; lui a4,16 -;; addi a0,a4,-1 -;; slli a3,a0,16 -;; add a1,a1,a3 -;; li a0,0 -;; sltu a2,zero,a2 -;; sub a3,zero,a2 -;; and a5,a0,a3 -;; not a2,a3 -;; and a3,a1,a2 -;; or a5,a5,a3 -;; lw a0,0(a5) +;; add a0,a1,a0 +;; lui a4,65535 +;; slli a1,a4,4 +;; add a0,a0,a1 +;; li a1,0 +;; sltu a2,zero,a5 +;; sub a2,zero,a2 +;; and a4,a1,a2 +;; not a1,a2 +;; and a2,a0,a1 +;; or a4,a4,a2 +;; lw a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index dda4d4e639fc..8543e04f2fa2 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -41,44 +41,42 @@ ;; function u0:0: ;; block0: -;; ld a3,8(a2) -;; ugt a3,a0,a3##ty=i64 +;; ld a5,8(a2) +;; ugt a5,a0,a5##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a0 -;; lui a4,16 -;; addi a0,a4,-1 -;; slli a4,a0,16 -;; add a2,a2,a4 -;; li a0,0 -;; sltu a3,zero,a3 +;; add a0,a2,a0 +;; lui a4,65535 +;; slli a2,a4,4 +;; add a0,a0,a2 +;; li a2,0 +;; sltu a3,zero,a5 ;; sub a3,zero,a3 -;; and a5,a0,a3 -;; not a3,a3 -;; and a3,a2,a3 -;; or a5,a5,a3 -;; sb a1,0(a5) +;; and a4,a2,a3 +;; not a2,a3 +;; and a2,a0,a2 +;; or a4,a4,a2 +;; sb a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a2,8(a1) -;; ugt a2,a0,a2##ty=i64 +;; ld a5,8(a1) +;; ugt a5,a0,a5##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a0 -;; lui a4,16 -;; addi a0,a4,-1 -;; slli a3,a0,16 -;; add a1,a1,a3 -;; li a0,0 -;; sltu a2,zero,a2 -;; sub a3,zero,a2 -;; and a5,a0,a3 -;; not a2,a3 -;; and a3,a1,a2 -;; or a5,a5,a3 -;; lbu a0,0(a5) +;; add a0,a1,a0 +;; lui a4,65535 +;; slli a1,a4,4 +;; add a0,a0,a1 +;; li a1,0 +;; sltu a2,zero,a5 +;; sub a2,zero,a2 +;; and a4,a1,a2 +;; not a1,a2 +;; and a2,a0,a1 +;; or a4,a4,a2 +;; lbu a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/isle/docs/language-reference.md b/cranelift/isle/docs/language-reference.md index cb3cadbe267a..8f0f346c159b 100644 --- a/cranelift/isle/docs/language-reference.md +++ b/cranelift/isle/docs/language-reference.md @@ -987,7 +987,7 @@ evaluate the expression (`EXPR2` or `EXPR3` above) first. An expression in an if-let context is allowed to be "fallible": the constructors return `Option` at the Rust level and can return `None`, in which case the whole rule application fails and we move on -to the next rule as if the main pattern had failed to match. (MOre on +to the next rule as if the main pattern had failed to match. (More on the fallible constructors below.) If the expression evaluation succeeds, we match the associated pattern (`PAT2` or `PAT3` above) against the resulting value. This too can fail, causing the whole rule From cc0d8bc6912912f32bc1f15502e052ff49f2b946 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 4 Oct 2023 12:59:23 -0500 Subject: [PATCH 055/199] Refactor HTTP tests to be less flaky and more robust (#7143) * Refactor HTTP tests to be less flaky and more robust This commit refactors the wasi-http tests we have in this repository with a few changes: * Each test now uses a dedicated port for the test. The port 0 is bound and whatever the OS gives is used for the duration of the test. This prevents tests from possibly interfering with each other. * Server spawning is abstracted behind a single `Server` type now which internally has http1/2 constructors. * Timeouts for server shutdown are removed since they were failing in CI and are likely best handled for each test individually if necessary. As a minor cleanup the `tokio` usage in guest programs was removed as it was boilerplate in this case. This shouldn't affect the runtimes of tests, however. * Remove unused import * Don't panic from worker thread * Improve failure error message prtest:full * Fix some windows-specific issues * Fix async tests * Relax test assertion string --- Cargo.lock | 1 - crates/test-programs/src/http_server.rs | 275 ++++++------------ .../tests/wasi-http-components-sync.rs | 77 ++--- .../tests/wasi-http-components.rs | 81 ++---- .../test-programs/wasi-http-tests/Cargo.toml | 1 - .../src/bin/outbound_request_get.rs | 14 +- .../bin/outbound_request_invalid_dnsname.rs | 12 +- .../src/bin/outbound_request_invalid_port.rs | 7 +- .../bin/outbound_request_invalid_version.rs | 19 +- .../src/bin/outbound_request_large_post.rs | 12 +- .../src/bin/outbound_request_post.rs | 12 +- .../src/bin/outbound_request_put.rs | 21 +- .../bin/outbound_request_unknown_method.rs | 7 +- .../outbound_request_unsupported_scheme.rs | 7 +- .../test-programs/wasi-http-tests/src/lib.rs | 27 +- 15 files changed, 183 insertions(+), 390 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f72c8cf99a7a..ebde4e29ce0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3063,7 +3063,6 @@ name = "wasi-http-tests" version = "0.0.0" dependencies = [ "anyhow", - "tokio", "wit-bindgen", ] diff --git a/crates/test-programs/src/http_server.rs b/crates/test-programs/src/http_server.rs index ff4c0d9cd475..d22fe5b201bc 100644 --- a/crates/test-programs/src/http_server.rs +++ b/crates/test-programs/src/http_server.rs @@ -1,14 +1,12 @@ -use anyhow::Context; +use anyhow::{Context, Result}; use http_body_util::{combinators::BoxBody, BodyExt, Full}; use hyper::{body::Bytes, service::service_fn, Request, Response}; use std::{ future::Future, - net::{SocketAddr, TcpListener}, - sync::{mpsc, OnceLock}, - time::Duration, + net::{SocketAddr, TcpStream}, + thread::JoinHandle, }; - -const DEFAULT_TIMEOUT: Duration = Duration::from_secs(50); +use tokio::net::TcpListener; async fn test( mut req: Request, @@ -26,66 +24,105 @@ async fn test( .body(Full::::from(buf).boxed()) } -struct ServerHttp1 { - receiver: mpsc::Receiver>, +pub struct Server { + addr: SocketAddr, + worker: Option>>, } -impl ServerHttp1 { - fn new() -> Self { - tracing::debug!("initializing http1 server"); - static CELL_HTTP1: OnceLock = OnceLock::new(); - let listener = CELL_HTTP1.get_or_init(|| { - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("preparing tcp listener at localhost:3000"); - TcpListener::bind(addr).unwrap() - }); - let (sender, receiver) = mpsc::channel::>(); - std::thread::spawn(move || { - tracing::debug!("dedicated thread to start listening"); - match tokio::runtime::Builder::new_current_thread() +impl Server { + fn new(run: impl FnOnce(tokio::net::TcpStream) -> F + Send + Sync + 'static) -> Result + where + F: Future>, + { + let thread = std::thread::spawn(|| -> Result<_> { + let rt = tokio::runtime::Builder::new_current_thread() .enable_all() .build() - { - Ok(rt) => { - tracing::debug!("using tokio runtime"); - sender - .send(rt.block_on(async move { - tracing::debug!("preparing to accept connection"); - let (stream, _) = listener.accept().map_err(anyhow::Error::from)?; - tracing::trace!("tcp stream {:?}", stream); + .context("failed to start tokio runtime")?; + let listener = rt.block_on(async move { + let addr = SocketAddr::from(([127, 0, 0, 1], 0)); + TcpListener::bind(addr).await.context("failed to bind") + })?; + Ok((rt, listener)) + }); + let (rt, listener) = thread.join().unwrap()?; + let addr = listener.local_addr().context("failed to get local addr")?; + let worker = std::thread::spawn(move || { + tracing::debug!("dedicated thread to start listening"); + rt.block_on(async move { + tracing::debug!("preparing to accept connection"); + let (stream, _) = listener.accept().await.map_err(anyhow::Error::from)?; + run(stream).await + }) + }); + Ok(Self { + worker: Some(worker), + addr, + }) + } - let mut builder = hyper::server::conn::http1::Builder::new(); - let http = builder.keep_alive(false).pipeline_flush(true); - let io = tokio::net::TcpStream::from_std(stream) - .map_err(anyhow::Error::from)?; + pub fn http1() -> Result { + tracing::debug!("initializing http1 server"); + Self::new(|io| async move { + let mut builder = hyper::server::conn::http1::Builder::new(); + let http = builder.keep_alive(false).pipeline_flush(true); + + tracing::debug!("preparing to bind connection to service"); + let conn = http.serve_connection(io, service_fn(test)).await; + tracing::trace!("connection result {:?}", conn); + conn?; + Ok(()) + }) + } - tracing::debug!("preparing to bind connection to service"); - let conn = http.serve_connection(io, service_fn(test)).await; - tracing::trace!("connection result {:?}", conn); - conn.map_err(anyhow::Error::from) - })) - .expect("value sent from http1 server dedicated thread"); - } - Err(e) => { - tracing::debug!("unable to start tokio runtime"); - sender.send(Err(anyhow::Error::from(e))).unwrap() + pub fn http2() -> Result { + tracing::debug!("initializing http2 server"); + Self::new(|io| async move { + let mut builder = hyper::server::conn::http2::Builder::new(TokioExecutor); + let http = builder.max_concurrent_streams(20); + + tracing::debug!("preparing to bind connection to service"); + let conn = http.serve_connection(io, service_fn(test)).await; + tracing::trace!("connection result {:?}", conn); + if let Err(e) = &conn { + let message = e.to_string(); + if message.contains("connection closed before reading preface") + || message.contains("unspecific protocol error detected") + { + return Ok(()); } - }; - }); - Self { receiver } + } + conn?; + Ok(()) + }) } - fn shutdown(self) -> anyhow::Result<()> { + pub fn addr(&self) -> String { + format!("localhost:{}", self.addr.port()) + } +} + +impl Drop for Server { + fn drop(&mut self) { tracing::debug!("shutting down http1 server"); - self.receiver - .recv_timeout(DEFAULT_TIMEOUT) - .context("value received from http1 server dedicated thread")? + // Force a connection to happen in case one hasn't happened already. + let _ = TcpStream::connect(&self.addr); + + // If the worker fails with an error, report it here but don't panic. + // Some tests don't make a connection so the error will be that the tcp + // stream created above is closed immediately afterwards. Let the test + // independently decide if it failed or not, and this should be in the + // logs to assist with debugging if necessary. + let worker = self.worker.take().unwrap(); + if let Err(e) = worker.join().unwrap() { + eprintln!("worker failed with error {e:?}"); + } } } #[derive(Clone)] /// An Executor that uses the tokio runtime. -pub struct TokioExecutor; +struct TokioExecutor; impl hyper::rt::Executor for TokioExecutor where @@ -96,139 +133,3 @@ where tokio::task::spawn(fut); } } - -struct ServerHttp2 { - receiver: mpsc::Receiver>, -} - -impl ServerHttp2 { - fn new() -> Self { - tracing::debug!("initializing http2 server"); - static CELL_HTTP2: OnceLock = OnceLock::new(); - let listener = CELL_HTTP2.get_or_init(|| { - let addr = SocketAddr::from(([127, 0, 0, 1], 3001)); - tracing::debug!("preparing tcp listener at localhost:3001"); - TcpListener::bind(addr).unwrap() - }); - let (sender, receiver) = mpsc::channel::>(); - std::thread::spawn(move || { - tracing::debug!("dedicated thread to start listening"); - match tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - { - Ok(rt) => { - tracing::debug!("using tokio runtime"); - sender - .send(rt.block_on(async move { - tracing::debug!("preparing to accept incoming connection"); - let (stream, _) = listener.accept().map_err(anyhow::Error::from)?; - tracing::trace!("tcp stream {:?}", stream); - - let mut builder = - hyper::server::conn::http2::Builder::new(TokioExecutor); - let http = builder.max_concurrent_streams(20); - let io = tokio::net::TcpStream::from_std(stream) - .map_err(anyhow::Error::from)?; - - tracing::debug!("preparing to bind connection to service"); - let conn = http.serve_connection(io, service_fn(test)).await; - tracing::trace!("connection result {:?}", conn); - if let Err(e) = &conn { - let message = e.to_string(); - if message.contains("connection closed before reading preface") - || message.contains("unspecific protocol error detected") - { - return Ok(()); - } - } - conn.map_err(anyhow::Error::from) - })) - .expect("value sent from http2 server dedicated thread"); - } - Err(e) => { - tracing::debug!("unable to start tokio runtime"); - sender.send(Err(anyhow::Error::from(e))).unwrap() - } - }; - }); - Self { receiver } - } - - fn shutdown(self) -> anyhow::Result<()> { - tracing::debug!("shutting down http2 server"); - self.receiver - .recv_timeout(DEFAULT_TIMEOUT) - .context("value received from http2 server dedicated thread")? - } -} - -pub async fn setup_http1(f: impl Future>) -> anyhow::Result<()> { - tracing::debug!("preparing http1 server asynchronously"); - let server = ServerHttp1::new(); - - tracing::debug!("running inner function (future)"); - let result = f.await; - - if let Err(err) = server.shutdown() { - tracing::error!("[host/server] failure {:?}", err); - } - result -} - -pub fn setup_http1_sync(f: F) -> anyhow::Result<()> -where - F: FnOnce() -> anyhow::Result<()> + Send + 'static, -{ - tracing::debug!("preparing http1 server synchronously"); - let server = ServerHttp1::new(); - - let (tx, rx) = mpsc::channel::>(); - tracing::debug!("running inner function in a dedicated thread"); - std::thread::spawn(move || { - let _ = tx.send(f()); - }); - let result = rx - .recv_timeout(DEFAULT_TIMEOUT) - .context("value received from request dedicated thread"); - - if let Err(err) = server.shutdown() { - tracing::error!("[host/server] failure {:?}", err); - } - result? -} - -pub async fn setup_http2(f: impl Future>) -> anyhow::Result<()> { - tracing::debug!("preparing http2 server asynchronously"); - let server = ServerHttp2::new(); - - tracing::debug!("running inner function (future)"); - let result = f.await; - - if let Err(err) = server.shutdown() { - tracing::error!("[host/server] Failure: {:?}", err); - } - result -} - -pub fn setup_http2_sync(f: F) -> anyhow::Result<()> -where - F: FnOnce() -> anyhow::Result<()> + Send + 'static, -{ - tracing::debug!("preparing http2 server synchronously"); - let server = ServerHttp2::new(); - - let (tx, rx) = mpsc::channel::>(); - tracing::debug!("running inner function in a dedicated thread"); - std::thread::spawn(move || { - let _ = tx.send(f()); - }); - let result = rx - .recv_timeout(DEFAULT_TIMEOUT) - .context("value received from request dedicated thread"); - - if let Err(err) = server.shutdown() { - tracing::error!("[host/server] failure {:?}", err); - } - result? -} diff --git a/crates/test-programs/tests/wasi-http-components-sync.rs b/crates/test-programs/tests/wasi-http-components-sync.rs index b27d1c2eec6d..1f5a196c361c 100644 --- a/crates/test-programs/tests/wasi-http-components-sync.rs +++ b/crates/test-programs/tests/wasi-http-components-sync.rs @@ -1,4 +1,7 @@ #![cfg(all(feature = "test_programs", not(skip_wasi_http_tests)))] + +use anyhow::Result; +use test_programs::http_server::Server; use wasmtime::{ component::{Component, Linker}, Config, Engine, Store, @@ -8,8 +11,6 @@ use wasmtime_wasi::preview2::{ }; use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; -use test_programs::http_server::{setup_http1_sync, setup_http2_sync}; - lazy_static::lazy_static! { static ref ENGINE: Engine = { let mut config = Config::new(); @@ -66,7 +67,7 @@ fn instantiate_component( Ok((store, command)) } -fn run(name: &str) -> anyhow::Result<()> { +fn run(name: &str, server: &Server) -> Result<()> { let stdout = MemoryOutputPipe::new(4096); let stderr = MemoryOutputPipe::new(4096); let r = { @@ -81,6 +82,7 @@ fn run(name: &str) -> anyhow::Result<()> { for (var, val) in test_programs::wasi_tests_environment() { builder.env(var, val); } + builder.env("HTTP_SERVER", server.addr().to_string()); let wasi = builder.build(); let http = WasiHttpCtx {}; @@ -109,70 +111,55 @@ fn run(name: &str) -> anyhow::Result<()> { } #[test_log::test] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -fn outbound_request_get() { - setup_http1_sync(|| run("outbound_request_get")).unwrap(); +fn outbound_request_get() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_get", &server) } #[test_log::test] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -fn outbound_request_post() { - setup_http1_sync(|| run("outbound_request_post")).unwrap(); +fn outbound_request_post() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_post", &server) } #[test_log::test] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -fn outbound_request_large_post() { - setup_http1_sync(|| run("outbound_request_large_post")).unwrap(); +fn outbound_request_large_post() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_large_post", &server) } #[test_log::test] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -fn outbound_request_put() { - setup_http1_sync(|| run("outbound_request_put")).unwrap(); +fn outbound_request_put() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_put", &server) } #[test_log::test] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -fn outbound_request_invalid_version() { - setup_http2_sync(|| run("outbound_request_invalid_version")).unwrap(); +fn outbound_request_invalid_version() -> Result<()> { + let server = Server::http2()?; + run("outbound_request_invalid_version", &server) } #[test_log::test] -fn outbound_request_unknown_method() { - run("outbound_request_unknown_method").unwrap(); +fn outbound_request_unknown_method() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_unknown_method", &server) } #[test_log::test] -fn outbound_request_unsupported_scheme() { - run("outbound_request_unsupported_scheme").unwrap(); +fn outbound_request_unsupported_scheme() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_unsupported_scheme", &server) } #[test_log::test] -fn outbound_request_invalid_port() { - run("outbound_request_invalid_port").unwrap(); +fn outbound_request_invalid_port() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_invalid_port", &server) } #[test_log::test] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -fn outbound_request_invalid_dnsname() { - run("outbound_request_invalid_dnsname").unwrap(); +fn outbound_request_invalid_dnsname() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_invalid_dnsname", &server) } diff --git a/crates/test-programs/tests/wasi-http-components.rs b/crates/test-programs/tests/wasi-http-components.rs index 51f194800854..7d70cb365036 100644 --- a/crates/test-programs/tests/wasi-http-components.rs +++ b/crates/test-programs/tests/wasi-http-components.rs @@ -1,4 +1,7 @@ #![cfg(all(feature = "test_programs", not(skip_wasi_http_tests)))] + +use anyhow::Result; +use test_programs::http_server::Server; use wasmtime::{ component::{Component, Linker}, Config, Engine, Store, @@ -8,8 +11,6 @@ use wasmtime_wasi::preview2::{ }; use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; -use test_programs::http_server::{setup_http1, setup_http2}; - lazy_static::lazy_static! { static ref ENGINE: Engine = { let mut config = Config::new(); @@ -66,7 +67,7 @@ async fn instantiate_component( Ok((store, command)) } -async fn run(name: &str) -> anyhow::Result<()> { +async fn run(name: &str, server: &Server) -> Result<()> { let stdout = MemoryOutputPipe::new(4096); let stderr = MemoryOutputPipe::new(4096); let r = { @@ -81,6 +82,7 @@ async fn run(name: &str) -> anyhow::Result<()> { for (var, val) in test_programs::wasi_tests_environment() { builder.env(var, val); } + builder.env("HTTP_SERVER", server.addr()); let wasi = builder.build(); let http = WasiHttpCtx; @@ -107,74 +109,55 @@ async fn run(name: &str) -> anyhow::Result<()> { } #[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_get() { - setup_http1(run("outbound_request_get")).await.unwrap(); +async fn outbound_request_get() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_get", &server).await } #[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_post() { - setup_http1(run("outbound_request_post")).await.unwrap(); +async fn outbound_request_post() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_post", &server).await } #[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_large_post() { - setup_http1(run("outbound_request_large_post")) - .await - .unwrap(); +async fn outbound_request_large_post() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_large_post", &server).await } #[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_put() { - setup_http1(run("outbound_request_put")).await.unwrap(); +async fn outbound_request_put() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_put", &server).await } #[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_invalid_version() { - setup_http2(run("outbound_request_invalid_version")) - .await - .unwrap(); +async fn outbound_request_invalid_version() -> Result<()> { + let server = Server::http2()?; + run("outbound_request_invalid_version", &server).await } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_unknown_method() { - run("outbound_request_unknown_method").await.unwrap(); +async fn outbound_request_unknown_method() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_unknown_method", &server).await } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_unsupported_scheme() { - run("outbound_request_unsupported_scheme").await.unwrap(); +async fn outbound_request_unsupported_scheme() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_unsupported_scheme", &server).await } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_invalid_port() { - run("outbound_request_invalid_port").await.unwrap(); +async fn outbound_request_invalid_port() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_invalid_port", &server).await } #[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr( - windows, - ignore = "test is currently flaky in ci and needs to be debugged" -)] -async fn outbound_request_invalid_dnsname() { - run("outbound_request_invalid_dnsname").await.unwrap(); +async fn outbound_request_invalid_dnsname() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_invalid_dnsname", &server).await } diff --git a/crates/test-programs/wasi-http-tests/Cargo.toml b/crates/test-programs/wasi-http-tests/Cargo.toml index a648458bedd2..14206c3bbaa4 100644 --- a/crates/test-programs/wasi-http-tests/Cargo.toml +++ b/crates/test-programs/wasi-http-tests/Cargo.toml @@ -7,7 +7,6 @@ publish = false [dependencies] anyhow = { workspace = true } -tokio = { workspace = true, features = ["macros", "rt"] } wit-bindgen = { workspace = true, default-features = false, features = [ "macros", ] } diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_get.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_get.rs index 0172ebba0c62..25d71c662ca7 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_get.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_get.rs @@ -2,30 +2,26 @@ use anyhow::Context; use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; fn main() { - wasi_http_tests::in_tokio(async { run().await }) -} - -async fn run() { + let addr = std::env::var("HTTP_SERVER").unwrap(); let res = wasi_http_tests::request( Method::Get, Scheme::Http, - "localhost:3000", + &addr, "/get?some=arg&goes=here", None, None, ) - .await - .context("localhost:3000 /get") + .context("/get") .unwrap(); - println!("localhost:3000 /get: {res:?}"); + println!("{addr} /get: {res:?}"); assert_eq!(res.status, 200); let method = res.header("x-wasmtime-test-method").unwrap(); assert_eq!(std::str::from_utf8(method).unwrap(), "GET"); let uri = res.header("x-wasmtime-test-uri").unwrap(); assert_eq!( std::str::from_utf8(uri).unwrap(), - "http://localhost:3000/get?some=arg&goes=here" + format!("http://{addr}/get?some=arg&goes=here") ); assert_eq!(res.body, b""); } diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs index 6cc5ce8bc97b..a36e5384336e 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs @@ -1,10 +1,6 @@ use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; fn main() { - wasi_http_tests::in_tokio(async { run().await }) -} - -async fn run() { let res = wasi_http_tests::request( Method::Get, Scheme::Http, @@ -12,9 +8,11 @@ async fn run() { "/", None, None, - ) - .await; + ); let error = res.unwrap_err().to_string(); - assert!(error.starts_with("Error::InvalidUrl(\"failed to lookup address information:")); + assert!( + error.starts_with("Error::InvalidUrl(\""), + "bad error: {error}" + ); } diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_port.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_port.rs index 88750fc3712b..fe0290a30d21 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_port.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_port.rs @@ -1,10 +1,6 @@ use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; fn main() { - wasi_http_tests::in_tokio(async { run().await }) -} - -async fn run() { let res = wasi_http_tests::request( Method::Get, Scheme::Http, @@ -12,8 +8,7 @@ async fn run() { "/", None, None, - ) - .await; + ); let error = res.unwrap_err(); assert_eq!( diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs index 53c767edec8f..c61bfeab1b32 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs @@ -1,24 +1,11 @@ use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; fn main() { - wasi_http_tests::in_tokio(async { run().await }) -} - -async fn run() { - let res = wasi_http_tests::request( - Method::Connect, - Scheme::Http, - "localhost:3001", - "/", - None, - Some(&[]), - ) - .await; + let addr = std::env::var("HTTP_SERVER").unwrap(); + let res = wasi_http_tests::request(Method::Connect, Scheme::Http, &addr, "/", None, Some(&[])); let error = res.unwrap_err().to_string(); - if error.ne("Error::ProtocolError(\"invalid HTTP version parsed\")") - && !error.starts_with("Error::ProtocolError(\"operation was canceled") - { + if !error.starts_with("Error::ProtocolError(\"") { panic!( r#"assertion failed: `(left == right)` left: `"{error}"`, diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs index 80e0688d47fc..e5620910fd17 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs @@ -3,27 +3,23 @@ use std::io::{self, Read}; use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; fn main() { - wasi_http_tests::in_tokio(async { run().await }) -} - -async fn run() { // TODO: ensure more than 700 bytes is allowed without error const LEN: usize = 700; let mut buffer = [0; LEN]; + let addr = std::env::var("HTTP_SERVER").unwrap(); io::repeat(0b001).read_exact(&mut buffer).unwrap(); let res = wasi_http_tests::request( Method::Post, Scheme::Http, - "localhost:3000", + &addr, "/post", Some(&buffer), None, ) - .await - .context("localhost:3000 /post large") + .context("/post large") .unwrap(); - println!("localhost:3000 /post large: {}", res.status); + println!("/post large: {}", res.status); assert_eq!(res.status, 200); let method = res.header("x-wasmtime-test-method").unwrap(); assert_eq!(std::str::from_utf8(method).unwrap(), "POST"); diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs index 131356fa91bc..6fa0e9ad02df 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs @@ -2,23 +2,19 @@ use anyhow::Context; use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; fn main() { - wasi_http_tests::in_tokio(async { run().await }) -} - -async fn run() { + let addr = std::env::var("HTTP_SERVER").unwrap(); let res = wasi_http_tests::request( Method::Post, Scheme::Http, - "localhost:3000", + &addr, "/post", Some(b"{\"foo\": \"bar\"}"), None, ) - .await - .context("localhost:3000 /post") + .context("/post") .unwrap(); - println!("localhost:3000 /post: {res:?}"); + println!("/post: {res:?}"); assert_eq!(res.status, 200); let method = res.header("x-wasmtime-test-method").unwrap(); assert_eq!(std::str::from_utf8(method).unwrap(), "POST"); diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_put.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_put.rs index 93bb7a053950..68e34f6cb578 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_put.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_put.rs @@ -2,23 +2,12 @@ use anyhow::Context; use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; fn main() { - wasi_http_tests::in_tokio(async { run().await }) -} - -async fn run() { - let res = wasi_http_tests::request( - Method::Put, - Scheme::Http, - "localhost:3000", - "/put", - Some(&[]), - None, - ) - .await - .context("localhost:3000 /put") - .unwrap(); + let addr = std::env::var("HTTP_SERVER").unwrap(); + let res = wasi_http_tests::request(Method::Put, Scheme::Http, &addr, "/put", Some(&[]), None) + .context("/put") + .unwrap(); - println!("localhost:3000 /put: {res:?}"); + println!("/put: {res:?}"); assert_eq!(res.status, 200); let method = res.header("x-wasmtime-test-method").unwrap(); assert_eq!(std::str::from_utf8(method).unwrap(), "PUT"); diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs index a2ab5e48dc02..cb7847e3b5c1 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs @@ -1,10 +1,6 @@ use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; fn main() { - wasi_http_tests::in_tokio(async { run().await }) -} - -async fn run() { let res = wasi_http_tests::request( Method::Other("OTHER".to_owned()), Scheme::Http, @@ -12,8 +8,7 @@ async fn run() { "/", None, None, - ) - .await; + ); let error = res.unwrap_err(); assert_eq!( diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs index 482550627e8d..b9d198a44381 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs @@ -1,10 +1,6 @@ use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; fn main() { - wasi_http_tests::in_tokio(async { run().await }) -} - -async fn run() { let res = wasi_http_tests::request( Method::Get, Scheme::Other("WS".to_owned()), @@ -12,8 +8,7 @@ async fn run() { "/", None, None, - ) - .await; + ); let error = res.unwrap_err(); assert_eq!( diff --git a/crates/test-programs/wasi-http-tests/src/lib.rs b/crates/test-programs/wasi-http-tests/src/lib.rs index 90ad31b79daa..94b103414340 100644 --- a/crates/test-programs/wasi-http-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-tests/src/lib.rs @@ -8,12 +8,10 @@ pub mod bindings { } use anyhow::{anyhow, Result}; -use std::fmt; -use std::sync::OnceLock; - use bindings::wasi::http::{outgoing_handler, types as http_types}; use bindings::wasi::io::poll; use bindings::wasi::io::streams; +use std::fmt; pub struct Response { pub status: http_types::StatusCode, @@ -42,7 +40,7 @@ impl Response { } } -pub async fn request( +pub fn request( method: http_types::Method, scheme: http_types::Scheme, authority: &str, @@ -170,24 +168,3 @@ pub async fn request( body, }) } - -static RUNTIME: OnceLock = OnceLock::new(); - -pub fn in_tokio(f: F) -> F::Output { - match tokio::runtime::Handle::try_current() { - Ok(h) => { - let _enter = h.enter(); - h.block_on(f) - } - Err(_) => { - let runtime = RUNTIME.get_or_init(|| { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - }); - let _enter = runtime.enter(); - runtime.block_on(f) - } - } -} From 611b3de3a268cc1db0d321c5c5866fe3115f81c0 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 4 Oct 2023 12:40:00 -0600 Subject: [PATCH 056/199] [wasmtime-wasi] make StdinStream and StdoutStream public (#7150) This allows embedders to implement those traits themselves rather than be restricted to using the built-in implementations. Fixes https://github.com/bytecodealliance/wasmtime/issues/7149 Signed-off-by: Joel Dice --- crates/wasi/src/preview2/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index 851c0d1a15c4..9b4a273b8af9 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -44,7 +44,9 @@ pub use self::error::I32Exit; pub use self::filesystem::{DirPerms, FilePerms}; pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe}; pub use self::random::{thread_rng, Deterministic}; -pub use self::stdio::{stderr, stdin, stdout, IsATTY, Stderr, Stdin, Stdout}; +pub use self::stdio::{ + stderr, stdin, stdout, IsATTY, Stderr, Stdin, StdinStream, Stdout, StdoutStream, +}; pub use self::stream::{HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError}; pub use self::table::{Table, TableError}; pub use cap_fs_ext::SystemTimeSpec; From 993e26e044d4705ce67163f0033b39aad10dd5d9 Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Wed, 4 Oct 2023 12:15:56 -0700 Subject: [PATCH 057/199] wasmtime: Handle common cli args in `wasmtime serve` (#7134) * Refactor option handling in the `run` and `serve` commands * Use a real thread to manage epoch increments * Move module/component loading into src/common.rs * Require wasi-http and component-model to not be disabled in serve --- src/commands/run.rs | 298 ++++++------------------------------------ src/commands/serve.rs | 247 ++++++++++++++++++++++++++++------ src/common.rs | 243 ++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 4 files changed, 490 insertions(+), 300 deletions(-) create mode 100644 src/common.rs diff --git a/src/commands/run.rs b/src/commands/run.rs index 2af92f6ddf24..b45f48a5ae6b 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -5,27 +5,23 @@ allow(irrefutable_let_patterns, unreachable_patterns) )] +use crate::common::{Profile, RunCommon, RunTarget}; + use anyhow::{anyhow, bail, Context as _, Error, Result}; use clap::Parser; use std::fs::File; use std::io::Write; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::Arc; use std::thread; -use std::time::Duration; use wasmtime::{ - AsContextMut, Engine, Func, GuestProfiler, Module, Precompiled, Store, StoreLimits, - StoreLimitsBuilder, UpdateDeadline, Val, ValType, + AsContextMut, Engine, Func, GuestProfiler, Module, Store, StoreLimits, UpdateDeadline, Val, + ValType, }; -use wasmtime_cli_flags::opt::WasmtimeOptionValue; -use wasmtime_cli_flags::CommonOptions; use wasmtime_wasi::maybe_exit_on_error; use wasmtime_wasi::preview2; use wasmtime_wasi::sync::{ambient_authority, Dir, TcpListener, WasiCtxBuilder}; -#[cfg(feature = "component-model")] -use wasmtime::component::Component; - #[cfg(feature = "wasi-nn")] use wasmtime_wasi_nn::WasiNnCtx; @@ -61,43 +57,12 @@ fn parse_preloads(s: &str) -> Result<(String, PathBuf)> { Ok((parts[0].into(), parts[1].into())) } -fn parse_profile(s: &str) -> Result { - let parts = s.split(',').collect::>(); - match &parts[..] { - ["perfmap"] => Ok(Profile::Native(wasmtime::ProfilingStrategy::PerfMap)), - ["jitdump"] => Ok(Profile::Native(wasmtime::ProfilingStrategy::JitDump)), - ["vtune"] => Ok(Profile::Native(wasmtime::ProfilingStrategy::VTune)), - ["guest"] => Ok(Profile::Guest { - path: "wasmtime-guest-profile.json".to_string(), - interval: Duration::from_millis(10), - }), - ["guest", path] => Ok(Profile::Guest { - path: path.to_string(), - interval: Duration::from_millis(10), - }), - ["guest", path, dur] => Ok(Profile::Guest { - path: path.to_string(), - interval: WasmtimeOptionValue::parse(Some(dur))?, - }), - _ => bail!("unknown profiling strategy: {s}"), - } -} - /// Runs a WebAssembly module #[derive(Parser)] #[structopt(name = "run")] pub struct RunCommand { #[clap(flatten)] - common: CommonOptions, - - /// Allow executing precompiled WebAssembly modules as `*.cwasm` files. - /// - /// Note that this option is not safe to pass if the module being passed in - /// is arbitrary user input. Only `wasmtime`-precompiled modules generated - /// via the `wasmtime compile` command or equivalent should be passed as an - /// argument with this option specified. - #[clap(long = "allow-precompiled")] - allow_precompiled: bool, + run: RunCommon, /// Grant access of a host directory to a guest. /// @@ -131,28 +96,6 @@ pub struct RunCommand { )] preloads: Vec<(String, PathBuf)>, - /// Profiling strategy (valid options are: perfmap, jitdump, vtune, guest) - /// - /// The perfmap, jitdump, and vtune profiling strategies integrate Wasmtime - /// with external profilers such as `perf`. The guest profiling strategy - /// enables in-process sampling and will write the captured profile to - /// `wasmtime-guest-profile.json` by default which can be viewed at - /// https://profiler.firefox.com/. - /// - /// The `guest` option can be additionally configured as: - /// - /// --profile=guest[,path[,interval]] - /// - /// where `path` is where to write the profile and `interval` is the - /// duration between samples. When used with `--wasm-timeout` the timeout - /// will be rounded up to the nearest multiple of this interval. - #[clap( - long, - value_name = "STRATEGY", - value_parser = parse_profile, - )] - profile: Option, - /// The WebAssembly module to run and arguments to pass to it. /// /// Arguments passed to the wasm module will be configured as WASI CLI @@ -162,53 +105,23 @@ pub struct RunCommand { module_and_args: Vec, } -#[derive(Clone)] -enum Profile { - Native(wasmtime::ProfilingStrategy), - Guest { path: String, interval: Duration }, -} - enum CliLinker { Core(wasmtime::Linker), #[cfg(feature = "component-model")] Component(wasmtime::component::Linker), } -enum CliModule { - Core(wasmtime::Module), - #[cfg(feature = "component-model")] - Component(Component), -} - -impl CliModule { - fn unwrap_core(&self) -> &Module { - match self { - CliModule::Core(module) => module, - #[cfg(feature = "component-model")] - CliModule::Component(_) => panic!("expected a core wasm module, not a component"), - } - } - - #[cfg(feature = "component-model")] - fn unwrap_component(&self) -> &Component { - match self { - CliModule::Component(c) => c, - CliModule::Core(_) => panic!("expected a component, not a core wasm module"), - } - } -} - impl RunCommand { /// Executes the command. pub fn execute(mut self) -> Result<()> { - self.common.init_logging(); + self.run.common.init_logging(); - let mut config = self.common.config(None)?; + let mut config = self.run.common.config(None)?; - if self.common.wasm.timeout.is_some() { + if self.run.common.wasm.timeout.is_some() { config.epoch_interruption(true); } - match self.profile { + match self.run.profile { Some(Profile::Native(s)) => { config.profiler(s); } @@ -222,23 +135,23 @@ impl RunCommand { let engine = Engine::new(&config)?; // Read the wasm module binary either as `*.wat` or a raw binary. - let main = self.load_module(&engine, &self.module_and_args[0])?; + let main = self.run.load_module(&engine, &self.module_and_args[0])?; // Validate coredump-on-trap argument - if let Some(path) = &self.common.debug.coredump { + if let Some(path) = &self.run.common.debug.coredump { if path.contains("%") { bail!("the coredump-on-trap path does not support patterns yet.") } } let mut linker = match &main { - CliModule::Core(_) => CliLinker::Core(wasmtime::Linker::new(&engine)), + RunTarget::Core(_) => CliLinker::Core(wasmtime::Linker::new(&engine)), #[cfg(feature = "component-model")] - CliModule::Component(_) => { + RunTarget::Component(_) => { CliLinker::Component(wasmtime::component::Linker::new(&engine)) } }; - if let Some(enable) = self.common.wasm.unknown_exports_allow { + if let Some(enable) = self.run.common.wasm.unknown_exports_allow { match &mut linker { CliLinker::Core(l) => { l.allow_unknown_exports(enable); @@ -254,45 +167,26 @@ impl RunCommand { let mut store = Store::new(&engine, host); self.populate_with_wasi(&mut linker, &mut store, &main)?; - let mut limits = StoreLimitsBuilder::new(); - if let Some(max) = self.common.wasm.max_memory_size { - limits = limits.memory_size(max); - } - if let Some(max) = self.common.wasm.max_table_elements { - limits = limits.table_elements(max); - } - if let Some(max) = self.common.wasm.max_instances { - limits = limits.instances(max); - } - if let Some(max) = self.common.wasm.max_tables { - limits = limits.tables(max); - } - if let Some(max) = self.common.wasm.max_memories { - limits = limits.memories(max); - } - if let Some(enable) = self.common.wasm.trap_on_grow_failure { - limits = limits.trap_on_grow_failure(enable); - } - store.data_mut().limits = limits.build(); + store.data_mut().limits = self.run.store_limits(); store.limiter(|t| &mut t.limits); // If fuel has been configured, we want to add the configured // fuel amount to this store. - if let Some(fuel) = self.common.wasm.fuel { + if let Some(fuel) = self.run.common.wasm.fuel { store.add_fuel(fuel)?; } // Load the preload wasm modules. let mut modules = Vec::new(); - if let CliModule::Core(m) = &main { + if let RunTarget::Core(m) = &main { modules.push((String::new(), m.clone())); } for (name, path) in self.preloads.iter() { // Read the wasm module binary either as `*.wat` or a raw binary - let module = match self.load_module(&engine, path)? { - CliModule::Core(m) => m, + let module = match self.run.load_module(&engine, path)? { + RunTarget::Core(m) => m, #[cfg(feature = "component-model")] - CliModule::Component(_) => bail!("components cannot be loaded with `--preload`"), + RunTarget::Component(_) => bail!("components cannot be loaded with `--preload`"), }; modules.push((name.clone(), module.clone())); @@ -350,7 +244,7 @@ impl RunCommand { fn compute_preopen_sockets(&self) -> Result> { let mut listeners = vec![]; - for address in &self.common.wasi.tcplisten { + for address in &self.run.common.wasi.tcplisten { let stdlistener = std::net::TcpListener::bind(address) .with_context(|| format!("failed to bind to address '{}'", address))?; @@ -387,7 +281,7 @@ impl RunCommand { store: &mut Store, modules: Vec<(String, Module)>, ) -> Box)> { - if let Some(Profile::Guest { path, interval }) = &self.profile { + if let Some(Profile::Guest { path, interval }) = &self.run.profile { let module_name = self.module_and_args[0].to_str().unwrap_or("
"); let interval = *interval; store.data_mut().guest_profiler = @@ -406,7 +300,7 @@ impl RunCommand { store.as_context_mut().data_mut().guest_profiler = Some(profiler); } - if let Some(timeout) = self.common.wasm.timeout { + if let Some(timeout) = self.run.common.wasm.timeout { let mut timeout = (timeout.as_secs_f64() / interval.as_secs_f64()).ceil() as u64; assert!(timeout > 0); store.epoch_deadline_callback(move |mut store| { @@ -448,7 +342,7 @@ impl RunCommand { }); } - if let Some(timeout) = self.common.wasm.timeout { + if let Some(timeout) = self.run.common.wasm.timeout { store.set_epoch_deadline(1); let engine = store.engine().clone(); thread::spawn(move || { @@ -464,12 +358,12 @@ impl RunCommand { &self, store: &mut Store, linker: &mut CliLinker, - module: &CliModule, + module: &RunTarget, modules: Vec<(String, Module)>, ) -> Result<()> { // The main module might be allowed to have unknown imports, which // should be defined as traps: - if self.common.wasm.unknown_imports_trap == Some(true) { + if self.run.common.wasm.unknown_imports_trap == Some(true) { match linker { CliLinker::Core(linker) => { linker.define_unknown_imports_as_traps(module.unwrap_core())?; @@ -479,7 +373,7 @@ impl RunCommand { } // ...or as default values. - if self.common.wasm.unknown_imports_default == Some(true) { + if self.run.common.wasm.unknown_imports_default == Some(true) { match linker { CliLinker::Core(linker) => { linker.define_unknown_imports_as_default_values(module.unwrap_core())?; @@ -620,7 +514,7 @@ impl RunCommand { } fn handle_core_dump(&self, store: &mut Store, err: Error) -> Error { - let coredump_path = match &self.common.debug.coredump { + let coredump_path = match &self.run.common.debug.coredump { Some(path) => path, None => return err, }; @@ -639,130 +533,17 @@ impl RunCommand { } } - fn load_module(&self, engine: &Engine, path: &Path) -> Result { - let path = match path.to_str() { - #[cfg(unix)] - Some("-") => "/dev/stdin".as_ref(), - _ => path, - }; - - // First attempt to load the module as an mmap. If this succeeds then - // detection can be done with the contents of the mmap and if a - // precompiled module is detected then `deserialize_file` can be used - // which is a slightly more optimal version than `deserialize` since we - // can leave most of the bytes on disk until they're referenced. - // - // If the mmap fails, for example if stdin is a pipe, then fall back to - // `std::fs::read` to load the contents. At that point precompiled - // modules must go through the `deserialize` functions. - // - // Note that this has the unfortunate side effect for precompiled - // modules on disk that they're opened once to detect what they are and - // then again internally in Wasmtime as part of the `deserialize_file` - // API. Currently there's no way to pass the `MmapVec` here through to - // Wasmtime itself (that'd require making `wasmtime-runtime` a public - // dependency or `MmapVec` a public type, both of which aren't ready to - // happen at this time). It's hoped though that opening a file twice - // isn't too bad in the grand scheme of things with respect to the CLI. - match wasmtime_runtime::MmapVec::from_file(path) { - Ok(map) => self.load_module_contents( - engine, - path, - &map, - || unsafe { Module::deserialize_file(engine, path) }, - #[cfg(feature = "component-model")] - || unsafe { Component::deserialize_file(engine, path) }, - ), - Err(_) => { - let bytes = std::fs::read(path) - .with_context(|| format!("failed to read file: {}", path.display()))?; - self.load_module_contents( - engine, - path, - &bytes, - || unsafe { Module::deserialize(engine, &bytes) }, - #[cfg(feature = "component-model")] - || unsafe { Component::deserialize(engine, &bytes) }, - ) - } - } - } - - fn load_module_contents( - &self, - engine: &Engine, - path: &Path, - bytes: &[u8], - deserialize_module: impl FnOnce() -> Result, - #[cfg(feature = "component-model")] deserialize_component: impl FnOnce() -> Result, - ) -> Result { - Ok(match engine.detect_precompiled(bytes) { - Some(Precompiled::Module) => { - self.ensure_allow_precompiled()?; - CliModule::Core(deserialize_module()?) - } - #[cfg(feature = "component-model")] - Some(Precompiled::Component) => { - self.ensure_allow_precompiled()?; - self.ensure_allow_components()?; - CliModule::Component(deserialize_component()?) - } - #[cfg(not(feature = "component-model"))] - Some(Precompiled::Component) => { - bail!("support for components was not enabled at compile time"); - } - None => { - // Parse the text format here specifically to add the `path` to - // the error message if there's a syntax error. - let wasm = wat::parse_bytes(bytes).map_err(|mut e| { - e.set_path(path); - e - })?; - if wasmparser::Parser::is_component(&wasm) { - #[cfg(feature = "component-model")] - { - self.ensure_allow_components()?; - CliModule::Component(Component::new(engine, &wasm)?) - } - #[cfg(not(feature = "component-model"))] - { - bail!("support for components was not enabled at compile time"); - } - } else { - CliModule::Core(Module::new(engine, &wasm)?) - } - } - }) - } - - fn ensure_allow_precompiled(&self) -> Result<()> { - if self.allow_precompiled { - Ok(()) - } else { - bail!("running a precompiled module requires the `--allow-precompiled` flag") - } - } - - #[cfg(feature = "component-model")] - fn ensure_allow_components(&self) -> Result<()> { - if self.common.wasm.component_model != Some(true) { - bail!("cannot execute a component without `--wasm component-model`"); - } - - Ok(()) - } - /// Populates the given `Linker` with WASI APIs. fn populate_with_wasi( &self, linker: &mut CliLinker, store: &mut Store, - module: &CliModule, + module: &RunTarget, ) -> Result<()> { - if self.common.wasi.common != Some(false) { + if self.run.common.wasi.common != Some(false) { match linker { CliLinker::Core(linker) => { - if self.common.wasi.preview2 == Some(true) { + if self.run.common.wasi.preview2 == Some(true) { preview2::preview1::add_to_linker_sync(linker)?; self.set_preview2_ctx(store)?; } else { @@ -780,7 +561,7 @@ impl RunCommand { } } - if self.common.wasi.nn == Some(true) { + if self.run.common.wasi.nn == Some(true) { #[cfg(not(feature = "wasi-nn"))] { bail!("Cannot enable wasi-nn when the binary is not compiled with this feature."); @@ -810,6 +591,7 @@ impl RunCommand { } } let graphs = self + .run .common .wasi .nn_graph @@ -821,7 +603,7 @@ impl RunCommand { } } - if self.common.wasi.threads == Some(true) { + if self.run.common.wasi.threads == Some(true) { #[cfg(not(feature = "wasi-threads"))] { // Silence the unused warning for `module` as it is only used in the @@ -849,7 +631,7 @@ impl RunCommand { } } - if self.common.wasi.http == Some(true) { + if self.run.common.wasi.http == Some(true) { #[cfg(not(all(feature = "wasi-http", feature = "component-model")))] { bail!("Cannot enable wasi-http when the binary is not compiled with this feature."); @@ -887,7 +669,7 @@ impl RunCommand { let mut num_fd: usize = 3; - if self.common.wasi.listenfd == Some(true) { + if self.run.common.wasi.listenfd == Some(true) { num_fd = ctx_set_listenfd(num_fd, &mut builder)?; } @@ -917,7 +699,7 @@ impl RunCommand { builder.env(key, &value); } - if self.common.wasi.listenfd == Some(true) { + if self.run.common.wasi.listenfd == Some(true) { bail!("components do not support --listenfd"); } for _ in self.compute_preopen_sockets()? { @@ -933,10 +715,10 @@ impl RunCommand { ); } - if self.common.wasi.inherit_network == Some(true) { + if self.run.common.wasi.inherit_network == Some(true) { builder.inherit_network(ambient_authority()); } - if let Some(enable) = self.common.wasi.allow_ip_name_lookup { + if let Some(enable) = self.run.common.wasi.allow_ip_name_lookup { builder.allow_ip_name_lookup(enable); } diff --git a/src/commands/serve.rs b/src/commands/serve.rs index bfca73c92cd2..6993217839a7 100644 --- a/src/commands/serve.rs +++ b/src/commands/serve.rs @@ -1,26 +1,31 @@ -use anyhow::Result; +use crate::common::{Profile, RunCommon, RunTarget}; +use anyhow::{bail, Result}; use clap::Parser; -use std::{path::PathBuf, pin::Pin, sync::Arc}; -use wasmtime::component::{Component, InstancePre, Linker}; -use wasmtime::{Engine, Store}; -use wasmtime_cli_flags::CommonOptions; +use std::{ + path::PathBuf, + pin::Pin, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, +}; +use wasmtime::component::{InstancePre, Linker}; +use wasmtime::{Engine, Store, StoreLimits}; use wasmtime_wasi::preview2::{Table, WasiCtx, WasiCtxBuilder, WasiView}; use wasmtime_wasi_http::{body::HyperOutgoingBody, WasiHttpCtx, WasiHttpView}; +#[cfg(feature = "wasi-nn")] +use wasmtime_wasi_nn::WasiNnCtx; + struct Host { table: Table, ctx: WasiCtx, http: WasiHttpCtx, -} -impl Host { - fn new() -> Self { - Host { - table: Table::new(), - ctx: WasiCtxBuilder::new().build(), - http: WasiHttpCtx, - } - } + limits: StoreLimits, + + #[cfg(feature = "wasi-nn")] + nn: Option, } impl WasiView for Host { @@ -51,16 +56,21 @@ impl WasiHttpView for Host { } } +const DEFAULT_ADDR: std::net::SocketAddr = std::net::SocketAddr::new( + std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)), + 8080, +); + /// Runs a WebAssembly module #[derive(Parser)] -#[structopt(name = "run")] +#[structopt(name = "serve")] pub struct ServeCommand { #[clap(flatten)] - common: CommonOptions, + run: RunCommon, - /// Socket address for the web server to bind to. Defaults to 0.0.0.0:8080. - #[clap(long = "addr", value_name = "SOCKADDR")] - addr: Option, + /// Socket address for the web server to bind to. + #[clap(long = "addr", value_name = "SOCKADDR", default_value_t = DEFAULT_ADDR )] + addr: std::net::SocketAddr, /// The WebAssembly component to run. #[clap(value_name = "WASM", required = true)] @@ -68,12 +78,43 @@ pub struct ServeCommand { } impl ServeCommand { - fn addr(&self) -> std::net::SocketAddr { - self.addr.unwrap_or("0.0.0.0:8080".parse().unwrap()) - } - /// Start a server to run the given wasi-http proxy component pub fn execute(mut self) -> Result<()> { + self.run.common.init_logging(); + + // We force cli errors before starting to listen for connections so tha we don't + // accidentally delay them to the first request. + if self.run.common.wasi.nn == Some(true) { + #[cfg(not(feature = "wasi-nn"))] + { + bail!("Cannot enable wasi-nn when the binary is not compiled with this feature."); + } + } + + if let Some(Profile::Guest { .. }) = &self.run.profile { + bail!("Cannot use the guest profiler with components"); + } + + if self.run.common.wasi.nn == Some(true) { + #[cfg(not(feature = "wasi-nn"))] + { + bail!("Cannot enable wasi-nn when the binary is not compiled with this feature."); + } + } + + if self.run.common.wasi.threads == Some(true) { + bail!("wasi-threads does not support components yet") + } + + // The serve command requires both wasi-http and the component model, so we enable those by + // default here. + if self.run.common.wasi.http.replace(true) == Some(false) { + bail!("wasi-http is required for the serve command, and must not be disabled"); + } + if self.run.common.wasm.component_model.replace(true) == Some(false) { + bail!("components are required for the serve command, and must not be disabled"); + } + let runtime = tokio::runtime::Builder::new_multi_thread() .enable_time() .enable_io() @@ -94,38 +135,124 @@ impl ServeCommand { Ok(()) } + fn new_store(&self, engine: &Engine) -> Result> { + let mut builder = WasiCtxBuilder::new(); + + // TODO: connect stdio to logging infrastructure + + let mut host = Host { + table: Table::new(), + ctx: builder.build(), + http: WasiHttpCtx, + + limits: StoreLimits::default(), + + #[cfg(feature = "wasi-nn")] + nn: None, + }; + + if self.run.common.wasi.nn == Some(true) { + #[cfg(feature = "wasi-nn")] + { + let graphs = self + .run + .common + .wasi + .nn_graph + .iter() + .map(|g| (g.format.clone(), g.dir.clone())) + .collect::>(); + let (backends, registry) = wasmtime_wasi_nn::preload(&graphs)?; + host.nn.replace(WasiNnCtx::new(backends, registry)); + } + } + + let mut store = Store::new(engine, host); + + if self.run.common.wasm.timeout.is_some() { + store.set_epoch_deadline(1); + } + + store.data_mut().limits = self.run.store_limits(); + store.limiter(|t| &mut t.limits); + + // If fuel has been configured, we want to add the configured + // fuel amount to this store. + if let Some(fuel) = self.run.common.wasm.fuel { + store.add_fuel(fuel)?; + } + + Ok(store) + } + fn add_to_linker(&self, linker: &mut Linker) -> Result<()> { + // wasi-http and the component model are implicitly enabled for `wasmtime serve`, so we + // don't test for `self.run.common.wasi.common` or `self.run.common.wasi.http` in this + // function. + wasmtime_wasi_http::proxy::add_to_linker(linker)?; + + if self.run.common.wasi.nn == Some(true) { + #[cfg(feature = "wasi-nn")] + { + wasmtime_wasi_nn::wit::ML::add_to_linker(linker, |host| host.nn.as_mut().unwrap())?; + } + } + Ok(()) } - async fn serve(&mut self) -> Result<()> { + async fn serve(mut self) -> Result<()> { use hyper::server::conn::http1; - let mut config = self.common.config(None)?; + let mut config = self.run.common.config(None)?; config.wasm_component_model(true); config.async_support(true); - let engine = Arc::new(Engine::new(&config)?); + if self.run.common.wasm.timeout.is_some() { + config.epoch_interruption(true); + } + + match self.run.profile { + Some(Profile::Native(s)) => { + config.profiler(s); + } + + // We bail early in `execute` if the guest profiler is configured. + Some(Profile::Guest { .. }) => unreachable!(), + + None => {} + } + + let engine = Engine::new(&config)?; let mut linker = Linker::new(&engine); self.add_to_linker(&mut linker)?; - let component = Component::from_file(&engine, &self.component)?; + let component = match self.run.load_module(&engine, &self.component)? { + RunTarget::Core(_) => bail!("The serve command currently requires a component"), + RunTarget::Component(c) => c, + }; + + let instance = linker.instantiate_pre(&component)?; - let instance = Arc::new(linker.instantiate_pre(&component)?); + let listener = tokio::net::TcpListener::bind(self.addr).await?; - let listener = tokio::net::TcpListener::bind(self.addr()).await?; + let _epoch_thread = if let Some(timeout) = self.run.common.wasm.timeout { + Some(EpochThread::spawn(timeout, engine.clone())) + } else { + None + }; + + let handler = ProxyHandler::new(self, engine, instance); loop { let (stream, _) = listener.accept().await?; - let engine = Arc::clone(&engine); - let instance = Arc::clone(&instance); + let h = handler.clone(); tokio::task::spawn(async move { - let handler = ProxyHandler::new(engine, instance); if let Err(e) = http1::Builder::new() .keep_alive(true) - .serve_connection(stream, handler) + .serve_connection(stream, h) .await { eprintln!("error: {e:?}"); @@ -135,18 +262,54 @@ impl ServeCommand { } } -#[derive(Clone)] -struct ProxyHandler { - engine: Arc, - instance_pre: Arc>, +struct EpochThread { + shutdown: Arc, + handle: Option>, } +impl EpochThread { + fn spawn(timeout: std::time::Duration, engine: Engine) -> Self { + let shutdown = Arc::new(AtomicBool::new(false)); + let handle = { + let shutdown = Arc::clone(&shutdown); + let handle = std::thread::spawn(move || { + while !shutdown.load(Ordering::Relaxed) { + std::thread::sleep(timeout); + engine.increment_epoch(); + } + }); + Some(handle) + }; + + EpochThread { shutdown, handle } + } +} + +impl Drop for EpochThread { + fn drop(&mut self) { + if let Some(handle) = self.handle.take() { + self.shutdown.store(true, Ordering::Relaxed); + handle.join().unwrap(); + } + } +} + +struct ProxyHandlerInner { + cmd: ServeCommand, + engine: Engine, + instance_pre: InstancePre, +} + +#[derive(Clone)] +struct ProxyHandler(Arc); + impl ProxyHandler { - fn new(engine: Arc, instance_pre: Arc>) -> Self { - Self { + fn new(cmd: ServeCommand, engine: Engine, instance_pre: InstancePre) -> Self { + Self(Arc::new(ProxyHandlerInner { + cmd, engine, instance_pre, - } + })) } } @@ -166,7 +329,7 @@ impl hyper::service::Service for ProxyHandler { // TODO: need to track the join handle, but don't want to block the response on it tokio::task::spawn(async move { - let mut store = Store::new(&handler.engine, Host::new()); + let mut store = handler.0.cmd.new_store(&handler.0.engine)?; let req = store.data_mut().new_incoming_request( req.map(|body| body.map_err(|e| anyhow::anyhow!(e)).boxed()), @@ -176,7 +339,7 @@ impl hyper::service::Service for ProxyHandler { let (proxy, _inst) = wasmtime_wasi_http::proxy::Proxy::instantiate_pre( &mut store, - &handler.instance_pre, + &handler.0.instance_pre, ) .await?; diff --git a/src/common.rs b/src/common.rs new file mode 100644 index 000000000000..92708335d1b6 --- /dev/null +++ b/src/common.rs @@ -0,0 +1,243 @@ +//! Common functionality shared between command implementations. + +use anyhow::{bail, Context, Result}; +use clap::Parser; +use std::{path::Path, time::Duration}; +use wasmtime::{Engine, Module, Precompiled, StoreLimits, StoreLimitsBuilder}; +use wasmtime_cli_flags::{opt::WasmtimeOptionValue, CommonOptions}; + +#[cfg(feature = "component-model")] +use wasmtime::component::Component; + +pub enum RunTarget { + Core(Module), + + #[cfg(feature = "component-model")] + Component(Component), +} + +impl RunTarget { + pub fn unwrap_core(&self) -> &Module { + match self { + RunTarget::Core(module) => module, + #[cfg(feature = "component-model")] + RunTarget::Component(_) => panic!("expected a core wasm module, not a component"), + } + } + + #[cfg(feature = "component-model")] + pub fn unwrap_component(&self) -> &Component { + match self { + RunTarget::Component(c) => c, + RunTarget::Core(_) => panic!("expected a component, not a core wasm module"), + } + } +} + +/// Common command line arguments for run commands. +#[derive(Parser)] +pub struct RunCommon { + #[clap(flatten)] + pub common: CommonOptions, + + /// Allow executing precompiled WebAssembly modules as `*.cwasm` files. + /// + /// Note that this option is not safe to pass if the module being passed in + /// is arbitrary user input. Only `wasmtime`-precompiled modules generated + /// via the `wasmtime compile` command or equivalent should be passed as an + /// argument with this option specified. + #[clap(long = "allow-precompiled")] + pub allow_precompiled: bool, + + /// Profiling strategy (valid options are: perfmap, jitdump, vtune, guest) + /// + /// The perfmap, jitdump, and vtune profiling strategies integrate Wasmtime + /// with external profilers such as `perf`. The guest profiling strategy + /// enables in-process sampling and will write the captured profile to + /// `wasmtime-guest-profile.json` by default which can be viewed at + /// https://profiler.firefox.com/. + /// + /// The `guest` option can be additionally configured as: + /// + /// --profile=guest[,path[,interval]] + /// + /// where `path` is where to write the profile and `interval` is the + /// duration between samples. When used with `--wasm-timeout` the timeout + /// will be rounded up to the nearest multiple of this interval. + #[clap( + long, + value_name = "STRATEGY", + value_parser = Profile::parse, + )] + pub profile: Option, +} + +impl RunCommon { + pub fn store_limits(&self) -> StoreLimits { + let mut limits = StoreLimitsBuilder::new(); + if let Some(max) = self.common.wasm.max_memory_size { + limits = limits.memory_size(max); + } + if let Some(max) = self.common.wasm.max_table_elements { + limits = limits.table_elements(max); + } + if let Some(max) = self.common.wasm.max_instances { + limits = limits.instances(max); + } + if let Some(max) = self.common.wasm.max_tables { + limits = limits.tables(max); + } + if let Some(max) = self.common.wasm.max_memories { + limits = limits.memories(max); + } + if let Some(enable) = self.common.wasm.trap_on_grow_failure { + limits = limits.trap_on_grow_failure(enable); + } + + limits.build() + } + + pub fn ensure_allow_precompiled(&self) -> Result<()> { + if self.allow_precompiled { + Ok(()) + } else { + bail!("running a precompiled module requires the `--allow-precompiled` flag") + } + } + + #[cfg(feature = "component-model")] + fn ensure_allow_components(&self) -> Result<()> { + if self.common.wasm.component_model != Some(true) { + bail!("cannot execute a component without `--wasm component-model`"); + } + + Ok(()) + } + + pub fn load_module(&self, engine: &Engine, path: &Path) -> Result { + let path = match path.to_str() { + #[cfg(unix)] + Some("-") => "/dev/stdin".as_ref(), + _ => path, + }; + + // First attempt to load the module as an mmap. If this succeeds then + // detection can be done with the contents of the mmap and if a + // precompiled module is detected then `deserialize_file` can be used + // which is a slightly more optimal version than `deserialize` since we + // can leave most of the bytes on disk until they're referenced. + // + // If the mmap fails, for example if stdin is a pipe, then fall back to + // `std::fs::read` to load the contents. At that point precompiled + // modules must go through the `deserialize` functions. + // + // Note that this has the unfortunate side effect for precompiled + // modules on disk that they're opened once to detect what they are and + // then again internally in Wasmtime as part of the `deserialize_file` + // API. Currently there's no way to pass the `MmapVec` here through to + // Wasmtime itself (that'd require making `wasmtime-runtime` a public + // dependency or `MmapVec` a public type, both of which aren't ready to + // happen at this time). It's hoped though that opening a file twice + // isn't too bad in the grand scheme of things with respect to the CLI. + match wasmtime_runtime::MmapVec::from_file(path) { + Ok(map) => self.load_module_contents( + engine, + path, + &map, + || unsafe { Module::deserialize_file(engine, path) }, + #[cfg(feature = "component-model")] + || unsafe { Component::deserialize_file(engine, path) }, + ), + Err(_) => { + let bytes = std::fs::read(path) + .with_context(|| format!("failed to read file: {}", path.display()))?; + self.load_module_contents( + engine, + path, + &bytes, + || unsafe { Module::deserialize(engine, &bytes) }, + #[cfg(feature = "component-model")] + || unsafe { Component::deserialize(engine, &bytes) }, + ) + } + } + } + + pub fn load_module_contents( + &self, + engine: &Engine, + path: &Path, + bytes: &[u8], + deserialize_module: impl FnOnce() -> Result, + #[cfg(feature = "component-model")] deserialize_component: impl FnOnce() -> Result, + ) -> Result { + Ok(match engine.detect_precompiled(bytes) { + Some(Precompiled::Module) => { + self.ensure_allow_precompiled()?; + RunTarget::Core(deserialize_module()?) + } + #[cfg(feature = "component-model")] + Some(Precompiled::Component) => { + self.ensure_allow_precompiled()?; + self.ensure_allow_components()?; + RunTarget::Component(deserialize_component()?) + } + #[cfg(not(feature = "component-model"))] + Some(Precompiled::Component) => { + bail!("support for components was not enabled at compile time"); + } + None => { + // Parse the text format here specifically to add the `path` to + // the error message if there's a syntax error. + let wasm = wat::parse_bytes(bytes).map_err(|mut e| { + e.set_path(path); + e + })?; + if wasmparser::Parser::is_component(&wasm) { + #[cfg(feature = "component-model")] + { + self.ensure_allow_components()?; + RunTarget::Component(Component::new(engine, &wasm)?) + } + #[cfg(not(feature = "component-model"))] + { + bail!("support for components was not enabled at compile time"); + } + } else { + RunTarget::Core(Module::new(engine, &wasm)?) + } + } + }) + } +} + +#[derive(Clone)] +pub enum Profile { + Native(wasmtime::ProfilingStrategy), + Guest { path: String, interval: Duration }, +} + +impl Profile { + /// Parse the `profile` argument to either the `run` or `serve` commands. + pub fn parse(s: &str) -> Result { + let parts = s.split(',').collect::>(); + match &parts[..] { + ["perfmap"] => Ok(Profile::Native(wasmtime::ProfilingStrategy::PerfMap)), + ["jitdump"] => Ok(Profile::Native(wasmtime::ProfilingStrategy::JitDump)), + ["vtune"] => Ok(Profile::Native(wasmtime::ProfilingStrategy::VTune)), + ["guest"] => Ok(Profile::Guest { + path: "wasmtime-guest-profile.json".to_string(), + interval: Duration::from_millis(10), + }), + ["guest", path] => Ok(Profile::Guest { + path: path.to_string(), + interval: Duration::from_millis(10), + }), + ["guest", path, dur] => Ok(Profile::Guest { + path: path.to_string(), + interval: WasmtimeOptionValue::parse(Some(dur))?, + }), + _ => bail!("unknown profiling strategy: {s}"), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 5cf21f6fe768..11473981a467 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,3 +11,5 @@ #![warn(unused_import_braces)] pub mod commands; + +pub(crate) mod common; From d4e4f610ce86289619e5962ae13031fec9e5d71d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 4 Oct 2023 15:00:21 -0500 Subject: [PATCH 058/199] riscv64: Consolidate conditional moves into one instruction (#7145) * riscv64: Add codegen tests for min/max * riscv64: Remove unused `Type` payload from `Select` * riscv64: Add codegen test for brif * riscv64: Consolidate conditional moves into one instruction This commit removes the `IntSelect` and `SelectReg` pseudo-instructions from the riscv64 backend and consolidates them into the `Select` instruction. Additionally the `Select` instruction is updated to subsume the functionality of these two previous instructions. Namely `Select` now operates with `ValueRegs` to handle i128 and additionally takes an `IntegerCompare` as the condition for the conditional branch to use. This commit touches a fair bit of the backend since conditional selection of registers was used in quite a few places. The previous `gen_select_*` functions are replaced with new typed equivalents of `gen_select_{xreg,vreg,freg,regs}`. Furthermore new `cmp_*` helpers were added to create `IntegerCompare` instructions which sort-of match conditional branch instructions, or at least the pnemonics they use. Finally since this affected the `select` CLIF instruction itself I went ahead and did some refactoring there too. The `select` instruction creates an `IntegerCompare` from its argument to use to generate the appropriate register selection instruction. This is basically the same thing that `brif` does and now both go through a new helper, `lower_int_compare`, which takes a `Value` and produces an `IntegerCompare` representing if that value is either true or false. This enables folding an `icmp` or an `fcmp`, for example, directly into a branching instruction. * Fix a test * Favor sign-extension in equality comparisons --- cranelift/codegen/src/isa/riscv64/inst.isle | 244 +++++---- .../codegen/src/isa/riscv64/inst/args.rs | 31 -- .../codegen/src/isa/riscv64/inst/emit.rs | 168 ++---- .../src/isa/riscv64/inst/emit_tests.rs | 8 - cranelift/codegen/src/isa/riscv64/inst/mod.rs | 97 ++-- .../src/isa/riscv64/inst/unwind/systemv.rs | 2 +- cranelift/codegen/src/isa/riscv64/lower.isle | 85 +-- .../codegen/src/isa/riscv64/lower/isle.rs | 41 -- cranelift/codegen/src/machinst/isle.rs | 10 + cranelift/codegen/src/prelude_lower.isle | 11 +- .../filetests/isa/riscv64/bitops.clif | 261 +++++---- .../filetests/filetests/isa/riscv64/brif.clif | 449 ++++++++++++++++ .../filetests/isa/riscv64/cls-zbb.clif | 56 +- .../filetests/isa/riscv64/clz-zbb.clif | 8 +- .../filetests/isa/riscv64/cold-blocks.clif | 28 +- .../filetests/isa/riscv64/condbr.clif | 35 +- .../filetests/isa/riscv64/condops.clif | 32 +- .../filetests/isa/riscv64/ctz-zbb.clif | 8 +- .../filetests/isa/riscv64/i128-bmask.clif | 32 +- .../filetests/filetests/isa/riscv64/iabs.clif | 20 +- .../filetests/isa/riscv64/ishl-const.clif | 137 +++-- .../filetests/filetests/isa/riscv64/ishl.clif | 133 +++-- .../filetests/isa/riscv64/issue-6954.clif | 500 ++++++------------ .../filetests/filetests/isa/riscv64/rotl.clif | 135 +++-- .../filetests/filetests/isa/riscv64/rotr.clif | 111 ++-- .../filetests/isa/riscv64/select-float.clif | 68 +-- .../filetests/isa/riscv64/select.clif | 366 +++++++------ .../filetests/isa/riscv64/simd-select.clif | 32 +- .../filetests/isa/riscv64/simd-umax.clif | 1 - .../filetests/filetests/isa/riscv64/smax.clif | 153 ++++++ .../filetests/filetests/isa/riscv64/smin.clif | 153 ++++++ .../filetests/isa/riscv64/sshr-const.clif | 162 +++--- .../filetests/filetests/isa/riscv64/sshr.clif | 162 +++--- .../filetests/isa/riscv64/traps.clif | 17 +- .../filetests/filetests/isa/riscv64/umax.clif | 153 ++++++ .../filetests/filetests/isa/riscv64/umin.clif | 153 ++++++ .../filetests/isa/riscv64/ushr-const.clif | 128 ++--- .../filetests/filetests/isa/riscv64/ushr.clif | 128 ++--- ...0_guard_no_spectre_i32_access_0_offset.wat | 32 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 38 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 56 +- ..._0_guard_no_spectre_i8_access_0_offset.wat | 26 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 38 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 42 +- ...f_guard_no_spectre_i32_access_0_offset.wat | 26 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 26 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 38 +- ...ff_guard_no_spectre_i8_access_0_offset.wat | 26 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 26 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 38 +- ...0_guard_no_spectre_i32_access_0_offset.wat | 18 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 34 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 46 +- ..._0_guard_no_spectre_i8_access_0_offset.wat | 12 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 34 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 38 +- ...f_guard_no_spectre_i32_access_0_offset.wat | 12 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 12 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 34 +- ...ff_guard_no_spectre_i8_access_0_offset.wat | 12 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 12 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 34 +- ...0_guard_no_spectre_i32_access_0_offset.wat | 30 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 30 +- ..._0_guard_no_spectre_i8_access_0_offset.wat | 30 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 30 +- ...0_guard_no_spectre_i32_access_0_offset.wat | 20 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 20 +- ..._0_guard_no_spectre_i8_access_0_offset.wat | 20 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 20 +- ...f_guard_no_spectre_i32_access_0_offset.wat | 20 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 20 +- ...ff_guard_no_spectre_i8_access_0_offset.wat | 20 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 20 +- .../filetests/filetests/isa/riscv64/zca.clif | 2 +- 75 files changed, 2988 insertions(+), 2322 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/riscv64/brif.clif create mode 100644 cranelift/filetests/filetests/isa/riscv64/smax.clif create mode 100644 cranelift/filetests/filetests/isa/riscv64/smin.clif create mode 100644 cranelift/filetests/filetests/isa/riscv64/umax.clif create mode 100644 cranelift/filetests/filetests/isa/riscv64/umin.clif diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 5d080578ca13..32968393af10 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -221,9 +221,8 @@ ;; select x or y base on condition (Select - (dst VecWritableReg) - (ty Type) - (condition Reg) + (dst WritableValueRegs) + (condition IntegerCompare) (x ValueRegs) (y ValueRegs)) @@ -242,13 +241,6 @@ (addr Reg) (v Reg) (ty Type)) - ;; select x or y base on op_code - (IntSelect - (op IntSelectOP) - (dst VecWritableReg) - (x ValueRegs) - (y ValueRegs) - (ty Type)) ;; an integer compare. (Icmp (cc IntCC) @@ -256,14 +248,6 @@ (a ValueRegs) (b ValueRegs) (ty Type)) - ;; select a reg base on condition. - ;; very useful because in lowering stage we can not have condition branch. - (SelectReg - (rd WritableReg) - (rs1 Reg) - (rs2 Reg) - (condition IntegerCompare)) - ;; (FcvtToInt (is_sat bool) (rd WritableReg) @@ -419,13 +403,6 @@ (Trunc) )) -(type IntSelectOP (enum - (Smax) - (Umax) - (Smin) - (Umin) -)) - (type AtomicOP (enum (LrW) (ScW) @@ -2233,7 +2210,7 @@ (part1 Reg (rv_sll rs shamt)) ;; (part2 Reg (rv_srl rs len_sub_shamt)) - (part3 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) part2))) + (part3 Reg (gen_select_xreg (cmp_eqz shamt) (zero_reg) part2))) (rv_or part1 part3))) @@ -2282,7 +2259,7 @@ ;; (part2 XReg (rv_sll rs len_sub_shamt)) ;; - (part3 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) part2))) + (part3 XReg (gen_select_xreg (cmp_eqz shamt) (zero_reg) part2))) (rv_or part1 part3))) @@ -2326,21 +2303,22 @@ (low_part1 XReg (rv_sll (value_regs_get x 0) shamt)) (low_part2 XReg (rv_srl (value_regs_get x 1) len_sub_shamt)) ;;; if shamt == 0 low_part2 will overflow we should zero instead. - (low_part3 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) low_part2)) + (low_part3 XReg (gen_select_xreg (cmp_eqz shamt) (zero_reg) low_part2)) (low XReg (rv_or low_part1 low_part3)) ;; (high_part1 XReg (rv_sll (value_regs_get x 1) shamt)) (high_part2 XReg (rv_srl (value_regs_get x 0) len_sub_shamt)) - (high_part3 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) high_part2)) + (high_part3 XReg (gen_select_xreg (cmp_eqz shamt) (zero_reg) high_part2)) (high XReg (rv_or high_part1 high_part3)) ;; (const64 XReg (imm $I64 64)) (shamt_128 XReg (rv_andi (value_regs_get y 0) (imm12_const 127)))) ;; right now we only rotate less than 64 bits. ;; if shamt is greater than or equal 64 , we should switch low and high. - (value_regs - (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high low) - (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 low high) + (gen_select_regs + (cmp_geu shamt_128 const64) + (value_regs high low) + (value_regs low high) ))) @@ -2355,12 +2333,12 @@ (low_part1 XReg (rv_srl (value_regs_get x 0) shamt)) (low_part2 XReg (rv_sll (value_regs_get x 1) len_sub_shamt)) ;;; if shamt == 0 low_part2 will overflow we should zero instead. - (low_part3 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) low_part2)) + (low_part3 XReg (gen_select_xreg (cmp_eqz shamt) (zero_reg) low_part2)) (low XReg (rv_or low_part1 low_part3)) ;; (high_part1 XReg (rv_srl (value_regs_get x 1) shamt)) (high_part2 XReg (rv_sll (value_regs_get x 0) len_sub_shamt)) - (high_part3 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) high_part2)) + (high_part3 XReg (gen_select_xreg (cmp_eqz shamt) (zero_reg) high_part2)) (high XReg (rv_or high_part1 high_part3)) ;; @@ -2368,9 +2346,10 @@ (shamt_128 XReg (rv_andi (value_regs_get y 0) (imm12_const 127)))) ;; right now we only rotate less than 64 bits. ;; if shamt is greater than or equal 64 , we should switch low and high. - (value_regs - (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high low) - (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 low high) + (gen_select_regs + (cmp_geu shamt_128 const64) + (value_regs high low) + (value_regs low high) ))) ;; Generates a AMode that points to a register plus an offset. @@ -2566,41 +2545,31 @@ (decl gen_stack_addr (StackSlot Offset32) Reg) (extern constructor gen_stack_addr gen_stack_addr) -;; -(decl gen_select (Type Reg ValueRegs ValueRegs) ValueRegs) -(rule - (gen_select ty c x y) +(decl gen_select_xreg (IntegerCompare XReg XReg) XReg) +(rule (gen_select_xreg c x y) (let - ((dst VecWritableReg (alloc_vec_writable ty)) - ;; - (reuslt VecWritableReg (vec_writable_clone dst)) - (_ Unit (emit (MInst.Select dst ty c x y)))) - (vec_writable_to_regs reuslt))) - -;; Parameters are "intcc compare_a compare_b rs1 rs2". -(decl gen_select_reg (IntCC XReg XReg Reg Reg) Reg) -(extern constructor gen_select_reg gen_select_reg) - -;;; clone WritableReg -;;; if not rust compiler will complain about use moved value. -(decl vec_writable_clone (VecWritableReg) VecWritableReg) -(extern constructor vec_writable_clone vec_writable_clone) - -(decl vec_writable_to_regs (VecWritableReg) ValueRegs) -(extern constructor vec_writable_to_regs vec_writable_to_regs) - -(decl alloc_vec_writable (Type) VecWritableReg) -(extern constructor alloc_vec_writable alloc_vec_writable) - -(decl gen_int_select (Type IntSelectOP ValueRegs ValueRegs) ValueRegs) -(rule - (gen_int_select ty op x y) + ((dst WritableReg (temp_writable_xreg)) + (_ Unit (emit (MInst.Select dst c x y)))) + (writable_reg_to_reg dst))) +(decl gen_select_vreg (IntegerCompare VReg VReg) VReg) +(rule (gen_select_vreg c x y) + (let + ((dst WritableReg (temp_writable_vreg)) + (_ Unit (emit (MInst.Select dst c (vreg_to_reg x) (vreg_to_reg y))))) + (writable_reg_to_reg dst))) +(decl gen_select_freg (IntegerCompare FReg FReg) FReg) +(rule (gen_select_freg c x y) (let - ( ;;; - (dst VecWritableReg (alloc_vec_writable ty)) - ;;; - (_ Unit (emit (MInst.IntSelect op (vec_writable_clone dst) x y ty)))) - (vec_writable_to_regs dst))) + ((dst WritableReg (temp_writable_freg)) + (_ Unit (emit (MInst.Select dst c (freg_to_reg x) (freg_to_reg y))))) + (writable_reg_to_reg dst))) +(decl gen_select_regs (IntegerCompare ValueRegs ValueRegs) ValueRegs) +(rule (gen_select_regs c x y) + (let + ((dst1 WritableReg (temp_writable_xreg)) + (dst2 WritableReg (temp_writable_xreg)) + (_ Unit (emit (MInst.Select (writable_value_regs dst1 dst2) c x y)))) + (value_regs dst1 dst2))) (decl udf (TrapCode) InstOutput) (rule @@ -2665,20 +2634,6 @@ (decl int_zero_reg (Type) ValueRegs) (extern constructor int_zero_reg int_zero_reg) -;; Convert a truthy value, possibly of more than one register (an I128), to -;; one register. -;; -;; Zero-extends as necessary to ensure that the returned register only contains -;; nonzero if the input value was logically nonzero. -(decl truthy_to_reg (Value) XReg) -(rule 1 (truthy_to_reg val @ (value_type (fits_in_64 _))) - (zext val)) -(rule 0 (truthy_to_reg val @ (value_type $I128)) - (let ((regs ValueRegs val) - (lo XReg (value_regs_get regs 0)) - (hi XReg (value_regs_get regs 1))) - (rv_or lo hi))) - ;; Consume a CmpResult, producing a branch on its result. (decl cond_br (IntegerCompare CondBrTarget CondBrTarget) SideEffectNoResult) (rule (cond_br cmp then else) @@ -2698,25 +2653,102 @@ (extern constructor label_to_br_target label_to_br_target) (convert MachLabel CondBrTarget label_to_br_target) -(decl partial lower_branch (Inst MachLabelSlice) Unit) -(rule (lower_branch (jump _) (single_target label)) - (emit_side_effect (rv_j label))) +(decl cmp_eqz (XReg) IntegerCompare) +(rule (cmp_eqz r) (int_compare (IntCC.Equal) r (zero_reg))) -;; Default behavior for branching based on an input value. -(rule (lower_branch (brif v @ (value_type (fits_in_64 ty)) _ _) (two_targets then else)) - (emit_side_effect (cond_br (int_compare (IntCC.NotEqual) (zext v) (zero_reg)) then else))) -(rule 2 (lower_branch (brif v @ (value_type $I128)_ _) (two_targets then else)) - (emit_side_effect (cond_br (int_compare (IntCC.NotEqual) (truthy_to_reg v) (zero_reg)) then else))) +(decl cmp_nez (XReg) IntegerCompare) +(rule (cmp_nez r) (int_compare (IntCC.NotEqual) r (zero_reg))) -;; Branching on the result of an fcmp. -(rule 1 (lower_branch (brif (maybe_uextend (fcmp cc a @ (value_type ty) b)) _ _) (two_targets then else)) - (emit_side_effect (cond_br (emit_fcmp cc ty a b) then else))) +(decl cmp_eq (XReg XReg) IntegerCompare) +(rule (cmp_eq rs1 rs2) (int_compare (IntCC.Equal) rs1 rs2)) -(decl fcmp_to_compare (FCmp) IntegerCompare) -(rule (fcmp_to_compare (FCmp.One r)) (int_compare (IntCC.NotEqual) r (zero_reg))) -(rule (fcmp_to_compare (FCmp.Zero r)) (int_compare (IntCC.Equal) r (zero_reg))) -(convert FCmp IntegerCompare fcmp_to_compare) +(decl cmp_ne (XReg XReg) IntegerCompare) +(rule (cmp_ne rs1 rs2) (int_compare (IntCC.NotEqual) rs1 rs2)) + +(decl cmp_lt (XReg XReg) IntegerCompare) +(rule (cmp_lt rs1 rs2) (int_compare (IntCC.SignedLessThan) rs1 rs2)) + +(decl cmp_ltz (XReg) IntegerCompare) +(rule (cmp_ltz rs) (int_compare (IntCC.SignedLessThan) rs (zero_reg))) + +(decl cmp_gt (XReg XReg) IntegerCompare) +(rule (cmp_gt rs1 rs2) (int_compare (IntCC.SignedGreaterThan) rs1 rs2)) + +(decl cmp_ge (XReg XReg) IntegerCompare) +(rule (cmp_ge rs1 rs2) (int_compare (IntCC.SignedGreaterThanOrEqual) rs1 rs2)) + +(decl cmp_le (XReg XReg) IntegerCompare) +(rule (cmp_le rs1 rs2) (int_compare (IntCC.SignedLessThanOrEqual) rs1 rs2)) + +(decl cmp_gtu (XReg XReg) IntegerCompare) +(rule (cmp_gtu rs1 rs2) (int_compare (IntCC.UnsignedGreaterThan) rs1 rs2)) + +(decl cmp_geu (XReg XReg) IntegerCompare) +(rule (cmp_geu rs1 rs2) (int_compare (IntCC.UnsignedGreaterThanOrEqual) rs1 rs2)) + +(decl cmp_ltu (XReg XReg) IntegerCompare) +(rule (cmp_ltu rs1 rs2) (int_compare (IntCC.UnsignedLessThan) rs1 rs2)) + +(decl cmp_leu (XReg XReg) IntegerCompare) +(rule (cmp_leu rs1 rs2) (int_compare (IntCC.UnsignedLessThanOrEqual) rs1 rs2)) + +;; Helper to generate an `IntegerCompare` which represents the "truthy" value of +;; the input provided. +;; +;; This is used in `Select` and `brif` for example to generate conditional +;; branches. The returned comparison, when taken, represents that `Value` is +;; nonzero. When not taken the input `Value` is zero. +(decl lower_int_compare (Value) IntegerCompare) + +;; Base case - convert to a "truthy" value and compare it against zero. +;; +;; Note that non-64-bit types need to be extended since the upper bits from +;; Cranelift's point of view are undefined. Favor a zero extension for 8-bit +;; types because that's a single `andi` instruction, but favor sign-extension +;; for 16 and 32-bit types because many RISC-V which operate on the low 32-bits. +;; Additionally the base 64-bit ISA has a single instruction for sign-extending +;; from 32 to 64-bits which makes that a bit cheaper if used. +;; of registers sign-extend the results. +(rule 0 (lower_int_compare val @ (value_type (fits_in_64 _))) + (cmp_nez (sext val))) +(rule 1 (lower_int_compare val @ (value_type $I8)) + (cmp_nez (zext val))) +(rule 1 (lower_int_compare val @ (value_type $I128)) + (cmp_nez (rv_or (value_regs_get val 0) (value_regs_get val 1)))) + +;; If the input value is itself an `icmp` we can avoid generating the result of +;; the `icmp` and instead move the comparison directly into the `IntegerCompare` +;; that's returned. Note that comparisons compare full registers so +;; sign-extension according to the integer comparison performed here is +;; required. +;; +;; Also note that as a small optimization `Equal` and `NotEqual` use +;; sign-extension for 32-bit values since the same result is produced with +;; either zero-or-sign extension and many values are already sign-extended given +;; the RV64 instruction set (e.g. `addw` adds 32-bit values and sign extends), +;; theoretically resulting in more efficient codegen. +(rule 2 (lower_int_compare (maybe_uextend (icmp cc a b @ (value_type (fits_in_64 in_ty))))) + (int_compare cc (zext a) (zext b))) +(rule 3 (lower_int_compare (maybe_uextend (icmp cc a b @ (value_type (fits_in_64 in_ty))))) + (if (signed_cond_code cc)) + (int_compare cc (sext a) (sext b))) +(rule 4 (lower_int_compare (maybe_uextend (icmp cc @ (IntCC.Equal) a b @ (value_type $I32)))) + (int_compare cc (sext a) (sext b))) +(rule 4 (lower_int_compare (maybe_uextend (icmp cc @ (IntCC.NotEqual) a b @ (value_type $I32)))) + (int_compare cc (sext a) (sext b))) + +;; If the input is an `fcmp` then the `FCmp` return value is directly +;; convertible to `IntegerCompare` which can shave off an instruction from the +;; fallback lowering above. +(rule 2 (lower_int_compare (maybe_uextend (fcmp cc a @ (value_type ty) b))) + (emit_fcmp cc ty a b)) + +(decl partial lower_branch (Inst MachLabelSlice) Unit) +(rule (lower_branch (jump _) (single_target label)) + (emit_side_effect (rv_j label))) +(rule (lower_branch (brif v _ _) (two_targets then else)) + (emit_side_effect (cond_br (lower_int_compare v) then else))) (decl lower_br_table (Reg MachLabelSlice) Unit) (extern constructor lower_br_table lower_br_table) @@ -2802,7 +2834,7 @@ (rule (max (fits_in_64 (ty_int ty)) x y) (if-let $false (has_zbb)) - (gen_select_reg (IntCC.SignedGreaterThan) x y x y)) + (gen_select_xreg (cmp_gt x y) x y)) ;; Builds an instruction sequence that traps if the comparision succeeds. @@ -2840,8 +2872,11 @@ ;; Generates either 0 if `Value` is zero or -1 otherwise. (decl gen_bmask (Value) XReg) -(rule (gen_bmask val) - (let ((non_zero XReg (rv_snez (truthy_to_reg val)))) +(rule 0 (gen_bmask val @ (value_type (fits_in_64 _))) + (let ((non_zero XReg (rv_snez (sext val)))) + (rv_neg non_zero))) +(rule 1 (gen_bmask val @ (value_type $I128)) + (let ((non_zero XReg (rv_snez (rv_or (value_regs_get val 0) (value_regs_get val 1))))) (rv_neg non_zero))) (decl lower_bmask (Value Type) ValueRegs) @@ -2898,6 +2933,11 @@ (rule (fcmp_invert (FCmp.One r)) (FCmp.Zero r)) (rule (fcmp_invert (FCmp.Zero r)) (FCmp.One r)) +(decl fcmp_to_compare (FCmp) IntegerCompare) +(rule (fcmp_to_compare (FCmp.One r)) (cmp_nez r)) +(rule (fcmp_to_compare (FCmp.Zero r)) (cmp_eqz r)) +(convert FCmp IntegerCompare fcmp_to_compare) + ;; Compare two floating point numbers and return a zero/non-zero result. (decl emit_fcmp (FloatCC Type FReg FReg) FCmp) diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs index a5acb54d0066..0ce56295f849 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/args.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs @@ -1628,37 +1628,6 @@ impl AtomicOP { } } -impl IntSelectOP { - #[inline] - pub(crate) fn from_ir_op(op: crate::ir::Opcode) -> Self { - match op { - crate::ir::Opcode::Smax => Self::Smax, - crate::ir::Opcode::Umax => Self::Umax, - crate::ir::Opcode::Smin => Self::Smin, - crate::ir::Opcode::Umin => Self::Umin, - _ => unreachable!(), - } - } - #[inline] - pub(crate) fn op_name(self) -> &'static str { - match self { - IntSelectOP::Smax => "smax", - IntSelectOP::Umax => "umax", - IntSelectOP::Smin => "smin", - IntSelectOP::Umin => "umin", - } - } - #[inline] - pub(crate) fn to_int_cc(self) -> IntCC { - match self { - IntSelectOP::Smax => IntCC::SignedGreaterThan, - IntSelectOP::Umax => IntCC::UnsignedGreaterThan, - IntSelectOP::Smin => IntCC::SignedLessThan, - IntSelectOP::Umin => IntCC::UnsignedLessThan, - } - } -} - ///Atomic Memory ordering. #[derive(Copy, Clone, Debug)] pub enum AMO { diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs index d6cf12d6066b..130eeaa12142 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs @@ -358,9 +358,7 @@ impl Inst { | Inst::Atomic { .. } | Inst::Select { .. } | Inst::AtomicCas { .. } - | Inst::IntSelect { .. } | Inst::Icmp { .. } - | Inst::SelectReg { .. } | Inst::FcvtToInt { .. } | Inst::RawData { .. } | Inst::AtomicStore { .. } @@ -1787,35 +1785,30 @@ impl Inst { condition, ref x, ref y, - ty: _ty, } => { - let mut insts = SmallInstVec::new(); + let label_true = sink.get_label(); let label_false = sink.get_label(); - insts.push(Inst::CondBr { - taken: CondBrTarget::Label(label_false), - not_taken: CondBrTarget::Fallthrough, - kind: IntegerCompare { - kind: IntCC::Equal, - rs1: condition, - rs2: zero_reg(), - }, - }); + let label_end = sink.get_label(); + Inst::CondBr { + taken: CondBrTarget::Label(label_true), + not_taken: CondBrTarget::Label(label_false), + kind: condition, + } + .emit(&[], sink, emit_info, state); + sink.bind_label(label_true, &mut state.ctrl_plane); // here is the true // select the first value - insts.extend(gen_moves(&dst[..], x.regs())); - let label_jump_over = sink.get_label(); - insts.push(Inst::gen_jump(label_jump_over)); - // here is false - insts - .drain(..) - .for_each(|i: Inst| i.emit(&[], sink, emit_info, state)); + for i in gen_moves(dst.regs(), x.regs()) { + i.emit(&[], sink, emit_info, state); + } + Inst::gen_jump(label_end).emit(&[], sink, emit_info, state); + sink.bind_label(label_false, &mut state.ctrl_plane); - // select second value1 - insts.extend(gen_moves(&dst[..], y.regs())); - insts - .into_iter() - .for_each(|i| i.emit(&[], sink, emit_info, state)); - sink.bind_label(label_jump_over, &mut state.ctrl_plane); + for i in gen_moves(dst.regs(), y.regs()) { + i.emit(&[], sink, emit_info, state); + } + + sink.bind_label(label_end, &mut state.ctrl_plane); } &Inst::Jalr { rd, base, offset } => { sink.put4(enc_jalr(rd, base, offset)); @@ -2143,80 +2136,6 @@ impl Inst { .emit(&[], sink, emit_info, state); } - &Inst::IntSelect { - op, - ref dst, - x, - y, - ty, - } => { - let label_true = sink.get_label(); - let label_false = sink.get_label(); - let label_done = sink.get_label(); - Inst::lower_br_icmp( - op.to_int_cc(), - x, - y, - CondBrTarget::Label(label_true), - CondBrTarget::Label(label_false), - ty, - ) - .into_iter() - .for_each(|i| i.emit(&[], sink, emit_info, state)); - - let gen_move = |dst: &Vec>, - val: &ValueRegs, - sink: &mut MachBuffer, - state: &mut EmitState| { - let mut insts = SmallInstVec::new(); - insts.push(Inst::Mov { - rd: dst[0], - rm: val.regs()[0], - ty: I64, - }); - if ty.bits() == 128 { - insts.push(Inst::Mov { - rd: dst[1], - rm: val.regs()[1], - ty, - }); - } - insts - .into_iter() - .for_each(|i| i.emit(&[], sink, emit_info, state)); - }; - //here is true , use x. - sink.bind_label(label_true, &mut state.ctrl_plane); - gen_move(dst, &x, sink, state); - Inst::gen_jump(label_done).emit(&[], sink, emit_info, state); - // here is false use y - sink.bind_label(label_false, &mut state.ctrl_plane); - gen_move(dst, &y, sink, state); - sink.bind_label(label_done, &mut state.ctrl_plane); - } - - &Inst::SelectReg { - condition, - rd, - rs1, - rs2, - } => { - let label_true = sink.get_label(); - let label_jump_over = sink.get_label(); - let ty = Inst::canonical_type_for_rc(rs1.class()); - - sink.use_label_at_offset(sink.cur_offset(), label_true, LabelUse::B12); - let x = condition.emit(); - sink.put4(x); - // here is false , use rs2 - Inst::gen_move(rd, rs2, ty).emit(&[], sink, emit_info, state); - // and jump over - Inst::gen_jump(label_jump_over).emit(&[], sink, emit_info, state); - // here condition is true , use rs1 - sink.bind_label(label_true, &mut state.ctrl_plane); - Inst::gen_move(rd, rs1, ty).emit(&[], sink, emit_info, state); - sink.bind_label(label_jump_over, &mut state.ctrl_plane); - } &Inst::FcvtToInt { is_sat, rd, @@ -3412,6 +3331,13 @@ impl Inst { } } + fn alloc_writable_value_regs( + origin: &ValueRegs>, + alloc: &mut AllocationConsumer, + ) -> ValueRegs> { + alloc_value_regs(&origin.map(|r| r.to_reg()), alloc).map(Writable::from_reg) + } + match self { Inst::Nop0 => self, Inst::Nop4 => self, @@ -3633,23 +3559,19 @@ impl Inst { condition, ref x, ref y, - ty, } => { - let condition = allocs.next(condition); + let mut condition: IntegerCompare = condition.clone(); + condition.rs1 = allocs.next(condition.rs1); + condition.rs2 = allocs.next(condition.rs2); let x = alloc_value_regs(x, allocs); let y = alloc_value_regs(y, allocs); - let dst: Vec<_> = dst - .clone() - .into_iter() - .map(|r| allocs.next_writable(r)) - .collect(); + let dst = alloc_writable_value_regs(dst, allocs); Inst::Select { dst, condition, x, y, - ty, } } Inst::Jalr { rd, base, offset } => { @@ -3714,36 +3636,6 @@ impl Inst { dst: allocs.next_writable(dst), }, - Inst::IntSelect { - op, - dst, - ref x, - ref y, - ty, - } => { - let x = alloc_value_regs(x, allocs); - let y = alloc_value_regs(y, allocs); - let dst: Vec<_> = dst.iter().map(|r| allocs.next_writable(*r)).collect(); - Inst::IntSelect { op, ty, dst, x, y } - } - - Inst::SelectReg { - condition, - rd, - rs1, - rs2, - } => { - let mut condition: IntegerCompare = condition.clone(); - condition.rs1 = allocs.next(condition.rs1); - condition.rs2 = allocs.next(condition.rs2); - Inst::SelectReg { - condition, - rs1: allocs.next(rs1), - rs2: allocs.next(rs2), - rd: allocs.next_writable(rd), - } - } - Inst::FcvtToInt { is_sat, rd, diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs b/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs index d5038bfca4b1..111259278469 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs @@ -2223,14 +2223,6 @@ fn riscv64_worst_case_instruction_size() { //there are all candidates potential generate a lot of bytes. let mut candidates: Vec = vec![]; - candidates.push(Inst::IntSelect { - dst: vec![writable_a0(), writable_a0()], - ty: I128, - op: IntSelectOP::Smax, - x: ValueRegs::two(x_reg(1), x_reg(2)), - y: ValueRegs::two(x_reg(3), x_reg(4)), - }); - candidates.push(Inst::FcvtToInt { rd: writable_a0(), rs: fa0(), diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs index 77a207e21788..4c10191eccfc 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs @@ -55,8 +55,7 @@ pub(crate) type VecWritableReg = Vec>; pub use crate::isa::riscv64::lower::isle::generated_code::{ AluOPRRI, AluOPRRR, AtomicOP, CsrImmOP, CsrRegOP, FClassResult, FFlagsException, FloatRoundOP, - FloatSelectOP, FpuOPRR, FpuOPRRR, FpuOPRRRR, IntSelectOP, LoadOP, MInst as Inst, StoreOP, CSR, - FRM, + FloatSelectOP, FpuOPRR, FpuOPRRR, FpuOPRRRR, LoadOP, MInst as Inst, StoreOP, CSR, FRM, }; use crate::isa::riscv64::lower::isle::generated_code::{CjOp, MInst, VecAluOpRRImm5, VecAluOpRRR}; @@ -523,11 +522,25 @@ fn riscv64_get_operands VReg>(inst: &Inst, collector: &mut Operan y, .. } => { - collector.reg_use(condition); + collector.reg_use(condition.rs1); + collector.reg_use(condition.rs2); collector.reg_uses(x.regs()); collector.reg_uses(y.regs()); - for d in dst.iter() { - collector.reg_early_def(d.clone()); + // If there's more than one destination register then use + // `reg_early_def` to prevent destination registers from overlapping + // with any operands. This ensures that the lowering doesn't have to + // deal with a situation such as when the input registers need to be + // swapped when moved to the destination. + // + // When there's only one destination register though don't use an + // early def because once the register is written no other inputs + // are read so it's ok for the destination to overlap the sources. + if dst.regs().len() > 1 { + for d in dst.regs() { + collector.reg_early_def(d.clone()); + } + } else { + collector.reg_defs(dst.regs()); } } &Inst::AtomicCas { @@ -543,18 +556,6 @@ fn riscv64_get_operands VReg>(inst: &Inst, collector: &mut Operan collector.reg_early_def(t0); collector.reg_early_def(dst); } - &Inst::IntSelect { - ref dst, - ref x, - ref y, - .. - } => { - collector.reg_uses(x.regs()); - collector.reg_uses(y.regs()); - for d in dst.iter() { - collector.reg_early_def(d.clone()); - } - } &Inst::Icmp { rd, a, b, .. } => { collector.reg_uses(a.regs()); @@ -562,18 +563,6 @@ fn riscv64_get_operands VReg>(inst: &Inst, collector: &mut Operan collector.reg_def(rd); } - &Inst::SelectReg { - rd, - rs1, - rs2, - condition, - } => { - collector.reg_use(condition.rs1); - collector.reg_use(condition.rs2); - collector.reg_use(rs1); - collector.reg_use(rs2); - collector.reg_def(rd); - } &Inst::FcvtToInt { rd, rs, tmp, .. } => { collector.reg_use(rs); collector.reg_early_def(tmp); @@ -1283,25 +1272,6 @@ impl Inst { tmp ) } - &Inst::SelectReg { - rd, - rs1, - rs2, - ref condition, - } => { - let c_rs1 = format_reg(condition.rs1, allocs); - let c_rs2 = format_reg(condition.rs2, allocs); - let rs1 = format_reg(rs1, allocs); - let rs2 = format_reg(rs2, allocs); - let rd = format_reg(rd.to_reg(), allocs); - format!( - "select_reg {},{},{}##condition={}", - rd, - rs1, - rs2, - format!("({} {} {})", c_rs1, condition.kind.to_static_str(), c_rs2), - ) - } &Inst::AtomicCas { offset, t0, @@ -1328,19 +1298,6 @@ impl Inst { let rd = format_reg(rd.to_reg(), allocs); format!("{} {},{},{}##ty={}", cc.to_static_str(), rd, a, b, ty) } - &Inst::IntSelect { - op, - ref dst, - x, - y, - ty, - } => { - let x = format_regs(x.regs(), allocs); - let y = format_regs(y.regs(), allocs); - let dst: Vec<_> = dst.iter().map(|r| r.to_reg()).collect(); - let dst = format_regs(&dst[..], allocs); - format!("{} {},{},{}##ty={}", op.op_name(), dst, x, y, ty,) - } &Inst::BrTable { index, tmp1, @@ -1735,14 +1692,22 @@ impl Inst { condition, ref x, ref y, - ty, } => { - let condition = format_reg(condition, allocs); + let c_rs1 = format_reg(condition.rs1, allocs); + let c_rs2 = format_reg(condition.rs2, allocs); let x = format_regs(x.regs(), allocs); let y = format_regs(y.regs(), allocs); - let dst: Vec<_> = dst.clone().into_iter().map(|r| r.to_reg()).collect(); - let dst = format_regs(&dst[..], allocs); - format!("select_{} {},{},{}##condition={}", ty, dst, x, y, condition) + let dst = dst.map(|r| r.to_reg()); + let dst = format_regs(dst.regs(), allocs); + format!( + "select {},{},{}##condition=({} {} {})", + dst, + x, + y, + c_rs1, + condition.kind.to_static_str(), + c_rs2 + ) } &MInst::Udf { trap_code } => format!("udf##trap_code={}", trap_code), &MInst::EBreak {} => String::from("ebreak"), diff --git a/cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs b/cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs index 9c0ee61191ae..75bd6cebf7ec 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs @@ -145,7 +145,7 @@ mod tests { assert_eq!( format!("{:?}", fde), - "FrameDescriptionEntry { address: Constant(4321), length: 20, lsda: None, instructions: [] }" + "FrameDescriptionEntry { address: Constant(4321), length: 16, lsda: None, instructions: [] }" ); } diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index 53051fa31d6e..0c6fbf248e87 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -880,7 +880,7 @@ (high XReg (lower_ctz $I64 x_hi)) (low XReg (lower_ctz $I64 x_lo)) ;; Only add the top half if the bottom is zero - (high XReg (gen_select_reg (IntCC.Equal) x_lo (zero_reg) high (zero_reg))) + (high XReg (gen_select_xreg (cmp_eqz x_lo) high (zero_reg))) (result XReg (rv_add low high))) (value_regs result (imm $I64 0)))) @@ -895,7 +895,7 @@ (high XReg (gen_clz x_hi)) (low XReg (gen_clz x_lo)) ;; Only add the bottom zeros if the top half is zero - (low XReg (gen_select_reg (IntCC.Equal) x_hi (zero_reg) low (zero_reg)))) + (low XReg (gen_select_xreg (cmp_eqz x_hi) low (zero_reg)))) (value_regs (rv_add high low) (imm $I64 0)))) (rule 2 (lower (has_type (fits_in_16 ty) (clz x))) @@ -924,7 +924,7 @@ (rule (lower (has_type (fits_in_64 ty) (cls x))) (let ((tmp XReg (sext x)) - (tmp2 XReg (gen_select_reg (IntCC.SignedLessThan) tmp (zero_reg) (rv_not tmp) tmp)) + (tmp2 XReg (gen_select_xreg (cmp_ltz tmp) (rv_not tmp) tmp)) (tmp3 XReg (gen_clz tmp2))) ;; clz counted the full register width, so subtract (64-$width), and then ;; additionally subtract one more, meaning here -65+width is added. @@ -936,14 +936,14 @@ (rule 1 (lower (has_type $I128 (cls x))) (let ((low XReg (value_regs_get x 0)) (high XReg (value_regs_get x 1)) - (low XReg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (rv_not low) low)) - (high XReg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (rv_not high) high)) + (low XReg (gen_select_xreg (cmp_ltz high) (rv_not low) low)) + (high XReg (gen_select_xreg (cmp_ltz high) (rv_not high) high)) ;; Count both halves (high_cnt XReg (gen_clz high)) (low_cnt XReg (gen_clz low)) ;; Only add the bottom zeros if the top half is zero - (low_cnt XReg (gen_select_reg (IntCC.Equal) high (zero_reg) low_cnt (zero_reg))) + (low_cnt XReg (gen_select_xreg (cmp_eqz high) low_cnt (zero_reg))) (count XReg (rv_add high_cnt low_cnt)) (result XReg (rv_addi count (imm12_const -1)))) (value_regs result (imm $I64 0)))) @@ -1106,16 +1106,17 @@ (low XReg (rv_sll (value_regs_get x 0) shamt)) ;; high part. (high_part1 XReg (rv_srl (value_regs_get x 0) len_sub_shamt)) - (high_part2 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) high_part1)) + (high_part2 XReg (gen_select_xreg (cmp_eqz shamt) (zero_reg) high_part1)) ;; (high_part3 XReg (rv_sll (value_regs_get x 1) shamt)) (high XReg (rv_or high_part2 high_part3)) ;; (const64 XReg (imm $I64 64)) (shamt_128 XReg (rv_andi (value_regs_get y 0) (imm12_const 127)))) - (value_regs - (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 (zero_reg) low) - (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 low high)))) + (gen_select_regs + (cmp_geu shamt_128 const64) + (value_regs (zero_reg) low) + (value_regs low high)))) ;; SIMD Cases ;; We don't need to mask anything since it is done by the instruction according to SEW. @@ -1158,7 +1159,7 @@ (len_sub_shamt XReg (value_regs_get tmp 1)) ;; low part. (low_part1 XReg (rv_sll (value_regs_get x 1) len_sub_shamt)) - (low_part2 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) low_part1)) + (low_part2 XReg (gen_select_xreg (cmp_eqz shamt) (zero_reg) low_part1)) ;; (low_part3 XReg (rv_srl (value_regs_get x 0) shamt)) (low XReg (rv_or low_part2 low_part3)) @@ -1167,9 +1168,10 @@ ;; (high XReg (rv_srl (value_regs_get x 1) shamt)) (shamt_128 XReg (rv_andi (value_regs_get y 0) (imm12_const 127)))) - (value_regs - (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high low) - (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 (zero_reg) high)))) + (gen_select_regs + (cmp_geu shamt_128 const64) + (value_regs high (zero_reg)) + (value_regs low high)))) ;; SIMD Cases ;; We don't need to mask or extend anything since it is done by the instruction according to SEW. @@ -1212,7 +1214,7 @@ (len_sub_shamt XReg (value_regs_get tmp 1)) ;; low part. (low_part1 XReg (rv_sll (value_regs_get x 1) len_sub_shamt)) - (low_part2 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) low_part1)) + (low_part2 XReg (gen_select_xreg (cmp_eqz shamt) (zero_reg) low_part1)) ;; (low_part3 XReg (rv_srl (value_regs_get x 0) shamt)) (low XReg (rv_or low_part2 low_part3)) @@ -1223,12 +1225,13 @@ ;; (const_neg_1 XReg (imm $I64 (i64_as_u64 -1))) ;; - (high_replacement XReg (gen_select_reg (IntCC.SignedLessThan) (value_regs_get x 1) (zero_reg) const_neg_1 (zero_reg))) + (high_replacement XReg (gen_select_xreg (cmp_ltz (value_regs_get x 1)) const_neg_1 (zero_reg))) (const64 XReg (imm $I64 64)) (shamt_128 XReg (rv_andi (value_regs_get y 0) (imm12_const 127)))) - (value_regs - (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high low) - (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high_replacement high)))) + (gen_select_regs + (cmp_geu shamt_128 const64) + (value_regs high high_replacement) + (value_regs low high)))) ;; SIMD Cases ;; We don't need to mask or extend anything since it is done by the instruction according to SEW. @@ -1557,19 +1560,17 @@ (rv_seqz (rv_addi v (imm12_const 1)))) ;;;;; Rules for `select`;;;;;;;;; -(rule 0 (lower (has_type ty (select c x y))) - (gen_select ty (truthy_to_reg c) x y)) +(rule 0 (lower (has_type (ty_int_ref_scalar_64 _) (select c x y))) + (gen_select_xreg (lower_int_compare c) x y)) -(rule 1 (lower (has_type (fits_in_64 ty) (select (icmp cc a b @ (value_type (fits_in_64 in_ty))) x y))) - (let ((a XReg (zext a)) - (b XReg (zext b))) - (gen_select_reg cc a b x y))) +(rule 1 (lower (has_type $I128 (select c x y))) + (gen_select_regs (lower_int_compare c) x y)) -(rule 2 (lower (has_type (fits_in_64 ty) (select (icmp cc a b @ (value_type (fits_in_64 in_ty))) x y))) - (if (signed_cond_code cc)) - (let ((a XReg (sext a)) - (b XReg (sext b))) - (gen_select_reg cc a b x y))) +(rule 2 (lower (has_type (ty_vec_fits_in_register _) (select c x y))) + (gen_select_vreg (lower_int_compare c) x y)) + +(rule 3 (lower (has_type (ty_scalar_float _) (select c x y))) + (gen_select_freg (lower_int_compare c) x y)) ;;;;; Rules for `bitselect`;;;;;;;;; @@ -1632,10 +1633,12 @@ ;;;;; Rules for `smax`;;;;;;;;; (rule 0 (lower (has_type (fits_in_64 ty) (smax x y))) - (gen_int_select ty (IntSelectOP.Smax) (sext x) (sext y))) + (let ((x XReg (sext x)) + (y XReg (sext y))) + (gen_select_xreg (cmp_gt x y) x y))) (rule 1 (lower (has_type $I128 (smax x y))) - (gen_int_select $I128 (IntSelectOP.Smax) x y)) + (gen_select_regs (cmp_nez (gen_icmp (IntCC.SignedGreaterThan) x y $I128)) x y)) (rule 2 (lower (has_type (ty_vec_fits_in_register ty) (smax x y))) (rv_vmax_vv x y (unmasked) ty)) @@ -1649,10 +1652,12 @@ ;;;;; Rules for `smin`;;;;;;;;; (rule 0 (lower (has_type (fits_in_64 ty) (smin x y))) - (gen_int_select ty (IntSelectOP.Smin) (sext x) (sext y))) + (let ((x XReg (sext x)) + (y XReg (sext y))) + (gen_select_xreg (cmp_lt x y) x y))) (rule 1 (lower (has_type $I128 (smin x y))) - (gen_int_select $I128 (IntSelectOP.Smin) x y)) + (gen_select_regs (cmp_nez (gen_icmp (IntCC.SignedLessThan) x y $I128)) x y)) (rule 2 (lower (has_type (ty_vec_fits_in_register ty) (smin x y))) (rv_vmin_vv x y (unmasked) ty)) @@ -1666,10 +1671,12 @@ ;;;;; Rules for `umax`;;;;;;;;; (rule 0 (lower (has_type (fits_in_64 ty) (umax x y))) - (gen_int_select ty (IntSelectOP.Umax) (zext x) (zext y))) + (let ((x XReg (zext x)) + (y XReg (zext y))) + (gen_select_xreg (cmp_gtu x y) x y))) (rule 1 (lower (has_type $I128 (umax x y))) - (gen_int_select $I128 (IntSelectOP.Umax) x y)) + (gen_select_regs (cmp_nez (gen_icmp (IntCC.UnsignedGreaterThan) x y $I128)) x y)) (rule 2 (lower (has_type (ty_vec_fits_in_register ty) (umax x y))) (rv_vmaxu_vv x y (unmasked) ty)) @@ -1683,10 +1690,12 @@ ;;;;; Rules for `umin`;;;;;;;;; (rule 0 (lower (has_type (fits_in_64 ty) (umin x y))) - (gen_int_select ty (IntSelectOP.Umin) (zext x) (zext y))) + (let ((x XReg (zext x)) + (y XReg (zext y))) + (gen_select_xreg (cmp_ltu x y) x y))) (rule 1 (lower (has_type $I128 (umin x y))) - (gen_int_select $I128 (IntSelectOP.Umin) x y)) + (gen_select_regs (cmp_nez (gen_icmp (IntCC.UnsignedLessThan) x y $I128)) x y)) (rule 2 (lower (has_type (ty_vec_fits_in_register ty) (umin x y))) (rv_vminu_vv x y (unmasked) ty)) diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs index 51886d28b8ae..699ed57b0cec 100644 --- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs +++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs @@ -167,14 +167,6 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> *arg0 } - fn vec_writable_to_regs(&mut self, val: &VecWritableReg) -> ValueRegs { - match val.len() { - 1 => ValueRegs::one(val[0].to_reg()), - 2 => ValueRegs::two(val[0].to_reg(), val[1].to_reg()), - _ => unreachable!(), - } - } - fn load_ra(&mut self) -> Reg { if self.backend.flags.preserve_frame_pointers() { let tmp = self.temp_writable_reg(I64); @@ -202,28 +194,10 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> CondBrTarget::Label(label) } - fn vec_writable_clone(&mut self, v: &VecWritableReg) -> VecWritableReg { - v.clone() - } - fn imm12_and(&mut self, imm: Imm12, x: u64) -> Imm12 { Imm12::from_i16(imm.as_i16() & (x as i16)) } - fn alloc_vec_writable(&mut self, ty: Type) -> VecWritableReg { - if ty.is_int() || ty == R32 || ty == R64 { - if ty.bits() <= 64 { - vec![self.temp_writable_reg(I64)] - } else { - vec![self.temp_writable_reg(I64), self.temp_writable_reg(I64)] - } - } else if ty.is_float() || ty.is_vector() { - vec![self.temp_writable_reg(ty)] - } else { - unimplemented!("ty:{:?}", ty) - } - } - fn i64_generate_imm(&mut self, imm: i64) -> Option<(Imm20, Imm12)> { MInst::generate_imm(imm as u64) } @@ -338,21 +312,6 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> UImm5::maybe_from_u8(frm.bits()).unwrap() } - fn gen_select_reg(&mut self, cc: &IntCC, a: XReg, b: XReg, rs1: Reg, rs2: Reg) -> Reg { - let rd = self.temp_writable_reg(MInst::canonical_type_for_rc(rs1.class())); - self.emit(&MInst::SelectReg { - rd, - rs1, - rs2, - condition: IntegerCompare { - kind: *cc, - rs1: a.to_reg(), - rs2: b.to_reg(), - }, - }); - rd.to_reg() - } - fn u8_as_i32(&mut self, x: u8) -> i32 { x as i32 } diff --git a/cranelift/codegen/src/machinst/isle.rs b/cranelift/codegen/src/machinst/isle.rs index 51d4f3e09168..f14d1045185e 100644 --- a/cranelift/codegen/src/machinst/isle.rs +++ b/cranelift/codegen/src/machinst/isle.rs @@ -63,6 +63,16 @@ macro_rules! isle_lower_prelude_methods { ValueRegs::two(r1, r2) } + #[inline] + fn writable_value_regs(&mut self, r1: WritableReg, r2: WritableReg) -> WritableValueRegs { + WritableValueRegs::two(r1, r2) + } + + #[inline] + fn writable_value_reg(&mut self, r: WritableReg) -> WritableValueRegs { + WritableValueRegs::one(r) + } + #[inline] fn value_regs_invalid(&mut self) -> ValueRegs { ValueRegs::invalid() diff --git a/cranelift/codegen/src/prelude_lower.isle b/cranelift/codegen/src/prelude_lower.isle index 4b5a3a4871ec..99aab0a4ea6e 100644 --- a/cranelift/codegen/src/prelude_lower.isle +++ b/cranelift/codegen/src/prelude_lower.isle @@ -40,10 +40,18 @@ (decl value_reg (Reg) ValueRegs) (extern constructor value_reg value_reg) +;; Construct a `WritableValueRegs` of one register. +(decl writable_value_reg (WritableReg) WritableValueRegs) +(extern constructor writable_value_reg writable_value_reg) + ;; Construct a `ValueRegs` of two registers. (decl value_regs (Reg Reg) ValueRegs) (extern constructor value_regs value_regs) +;; Construct a `WritableValueRegs` of two registers. +(decl writable_value_regs (WritableReg WritableReg) WritableValueRegs) +(extern constructor writable_value_regs writable_value_regs) + ;; Construct an empty `ValueRegs` containing only invalid register sentinels. (decl value_regs_invalid () ValueRegs) (extern constructor value_regs_invalid value_regs_invalid) @@ -130,7 +138,7 @@ (extern constructor value_regs_get value_regs_get) ;; Get the number of registers in a `ValueRegs`. -(decl value_regs_len (ValueRegs) usize) +(decl pure value_regs_len (ValueRegs) usize) (extern constructor value_regs_len value_regs_len) ;; Get a range for the number of regs in a `ValueRegs`. @@ -1037,6 +1045,7 @@ (convert Inst Value def_inst) (convert Reg ValueRegs value_reg) +(convert WritableReg WritableValueRegs writable_value_reg) (convert Value Reg put_in_reg) (convert Value ValueRegs put_in_regs) (convert WritableReg Reg writable_reg_to_reg) diff --git a/cranelift/filetests/filetests/isa/riscv64/bitops.clif b/cranelift/filetests/filetests/isa/riscv64/bitops.clif index 6b6d5abbc132..f1d34cddff8d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/bitops.clif +++ b/cranelift/filetests/filetests/isa/riscv64/bitops.clif @@ -408,7 +408,7 @@ block0(v0: i128): ; block0: ; clz a5,a1##ty=i64 tmp=a3 step=a4 ; clz a3,a0##ty=i64 tmp=a4 step=a2 -; select_reg a0,a3,zero##condition=(a1 eq zero) +; select a0,a3,zero##condition=(a1 eq zero) ; add a0,a5,a0 ; li a1,0 ; ret @@ -437,10 +437,10 @@ block0(v0: i128): ; addi a2, a2, -1 ; srli a4, a4, 1 ; j -0x18 -; beqz a1, 0xc -; mv a0, zero -; j 8 +; bnez a1, 0xc ; mv a0, a3 +; j 8 +; mv a0, zero ; add a0, a5, a0 ; mv a1, zero ; ret @@ -456,7 +456,7 @@ block0(v0: i8): ; slli a2,a0,56 ; srai a4,a2,56 ; not a0,a4 -; select_reg a2,a0,a4##condition=(a4 slt zero) +; select a2,a0,a4##condition=(a4 slt zero) ; clz a0,a2##ty=i64 tmp=a4 step=a5 ; addi a0,a0,-57 ; ret @@ -466,10 +466,10 @@ block0(v0: i8): ; slli a2, a0, 0x38 ; srai a4, a2, 0x38 ; not a0, a4 -; bltz a4, 0xc -; mv a2, a4 -; j 8 +; bgez a4, 0xc ; mv a2, a0 +; j 8 +; mv a2, a4 ; mv a0, zero ; addi a5, zero, 0x40 ; addi a4, zero, 1 @@ -495,7 +495,7 @@ block0(v0: i16): ; slli a2,a0,48 ; srai a4,a2,48 ; not a0,a4 -; select_reg a2,a0,a4##condition=(a4 slt zero) +; select a2,a0,a4##condition=(a4 slt zero) ; clz a0,a2##ty=i64 tmp=a4 step=a5 ; addi a0,a0,-49 ; ret @@ -505,10 +505,10 @@ block0(v0: i16): ; slli a2, a0, 0x30 ; srai a4, a2, 0x30 ; not a0, a4 -; bltz a4, 0xc -; mv a2, a4 -; j 8 +; bgez a4, 0xc ; mv a2, a0 +; j 8 +; mv a2, a4 ; mv a0, zero ; addi a5, zero, 0x40 ; addi a4, zero, 1 @@ -533,7 +533,7 @@ block0(v0: i32): ; block0: ; sext.w a2,a0 ; not a4,a2 -; select_reg a0,a4,a2##condition=(a2 slt zero) +; select a0,a4,a2##condition=(a2 slt zero) ; clz a4,a0##ty=i64 tmp=a2 step=a3 ; addi a0,a4,-33 ; ret @@ -542,10 +542,10 @@ block0(v0: i32): ; block0: ; offset 0x0 ; sext.w a2, a0 ; not a4, a2 -; bltz a2, 0xc -; mv a0, a2 -; j 8 +; bgez a2, 0xc ; mv a0, a4 +; j 8 +; mv a0, a2 ; mv a4, zero ; addi a3, zero, 0x40 ; addi a2, zero, 1 @@ -569,7 +569,7 @@ block0(v0: i64): ; VCode: ; block0: ; not a2,a0 -; select_reg a4,a2,a0##condition=(a0 slt zero) +; select a4,a2,a0##condition=(a0 slt zero) ; clz a2,a4##ty=i64 tmp=a0 step=a1 ; addi a0,a2,-1 ; ret @@ -577,10 +577,10 @@ block0(v0: i64): ; Disassembled: ; block0: ; offset 0x0 ; not a2, a0 -; bltz a0, 0xc -; mv a4, a0 -; j 8 +; bgez a0, 0xc ; mv a4, a2 +; j 8 +; mv a4, a0 ; mv a2, zero ; addi a1, zero, 0x40 ; addi a0, zero, 1 @@ -604,12 +604,12 @@ block0(v0: i128): ; VCode: ; block0: ; not a3,a0 -; select_reg a5,a3,a0##condition=(a1 slt zero) +; select a5,a3,a0##condition=(a1 slt zero) ; not a2,a1 -; select_reg a3,a2,a1##condition=(a1 slt zero) +; select a3,a2,a1##condition=(a1 slt zero) ; clz a1,a3##ty=i64 tmp=a2 step=a0 ; clz a0,a5##ty=i64 tmp=a2 step=a4 -; select_reg a2,a0,zero##condition=(a3 eq zero) +; select a2,a0,zero##condition=(a3 eq zero) ; add a3,a1,a2 ; addi a0,a3,-1 ; li a1,0 @@ -618,15 +618,15 @@ block0(v0: i128): ; Disassembled: ; block0: ; offset 0x0 ; not a3, a0 -; bltz a1, 0xc -; mv a5, a0 -; j 8 +; bgez a1, 0xc ; mv a5, a3 -; not a2, a1 -; bltz a1, 0xc -; mv a3, a1 ; j 8 +; mv a5, a0 +; not a2, a1 +; bgez a1, 0xc ; mv a3, a2 +; j 8 +; mv a3, a1 ; mv a1, zero ; addi a0, zero, 0x40 ; addi a2, zero, 1 @@ -649,10 +649,10 @@ block0(v0: i128): ; addi a4, a4, -1 ; srli a2, a2, 1 ; j -0x18 -; beqz a3, 0xc -; mv a2, zero -; j 8 +; bnez a3, 0xc ; mv a2, a0 +; j 8 +; mv a2, zero ; add a3, a1, a2 ; addi a0, a3, -1 ; mv a1, zero @@ -776,7 +776,7 @@ block0(v0: i128): ; block0: ; ctz a5,a1##ty=i64 tmp=a3 step=a4 ; ctz a3,a0##ty=i64 tmp=a1 step=a2 -; select_reg a5,a5,zero##condition=(a0 eq zero) +; select a5,a5,zero##condition=(a0 eq zero) ; add a0,a3,a5 ; li a1,0 ; ret @@ -1725,13 +1725,12 @@ block0(v0: i128, v1: i8): ; sub a3,a3,a5 ; sll a4,a0,a5 ; srl a0,a0,a3 -; select_reg a3,zero,a0##condition=(a5 eq zero) +; select a3,zero,a0##condition=(a5 eq zero) ; sll a5,a1,a5 -; or a1,a3,a5 +; or a5,a3,a5 ; li a3,64 -; andi a5,a2,127 -; select_reg a0,zero,a4##condition=(a5 uge a3) -; select_reg a1,a4,a1##condition=(a5 uge a3) +; andi a2,a2,127 +; select [a0,a1],[zero,a4],[a4,a5]##condition=(a2 uge a3) ; ret ; ; Disassembled: @@ -1741,21 +1740,20 @@ block0(v0: i128, v1: i8): ; sub a3, a3, a5 ; sll a4, a0, a5 ; srl a0, a0, a3 -; beqz a5, 0xc -; mv a3, a0 -; j 8 +; bnez a5, 0xc ; mv a3, zero +; j 8 +; mv a3, a0 ; sll a5, a1, a5 -; or a1, a3, a5 +; or a5, a3, a5 ; addi a3, zero, 0x40 -; andi a5, a2, 0x7f -; bgeu a5, a3, 0xc -; mv a0, a4 -; j 8 +; andi a2, a2, 0x7f +; bltu a2, a3, 0x10 ; mv a0, zero -; bgeu a5, a3, 8 -; j 8 ; mv a1, a4 +; j 0xc +; mv a0, a4 +; mv a1, a5 ; ret function %ishl_i128_i128(i128, i128) -> i128 { @@ -1773,13 +1771,12 @@ block0(v0: i128, v1: i128): ; mv a4,a5 ; sll a5,a4,a0 ; srl a3,a4,a3 -; select_reg a3,zero,a3##condition=(a0 eq zero) +; select a3,zero,a3##condition=(a0 eq zero) ; sll a0,a1,a0 -; or a1,a3,a0 +; or a4,a3,a0 ; li a3,64 ; andi a2,a2,127 -; select_reg a0,zero,a5##condition=(a2 uge a3) -; select_reg a1,a5,a1##condition=(a2 uge a3) +; select [a0,a1],[zero,a5],[a5,a4]##condition=(a2 uge a3) ; ret ; ; Disassembled: @@ -1791,20 +1788,18 @@ block0(v0: i128, v1: i128): ; mv a4, a5 ; sll a5, a4, a0 ; srl a3, a4, a3 -; beqz a0, 8 -; j 8 +; bnez a0, 8 ; mv a3, zero ; sll a0, a1, a0 -; or a1, a3, a0 +; or a4, a3, a0 ; addi a3, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a3, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a3, 0x10 ; mv a0, zero -; bgeu a2, a3, 8 -; j 8 ; mv a1, a5 +; j 0xc +; mv a0, a5 +; mv a1, a4 ; ret function %ushr_i128_i8(i128, i8) -> i128 { @@ -1819,14 +1814,13 @@ block0(v0: i128, v1: i8): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li t0,64 ; srl a3,a1,a4 ; andi a4,a2,127 -; select_reg a0,a3,a5##condition=(a4 uge t0) -; select_reg a1,zero,a3##condition=(a4 uge t0) +; select [a0,a1],[a3,zero],[a5,a3]##condition=(a4 uge t0) ; ret ; ; Disassembled: @@ -1835,23 +1829,21 @@ block0(v0: i128, v1: i8): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi t0, zero, 0x40 ; srl a3, a1, a4 ; andi a4, a2, 0x7f -; bgeu a4, t0, 0xc -; mv a0, a5 -; j 8 +; bltu a4, t0, 0x10 ; mv a0, a3 -; bgeu a4, t0, 0xc -; mv a1, a3 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, a5 +; mv a1, a3 ; ret function %ushr_i128_i128(i128, i128) -> i128 { @@ -1861,42 +1853,62 @@ block0(v0: i128, v1: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s11,-8(sp) +; add sp,-16 ; block0: ; andi a5,a2,63 ; li a3,64 ; sub a3,a3,a5 ; sll a3,a1,a3 -; select_reg a3,zero,a3##condition=(a5 eq zero) +; select a3,zero,a3##condition=(a5 eq zero) ; srl a4,a0,a5 -; or a0,a3,a4 +; or s11,a3,a4 ; li a3,64 ; srl a4,a1,a5 ; andi a5,a2,127 -; select_reg a0,a4,a0##condition=(a5 uge a3) -; select_reg a1,zero,a4##condition=(a5 uge a3) +; select [a0,a1],[a4,zero],[s11,a4]##condition=(a5 uge a3) +; add sp,+16 +; ld s11,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s11, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 ; andi a5, a2, 0x3f ; addi a3, zero, 0x40 ; sub a3, a3, a5 ; sll a3, a1, a3 -; beqz a5, 8 -; j 8 +; bnez a5, 8 ; mv a3, zero ; srl a4, a0, a5 -; or a0, a3, a4 +; or s11, a3, a4 ; addi a3, zero, 0x40 ; srl a4, a1, a5 ; andi a5, a2, 0x7f -; bgeu a5, a3, 8 -; j 8 +; bltu a5, a3, 0x10 ; mv a0, a4 -; bgeu a5, a3, 0xc -; mv a1, a4 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, s11 +; mv a1, a4 +; addi sp, sp, 0x10 +; ld s11, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret function %sshr_i128_i8(i128, i8) -> i128 { @@ -1911,17 +1923,16 @@ block0(v0: i128, v1: i8): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li a0,64 ; sra a3,a1,a4 ; li a4,-1 -; select_reg a1,a4,zero##condition=(a1 slt zero) +; select t4,a4,zero##condition=(a1 slt zero) ; li a4,64 ; andi a2,a2,127 -; select_reg a0,a3,a5##condition=(a2 uge a4) -; select_reg a1,a1,a3##condition=(a2 uge a4) +; select [a0,a1],[a3,t4],[a5,a3]##condition=(a2 uge a4) ; ret ; ; Disassembled: @@ -1930,26 +1941,26 @@ block0(v0: i128, v1: i8): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi a0, zero, 0x40 ; sra a3, a1, a4 ; addi a4, zero, -1 -; bltz a1, 0xc -; mv a1, zero +; bgez a1, 0xc +; mv t4, a4 ; j 8 -; mv a1, a4 +; mv t4, zero ; addi a4, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a4, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a2, a4, 8 +; mv a1, t4 +; j 0xc +; mv a0, a5 ; mv a1, a3 ; ret @@ -1960,48 +1971,68 @@ block0(v0: i128, v1: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s11,-8(sp) +; add sp,-16 ; block0: ; andi a5,a2,63 ; li a3,64 ; sub a3,a3,a5 ; sll a3,a1,a3 -; select_reg a3,zero,a3##condition=(a5 eq zero) +; select a3,zero,a3##condition=(a5 eq zero) ; srl a4,a0,a5 -; or a0,a3,a4 +; or s11,a3,a4 ; li a3,64 ; sra a3,a1,a5 ; li a5,-1 -; select_reg a1,a5,zero##condition=(a1 slt zero) +; select a5,a5,zero##condition=(a1 slt zero) ; li a4,64 -; andi a5,a2,127 -; select_reg a0,a3,a0##condition=(a5 uge a4) -; select_reg a1,a1,a3##condition=(a5 uge a4) +; andi a2,a2,127 +; select [a0,a1],[a3,a5],[s11,a3]##condition=(a2 uge a4) +; add sp,+16 +; ld s11,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s11, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 ; andi a5, a2, 0x3f ; addi a3, zero, 0x40 ; sub a3, a3, a5 ; sll a3, a1, a3 -; beqz a5, 8 -; j 8 +; bnez a5, 8 ; mv a3, zero ; srl a4, a0, a5 -; or a0, a3, a4 +; or s11, a3, a4 ; addi a3, zero, 0x40 ; sra a3, a1, a5 ; addi a5, zero, -1 -; bltz a1, 0xc -; mv a1, zero -; j 8 -; mv a1, a5 +; bltz a1, 8 +; mv a5, zero ; addi a4, zero, 0x40 -; andi a5, a2, 0x7f -; bgeu a5, a4, 8 -; j 8 +; andi a2, a2, 0x7f +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a5, a4, 8 +; mv a1, a5 +; j 0xc +; mv a0, s11 ; mv a1, a3 +; addi sp, sp, 0x10 +; ld s11, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/brif.clif b/cranelift/filetests/filetests/isa/riscv64/brif.clif new file mode 100644 index 000000000000..8f61a5982ae3 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/brif.clif @@ -0,0 +1,449 @@ +test compile precise-output +set unwind_info=false +target riscv64 + + +function %brif_i8(i8) -> i8 { +block0(v0: i8): + brif v0, block1, block2 + +block1: + v1 = iconst.i8 1 + return v1 + +block2: + v2 = iconst.i8 0 + return v2 +} + +; VCode: +; block0: +; andi a4,a0,255 +; bne a4,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; andi a4, a0, 0xff +; bnez a4, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %brif_i16(i16) -> i8 { +block0(v0: i16): + brif v0, block1, block2 + +block1: + v1 = iconst.i8 1 + return v1 + +block2: + v2 = iconst.i8 0 + return v2 +} + +; VCode: +; block0: +; slli a4,a0,48 +; srai a0,a4,48 +; bne a0,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a4, a0, 0x30 +; srai a0, a4, 0x30 +; bnez a0, 0xc +; block1: ; offset 0xc +; mv a0, zero +; ret +; block2: ; offset 0x14 +; addi a0, zero, 1 +; ret + +function %brif_i32(i32) -> i8 { +block0(v0: i32): + brif v0, block1, block2 + +block1: + v1 = iconst.i8 1 + return v1 + +block2: + v2 = iconst.i8 0 + return v2 +} + +; VCode: +; block0: +; sext.w a4,a0 +; bne a4,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; sext.w a4, a0 +; bnez a4, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %brif_i64(i64) -> i8 { +block0(v0: i64): + brif v0, block1, block2 + +block1: + v1 = iconst.i8 1 + return v1 + +block2: + v2 = iconst.i8 0 + return v2 +} + +; VCode: +; block0: +; bne a0,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; bnez a0, 0xc +; block1: ; offset 0x4 +; mv a0, zero +; ret +; block2: ; offset 0xc +; addi a0, zero, 1 +; ret + +function %brif_i128(i128) -> i8 { +block0(v0: i128): + brif v0, block1, block2 + +block1: + v1 = iconst.i8 1 + return v1 + +block2: + v2 = iconst.i8 0 + return v2 +} + +; VCode: +; block0: +; or a5,a0,a1 +; bne a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; or a5, a0, a1 +; bnez a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %brif_icmp_i8(i8, i8) -> i8 { +block0(v0: i8, v1: i8): + v2 = icmp eq v0, v1 + brif v2, block1, block2 + +block1: + v3 = iconst.i8 1 + return v3 + +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; andi a5,a0,255 +; andi a1,a1,255 +; beq a5,a1,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; andi a5, a0, 0xff +; andi a1, a1, 0xff +; beq a5, a1, 0xc +; block1: ; offset 0xc +; mv a0, zero +; ret +; block2: ; offset 0x14 +; addi a0, zero, 1 +; ret + +function %brif_icmp_i16(i16, i16) -> i8 { +block0(v0: i16, v1: i16): + v2 = icmp ne v0, v1 + brif v2, block1, block2 + +block1: + v3 = iconst.i8 1 + return v3 + +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; mv a3,a1 +; slli a5,a0,48 +; srli a1,a5,48 +; mv a5,a3 +; slli a3,a5,48 +; srli a5,a3,48 +; bne a1,a5,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; mv a3, a1 +; slli a5, a0, 0x30 +; srli a1, a5, 0x30 +; mv a5, a3 +; slli a3, a5, 0x30 +; srli a5, a3, 0x30 +; bne a1, a5, 0xc +; block1: ; offset 0x1c +; mv a0, zero +; ret +; block2: ; offset 0x24 +; addi a0, zero, 1 +; ret + +function %brif_icmp_i32(i32, i32) -> i8 { +block0(v0: i32, v1: i32): + v2 = icmp slt v0, v1 + brif v2, block1, block2 + +block1: + v3 = iconst.i8 1 + return v3 + +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; sext.w a5,a0 +; sext.w a1,a1 +; blt a5,a1,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; sext.w a5, a0 +; sext.w a1, a1 +; blt a5, a1, 0xc +; block1: ; offset 0xc +; mv a0, zero +; ret +; block2: ; offset 0x14 +; addi a0, zero, 1 +; ret + +function %brif_icmp_i64(i64, i64) -> i8 { +block0(v0: i64, v1: i64): + v2 = icmp uge v0, v1 + brif v2, block1, block2 + +block1: + v3 = iconst.i8 1 + return v3 + +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; bgeu a0,a1,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; bgeu a0, a1, 0xc +; block1: ; offset 0x4 +; mv a0, zero +; ret +; block2: ; offset 0xc +; addi a0, zero, 1 +; ret + +function %brif_icmp_i128(i128, i128) -> i8 { +block0(v0: i128, v1: i128): + v2 = icmp sgt v0, v1 + brif v2, block1, block2 + +block1: + v3 = iconst.i8 1 + return v3 + +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; sgt a1,[a0,a1],[a2,a3]##ty=i128 +; bne a1,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; blt a3, a1, 0xc +; bne a1, a3, 0x10 +; bgeu a2, a0, 0xc +; addi a1, zero, 1 +; j 8 +; mv a1, zero +; bnez a1, 0xc +; block1: ; offset 0x1c +; mv a0, zero +; ret +; block2: ; offset 0x24 +; addi a0, zero, 1 +; ret + +function %brif_fcmp_f32(f32, f32) -> i8 { +block0(v0: f32, v1: f32): + v2 = fcmp lt v0, v1 + brif v2, block1, block2 + +block1: + v3 = iconst.i8 1 + return v3 + +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; flt.s a5,fa0,fa1 +; bne a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.s a5, fa0, fa1 +; bnez a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + +function %brif_fcmp_f64(f64, f64) -> i8 { +block0(v0: f64, v1: f64): + v2 = fcmp uge v0, v1 + brif v2, block1, block2 + +block1: + v3 = iconst.i8 1 + return v3 + +block2: + v4 = iconst.i8 0 + return v4 +} + +; VCode: +; block0: +; flt.d a5,fa0,fa1 +; beq a5,zero,taken(label2),not_taken(label1) +; block1: +; li a0,0 +; ret +; block2: +; li a0,1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.d a5, fa0, fa1 +; beqz a5, 0xc +; block1: ; offset 0x8 +; mv a0, zero +; ret +; block2: ; offset 0x10 +; addi a0, zero, 1 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif index 3f9fd7b2f673..1477bb8ba5d4 100644 --- a/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif +++ b/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif @@ -13,7 +13,7 @@ block0(v0: i8): ; block0: ; sext.b a2,a0 ; not a4,a2 -; select_reg a0,a4,a2##condition=(a2 slt zero) +; select a0,a4,a2##condition=(a2 slt zero) ; clz a2,a0 ; addi a0,a2,-57 ; ret @@ -22,10 +22,10 @@ block0(v0: i8): ; block0: ; offset 0x0 ; .byte 0x13, 0x16, 0x45, 0x60 ; not a4, a2 -; bltz a2, 0xc -; mv a0, a2 -; j 8 +; bgez a2, 0xc ; mv a0, a4 +; j 8 +; mv a0, a2 ; .byte 0x13, 0x16, 0x05, 0x60 ; addi a0, a2, -0x39 ; ret @@ -40,7 +40,7 @@ block0(v0: i16): ; block0: ; sext.h a2,a0 ; not a4,a2 -; select_reg a0,a4,a2##condition=(a2 slt zero) +; select a0,a4,a2##condition=(a2 slt zero) ; clz a2,a0 ; addi a0,a2,-49 ; ret @@ -49,10 +49,10 @@ block0(v0: i16): ; block0: ; offset 0x0 ; .byte 0x13, 0x16, 0x55, 0x60 ; not a4, a2 -; bltz a2, 0xc -; mv a0, a2 -; j 8 +; bgez a2, 0xc ; mv a0, a4 +; j 8 +; mv a0, a2 ; .byte 0x13, 0x16, 0x05, 0x60 ; addi a0, a2, -0x31 ; ret @@ -67,7 +67,7 @@ block0(v0: i32): ; block0: ; sext.w a2,a0 ; not a4,a2 -; select_reg a0,a4,a2##condition=(a2 slt zero) +; select a0,a4,a2##condition=(a2 slt zero) ; clz a2,a0 ; addi a0,a2,-33 ; ret @@ -76,10 +76,10 @@ block0(v0: i32): ; block0: ; offset 0x0 ; sext.w a2, a0 ; not a4, a2 -; bltz a2, 0xc -; mv a0, a2 -; j 8 +; bgez a2, 0xc ; mv a0, a4 +; j 8 +; mv a0, a2 ; .byte 0x13, 0x16, 0x05, 0x60 ; addi a0, a2, -0x21 ; ret @@ -93,7 +93,7 @@ block0(v0: i64): ; VCode: ; block0: ; not a2,a0 -; select_reg a4,a2,a0##condition=(a0 slt zero) +; select a4,a2,a0##condition=(a0 slt zero) ; clz a0,a4 ; addi a0,a0,-1 ; ret @@ -101,10 +101,10 @@ block0(v0: i64): ; Disassembled: ; block0: ; offset 0x0 ; not a2, a0 -; bltz a0, 0xc -; mv a4, a0 -; j 8 +; bgez a0, 0xc ; mv a4, a2 +; j 8 +; mv a4, a0 ; .byte 0x13, 0x15, 0x07, 0x60 ; addi a0, a0, -1 ; ret @@ -118,12 +118,12 @@ block0(v0: i128): ; VCode: ; block0: ; not a3,a0 -; select_reg a5,a3,a0##condition=(a1 slt zero) +; select a5,a3,a0##condition=(a1 slt zero) ; not a2,a1 -; select_reg a3,a2,a1##condition=(a1 slt zero) +; select a3,a2,a1##condition=(a1 slt zero) ; clz a0,a3 ; clz a1,a5 -; select_reg a3,a1,zero##condition=(a3 eq zero) +; select a3,a1,zero##condition=(a3 eq zero) ; add a5,a0,a3 ; addi a0,a5,-1 ; li a1,0 @@ -132,21 +132,21 @@ block0(v0: i128): ; Disassembled: ; block0: ; offset 0x0 ; not a3, a0 -; bltz a1, 0xc -; mv a5, a0 -; j 8 +; bgez a1, 0xc ; mv a5, a3 -; not a2, a1 -; bltz a1, 0xc -; mv a3, a1 ; j 8 +; mv a5, a0 +; not a2, a1 +; bgez a1, 0xc ; mv a3, a2 +; j 8 +; mv a3, a1 ; .byte 0x13, 0x95, 0x06, 0x60 ; .byte 0x93, 0x95, 0x07, 0x60 -; beqz a3, 0xc -; mv a3, zero -; j 8 +; bnez a3, 0xc ; mv a3, a1 +; j 8 +; mv a3, zero ; add a5, a0, a3 ; addi a0, a5, -1 ; mv a1, zero diff --git a/cranelift/filetests/filetests/isa/riscv64/clz-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/clz-zbb.clif index 714ca6a33aac..fda9f412930d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/clz-zbb.clif +++ b/cranelift/filetests/filetests/isa/riscv64/clz-zbb.clif @@ -84,7 +84,7 @@ block0(v0: i128): ; block0: ; clz a3,a1 ; clz a5,a0 -; select_reg a1,a5,zero##condition=(a1 eq zero) +; select a1,a5,zero##condition=(a1 eq zero) ; add a0,a3,a1 ; li a1,0 ; ret @@ -93,10 +93,10 @@ block0(v0: i128): ; block0: ; offset 0x0 ; .byte 0x93, 0x96, 0x05, 0x60 ; .byte 0x93, 0x17, 0x05, 0x60 -; beqz a1, 0xc -; mv a1, zero -; j 8 +; bnez a1, 0xc ; mv a1, a5 +; j 8 +; mv a1, zero ; add a0, a3, a1 ; mv a1, zero ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif b/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif index 771a4e9635f3..98db409e8a61 100644 --- a/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif +++ b/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif @@ -16,9 +16,8 @@ block2: ; VCode: ; block0: -; slli a5,a0,32 -; srli a1,a5,32 -; bne a1,zero,taken(label1),not_taken(label2) +; sext.w a5,a0 +; bne a5,zero,taken(label1),not_taken(label2) ; block1: ; j label3 ; block2: @@ -29,12 +28,11 @@ block2: ; ; Disassembled: ; block0: ; offset 0x0 -; slli a5, a0, 0x20 -; srli a1, a5, 0x20 -; bnez a1, 8 -; block1: ; offset 0xc +; sext.w a5, a0 +; bnez a5, 8 +; block1: ; offset 0x8 ; addi a0, zero, 0x61 -; block2: ; offset 0x10 +; block2: ; offset 0xc ; ret function %cold_annotation(i32) -> i32 { @@ -51,9 +49,8 @@ block2 cold: ; VCode: ; block0: -; slli a5,a0,32 -; srli a1,a5,32 -; bne a1,zero,taken(label1),not_taken(label2) +; sext.w a5,a0 +; bne a5,zero,taken(label1),not_taken(label2) ; block1: ; j label3 ; block3: @@ -64,12 +61,11 @@ block2 cold: ; ; Disassembled: ; block0: ; offset 0x0 -; slli a5, a0, 0x20 -; srli a1, a5, 0x20 -; beqz a1, 8 -; block1: ; offset 0xc +; sext.w a5, a0 +; beqz a5, 8 +; block1: ; offset 0x8 ; ret -; block2: ; offset 0x10 +; block2: ; offset 0xc ; addi a0, zero, 0x61 ; j -8 diff --git a/cranelift/filetests/filetests/isa/riscv64/condbr.clif b/cranelift/filetests/filetests/isa/riscv64/condbr.clif index ee1e402883c5..c9c93c2016e7 100644 --- a/cranelift/filetests/filetests/isa/riscv64/condbr.clif +++ b/cranelift/filetests/filetests/isa/riscv64/condbr.clif @@ -245,8 +245,7 @@ block2: ; VCode: ; block0: -; eq a5,a0,a1##ty=i64 -; bne a5,zero,taken(label2),not_taken(label1) +; beq a0,a1,taken(label2),not_taken(label1) ; block1: ; li a0,2 ; ret @@ -256,15 +255,11 @@ block2: ; ; Disassembled: ; block0: ; offset 0x0 -; bne a0, a1, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; bnez a5, 0xc -; block1: ; offset 0x14 +; beq a0, a1, 0xc +; block1: ; offset 0x4 ; addi a0, zero, 2 ; ret -; block2: ; offset 0x1c +; block2: ; offset 0xc ; addi a0, zero, 1 ; ret @@ -280,8 +275,7 @@ block1: ; VCode: ; block0: -; eq a4,a0,a1##ty=i64 -; bne a4,zero,taken(label1),not_taken(label2) +; beq a0,a1,taken(label1),not_taken(label2) ; block1: ; j label3 ; block2: @@ -292,11 +286,6 @@ block1: ; ; Disassembled: ; block0: ; offset 0x0 -; bne a0, a1, 0xc -; addi a4, zero, 1 -; j 8 -; mv a4, zero -; block1: ; offset 0x10 ; addi a0, zero, 1 ; ret @@ -672,7 +661,7 @@ block1: ; VCode: ; block0: ; slli a2,a0,48 -; srli a4,a2,48 +; srai a4,a2,48 ; bne a4,zero,taken(label1),not_taken(label2) ; block1: ; j label3 @@ -684,7 +673,7 @@ block1: ; Disassembled: ; block0: ; offset 0x0 ; slli a2, a0, 0x30 -; srli a4, a2, 0x30 +; srai a4, a2, 0x30 ; block1: ; offset 0x8 ; ret @@ -699,9 +688,8 @@ block1: ; VCode: ; block0: -; slli a2,a0,32 -; srli a4,a2,32 -; bne a4,zero,taken(label1),not_taken(label2) +; sext.w a2,a0 +; bne a2,zero,taken(label1),not_taken(label2) ; block1: ; j label3 ; block2: @@ -711,9 +699,8 @@ block1: ; ; Disassembled: ; block0: ; offset 0x0 -; slli a2, a0, 0x20 -; srli a4, a2, 0x20 -; block1: ; offset 0x8 +; sext.w a2, a0 +; block1: ; offset 0x4 ; ret function %i64_brif(i64){ diff --git a/cranelift/filetests/filetests/isa/riscv64/condops.clif b/cranelift/filetests/filetests/isa/riscv64/condops.clif index 578e27556a6a..6dbba5087c05 100644 --- a/cranelift/filetests/filetests/isa/riscv64/condops.clif +++ b/cranelift/filetests/filetests/isa/riscv64/condops.clif @@ -15,7 +15,7 @@ block0(v0: i8, v1: i64, v2: i64): ; li a3,42 ; andi a5,a0,255 ; andi a3,a3,255 -; select_reg a0,a1,a2##condition=(a5 eq a3) +; select a0,a1,a2##condition=(a5 eq a3) ; ret ; ; Disassembled: @@ -23,10 +23,10 @@ block0(v0: i8, v1: i64, v2: i64): ; addi a3, zero, 0x2a ; andi a5, a0, 0xff ; andi a3, a3, 0xff -; beq a5, a3, 0xc -; mv a0, a2 -; j 8 +; bne a5, a3, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %g(i8) -> i8 { @@ -86,7 +86,7 @@ block0(v0: i8, v1: i8, v2: i8): ; VCode: ; block0: ; andi a4,a0,255 -; select_i8 a0,a1,a2##condition=a4 +; select a0,a1,a2##condition=(a4 ne zero) ; ret ; ; Disassembled: @@ -109,24 +109,20 @@ block0(v0: i32, v1: i8, v2: i8): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,32 -; srli a4,a5,32 -; slli a3,a3,32 -; srli a5,a3,32 -; select_reg a0,a1,a2##condition=(a4 eq a5) +; sext.w a5,a0 +; sext.w a3,a3 +; select a0,a1,a2##condition=(a5 eq a3) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a4, a5, 0x20 -; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; beq a4, a5, 0xc -; mv a0, a2 -; j 8 +; sext.w a5, a0 +; sext.w a3, a3 +; bne a5, a3, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %i128_select(i8, i128, i128) -> i128 { @@ -145,7 +141,7 @@ block0(v0: i8, v1: i128, v2: i128): ; block0: ; mv s4,a1 ; andi a5,a0,255 -; select_i128 [a0,a1],[s4,a2],[a3,a4]##condition=a5 +; select [a0,a1],[s4,a2],[a3,a4]##condition=(a5 ne zero) ; add sp,+16 ; ld s4,-8(sp) ; ld ra,8(sp) diff --git a/cranelift/filetests/filetests/isa/riscv64/ctz-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/ctz-zbb.clif index ab4a4d16a184..d5959eb63d3c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/ctz-zbb.clif +++ b/cranelift/filetests/filetests/isa/riscv64/ctz-zbb.clif @@ -83,7 +83,7 @@ block0(v0: i128): ; block0: ; ctz a3,a1 ; ctz a5,a0 -; select_reg a1,a3,zero##condition=(a0 eq zero) +; select a1,a3,zero##condition=(a0 eq zero) ; add a0,a5,a1 ; li a1,0 ; ret @@ -92,10 +92,10 @@ block0(v0: i128): ; block0: ; offset 0x0 ; .byte 0x93, 0x96, 0x15, 0x60 ; .byte 0x93, 0x17, 0x15, 0x60 -; beqz a0, 0xc -; mv a1, zero -; j 8 +; bnez a0, 0xc ; mv a1, a3 +; j 8 +; mv a1, zero ; add a0, a5, a1 ; mv a1, zero ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/i128-bmask.clif b/cranelift/filetests/filetests/isa/riscv64/i128-bmask.clif index 2171a16ba752..a258225f5217 100644 --- a/cranelift/filetests/filetests/isa/riscv64/i128-bmask.clif +++ b/cranelift/filetests/filetests/isa/riscv64/i128-bmask.clif @@ -132,19 +132,17 @@ block0(v0: i32): ; VCode: ; block0: -; slli a2,a0,32 -; srli a4,a2,32 -; sltu a0,zero,a4 -; sub a1,zero,a0 +; sext.w a2,a0 +; sltu a4,zero,a2 +; sub a1,zero,a4 ; mv a0,a1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; slli a2, a0, 0x20 -; srli a4, a2, 0x20 -; snez a0, a4 -; neg a1, a0 +; sext.w a2, a0 +; snez a4, a2 +; neg a1, a4 ; mv a0, a1 ; ret @@ -157,7 +155,7 @@ block0(v0: i16): ; VCode: ; block0: ; slli a2,a0,48 -; srli a4,a2,48 +; srai a4,a2,48 ; sltu a0,zero,a4 ; sub a1,zero,a0 ; mv a0,a1 @@ -166,7 +164,7 @@ block0(v0: i16): ; Disassembled: ; block0: ; offset 0x0 ; slli a2, a0, 0x30 -; srli a4, a2, 0x30 +; srai a4, a2, 0x30 ; snez a0, a4 ; neg a1, a0 ; mv a0, a1 @@ -180,17 +178,19 @@ block0(v0: i8): ; VCode: ; block0: -; andi a2,a0,255 -; sltu a4,zero,a2 -; sub a1,zero,a4 +; slli a2,a0,56 +; srai a4,a2,56 +; sltu a0,zero,a4 +; sub a1,zero,a0 ; mv a0,a1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; andi a2, a0, 0xff -; snez a4, a2 -; neg a1, a4 +; slli a2, a0, 0x38 +; srai a4, a2, 0x38 +; snez a0, a4 +; neg a1, a0 ; mv a0, a1 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/iabs.clif b/cranelift/filetests/filetests/isa/riscv64/iabs.clif index 8ff903a910b2..6a91b7cb1dde 100644 --- a/cranelift/filetests/filetests/isa/riscv64/iabs.clif +++ b/cranelift/filetests/filetests/isa/riscv64/iabs.clif @@ -12,7 +12,7 @@ block0(v0: i8): ; slli a2,a0,56 ; srai a4,a2,56 ; sub a0,zero,a4 -; select_reg a0,a4,a0##condition=(a4 sgt a0) +; select a0,a4,a0##condition=(a4 sgt a0) ; ret ; ; Disassembled: @@ -20,8 +20,7 @@ block0(v0: i8): ; slli a2, a0, 0x38 ; srai a4, a2, 0x38 ; neg a0, a4 -; blt a0, a4, 8 -; j 8 +; bge a0, a4, 8 ; mv a0, a4 ; ret @@ -36,7 +35,7 @@ block0(v0: i16): ; slli a2,a0,48 ; srai a4,a2,48 ; sub a0,zero,a4 -; select_reg a0,a4,a0##condition=(a4 sgt a0) +; select a0,a4,a0##condition=(a4 sgt a0) ; ret ; ; Disassembled: @@ -44,8 +43,7 @@ block0(v0: i16): ; slli a2, a0, 0x30 ; srai a4, a2, 0x30 ; neg a0, a4 -; blt a0, a4, 8 -; j 8 +; bge a0, a4, 8 ; mv a0, a4 ; ret @@ -59,17 +57,17 @@ block0(v0: i32): ; block0: ; sext.w a2,a0 ; sub a4,zero,a2 -; select_reg a0,a2,a4##condition=(a2 sgt a4) +; select a0,a2,a4##condition=(a2 sgt a4) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; sext.w a2, a0 ; neg a4, a2 -; blt a4, a2, 0xc -; mv a0, a4 -; j 8 +; bge a4, a2, 0xc ; mv a0, a2 +; j 8 +; mv a0, a4 ; ret function %iabs_i64(i64) -> i64 { @@ -81,7 +79,7 @@ block0(v0: i64): ; VCode: ; block0: ; sub a2,zero,a0 -; select_reg a0,a0,a2##condition=(a0 sgt a2) +; select a0,a0,a2##condition=(a0 sgt a2) ; ret ; ; Disassembled: diff --git a/cranelift/filetests/filetests/isa/riscv64/ishl-const.clif b/cranelift/filetests/filetests/isa/riscv64/ishl-const.clif index 3e2fe0c7cdca..c46b536daa88 100644 --- a/cranelift/filetests/filetests/isa/riscv64/ishl-const.clif +++ b/cranelift/filetests/filetests/isa/riscv64/ishl-const.clif @@ -362,13 +362,12 @@ block0(v0: i128): ; sub a2,a2,a5 ; sll a4,a0,a5 ; srl a0,a0,a2 -; select_reg a2,zero,a0##condition=(a5 eq zero) +; select a2,zero,a0##condition=(a5 eq zero) ; sll a5,a1,a5 -; or a1,a2,a5 +; or a5,a2,a5 ; li a2,64 -; andi a5,a3,127 -; select_reg a0,zero,a4##condition=(a5 uge a2) -; select_reg a1,a4,a1##condition=(a5 uge a2) +; andi a3,a3,127 +; select [a0,a1],[zero,a4],[a4,a5]##condition=(a3 uge a2) ; ret ; ; Disassembled: @@ -379,21 +378,20 @@ block0(v0: i128): ; sub a2, a2, a5 ; sll a4, a0, a5 ; srl a0, a0, a2 -; beqz a5, 0xc -; mv a2, a0 -; j 8 +; bnez a5, 0xc ; mv a2, zero +; j 8 +; mv a2, a0 ; sll a5, a1, a5 -; or a1, a2, a5 +; or a5, a2, a5 ; addi a2, zero, 0x40 -; andi a5, a3, 0x7f -; bgeu a5, a2, 0xc -; mv a0, a4 -; j 8 +; andi a3, a3, 0x7f +; bltu a3, a2, 0x10 ; mv a0, zero -; bgeu a5, a2, 8 -; j 8 ; mv a1, a4 +; j 0xc +; mv a0, a4 +; mv a1, a5 ; ret function %ishl_i128_const_i16(i128) -> i128 { @@ -411,13 +409,12 @@ block0(v0: i128): ; sub a2,a2,a5 ; sll a4,a0,a5 ; srl a0,a0,a2 -; select_reg a2,zero,a0##condition=(a5 eq zero) +; select a2,zero,a0##condition=(a5 eq zero) ; sll a5,a1,a5 -; or a1,a2,a5 +; or a5,a2,a5 ; li a2,64 -; andi a5,a3,127 -; select_reg a0,zero,a4##condition=(a5 uge a2) -; select_reg a1,a4,a1##condition=(a5 uge a2) +; andi a3,a3,127 +; select [a0,a1],[zero,a4],[a4,a5]##condition=(a3 uge a2) ; ret ; ; Disassembled: @@ -428,21 +425,20 @@ block0(v0: i128): ; sub a2, a2, a5 ; sll a4, a0, a5 ; srl a0, a0, a2 -; beqz a5, 0xc -; mv a2, a0 -; j 8 +; bnez a5, 0xc ; mv a2, zero +; j 8 +; mv a2, a0 ; sll a5, a1, a5 -; or a1, a2, a5 +; or a5, a2, a5 ; addi a2, zero, 0x40 -; andi a5, a3, 0x7f -; bgeu a5, a2, 0xc -; mv a0, a4 -; j 8 +; andi a3, a3, 0x7f +; bltu a3, a2, 0x10 ; mv a0, zero -; bgeu a5, a2, 8 -; j 8 ; mv a1, a4 +; j 0xc +; mv a0, a4 +; mv a1, a5 ; ret function %ishl_i128_const_i32(i128) -> i128 { @@ -460,13 +456,12 @@ block0(v0: i128): ; sub a2,a2,a5 ; sll a4,a0,a5 ; srl a0,a0,a2 -; select_reg a2,zero,a0##condition=(a5 eq zero) +; select a2,zero,a0##condition=(a5 eq zero) ; sll a5,a1,a5 -; or a1,a2,a5 +; or a5,a2,a5 ; li a2,64 -; andi a5,a3,127 -; select_reg a0,zero,a4##condition=(a5 uge a2) -; select_reg a1,a4,a1##condition=(a5 uge a2) +; andi a3,a3,127 +; select [a0,a1],[zero,a4],[a4,a5]##condition=(a3 uge a2) ; ret ; ; Disassembled: @@ -477,21 +472,20 @@ block0(v0: i128): ; sub a2, a2, a5 ; sll a4, a0, a5 ; srl a0, a0, a2 -; beqz a5, 0xc -; mv a2, a0 -; j 8 +; bnez a5, 0xc ; mv a2, zero +; j 8 +; mv a2, a0 ; sll a5, a1, a5 -; or a1, a2, a5 +; or a5, a2, a5 ; addi a2, zero, 0x40 -; andi a5, a3, 0x7f -; bgeu a5, a2, 0xc -; mv a0, a4 -; j 8 +; andi a3, a3, 0x7f +; bltu a3, a2, 0x10 ; mv a0, zero -; bgeu a5, a2, 8 -; j 8 ; mv a1, a4 +; j 0xc +; mv a0, a4 +; mv a1, a5 ; ret function %ishl_i128_const_i64(i128) -> i128 { @@ -509,13 +503,12 @@ block0(v0: i128): ; sub a2,a2,a5 ; sll a4,a0,a5 ; srl a0,a0,a2 -; select_reg a2,zero,a0##condition=(a5 eq zero) +; select a2,zero,a0##condition=(a5 eq zero) ; sll a5,a1,a5 -; or a1,a2,a5 +; or a5,a2,a5 ; li a2,64 -; andi a5,a3,127 -; select_reg a0,zero,a4##condition=(a5 uge a2) -; select_reg a1,a4,a1##condition=(a5 uge a2) +; andi a3,a3,127 +; select [a0,a1],[zero,a4],[a4,a5]##condition=(a3 uge a2) ; ret ; ; Disassembled: @@ -526,21 +519,20 @@ block0(v0: i128): ; sub a2, a2, a5 ; sll a4, a0, a5 ; srl a0, a0, a2 -; beqz a5, 0xc -; mv a2, a0 -; j 8 +; bnez a5, 0xc ; mv a2, zero +; j 8 +; mv a2, a0 ; sll a5, a1, a5 -; or a1, a2, a5 +; or a5, a2, a5 ; addi a2, zero, 0x40 -; andi a5, a3, 0x7f -; bgeu a5, a2, 0xc -; mv a0, a4 -; j 8 +; andi a3, a3, 0x7f +; bltu a3, a2, 0x10 ; mv a0, zero -; bgeu a5, a2, 8 -; j 8 ; mv a1, a4 +; j 0xc +; mv a0, a4 +; mv a1, a5 ; ret function %ishl_i128_const_i128(i128) -> i128 { @@ -560,13 +552,12 @@ block0(v0: i128): ; sub a3,a3,a2 ; sll a5,a0,a2 ; srl a3,a0,a3 -; select_reg a3,zero,a3##condition=(a2 eq zero) +; select a3,zero,a3##condition=(a2 eq zero) ; sll a0,a1,a2 -; or a1,a3,a0 +; or a2,a3,a0 ; li a3,64 -; andi a2,a4,127 -; select_reg a0,zero,a5##condition=(a2 uge a3) -; select_reg a1,a5,a1##condition=(a2 uge a3) +; andi a4,a4,127 +; select [a0,a1],[zero,a5],[a5,a2]##condition=(a4 uge a3) ; ret ; ; Disassembled: @@ -578,19 +569,17 @@ block0(v0: i128): ; sub a3, a3, a2 ; sll a5, a0, a2 ; srl a3, a0, a3 -; beqz a2, 8 -; j 8 +; bnez a2, 8 ; mv a3, zero ; sll a0, a1, a2 -; or a1, a3, a0 +; or a2, a3, a0 ; addi a3, zero, 0x40 -; andi a2, a4, 0x7f -; bgeu a2, a3, 0xc -; mv a0, a5 -; j 8 +; andi a4, a4, 0x7f +; bltu a4, a3, 0x10 ; mv a0, zero -; bgeu a2, a3, 8 -; j 8 ; mv a1, a5 +; j 0xc +; mv a0, a5 +; mv a1, a2 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/ishl.clif b/cranelift/filetests/filetests/isa/riscv64/ishl.clif index 7f3c5d090ab7..3dbb73cdd11b 100644 --- a/cranelift/filetests/filetests/isa/riscv64/ishl.clif +++ b/cranelift/filetests/filetests/isa/riscv64/ishl.clif @@ -356,13 +356,12 @@ block0(v0: i128, v1: i8): ; sub a3,a3,a5 ; sll a4,a0,a5 ; srl a0,a0,a3 -; select_reg a3,zero,a0##condition=(a5 eq zero) +; select a3,zero,a0##condition=(a5 eq zero) ; sll a5,a1,a5 -; or a1,a3,a5 +; or a5,a3,a5 ; li a3,64 -; andi a5,a2,127 -; select_reg a0,zero,a4##condition=(a5 uge a3) -; select_reg a1,a4,a1##condition=(a5 uge a3) +; andi a2,a2,127 +; select [a0,a1],[zero,a4],[a4,a5]##condition=(a2 uge a3) ; ret ; ; Disassembled: @@ -372,21 +371,20 @@ block0(v0: i128, v1: i8): ; sub a3, a3, a5 ; sll a4, a0, a5 ; srl a0, a0, a3 -; beqz a5, 0xc -; mv a3, a0 -; j 8 +; bnez a5, 0xc ; mv a3, zero +; j 8 +; mv a3, a0 ; sll a5, a1, a5 -; or a1, a3, a5 +; or a5, a3, a5 ; addi a3, zero, 0x40 -; andi a5, a2, 0x7f -; bgeu a5, a3, 0xc -; mv a0, a4 -; j 8 +; andi a2, a2, 0x7f +; bltu a2, a3, 0x10 ; mv a0, zero -; bgeu a5, a3, 8 -; j 8 ; mv a1, a4 +; j 0xc +; mv a0, a4 +; mv a1, a5 ; ret function %ishl_i128_i16(i128, i16) -> i128 { @@ -402,13 +400,12 @@ block0(v0: i128, v1: i16): ; sub a3,a3,a5 ; sll a4,a0,a5 ; srl a0,a0,a3 -; select_reg a3,zero,a0##condition=(a5 eq zero) +; select a3,zero,a0##condition=(a5 eq zero) ; sll a5,a1,a5 -; or a1,a3,a5 +; or a5,a3,a5 ; li a3,64 -; andi a5,a2,127 -; select_reg a0,zero,a4##condition=(a5 uge a3) -; select_reg a1,a4,a1##condition=(a5 uge a3) +; andi a2,a2,127 +; select [a0,a1],[zero,a4],[a4,a5]##condition=(a2 uge a3) ; ret ; ; Disassembled: @@ -418,21 +415,20 @@ block0(v0: i128, v1: i16): ; sub a3, a3, a5 ; sll a4, a0, a5 ; srl a0, a0, a3 -; beqz a5, 0xc -; mv a3, a0 -; j 8 +; bnez a5, 0xc ; mv a3, zero +; j 8 +; mv a3, a0 ; sll a5, a1, a5 -; or a1, a3, a5 +; or a5, a3, a5 ; addi a3, zero, 0x40 -; andi a5, a2, 0x7f -; bgeu a5, a3, 0xc -; mv a0, a4 -; j 8 +; andi a2, a2, 0x7f +; bltu a2, a3, 0x10 ; mv a0, zero -; bgeu a5, a3, 8 -; j 8 ; mv a1, a4 +; j 0xc +; mv a0, a4 +; mv a1, a5 ; ret function %ishl_i128_i32(i128, i32) -> i128 { @@ -448,13 +444,12 @@ block0(v0: i128, v1: i32): ; sub a3,a3,a5 ; sll a4,a0,a5 ; srl a0,a0,a3 -; select_reg a3,zero,a0##condition=(a5 eq zero) +; select a3,zero,a0##condition=(a5 eq zero) ; sll a5,a1,a5 -; or a1,a3,a5 +; or a5,a3,a5 ; li a3,64 -; andi a5,a2,127 -; select_reg a0,zero,a4##condition=(a5 uge a3) -; select_reg a1,a4,a1##condition=(a5 uge a3) +; andi a2,a2,127 +; select [a0,a1],[zero,a4],[a4,a5]##condition=(a2 uge a3) ; ret ; ; Disassembled: @@ -464,21 +459,20 @@ block0(v0: i128, v1: i32): ; sub a3, a3, a5 ; sll a4, a0, a5 ; srl a0, a0, a3 -; beqz a5, 0xc -; mv a3, a0 -; j 8 +; bnez a5, 0xc ; mv a3, zero +; j 8 +; mv a3, a0 ; sll a5, a1, a5 -; or a1, a3, a5 +; or a5, a3, a5 ; addi a3, zero, 0x40 -; andi a5, a2, 0x7f -; bgeu a5, a3, 0xc -; mv a0, a4 -; j 8 +; andi a2, a2, 0x7f +; bltu a2, a3, 0x10 ; mv a0, zero -; bgeu a5, a3, 8 -; j 8 ; mv a1, a4 +; j 0xc +; mv a0, a4 +; mv a1, a5 ; ret function %ishl_i128_i64(i128, i64) -> i128 { @@ -494,13 +488,12 @@ block0(v0: i128, v1: i64): ; sub a3,a3,a5 ; sll a4,a0,a5 ; srl a0,a0,a3 -; select_reg a3,zero,a0##condition=(a5 eq zero) +; select a3,zero,a0##condition=(a5 eq zero) ; sll a5,a1,a5 -; or a1,a3,a5 +; or a5,a3,a5 ; li a3,64 -; andi a5,a2,127 -; select_reg a0,zero,a4##condition=(a5 uge a3) -; select_reg a1,a4,a1##condition=(a5 uge a3) +; andi a2,a2,127 +; select [a0,a1],[zero,a4],[a4,a5]##condition=(a2 uge a3) ; ret ; ; Disassembled: @@ -510,21 +503,20 @@ block0(v0: i128, v1: i64): ; sub a3, a3, a5 ; sll a4, a0, a5 ; srl a0, a0, a3 -; beqz a5, 0xc -; mv a3, a0 -; j 8 +; bnez a5, 0xc ; mv a3, zero +; j 8 +; mv a3, a0 ; sll a5, a1, a5 -; or a1, a3, a5 +; or a5, a3, a5 ; addi a3, zero, 0x40 -; andi a5, a2, 0x7f -; bgeu a5, a3, 0xc -; mv a0, a4 -; j 8 +; andi a2, a2, 0x7f +; bltu a2, a3, 0x10 ; mv a0, zero -; bgeu a5, a3, 8 -; j 8 ; mv a1, a4 +; j 0xc +; mv a0, a4 +; mv a1, a5 ; ret function %ishl_i128_i128(i128, i128) -> i128 { @@ -542,13 +534,12 @@ block0(v0: i128, v1: i128): ; mv a4,a5 ; sll a5,a4,a0 ; srl a3,a4,a3 -; select_reg a3,zero,a3##condition=(a0 eq zero) +; select a3,zero,a3##condition=(a0 eq zero) ; sll a0,a1,a0 -; or a1,a3,a0 +; or a4,a3,a0 ; li a3,64 ; andi a2,a2,127 -; select_reg a0,zero,a5##condition=(a2 uge a3) -; select_reg a1,a5,a1##condition=(a2 uge a3) +; select [a0,a1],[zero,a5],[a5,a4]##condition=(a2 uge a3) ; ret ; ; Disassembled: @@ -560,19 +551,17 @@ block0(v0: i128, v1: i128): ; mv a4, a5 ; sll a5, a4, a0 ; srl a3, a4, a3 -; beqz a0, 8 -; j 8 +; bnez a0, 8 ; mv a3, zero ; sll a0, a1, a0 -; or a1, a3, a0 +; or a4, a3, a0 ; addi a3, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a3, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a3, 0x10 ; mv a0, zero -; bgeu a2, a3, 8 -; j 8 ; mv a1, a5 +; j 0xc +; mv a0, a5 +; mv a1, a4 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif b/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif index 19b7b783276a..9aa772b2539b 100644 --- a/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif +++ b/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif @@ -184,12 +184,12 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; sd a3,368(nominal_sp) ; sw a2,376(nominal_sp) ; sh a0,380(nominal_sp) -; zext.w a2,a1 -; select_i16x8 v13,v12,v12##condition=a2 -; zext.w a2,a1 -; select_i16x8 v14,v13,v13##condition=a2 -; zext.w a2,a1 -; select_i16x8 v12,v14,v14##condition=a2 +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) ; vfsqrt.v v10,v11 #avl=2, #vtype=(e64, m1, ta, ma) ; lui a0,4095 ; slli a2,a0,39 @@ -204,109 +204,109 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; vfmv.v.f v13,fa4 #avl=2, #vtype=(e64, m1, ta, ma) ; vmfne.vv v0,v10,v10 #avl=2, #vtype=(e64, m1, ta, ma) ; vmerge.vvm v11,v10,v13,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a2,a1 -; select_i16x8 v13,v12,v12##condition=a2 -; zext.w a2,a1 -; select_i16x8 v12,v13,v13##condition=a2 -; zext.w a2,a1 -; select_i16x8 v13,v12,v12##condition=a2 -; zext.w a2,a1 -; select_i16x8 v12,v13,v13##condition=a2 -; zext.w a2,a1 -; select_i16x8 v13,v12,v12##condition=a2 -; zext.w a2,a1 -; select_i16x8 v12,v13,v13##condition=a2 -; zext.w a2,a1 -; select_i16x8 v13,v12,v12##condition=a2 -; zext.w a2,a1 -; select_i16x8 v12,v13,v13##condition=a2 +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) +; sext.w a2,a1 +; select v12,v12,v12##condition=(a2 ne zero) ; add a2,a1,a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) ; vmax.vv v10,v15,v15 #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) ; load_addr a3,3(nominal_sp) ; addi a3,a3,0 ; andi a0,a3,3 ; slli a4,a0,3 ; andi a1,a3,-4 ; atomic_rmw.i8 and a0,a5,(a1)##t0=a3 offset=a4 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v14,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v14,v14##condition=a1 +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) ; vse64.v v10,33(nominal_sp) #avl=2, #vtype=(e64, m1, ta, ma) -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 -; zext.w a1,a2 -; select_i16x8 v13,v12,v12##condition=a1 -; zext.w a1,a2 -; select_i16x8 v12,v13,v13##condition=a1 +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) +; sext.w a1,a2 +; select v12,v12,v12##condition=(a1 ne zero) ; vse8.v v11,0(a6) #avl=16, #vtype=(e8, m1, ta, ma) ; vse8.v v12,16(a6) #avl=16, #vtype=(e8, m1, ta, ma) ; vse8.v v11,32(a6) #avl=16, #vtype=(e8, m1, ta, ma) @@ -392,21 +392,9 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; sd a3, 0x170(sp) ; sw a2, 0x178(sp) ; sh a0, 0x17c(sp) -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x37, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xd0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x36, 0xe0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xe0, 0x9e +; sext.w a2, a1 +; sext.w a2, a1 +; sext.w a2, a1 ; .byte 0x57, 0x70, 0x81, 0xcd ; .byte 0x57, 0x15, 0xb0, 0x4e ; lui a0, 0xfff @@ -422,78 +410,22 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; .byte 0xd7, 0x56, 0x07, 0x5e ; .byte 0x57, 0x10, 0xa5, 0x72 ; .byte 0xd7, 0x85, 0xa6, 0x5c -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0x3b, 0x86, 0x05, 0x08 -; beqz a2, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e +; sext.w a2, a1 +; sext.w a2, a1 +; sext.w a2, a1 +; sext.w a2, a1 +; sext.w a2, a1 +; sext.w a2, a1 +; sext.w a2, a1 +; sext.w a2, a1 ; add a2, a1, a1 -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 ; .byte 0x57, 0x85, 0xf7, 0x1e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e +; sext.w a1, a2 ; addi a3, sp, 3 ; mv a3, a3 ; andi a0, a3, 3 @@ -513,173 +445,41 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; or t5, t5, t6 ; sc.w.aqrl a3, t5, (a1) ; bnez a3, -0x34 -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x37, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x37, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xe0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xe0, 0x9e +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 ; addi t6, sp, 0x21 ; .byte 0x27, 0xf5, 0x0f, 0x02 -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0xd7, 0x36, 0xc0, 0x9e -; j 8 -; .byte 0xd7, 0x36, 0xc0, 0x9e -; .byte 0xbb, 0x05, 0x06, 0x08 -; beqz a1, 0xc -; .byte 0x57, 0x36, 0xd0, 0x9e -; j 8 -; .byte 0x57, 0x36, 0xd0, 0x9e +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 +; sext.w a1, a2 ; .byte 0x57, 0x70, 0x08, 0xcc ; .byte 0xa7, 0x05, 0x08, 0x02 ; addi t6, a6, 0x10 diff --git a/cranelift/filetests/filetests/isa/riscv64/rotl.clif b/cranelift/filetests/filetests/isa/riscv64/rotl.clif index 8c1220226284..b67632cbec15 100644 --- a/cranelift/filetests/filetests/isa/riscv64/rotl.clif +++ b/cranelift/filetests/filetests/isa/riscv64/rotl.clif @@ -14,27 +14,26 @@ block0(v0: i128, v1: i128): ; sd fp,0(sp) ; mv fp,sp ; sd s7,-8(sp) +; sd s9,-16(sp) ; add sp,-16 ; block0: ; andi a5,a2,63 ; li a3,64 -; sub a3,a3,a5 -; sll a4,a0,a5 -; srl s7,a1,a3 -; mv t2,a1 -; select_reg a1,zero,s7##condition=(a5 eq zero) -; or a1,a4,a1 -; mv a4,t2 -; sll a4,a4,a5 -; srl a3,a0,a3 -; select_reg a5,zero,a3##condition=(a5 eq zero) -; or a3,a4,a5 +; sub a4,a3,a5 +; sll a3,a0,a5 +; srl s7,a1,a4 +; select s9,zero,s7##condition=(a5 eq zero) +; or a3,a3,s9 +; sll a1,a1,a5 +; srl a4,a0,a4 +; select a5,zero,a4##condition=(a5 eq zero) +; or a5,a1,a5 ; li a4,64 -; andi a5,a2,127 -; select_reg a0,a3,a1##condition=(a5 uge a4) -; select_reg a1,a1,a3##condition=(a5 uge a4) +; andi a2,a2,127 +; select [a0,a1],[a5,a3],[a3,a5]##condition=(a2 uge a4) ; add sp,+16 ; ld s7,-8(sp) +; ld s9,-16(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -47,37 +46,37 @@ block0(v0: i128, v1: i128): ; sd s0, 0(sp) ; mv s0, sp ; sd s7, -8(sp) +; sd s9, -0x10(sp) ; addi sp, sp, -0x10 -; block1: ; offset 0x18 +; block1: ; offset 0x1c ; andi a5, a2, 0x3f ; addi a3, zero, 0x40 -; sub a3, a3, a5 -; sll a4, a0, a5 -; srl s7, a1, a3 -; mv t2, a1 -; beqz a5, 0xc -; mv a1, s7 -; j 8 -; mv a1, zero -; or a1, a4, a1 -; mv a4, t2 -; sll a4, a4, a5 -; srl a3, a0, a3 -; beqz a5, 0xc -; mv a5, a3 +; sub a4, a3, a5 +; sll a3, a0, a5 +; srl s7, a1, a4 +; bnez a5, 0xc +; mv s9, zero ; j 8 +; mv s9, s7 +; or a3, a3, s9 +; sll a1, a1, a5 +; srl a4, a0, a4 +; bnez a5, 0xc ; mv a5, zero -; or a3, a4, a5 -; addi a4, zero, 0x40 -; andi a5, a2, 0x7f -; bgeu a5, a4, 0xc -; mv a0, a1 ; j 8 -; mv a0, a3 -; bgeu a5, a4, 8 +; mv a5, a4 +; or a5, a1, a5 +; addi a4, zero, 0x40 +; andi a2, a2, 0x7f +; bltu a2, a4, 0x10 +; mv a0, a5 ; mv a1, a3 +; j 0xc +; mv a0, a3 +; mv a1, a5 ; addi sp, sp, 0x10 ; ld s7, -8(sp) +; ld s9, -0x10(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -96,7 +95,7 @@ block0(v0: i64, v1: i64): ; sub a1,a5,a3 ; sll a4,a0,a3 ; srl a5,a0,a1 -; select_reg a1,zero,a5##condition=(a3 eq zero) +; select a1,zero,a5##condition=(a3 eq zero) ; or a0,a4,a1 ; ret ; @@ -107,10 +106,10 @@ block0(v0: i64, v1: i64): ; sub a1, a5, a3 ; sll a4, a0, a3 ; srl a5, a0, a1 -; beqz a3, 0xc -; mv a1, a5 -; j 8 +; bnez a3, 0xc ; mv a1, zero +; j 8 +; mv a1, a5 ; or a0, a4, a1 ; ret @@ -129,7 +128,7 @@ block0(v0: i32, v1: i32): ; sub a0,a3,a1 ; sll a2,a5,a1 ; srl a3,a5,a0 -; select_reg a5,zero,a3##condition=(a1 eq zero) +; select a5,zero,a3##condition=(a1 eq zero) ; or a0,a2,a5 ; ret ; @@ -142,10 +141,10 @@ block0(v0: i32, v1: i32): ; sub a0, a3, a1 ; sll a2, a5, a1 ; srl a3, a5, a0 -; beqz a1, 0xc -; mv a5, a3 -; j 8 +; bnez a1, 0xc ; mv a5, zero +; j 8 +; mv a5, a3 ; or a0, a2, a5 ; ret @@ -164,7 +163,7 @@ block0(v0: i16, v1: i16): ; sub a0,a3,a1 ; sll a2,a5,a1 ; srl a3,a5,a0 -; select_reg a5,zero,a3##condition=(a1 eq zero) +; select a5,zero,a3##condition=(a1 eq zero) ; or a0,a2,a5 ; ret ; @@ -177,10 +176,10 @@ block0(v0: i16, v1: i16): ; sub a0, a3, a1 ; sll a2, a5, a1 ; srl a3, a5, a0 -; beqz a1, 0xc -; mv a5, a3 -; j 8 +; bnez a1, 0xc ; mv a5, zero +; j 8 +; mv a5, a3 ; or a0, a2, a5 ; ret @@ -198,7 +197,7 @@ block0(v0: i8, v1: i8): ; sub a4,a1,a5 ; sll a0,a3,a5 ; srl a1,a3,a4 -; select_reg a3,zero,a1##condition=(a5 eq zero) +; select a3,zero,a1##condition=(a5 eq zero) ; or a0,a0,a3 ; ret ; @@ -210,10 +209,10 @@ block0(v0: i8, v1: i8): ; sub a4, a1, a5 ; sll a0, a3, a5 ; srl a1, a3, a4 -; beqz a5, 0xc -; mv a3, a1 -; j 8 +; bnez a5, 0xc ; mv a3, zero +; j 8 +; mv a3, a1 ; or a0, a0, a3 ; ret @@ -232,7 +231,7 @@ block0(v0: i64): ; sub a1,a5,a3 ; sll a4,a0,a3 ; srl a5,a0,a1 -; select_reg a1,zero,a5##condition=(a3 eq zero) +; select a1,zero,a5##condition=(a3 eq zero) ; or a0,a4,a1 ; ret ; @@ -244,10 +243,10 @@ block0(v0: i64): ; sub a1, a5, a3 ; sll a4, a0, a3 ; srl a5, a0, a1 -; beqz a3, 0xc -; mv a1, a5 -; j 8 +; bnez a3, 0xc ; mv a1, zero +; j 8 +; mv a1, a5 ; or a0, a4, a1 ; ret @@ -268,7 +267,7 @@ block0(v0: i32): ; sub a0,a3,a1 ; sll a2,a5,a1 ; srl a3,a5,a0 -; select_reg a5,zero,a3##condition=(a1 eq zero) +; select a5,zero,a3##condition=(a1 eq zero) ; or a0,a2,a5 ; ret ; @@ -282,10 +281,10 @@ block0(v0: i32): ; sub a0, a3, a1 ; sll a2, a5, a1 ; srl a3, a5, a0 -; beqz a1, 0xc -; mv a5, a3 -; j 8 +; bnez a1, 0xc ; mv a5, zero +; j 8 +; mv a5, a3 ; or a0, a2, a5 ; ret @@ -306,7 +305,7 @@ block0(v0: i16): ; sub a0,a3,a1 ; sll a2,a5,a1 ; srl a3,a5,a0 -; select_reg a5,zero,a3##condition=(a1 eq zero) +; select a5,zero,a3##condition=(a1 eq zero) ; or a0,a2,a5 ; ret ; @@ -320,10 +319,10 @@ block0(v0: i16): ; sub a0, a3, a1 ; sll a2, a5, a1 ; srl a3, a5, a0 -; beqz a1, 0xc -; mv a5, a3 -; j 8 +; bnez a1, 0xc ; mv a5, zero +; j 8 +; mv a5, a3 ; or a0, a2, a5 ; ret @@ -343,7 +342,7 @@ block0(v0: i8): ; sub a4,a1,a5 ; sll a0,a3,a5 ; srl a1,a3,a4 -; select_reg a3,zero,a1##condition=(a5 eq zero) +; select a3,zero,a1##condition=(a5 eq zero) ; or a0,a0,a3 ; ret ; @@ -356,10 +355,10 @@ block0(v0: i8): ; sub a4, a1, a5 ; sll a0, a3, a5 ; srl a1, a3, a4 -; beqz a5, 0xc -; mv a3, a1 -; j 8 +; bnez a5, 0xc ; mv a3, zero +; j 8 +; mv a3, a1 ; or a0, a0, a3 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/rotr.clif b/cranelift/filetests/filetests/isa/riscv64/rotr.clif index 950fa9bec3ee..196206a92f80 100644 --- a/cranelift/filetests/filetests/isa/riscv64/rotr.clif +++ b/cranelift/filetests/filetests/isa/riscv64/rotr.clif @@ -14,27 +14,26 @@ block0(v0: i128, v1: i128): ; sd fp,0(sp) ; mv fp,sp ; sd s7,-8(sp) +; sd s9,-16(sp) ; add sp,-16 ; block0: ; andi a5,a2,63 ; li a3,64 -; sub a3,a3,a5 -; srl a4,a0,a5 -; sll s7,a1,a3 -; mv t2,a1 -; select_reg a1,zero,s7##condition=(a5 eq zero) -; or a1,a4,a1 -; mv a4,t2 -; srl a4,a4,a5 -; sll a3,a0,a3 -; select_reg a5,zero,a3##condition=(a5 eq zero) -; or a3,a4,a5 +; sub a4,a3,a5 +; srl a3,a0,a5 +; sll s7,a1,a4 +; select s9,zero,s7##condition=(a5 eq zero) +; or a3,a3,s9 +; srl a1,a1,a5 +; sll a4,a0,a4 +; select a5,zero,a4##condition=(a5 eq zero) +; or a5,a1,a5 ; li a4,64 -; andi a5,a2,127 -; select_reg a0,a3,a1##condition=(a5 uge a4) -; select_reg a1,a1,a3##condition=(a5 uge a4) +; andi a2,a2,127 +; select [a0,a1],[a5,a3],[a3,a5]##condition=(a2 uge a4) ; add sp,+16 ; ld s7,-8(sp) +; ld s9,-16(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -47,37 +46,37 @@ block0(v0: i128, v1: i128): ; sd s0, 0(sp) ; mv s0, sp ; sd s7, -8(sp) +; sd s9, -0x10(sp) ; addi sp, sp, -0x10 -; block1: ; offset 0x18 +; block1: ; offset 0x1c ; andi a5, a2, 0x3f ; addi a3, zero, 0x40 -; sub a3, a3, a5 -; srl a4, a0, a5 -; sll s7, a1, a3 -; mv t2, a1 -; beqz a5, 0xc -; mv a1, s7 -; j 8 -; mv a1, zero -; or a1, a4, a1 -; mv a4, t2 -; srl a4, a4, a5 -; sll a3, a0, a3 -; beqz a5, 0xc -; mv a5, a3 +; sub a4, a3, a5 +; srl a3, a0, a5 +; sll s7, a1, a4 +; bnez a5, 0xc +; mv s9, zero ; j 8 +; mv s9, s7 +; or a3, a3, s9 +; srl a1, a1, a5 +; sll a4, a0, a4 +; bnez a5, 0xc ; mv a5, zero -; or a3, a4, a5 -; addi a4, zero, 0x40 -; andi a5, a2, 0x7f -; bgeu a5, a4, 0xc -; mv a0, a1 ; j 8 -; mv a0, a3 -; bgeu a5, a4, 8 +; mv a5, a4 +; or a5, a1, a5 +; addi a4, zero, 0x40 +; andi a2, a2, 0x7f +; bltu a2, a4, 0x10 +; mv a0, a5 ; mv a1, a3 +; j 0xc +; mv a0, a3 +; mv a1, a5 ; addi sp, sp, 0x10 ; ld s7, -8(sp) +; ld s9, -0x10(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -96,7 +95,7 @@ block0(v0: i64, v1: i64): ; sub a1,a5,a3 ; srl a4,a0,a3 ; sll a5,a0,a1 -; select_reg a1,zero,a5##condition=(a3 eq zero) +; select a1,zero,a5##condition=(a3 eq zero) ; or a0,a4,a1 ; ret ; @@ -107,10 +106,10 @@ block0(v0: i64, v1: i64): ; sub a1, a5, a3 ; srl a4, a0, a3 ; sll a5, a0, a1 -; beqz a3, 0xc -; mv a1, a5 -; j 8 +; bnez a3, 0xc ; mv a1, zero +; j 8 +; mv a1, a5 ; or a0, a4, a1 ; ret @@ -129,7 +128,7 @@ block0(v0: i32, v1: i32): ; sub a0,a3,a1 ; srl a2,a5,a1 ; sll a3,a5,a0 -; select_reg a5,zero,a3##condition=(a1 eq zero) +; select a5,zero,a3##condition=(a1 eq zero) ; or a0,a2,a5 ; ret ; @@ -142,10 +141,10 @@ block0(v0: i32, v1: i32): ; sub a0, a3, a1 ; srl a2, a5, a1 ; sll a3, a5, a0 -; beqz a1, 0xc -; mv a5, a3 -; j 8 +; bnez a1, 0xc ; mv a5, zero +; j 8 +; mv a5, a3 ; or a0, a2, a5 ; ret @@ -164,7 +163,7 @@ block0(v0: i16, v1: i16): ; sub a0,a3,a1 ; srl a2,a5,a1 ; sll a3,a5,a0 -; select_reg a5,zero,a3##condition=(a1 eq zero) +; select a5,zero,a3##condition=(a1 eq zero) ; or a0,a2,a5 ; ret ; @@ -177,10 +176,10 @@ block0(v0: i16, v1: i16): ; sub a0, a3, a1 ; srl a2, a5, a1 ; sll a3, a5, a0 -; beqz a1, 0xc -; mv a5, a3 -; j 8 +; bnez a1, 0xc ; mv a5, zero +; j 8 +; mv a5, a3 ; or a0, a2, a5 ; ret @@ -198,7 +197,7 @@ block0(v0: i8, v1: i8): ; sub a4,a1,a5 ; srl a0,a3,a5 ; sll a1,a3,a4 -; select_reg a3,zero,a1##condition=(a5 eq zero) +; select a3,zero,a1##condition=(a5 eq zero) ; or a0,a0,a3 ; ret ; @@ -210,10 +209,10 @@ block0(v0: i8, v1: i8): ; sub a4, a1, a5 ; srl a0, a3, a5 ; sll a1, a3, a4 -; beqz a5, 0xc -; mv a3, a1 -; j 8 +; bnez a5, 0xc ; mv a3, zero +; j 8 +; mv a3, a1 ; or a0, a0, a3 ; ret @@ -232,7 +231,7 @@ block0(v0: i64): ; sub a1,a5,a3 ; srl a4,a0,a3 ; sll a5,a0,a1 -; select_reg a1,zero,a5##condition=(a3 eq zero) +; select a1,zero,a5##condition=(a3 eq zero) ; or a0,a4,a1 ; ret ; @@ -244,10 +243,10 @@ block0(v0: i64): ; sub a1, a5, a3 ; srl a4, a0, a3 ; sll a5, a0, a1 -; beqz a3, 0xc -; mv a1, a5 -; j 8 +; bnez a3, 0xc ; mv a1, zero +; j 8 +; mv a1, a5 ; or a0, a4, a1 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/select-float.clif b/cranelift/filetests/filetests/isa/riscv64/select-float.clif index 6a70010fb5b1..3e1b7ffceff1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/select-float.clif +++ b/cranelift/filetests/filetests/isa/riscv64/select-float.clif @@ -16,7 +16,7 @@ block0(v0: i8, v1: f32, v2: f32): ; li a1,42 ; andi a5,a0,255 ; andi a1,a1,255 -; select_reg fa0,fa0,fa1##condition=(a5 eq a1) +; select fa0,fa0,fa1##condition=(a5 eq a1) ; ret ; ; Disassembled: @@ -41,7 +41,7 @@ block0(v0: i8, v1: f64, v2: f64): ; li a1,42 ; andi a5,a0,255 ; andi a1,a1,255 -; select_reg fa0,fa0,fa1##condition=(a5 eq a1) +; select fa0,fa0,fa1##condition=(a5 eq a1) ; ret ; ; Disassembled: @@ -68,7 +68,7 @@ block0(v0: i16, v1: f32, v2: f32): ; srli a1,a5,48 ; slli a3,a3,48 ; srli a5,a3,48 -; select_reg fa0,fa0,fa1##condition=(a1 eq a5) +; select fa0,fa0,fa1##condition=(a1 eq a5) ; ret ; ; Disassembled: @@ -97,7 +97,7 @@ block0(v0: i16, v1: f64, v2: f64): ; srli a1,a5,48 ; slli a3,a3,48 ; srli a5,a3,48 -; select_reg fa0,fa0,fa1##condition=(a1 eq a5) +; select fa0,fa0,fa1##condition=(a1 eq a5) ; ret ; ; Disassembled: @@ -121,22 +121,18 @@ block0(v0: i32, v1: f32, v2: f32): ; VCode: ; block0: -; li a3,42 -; slli a5,a0,32 -; srli a1,a5,32 -; slli a3,a3,32 -; srli a5,a3,32 -; select_reg fa0,fa0,fa1##condition=(a1 eq a5) +; li a1,42 +; sext.w a5,a0 +; sext.w a1,a1 +; select fa0,fa0,fa1##condition=(a5 eq a1) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a1, a5, 0x20 -; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; beq a1, a5, 8 +; addi a1, zero, 0x2a +; sext.w a5, a0 +; sext.w a1, a1 +; beq a5, a1, 8 ; fmv.d fa0, fa1 ; ret @@ -150,22 +146,18 @@ block0(v0: i32, v1: f64, v2: f64): ; VCode: ; block0: -; li a3,42 -; slli a5,a0,32 -; srli a1,a5,32 -; slli a3,a3,32 -; srli a5,a3,32 -; select_reg fa0,fa0,fa1##condition=(a1 eq a5) +; li a1,42 +; sext.w a5,a0 +; sext.w a1,a1 +; select fa0,fa0,fa1##condition=(a5 eq a1) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a1, a5, 0x20 -; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; beq a1, a5, 8 +; addi a1, zero, 0x2a +; sext.w a5, a0 +; sext.w a1, a1 +; beq a5, a1, 8 ; fmv.d fa0, fa1 ; ret @@ -180,7 +172,7 @@ block0(v0: i64, v1: f32, v2: f32): ; VCode: ; block0: ; li a5,42 -; select_reg fa0,fa0,fa1##condition=(a0 eq a5) +; select fa0,fa0,fa1##condition=(a0 eq a5) ; ret ; ; Disassembled: @@ -201,7 +193,7 @@ block0(v0: i64, v1: f64, v2: f64): ; VCode: ; block0: ; li a5,42 -; select_reg fa0,fa0,fa1##condition=(a0 eq a5) +; select fa0,fa0,fa1##condition=(a0 eq a5) ; ret ; ; Disassembled: @@ -222,16 +214,14 @@ block0(v0: i128, v1: f32, v2: f32): ; VCode: ; block0: -; fmv.d fa2,fa0 ; li a2,42 ; li a3,0 ; eq a2,[a0,a1],[a2,a3]##ty=i128 -; select_f32 fa0,fa2,fa1##condition=a2 +; select fa0,fa0,fa1##condition=(a2 ne zero) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; fmv.d fa2, fa0 ; addi a2, zero, 0x2a ; mv a3, zero ; bne a1, a3, 0x10 @@ -239,9 +229,7 @@ block0(v0: i128, v1: f32, v2: f32): ; addi a2, zero, 1 ; j 8 ; mv a2, zero -; beqz a2, 0xc -; fmv.d fa0, fa2 -; j 8 +; bnez a2, 8 ; fmv.d fa0, fa1 ; ret @@ -256,16 +244,14 @@ block0(v0: i128, v1: f64, v2: f64): ; VCode: ; block0: -; fmv.d fa2,fa0 ; li a2,42 ; li a3,0 ; eq a2,[a0,a1],[a2,a3]##ty=i128 -; select_f64 fa0,fa2,fa1##condition=a2 +; select fa0,fa0,fa1##condition=(a2 ne zero) ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; fmv.d fa2, fa0 ; addi a2, zero, 0x2a ; mv a3, zero ; bne a1, a3, 0x10 @@ -273,9 +259,7 @@ block0(v0: i128, v1: f64, v2: f64): ; addi a2, zero, 1 ; j 8 ; mv a2, zero -; beqz a2, 0xc -; fmv.d fa0, fa2 -; j 8 +; bnez a2, 8 ; fmv.d fa0, fa1 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/select.clif b/cranelift/filetests/filetests/isa/riscv64/select.clif index 2633b9b7e2ca..57b9ec24c866 100644 --- a/cranelift/filetests/filetests/isa/riscv64/select.clif +++ b/cranelift/filetests/filetests/isa/riscv64/select.clif @@ -15,7 +15,7 @@ block0(v0: i8, v1: i8, v2: i8): ; li a3,42 ; andi a5,a0,255 ; andi a3,a3,255 -; select_reg a0,a1,a2##condition=(a5 eq a3) +; select a0,a1,a2##condition=(a5 eq a3) ; ret ; ; Disassembled: @@ -23,10 +23,10 @@ block0(v0: i8, v1: i8, v2: i8): ; addi a3, zero, 0x2a ; andi a5, a0, 0xff ; andi a3, a3, 0xff -; beq a5, a3, 0xc -; mv a0, a2 -; j 8 +; bne a5, a3, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i8_i16(i8, i16, i16) -> i16 { @@ -42,7 +42,7 @@ block0(v0: i8, v1: i16, v2: i16): ; li a3,42 ; andi a5,a0,255 ; andi a3,a3,255 -; select_reg a0,a1,a2##condition=(a5 eq a3) +; select a0,a1,a2##condition=(a5 eq a3) ; ret ; ; Disassembled: @@ -50,10 +50,10 @@ block0(v0: i8, v1: i16, v2: i16): ; addi a3, zero, 0x2a ; andi a5, a0, 0xff ; andi a3, a3, 0xff -; beq a5, a3, 0xc -; mv a0, a2 -; j 8 +; bne a5, a3, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i8_i32(i8, i32, i32) -> i32 { @@ -69,7 +69,7 @@ block0(v0: i8, v1: i32, v2: i32): ; li a3,42 ; andi a5,a0,255 ; andi a3,a3,255 -; select_reg a0,a1,a2##condition=(a5 eq a3) +; select a0,a1,a2##condition=(a5 eq a3) ; ret ; ; Disassembled: @@ -77,10 +77,10 @@ block0(v0: i8, v1: i32, v2: i32): ; addi a3, zero, 0x2a ; andi a5, a0, 0xff ; andi a3, a3, 0xff -; beq a5, a3, 0xc -; mv a0, a2 -; j 8 +; bne a5, a3, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i8_i64(i8, i64, i64) -> i64 { @@ -96,7 +96,7 @@ block0(v0: i8, v1: i64, v2: i64): ; li a3,42 ; andi a5,a0,255 ; andi a3,a3,255 -; select_reg a0,a1,a2##condition=(a5 eq a3) +; select a0,a1,a2##condition=(a5 eq a3) ; ret ; ; Disassembled: @@ -104,10 +104,10 @@ block0(v0: i8, v1: i64, v2: i64): ; addi a3, zero, 0x2a ; andi a5, a0, 0xff ; andi a3, a3, 0xff -; beq a5, a3, 0xc -; mv a0, a2 -; j 8 +; bne a5, a3, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i8_i128(i8, i128, i128) -> i128 { @@ -123,17 +123,18 @@ block0(v0: i8, v1: i128, v2: i128): ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s10,-8(sp) +; sd s1,-8(sp) +; sd s8,-16(sp) ; add sp,-16 ; block0: -; mv s10,a1 +; mv s8,a1 ; li a5,42 -; andi a0,a0,255 +; andi s1,a0,255 ; andi a5,a5,255 -; eq a5,a0,a5##ty=i8 -; select_i128 [a0,a1],[s10,a2],[a3,a4]##condition=a5 +; select [a0,a1],[s8,a2],[a3,a4]##condition=(s1 eq a5) ; add sp,+16 -; ld s10,-8(sp) +; ld s1,-8(sp) +; ld s8,-16(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -145,25 +146,23 @@ block0(v0: i8, v1: i128, v2: i128): ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s10, -8(sp) +; sd s1, -8(sp) +; sd s8, -0x10(sp) ; addi sp, sp, -0x10 -; block1: ; offset 0x18 -; mv s10, a1 +; block1: ; offset 0x1c +; mv s8, a1 ; addi a5, zero, 0x2a -; andi a0, a0, 0xff +; andi s1, a0, 0xff ; andi a5, a5, 0xff -; bne a0, a5, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; beqz a5, 0x10 -; mv a0, s10 +; bne s1, a5, 0x10 +; mv a0, s8 ; mv a1, a2 ; j 0xc ; mv a0, a3 ; mv a1, a4 ; addi sp, sp, 0x10 -; ld s10, -8(sp) +; ld s1, -8(sp) +; ld s8, -0x10(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -184,7 +183,7 @@ block0(v0: i16, v1: i8, v2: i8): ; srli a4,a5,48 ; slli a3,a3,48 ; srli a5,a3,48 -; select_reg a0,a1,a2##condition=(a4 eq a5) +; select a0,a1,a2##condition=(a4 eq a5) ; ret ; ; Disassembled: @@ -194,10 +193,10 @@ block0(v0: i16, v1: i8, v2: i8): ; srli a4, a5, 0x30 ; slli a3, a3, 0x30 ; srli a5, a3, 0x30 -; beq a4, a5, 0xc -; mv a0, a2 -; j 8 +; bne a4, a5, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i16_i16(i16, i16, i16) -> i16 { @@ -215,7 +214,7 @@ block0(v0: i16, v1: i16, v2: i16): ; srli a4,a5,48 ; slli a3,a3,48 ; srli a5,a3,48 -; select_reg a0,a1,a2##condition=(a4 eq a5) +; select a0,a1,a2##condition=(a4 eq a5) ; ret ; ; Disassembled: @@ -225,10 +224,10 @@ block0(v0: i16, v1: i16, v2: i16): ; srli a4, a5, 0x30 ; slli a3, a3, 0x30 ; srli a5, a3, 0x30 -; beq a4, a5, 0xc -; mv a0, a2 -; j 8 +; bne a4, a5, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i16_i32(i16, i32, i32) -> i32 { @@ -246,7 +245,7 @@ block0(v0: i16, v1: i32, v2: i32): ; srli a4,a5,48 ; slli a3,a3,48 ; srli a5,a3,48 -; select_reg a0,a1,a2##condition=(a4 eq a5) +; select a0,a1,a2##condition=(a4 eq a5) ; ret ; ; Disassembled: @@ -256,10 +255,10 @@ block0(v0: i16, v1: i32, v2: i32): ; srli a4, a5, 0x30 ; slli a3, a3, 0x30 ; srli a5, a3, 0x30 -; beq a4, a5, 0xc -; mv a0, a2 -; j 8 +; bne a4, a5, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i16_i64(i16, i64, i64) -> i64 { @@ -277,7 +276,7 @@ block0(v0: i16, v1: i64, v2: i64): ; srli a4,a5,48 ; slli a3,a3,48 ; srli a5,a3,48 -; select_reg a0,a1,a2##condition=(a4 eq a5) +; select a0,a1,a2##condition=(a4 eq a5) ; ret ; ; Disassembled: @@ -287,10 +286,10 @@ block0(v0: i16, v1: i64, v2: i64): ; srli a4, a5, 0x30 ; slli a3, a3, 0x30 ; srli a5, a3, 0x30 -; beq a4, a5, 0xc -; mv a0, a2 -; j 8 +; bne a4, a5, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i16_i128(i16, i128, i128) -> i128 { @@ -302,35 +301,57 @@ block0(v0: i16, v1: i128, v2: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s6,-8(sp) +; sd s7,-16(sp) +; add sp,-16 ; block0: -; mv t2,a1 -; li a5,42 -; slli a0,a0,48 -; srli a0,a0,48 -; slli a5,a5,48 -; srli a5,a5,48 -; eq a5,a0,a5##ty=i16 -; select_i128 [a0,a1],[t2,a2],[a3,a4]##condition=a5 +; mv t0,a1 +; li s6,42 +; slli a1,a0,48 +; srli a5,a1,48 +; slli a0,s6,48 +; srli s7,a0,48 +; select [a0,a1],[t0,a2],[a3,a4]##condition=(a5 eq s7) +; add sp,+16 +; ld s6,-8(sp) +; ld s7,-16(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mv t2, a1 -; addi a5, zero, 0x2a -; slli a0, a0, 0x30 -; srli a0, a0, 0x30 -; slli a5, a5, 0x30 -; srli a5, a5, 0x30 -; bne a0, a5, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; beqz a5, 0x10 -; mv a0, t2 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s6, -8(sp) +; sd s7, -0x10(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x1c +; mv t0, a1 +; addi s6, zero, 0x2a +; slli a1, a0, 0x30 +; srli a5, a1, 0x30 +; slli a0, s6, 0x30 +; srli s7, a0, 0x30 +; bne a5, s7, 0x10 +; mv a0, t0 ; mv a1, a2 ; j 0xc ; mv a0, a3 ; mv a1, a4 +; addi sp, sp, 0x10 +; ld s6, -8(sp) +; ld s7, -0x10(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret function %select_icmp_i32_i8(i32, i8, i8) -> i8 { @@ -344,24 +365,20 @@ block0(v0: i32, v1: i8, v2: i8): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,32 -; srli a4,a5,32 -; slli a3,a3,32 -; srli a5,a3,32 -; select_reg a0,a1,a2##condition=(a4 eq a5) +; sext.w a5,a0 +; sext.w a3,a3 +; select a0,a1,a2##condition=(a5 eq a3) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a4, a5, 0x20 -; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; beq a4, a5, 0xc -; mv a0, a2 -; j 8 +; sext.w a5, a0 +; sext.w a3, a3 +; bne a5, a3, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i32_i16(i32, i16, i16) -> i16 { @@ -375,24 +392,20 @@ block0(v0: i32, v1: i16, v2: i16): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,32 -; srli a4,a5,32 -; slli a3,a3,32 -; srli a5,a3,32 -; select_reg a0,a1,a2##condition=(a4 eq a5) +; sext.w a5,a0 +; sext.w a3,a3 +; select a0,a1,a2##condition=(a5 eq a3) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a4, a5, 0x20 -; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; beq a4, a5, 0xc -; mv a0, a2 -; j 8 +; sext.w a5, a0 +; sext.w a3, a3 +; bne a5, a3, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i32_i32(i32, i32, i32) -> i32 { @@ -406,24 +419,20 @@ block0(v0: i32, v1: i32, v2: i32): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,32 -; srli a4,a5,32 -; slli a3,a3,32 -; srli a5,a3,32 -; select_reg a0,a1,a2##condition=(a4 eq a5) +; sext.w a5,a0 +; sext.w a3,a3 +; select a0,a1,a2##condition=(a5 eq a3) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a4, a5, 0x20 -; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; beq a4, a5, 0xc -; mv a0, a2 -; j 8 +; sext.w a5, a0 +; sext.w a3, a3 +; bne a5, a3, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i32_i64(i32, i64, i64) -> i64 { @@ -437,24 +446,20 @@ block0(v0: i32, v1: i64, v2: i64): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,32 -; srli a4,a5,32 -; slli a3,a3,32 -; srli a5,a3,32 -; select_reg a0,a1,a2##condition=(a4 eq a5) +; sext.w a5,a0 +; sext.w a3,a3 +; select a0,a1,a2##condition=(a5 eq a3) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a4, a5, 0x20 -; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; beq a4, a5, 0xc -; mv a0, a2 -; j 8 +; sext.w a5, a0 +; sext.w a3, a3 +; bne a5, a3, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i32_i128(i32, i128, i128) -> i128 { @@ -466,35 +471,53 @@ block0(v0: i32, v1: i128, v2: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s1,-8(sp) +; sd s8,-16(sp) +; add sp,-16 ; block0: -; mv t2,a1 +; mv s8,a1 ; li a5,42 -; slli a0,a0,32 -; srli a0,a0,32 -; slli a5,a5,32 -; srli a5,a5,32 -; eq a5,a0,a5##ty=i32 -; select_i128 [a0,a1],[t2,a2],[a3,a4]##condition=a5 +; sext.w s1,a0 +; sext.w a5,a5 +; select [a0,a1],[s8,a2],[a3,a4]##condition=(s1 eq a5) +; add sp,+16 +; ld s1,-8(sp) +; ld s8,-16(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mv t2, a1 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s1, -8(sp) +; sd s8, -0x10(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x1c +; mv s8, a1 ; addi a5, zero, 0x2a -; slli a0, a0, 0x20 -; srli a0, a0, 0x20 -; slli a5, a5, 0x20 -; srli a5, a5, 0x20 -; bne a0, a5, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; beqz a5, 0x10 -; mv a0, t2 +; sext.w s1, a0 +; sext.w a5, a5 +; bne s1, a5, 0x10 +; mv a0, s8 ; mv a1, a2 ; j 0xc ; mv a0, a3 ; mv a1, a4 +; addi sp, sp, 0x10 +; ld s1, -8(sp) +; ld s8, -0x10(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret function %select_icmp_i64_i8(i64, i8, i8) -> i8 { @@ -508,16 +531,16 @@ block0(v0: i64, v1: i8, v2: i8): ; VCode: ; block0: ; li a5,42 -; select_reg a0,a1,a2##condition=(a0 eq a5) +; select a0,a1,a2##condition=(a0 eq a5) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a5, zero, 0x2a -; beq a0, a5, 0xc -; mv a0, a2 -; j 8 +; bne a0, a5, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i64_i16(i64, i16, i16) -> i16 { @@ -531,16 +554,16 @@ block0(v0: i64, v1: i16, v2: i16): ; VCode: ; block0: ; li a5,42 -; select_reg a0,a1,a2##condition=(a0 eq a5) +; select a0,a1,a2##condition=(a0 eq a5) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a5, zero, 0x2a -; beq a0, a5, 0xc -; mv a0, a2 -; j 8 +; bne a0, a5, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i64_i32(i64, i32, i32) -> i32 { @@ -554,16 +577,16 @@ block0(v0: i64, v1: i32, v2: i32): ; VCode: ; block0: ; li a5,42 -; select_reg a0,a1,a2##condition=(a0 eq a5) +; select a0,a1,a2##condition=(a0 eq a5) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a5, zero, 0x2a -; beq a0, a5, 0xc -; mv a0, a2 -; j 8 +; bne a0, a5, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i64_i64(i64, i64, i64) -> i64 { @@ -577,16 +600,16 @@ block0(v0: i64, v1: i64, v2: i64): ; VCode: ; block0: ; li a5,42 -; select_reg a0,a1,a2##condition=(a0 eq a5) +; select a0,a1,a2##condition=(a0 eq a5) ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a5, zero, 0x2a -; beq a0, a5, 0xc -; mv a0, a2 -; j 8 +; bne a0, a5, 0xc ; mv a0, a1 +; j 8 +; mv a0, a2 ; ret function %select_icmp_i64_i128(i64, i128, i128) -> i128 { @@ -602,15 +625,17 @@ block0(v0: i64, v1: i128, v2: i128): ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s6,-8(sp) +; sd s4,-8(sp) +; sd s6,-16(sp) ; add sp,-16 ; block0: -; mv s6,a1 +; mv s6,a0 +; mv s4,a1 ; li a5,42 -; eq a5,a0,a5##ty=i64 -; select_i128 [a0,a1],[s6,a2],[a3,a4]##condition=a5 +; select [a0,a1],[s4,a2],[a3,a4]##condition=(s6 eq a5) ; add sp,+16 -; ld s6,-8(sp) +; ld s4,-8(sp) +; ld s6,-16(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -622,23 +647,22 @@ block0(v0: i64, v1: i128, v2: i128): ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s6, -8(sp) +; sd s4, -8(sp) +; sd s6, -0x10(sp) ; addi sp, sp, -0x10 -; block1: ; offset 0x18 -; mv s6, a1 +; block1: ; offset 0x1c +; mv s6, a0 +; mv s4, a1 ; addi a5, zero, 0x2a -; bne a0, a5, 0xc -; addi a5, zero, 1 -; j 8 -; mv a5, zero -; beqz a5, 0x10 -; mv a0, s6 +; bne s6, a5, 0x10 +; mv a0, s4 ; mv a1, a2 ; j 0xc ; mv a0, a3 ; mv a1, a4 ; addi sp, sp, 0x10 -; ld s6, -8(sp) +; ld s4, -8(sp) +; ld s6, -0x10(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -658,7 +682,7 @@ block0(v0: i128, v1: i8, v2: i8): ; li a4,42 ; li a5,0 ; eq a4,[a0,a1],[a4,a5]##ty=i128 -; select_i8 a0,a2,a3##condition=a4 +; select a0,a2,a3##condition=(a4 ne zero) ; ret ; ; Disassembled: @@ -690,7 +714,7 @@ block0(v0: i128, v1: i16, v2: i16): ; li a4,42 ; li a5,0 ; eq a4,[a0,a1],[a4,a5]##ty=i128 -; select_i16 a0,a2,a3##condition=a4 +; select a0,a2,a3##condition=(a4 ne zero) ; ret ; ; Disassembled: @@ -722,7 +746,7 @@ block0(v0: i128, v1: i32, v2: i32): ; li a4,42 ; li a5,0 ; eq a4,[a0,a1],[a4,a5]##ty=i128 -; select_i32 a0,a2,a3##condition=a4 +; select a0,a2,a3##condition=(a4 ne zero) ; ret ; ; Disassembled: @@ -754,7 +778,7 @@ block0(v0: i128, v1: i64, v2: i64): ; li a4,42 ; li a5,0 ; eq a4,[a0,a1],[a4,a5]##ty=i128 -; select_i64 a0,a2,a3##condition=a4 +; select a0,a2,a3##condition=(a4 ne zero) ; ret ; ; Disassembled: @@ -794,7 +818,7 @@ block0(v0: i128, v1: i128, v2: i128): ; li s5,42 ; li s6,0 ; eq a0,[a0,a1],[s5,s6]##ty=i128 -; select_i128 [s4,a1],[a2,a3],[a4,a5]##condition=a0 +; select [s4,a1],[a2,a3],[a4,a5]##condition=(a0 ne zero) ; mv a0,s4 ; add sp,+32 ; ld s4,-8(sp) diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-select.clif b/cranelift/filetests/filetests/isa/riscv64/simd-select.clif index 221baa2ab544..5429e72e69ea 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-select.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-select.clif @@ -16,7 +16,7 @@ block0(v0: i64, v1: i64x2, v2: i64x2): ; block0: ; vle8.v v10,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v12,32(fp) #avl=16, #vtype=(e8, m1, ta, ma) -; select_i64x2 v15,v10,v12##condition=a0 +; select v15,v10,v12##condition=(a0 ne zero) ; vse8.v v15,0(a1) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) @@ -59,10 +59,9 @@ block0(v0: i32, v1: i32x4, v2: i32x4): ; block0: ; vle8.v v10,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v12,32(fp) #avl=16, #vtype=(e8, m1, ta, ma) -; slli a2,a0,32 -; srli a3,a2,32 -; select_i32x4 v11,v10,v12##condition=a3 -; vse8.v v11,0(a1) #avl=16, #vtype=(e8, m1, ta, ma) +; sext.w a2,a0 +; select v9,v10,v12##condition=(a2 ne zero) +; vse8.v v9,0(a1) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -80,13 +79,12 @@ block0(v0: i32, v1: i32x4, v2: i32x4): ; .byte 0x07, 0x85, 0x0f, 0x02 ; addi t6, s0, 0x20 ; .byte 0x07, 0x86, 0x0f, 0x02 -; slli a2, a0, 0x20 -; srli a3, a2, 0x20 -; beqz a3, 0xc -; .byte 0xd7, 0x35, 0xa0, 0x9e +; sext.w a2, a0 +; beqz a2, 0xc +; .byte 0xd7, 0x34, 0xa0, 0x9e ; j 8 -; .byte 0xd7, 0x35, 0xc0, 0x9e -; .byte 0xa7, 0x85, 0x05, 0x02 +; .byte 0xd7, 0x34, 0xc0, 0x9e +; .byte 0xa7, 0x84, 0x05, 0x02 ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -107,8 +105,8 @@ block0(v0: i16, v1: i16x8, v2: i16x8): ; vle8.v v10,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v12,32(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; slli a2,a0,48 -; srli a3,a2,48 -; select_i16x8 v11,v10,v12##condition=a3 +; srai a3,a2,48 +; select v11,v10,v12##condition=(a3 ne zero) ; vse8.v v11,0(a1) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) @@ -128,7 +126,7 @@ block0(v0: i16, v1: i16x8, v2: i16x8): ; addi t6, s0, 0x20 ; .byte 0x07, 0x86, 0x0f, 0x02 ; slli a2, a0, 0x30 -; srli a3, a2, 0x30 +; srai a3, a2, 0x30 ; beqz a3, 0xc ; .byte 0xd7, 0x35, 0xa0, 0x9e ; j 8 @@ -154,7 +152,7 @@ block0(v0: i8, v1: i8x16, v2: i8x16): ; vle8.v v10,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v12,32(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; andi a2,a0,255 -; select_i8x16 v9,v10,v12##condition=a2 +; select v9,v10,v12##condition=(a2 ne zero) ; vse8.v v9,0(a1) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) @@ -198,7 +196,7 @@ block0(v0: i64, v1: f64x2, v2: f64x2): ; block0: ; vle8.v v10,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v12,32(fp) #avl=16, #vtype=(e8, m1, ta, ma) -; select_f64x2 v15,v10,v12##condition=a0 +; select v15,v10,v12##condition=(a0 ne zero) ; vse8.v v15,0(a1) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) @@ -241,7 +239,7 @@ block0(v0: i64, v1: f32x4, v2: f32x4): ; block0: ; vle8.v v10,16(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v12,32(fp) #avl=16, #vtype=(e8, m1, ta, ma) -; select_f32x4 v15,v10,v12##condition=a0 +; select v15,v10,v12##condition=(a0 ne zero) ; vse8.v v15,0(a1) #avl=16, #vtype=(e8, m1, ta, ma) ; ld ra,8(sp) ; ld fp,0(sp) diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-umax.clif b/cranelift/filetests/filetests/isa/riscv64/simd-umax.clif index 0565fdf43c4c..e08860a3ea26 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-umax.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-umax.clif @@ -2,7 +2,6 @@ test compile precise-output set unwind_info=false target riscv64 has_v - function %umax_i8x16(i8x16, i8x16) -> i8x16 { block0(v0: i8x16, v1: i8x16): v2 = umax v0, v1 diff --git a/cranelift/filetests/filetests/isa/riscv64/smax.clif b/cranelift/filetests/filetests/isa/riscv64/smax.clif new file mode 100644 index 000000000000..271ab6bdd047 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/smax.clif @@ -0,0 +1,153 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_v + +function %smax_i8(i8, i8) -> i8{ +block0(v0: i8, v1: i8): + v2 = smax v0, v1 + return v2 +} + +; VCode: +; block0: +; slli a3,a0,56 +; srai a5,a3,56 +; slli a1,a1,56 +; srai a3,a1,56 +; select a0,a5,a3##condition=(a5 sgt a3) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a3, a0, 0x38 +; srai a5, a3, 0x38 +; slli a1, a1, 0x38 +; srai a3, a1, 0x38 +; bge a3, a5, 0xc +; mv a0, a5 +; j 8 +; mv a0, a3 +; ret + +function %smax_i16(i16, i16) -> i16{ +block0(v0: i16, v1: i16): + v2 = smax v0, v1 + return v2 +} + +; VCode: +; block0: +; slli a3,a0,48 +; srai a5,a3,48 +; slli a1,a1,48 +; srai a3,a1,48 +; select a0,a5,a3##condition=(a5 sgt a3) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a3, a0, 0x30 +; srai a5, a3, 0x30 +; slli a1, a1, 0x30 +; srai a3, a1, 0x30 +; bge a3, a5, 0xc +; mv a0, a5 +; j 8 +; mv a0, a3 +; ret + +function %smax_i32(i32, i32) -> i32{ +block0(v0: i32, v1: i32): + v2 = smax v0, v1 + return v2 +} + +; VCode: +; block0: +; sext.w a3,a0 +; sext.w a5,a1 +; select a0,a3,a5##condition=(a3 sgt a5) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; sext.w a3, a0 +; sext.w a5, a1 +; bge a5, a3, 0xc +; mv a0, a3 +; j 8 +; mv a0, a5 +; ret + +function %smax_i64(i64, i64) -> i64{ +block0(v0: i64, v1: i64): + v2 = smax v0, v1 + return v2 +} + +; VCode: +; block0: +; select a0,a0,a1##condition=(a0 sgt a1) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; blt a1, a0, 8 +; mv a0, a1 +; ret + +function %smax_i128(i128, i128) -> i128{ +block0(v0: i128, v1: i128): + v2 = smax v0, v1 + return v2 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s3,-8(sp) +; add sp,-16 +; block0: +; sgt a5,[a0,a1],[a2,a3]##ty=i128 +; mv a4,a0 +; mv s3,a1 +; select [a0,a1],[a4,s3],[a2,a3]##condition=(a5 ne zero) +; add sp,+16 +; ld s3,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s3, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 +; blt a3, a1, 0xc +; bne a1, a3, 0x10 +; bgeu a2, a0, 0xc +; addi a5, zero, 1 +; j 8 +; mv a5, zero +; mv a4, a0 +; mv s3, a1 +; beqz a5, 0x10 +; mv a0, a4 +; mv a1, s3 +; j 0xc +; mv a0, a2 +; mv a1, a3 +; addi sp, sp, 0x10 +; ld s3, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/smin.clif b/cranelift/filetests/filetests/isa/riscv64/smin.clif new file mode 100644 index 000000000000..92ed89a5bdce --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/smin.clif @@ -0,0 +1,153 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_v + +function %smin_i8(i8, i8) -> i8{ +block0(v0: i8, v1: i8): + v2 = smin v0, v1 + return v2 +} + +; VCode: +; block0: +; slli a3,a0,56 +; srai a5,a3,56 +; slli a1,a1,56 +; srai a3,a1,56 +; select a0,a5,a3##condition=(a5 slt a3) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a3, a0, 0x38 +; srai a5, a3, 0x38 +; slli a1, a1, 0x38 +; srai a3, a1, 0x38 +; bge a5, a3, 0xc +; mv a0, a5 +; j 8 +; mv a0, a3 +; ret + +function %smin_i16(i16, i16) -> i16{ +block0(v0: i16, v1: i16): + v2 = smin v0, v1 + return v2 +} + +; VCode: +; block0: +; slli a3,a0,48 +; srai a5,a3,48 +; slli a1,a1,48 +; srai a3,a1,48 +; select a0,a5,a3##condition=(a5 slt a3) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a3, a0, 0x30 +; srai a5, a3, 0x30 +; slli a1, a1, 0x30 +; srai a3, a1, 0x30 +; bge a5, a3, 0xc +; mv a0, a5 +; j 8 +; mv a0, a3 +; ret + +function %smin_i32(i32, i32) -> i32{ +block0(v0: i32, v1: i32): + v2 = smin v0, v1 + return v2 +} + +; VCode: +; block0: +; sext.w a3,a0 +; sext.w a5,a1 +; select a0,a3,a5##condition=(a3 slt a5) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; sext.w a3, a0 +; sext.w a5, a1 +; bge a3, a5, 0xc +; mv a0, a3 +; j 8 +; mv a0, a5 +; ret + +function %smin_i64(i64, i64) -> i64{ +block0(v0: i64, v1: i64): + v2 = smin v0, v1 + return v2 +} + +; VCode: +; block0: +; select a0,a0,a1##condition=(a0 slt a1) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; blt a0, a1, 8 +; mv a0, a1 +; ret + +function %smin_i128(i128, i128) -> i128{ +block0(v0: i128, v1: i128): + v2 = smin v0, v1 + return v2 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s3,-8(sp) +; add sp,-16 +; block0: +; slt a5,[a0,a1],[a2,a3]##ty=i128 +; mv a4,a0 +; mv s3,a1 +; select [a0,a1],[a4,s3],[a2,a3]##condition=(a5 ne zero) +; add sp,+16 +; ld s3,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s3, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 +; blt a1, a3, 0xc +; bne a1, a3, 0x10 +; bgeu a0, a2, 0xc +; addi a5, zero, 1 +; j 8 +; mv a5, zero +; mv a4, a0 +; mv s3, a1 +; beqz a5, 0x10 +; mv a0, a4 +; mv a1, s3 +; j 0xc +; mv a0, a2 +; mv a1, a3 +; addi sp, sp, 0x10 +; ld s3, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/sshr-const.clif b/cranelift/filetests/filetests/isa/riscv64/sshr-const.clif index b1f7ed83f95b..a524ce84bb6d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/sshr-const.clif +++ b/cranelift/filetests/filetests/isa/riscv64/sshr-const.clif @@ -401,17 +401,16 @@ block0(v0: i128): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li a0,64 ; sra a3,a1,a4 ; li a4,-1 -; select_reg a1,a4,zero##condition=(a1 slt zero) +; select t4,a4,zero##condition=(a1 slt zero) ; li a4,64 ; andi a2,a2,127 -; select_reg a0,a3,a5##condition=(a2 uge a4) -; select_reg a1,a1,a3##condition=(a2 uge a4) +; select [a0,a1],[a3,t4],[a5,a3]##condition=(a2 uge a4) ; ret ; ; Disassembled: @@ -421,26 +420,26 @@ block0(v0: i128): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi a0, zero, 0x40 ; sra a3, a1, a4 ; addi a4, zero, -1 -; bltz a1, 0xc -; mv a1, zero +; bgez a1, 0xc +; mv t4, a4 ; j 8 -; mv a1, a4 +; mv t4, zero ; addi a4, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a4, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a2, a4, 8 +; mv a1, t4 +; j 0xc +; mv a0, a5 ; mv a1, a3 ; ret @@ -458,17 +457,16 @@ block0(v0: i128): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li a0,64 ; sra a3,a1,a4 ; li a4,-1 -; select_reg a1,a4,zero##condition=(a1 slt zero) +; select t4,a4,zero##condition=(a1 slt zero) ; li a4,64 ; andi a2,a2,127 -; select_reg a0,a3,a5##condition=(a2 uge a4) -; select_reg a1,a1,a3##condition=(a2 uge a4) +; select [a0,a1],[a3,t4],[a5,a3]##condition=(a2 uge a4) ; ret ; ; Disassembled: @@ -478,26 +476,26 @@ block0(v0: i128): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi a0, zero, 0x40 ; sra a3, a1, a4 ; addi a4, zero, -1 -; bltz a1, 0xc -; mv a1, zero +; bgez a1, 0xc +; mv t4, a4 ; j 8 -; mv a1, a4 +; mv t4, zero ; addi a4, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a4, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a2, a4, 8 +; mv a1, t4 +; j 0xc +; mv a0, a5 ; mv a1, a3 ; ret @@ -515,17 +513,16 @@ block0(v0: i128): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li a0,64 ; sra a3,a1,a4 ; li a4,-1 -; select_reg a1,a4,zero##condition=(a1 slt zero) +; select t4,a4,zero##condition=(a1 slt zero) ; li a4,64 ; andi a2,a2,127 -; select_reg a0,a3,a5##condition=(a2 uge a4) -; select_reg a1,a1,a3##condition=(a2 uge a4) +; select [a0,a1],[a3,t4],[a5,a3]##condition=(a2 uge a4) ; ret ; ; Disassembled: @@ -535,26 +532,26 @@ block0(v0: i128): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi a0, zero, 0x40 ; sra a3, a1, a4 ; addi a4, zero, -1 -; bltz a1, 0xc -; mv a1, zero +; bgez a1, 0xc +; mv t4, a4 ; j 8 -; mv a1, a4 +; mv t4, zero ; addi a4, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a4, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a2, a4, 8 +; mv a1, t4 +; j 0xc +; mv a0, a5 ; mv a1, a3 ; ret @@ -572,17 +569,16 @@ block0(v0: i128): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li a0,64 ; sra a3,a1,a4 ; li a4,-1 -; select_reg a1,a4,zero##condition=(a1 slt zero) +; select t4,a4,zero##condition=(a1 slt zero) ; li a4,64 ; andi a2,a2,127 -; select_reg a0,a3,a5##condition=(a2 uge a4) -; select_reg a1,a1,a3##condition=(a2 uge a4) +; select [a0,a1],[a3,t4],[a5,a3]##condition=(a2 uge a4) ; ret ; ; Disassembled: @@ -592,26 +588,26 @@ block0(v0: i128): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi a0, zero, 0x40 ; sra a3, a1, a4 ; addi a4, zero, -1 -; bltz a1, 0xc -; mv a1, zero +; bgez a1, 0xc +; mv t4, a4 ; j 8 -; mv a1, a4 +; mv t4, zero ; addi a4, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a4, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a2, a4, 8 +; mv a1, t4 +; j 0xc +; mv a0, a5 ; mv a1, a3 ; ret @@ -624,6 +620,12 @@ block0(v0: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s11,-8(sp) +; add sp,-16 ; block0: ; li a2,5 ; li a3,0 @@ -631,45 +633,59 @@ block0(v0: i128): ; li a3,64 ; sub a3,a3,a5 ; sll a3,a1,a3 -; select_reg a3,zero,a3##condition=(a5 eq zero) +; select a3,zero,a3##condition=(a5 eq zero) ; srl a4,a0,a5 -; or a0,a3,a4 +; or s11,a3,a4 ; li a3,64 ; sra a3,a1,a5 ; li a5,-1 -; select_reg a1,a5,zero##condition=(a1 slt zero) +; select a5,a5,zero##condition=(a1 slt zero) ; li a4,64 -; andi a5,a2,127 -; select_reg a0,a3,a0##condition=(a5 uge a4) -; select_reg a1,a1,a3##condition=(a5 uge a4) +; andi a2,a2,127 +; select [a0,a1],[a3,a5],[s11,a3]##condition=(a2 uge a4) +; add sp,+16 +; ld s11,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s11, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 ; addi a2, zero, 5 ; mv a3, zero ; andi a5, a2, 0x3f ; addi a3, zero, 0x40 ; sub a3, a3, a5 ; sll a3, a1, a3 -; beqz a5, 8 -; j 8 +; bnez a5, 8 ; mv a3, zero ; srl a4, a0, a5 -; or a0, a3, a4 +; or s11, a3, a4 ; addi a3, zero, 0x40 ; sra a3, a1, a5 ; addi a5, zero, -1 -; bltz a1, 0xc -; mv a1, zero -; j 8 -; mv a1, a5 +; bltz a1, 8 +; mv a5, zero ; addi a4, zero, 0x40 -; andi a5, a2, 0x7f -; bgeu a5, a4, 8 -; j 8 +; andi a2, a2, 0x7f +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a5, a4, 8 +; mv a1, a5 +; j 0xc +; mv a0, s11 ; mv a1, a3 +; addi sp, sp, 0x10 +; ld s11, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/sshr.clif b/cranelift/filetests/filetests/isa/riscv64/sshr.clif index 39dc9a867564..66f684b0620a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/sshr.clif +++ b/cranelift/filetests/filetests/isa/riscv64/sshr.clif @@ -395,17 +395,16 @@ block0(v0: i128, v1: i8): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li a0,64 ; sra a3,a1,a4 ; li a4,-1 -; select_reg a1,a4,zero##condition=(a1 slt zero) +; select t4,a4,zero##condition=(a1 slt zero) ; li a4,64 ; andi a2,a2,127 -; select_reg a0,a3,a5##condition=(a2 uge a4) -; select_reg a1,a1,a3##condition=(a2 uge a4) +; select [a0,a1],[a3,t4],[a5,a3]##condition=(a2 uge a4) ; ret ; ; Disassembled: @@ -414,26 +413,26 @@ block0(v0: i128, v1: i8): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi a0, zero, 0x40 ; sra a3, a1, a4 ; addi a4, zero, -1 -; bltz a1, 0xc -; mv a1, zero +; bgez a1, 0xc +; mv t4, a4 ; j 8 -; mv a1, a4 +; mv t4, zero ; addi a4, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a4, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a2, a4, 8 +; mv a1, t4 +; j 0xc +; mv a0, a5 ; mv a1, a3 ; ret @@ -449,17 +448,16 @@ block0(v0: i128, v1: i16): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li a0,64 ; sra a3,a1,a4 ; li a4,-1 -; select_reg a1,a4,zero##condition=(a1 slt zero) +; select t4,a4,zero##condition=(a1 slt zero) ; li a4,64 ; andi a2,a2,127 -; select_reg a0,a3,a5##condition=(a2 uge a4) -; select_reg a1,a1,a3##condition=(a2 uge a4) +; select [a0,a1],[a3,t4],[a5,a3]##condition=(a2 uge a4) ; ret ; ; Disassembled: @@ -468,26 +466,26 @@ block0(v0: i128, v1: i16): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi a0, zero, 0x40 ; sra a3, a1, a4 ; addi a4, zero, -1 -; bltz a1, 0xc -; mv a1, zero +; bgez a1, 0xc +; mv t4, a4 ; j 8 -; mv a1, a4 +; mv t4, zero ; addi a4, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a4, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a2, a4, 8 +; mv a1, t4 +; j 0xc +; mv a0, a5 ; mv a1, a3 ; ret @@ -503,17 +501,16 @@ block0(v0: i128, v1: i32): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li a0,64 ; sra a3,a1,a4 ; li a4,-1 -; select_reg a1,a4,zero##condition=(a1 slt zero) +; select t4,a4,zero##condition=(a1 slt zero) ; li a4,64 ; andi a2,a2,127 -; select_reg a0,a3,a5##condition=(a2 uge a4) -; select_reg a1,a1,a3##condition=(a2 uge a4) +; select [a0,a1],[a3,t4],[a5,a3]##condition=(a2 uge a4) ; ret ; ; Disassembled: @@ -522,26 +519,26 @@ block0(v0: i128, v1: i32): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi a0, zero, 0x40 ; sra a3, a1, a4 ; addi a4, zero, -1 -; bltz a1, 0xc -; mv a1, zero +; bgez a1, 0xc +; mv t4, a4 ; j 8 -; mv a1, a4 +; mv t4, zero ; addi a4, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a4, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a2, a4, 8 +; mv a1, t4 +; j 0xc +; mv a0, a5 ; mv a1, a3 ; ret @@ -557,17 +554,16 @@ block0(v0: i128, v1: i64): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li a0,64 ; sra a3,a1,a4 ; li a4,-1 -; select_reg a1,a4,zero##condition=(a1 slt zero) +; select t4,a4,zero##condition=(a1 slt zero) ; li a4,64 ; andi a2,a2,127 -; select_reg a0,a3,a5##condition=(a2 uge a4) -; select_reg a1,a1,a3##condition=(a2 uge a4) +; select [a0,a1],[a3,t4],[a5,a3]##condition=(a2 uge a4) ; ret ; ; Disassembled: @@ -576,26 +572,26 @@ block0(v0: i128, v1: i64): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi a0, zero, 0x40 ; sra a3, a1, a4 ; addi a4, zero, -1 -; bltz a1, 0xc -; mv a1, zero +; bgez a1, 0xc +; mv t4, a4 ; j 8 -; mv a1, a4 +; mv t4, zero ; addi a4, zero, 0x40 ; andi a2, a2, 0x7f -; bgeu a2, a4, 0xc -; mv a0, a5 -; j 8 +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a2, a4, 8 +; mv a1, t4 +; j 0xc +; mv a0, a5 ; mv a1, a3 ; ret @@ -606,48 +602,68 @@ block0(v0: i128, v1: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s11,-8(sp) +; add sp,-16 ; block0: ; andi a5,a2,63 ; li a3,64 ; sub a3,a3,a5 ; sll a3,a1,a3 -; select_reg a3,zero,a3##condition=(a5 eq zero) +; select a3,zero,a3##condition=(a5 eq zero) ; srl a4,a0,a5 -; or a0,a3,a4 +; or s11,a3,a4 ; li a3,64 ; sra a3,a1,a5 ; li a5,-1 -; select_reg a1,a5,zero##condition=(a1 slt zero) +; select a5,a5,zero##condition=(a1 slt zero) ; li a4,64 -; andi a5,a2,127 -; select_reg a0,a3,a0##condition=(a5 uge a4) -; select_reg a1,a1,a3##condition=(a5 uge a4) +; andi a2,a2,127 +; select [a0,a1],[a3,a5],[s11,a3]##condition=(a2 uge a4) +; add sp,+16 +; ld s11,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s11, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 ; andi a5, a2, 0x3f ; addi a3, zero, 0x40 ; sub a3, a3, a5 ; sll a3, a1, a3 -; beqz a5, 8 -; j 8 +; bnez a5, 8 ; mv a3, zero ; srl a4, a0, a5 -; or a0, a3, a4 +; or s11, a3, a4 ; addi a3, zero, 0x40 ; sra a3, a1, a5 ; addi a5, zero, -1 -; bltz a1, 0xc -; mv a1, zero -; j 8 -; mv a1, a5 +; bltz a1, 8 +; mv a5, zero ; addi a4, zero, 0x40 -; andi a5, a2, 0x7f -; bgeu a5, a4, 8 -; j 8 +; andi a2, a2, 0x7f +; bltu a2, a4, 0x10 ; mv a0, a3 -; bgeu a5, a4, 8 +; mv a1, a5 +; j 0xc +; mv a0, s11 ; mv a1, a3 +; addi sp, sp, 0x10 +; ld s11, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/traps.clif b/cranelift/filetests/filetests/isa/riscv64/traps.clif index 3abd6adcf215..24de5ef0ad90 100644 --- a/cranelift/filetests/filetests/isa/riscv64/traps.clif +++ b/cranelift/filetests/filetests/isa/riscv64/traps.clif @@ -25,9 +25,8 @@ block0(v0: i64): ; VCode: ; block0: -; li a3,42 -; eq a3,a0,a3##ty=i64 -; bne a3,zero,taken(label2),not_taken(label1) +; li a2,42 +; beq a0,a2,taken(label2),not_taken(label1) ; block1: ; ret ; block2: @@ -35,15 +34,11 @@ block0(v0: i64): ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; bne a0, a3, 0xc -; addi a3, zero, 1 -; j 8 -; mv a3, zero -; bnez a3, 8 -; block1: ; offset 0x18 +; addi a2, zero, 0x2a +; beq a0, a2, 8 +; block1: ; offset 0x8 ; ret -; block2: ; offset 0x1c +; block2: ; offset 0xc ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: user0 function %h() { diff --git a/cranelift/filetests/filetests/isa/riscv64/umax.clif b/cranelift/filetests/filetests/isa/riscv64/umax.clif new file mode 100644 index 000000000000..a2eb42d2e5e9 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/umax.clif @@ -0,0 +1,153 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_v + +function %umax_i8(i8, i8) -> i8{ +block0(v0: i8, v1: i8): + v2 = umax v0, v1 + return v2 +} + +; VCode: +; block0: +; andi a3,a0,255 +; andi a5,a1,255 +; select a0,a3,a5##condition=(a3 ugt a5) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; andi a3, a0, 0xff +; andi a5, a1, 0xff +; bgeu a5, a3, 0xc +; mv a0, a3 +; j 8 +; mv a0, a5 +; ret + +function %umax_i16(i16, i16) -> i16{ +block0(v0: i16, v1: i16): + v2 = umax v0, v1 + return v2 +} + +; VCode: +; block0: +; slli a3,a0,48 +; srli a5,a3,48 +; slli a1,a1,48 +; srli a3,a1,48 +; select a0,a5,a3##condition=(a5 ugt a3) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a3, a0, 0x30 +; srli a5, a3, 0x30 +; slli a1, a1, 0x30 +; srli a3, a1, 0x30 +; bgeu a3, a5, 0xc +; mv a0, a5 +; j 8 +; mv a0, a3 +; ret + +function %umax_i32(i32, i32) -> i32{ +block0(v0: i32, v1: i32): + v2 = umax v0, v1 + return v2 +} + +; VCode: +; block0: +; slli a3,a0,32 +; srli a5,a3,32 +; slli a1,a1,32 +; srli a3,a1,32 +; select a0,a5,a3##condition=(a5 ugt a3) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a3, a0, 0x20 +; srli a5, a3, 0x20 +; slli a1, a1, 0x20 +; srli a3, a1, 0x20 +; bgeu a3, a5, 0xc +; mv a0, a5 +; j 8 +; mv a0, a3 +; ret + +function %umax_i64(i64, i64) -> i64{ +block0(v0: i64, v1: i64): + v2 = umax v0, v1 + return v2 +} + +; VCode: +; block0: +; select a0,a0,a1##condition=(a0 ugt a1) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; bltu a1, a0, 8 +; mv a0, a1 +; ret + +function %umax_i128(i128, i128) -> i128{ +block0(v0: i128, v1: i128): + v2 = umax v0, v1 + return v2 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s3,-8(sp) +; add sp,-16 +; block0: +; ugt a5,[a0,a1],[a2,a3]##ty=i128 +; mv a4,a0 +; mv s3,a1 +; select [a0,a1],[a4,s3],[a2,a3]##condition=(a5 ne zero) +; add sp,+16 +; ld s3,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s3, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 +; bltu a3, a1, 0xc +; bne a1, a3, 0x10 +; bgeu a2, a0, 0xc +; addi a5, zero, 1 +; j 8 +; mv a5, zero +; mv a4, a0 +; mv s3, a1 +; beqz a5, 0x10 +; mv a0, a4 +; mv a1, s3 +; j 0xc +; mv a0, a2 +; mv a1, a3 +; addi sp, sp, 0x10 +; ld s3, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/umin.clif b/cranelift/filetests/filetests/isa/riscv64/umin.clif new file mode 100644 index 000000000000..74eb80d45c56 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/umin.clif @@ -0,0 +1,153 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_v + +function %umin_i8(i8, i8) -> i8{ +block0(v0: i8, v1: i8): + v2 = umin v0, v1 + return v2 +} + +; VCode: +; block0: +; andi a3,a0,255 +; andi a5,a1,255 +; select a0,a3,a5##condition=(a3 ult a5) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; andi a3, a0, 0xff +; andi a5, a1, 0xff +; bgeu a3, a5, 0xc +; mv a0, a3 +; j 8 +; mv a0, a5 +; ret + +function %umin_i16(i16, i16) -> i16{ +block0(v0: i16, v1: i16): + v2 = umin v0, v1 + return v2 +} + +; VCode: +; block0: +; slli a3,a0,48 +; srli a5,a3,48 +; slli a1,a1,48 +; srli a3,a1,48 +; select a0,a5,a3##condition=(a5 ult a3) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a3, a0, 0x30 +; srli a5, a3, 0x30 +; slli a1, a1, 0x30 +; srli a3, a1, 0x30 +; bgeu a5, a3, 0xc +; mv a0, a5 +; j 8 +; mv a0, a3 +; ret + +function %umin_i32(i32, i32) -> i32{ +block0(v0: i32, v1: i32): + v2 = umin v0, v1 + return v2 +} + +; VCode: +; block0: +; slli a3,a0,32 +; srli a5,a3,32 +; slli a1,a1,32 +; srli a3,a1,32 +; select a0,a5,a3##condition=(a5 ult a3) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a3, a0, 0x20 +; srli a5, a3, 0x20 +; slli a1, a1, 0x20 +; srli a3, a1, 0x20 +; bgeu a5, a3, 0xc +; mv a0, a5 +; j 8 +; mv a0, a3 +; ret + +function %umin_i64(i64, i64) -> i64{ +block0(v0: i64, v1: i64): + v2 = umin v0, v1 + return v2 +} + +; VCode: +; block0: +; select a0,a0,a1##condition=(a0 ult a1) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; bltu a0, a1, 8 +; mv a0, a1 +; ret + +function %umin_i128(i128, i128) -> i128{ +block0(v0: i128, v1: i128): + v2 = umin v0, v1 + return v2 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s3,-8(sp) +; add sp,-16 +; block0: +; ult a5,[a0,a1],[a2,a3]##ty=i128 +; mv a4,a0 +; mv s3,a1 +; select [a0,a1],[a4,s3],[a2,a3]##condition=(a5 ne zero) +; add sp,+16 +; ld s3,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s3, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 +; bltu a1, a3, 0xc +; bne a1, a3, 0x10 +; bgeu a0, a2, 0xc +; addi a5, zero, 1 +; j 8 +; mv a5, zero +; mv a4, a0 +; mv s3, a1 +; beqz a5, 0x10 +; mv a0, a4 +; mv a1, s3 +; j 0xc +; mv a0, a2 +; mv a1, a3 +; addi sp, sp, 0x10 +; ld s3, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/ushr-const.clif b/cranelift/filetests/filetests/isa/riscv64/ushr-const.clif index 5989ce2e6d92..5851a974b373 100644 --- a/cranelift/filetests/filetests/isa/riscv64/ushr-const.clif +++ b/cranelift/filetests/filetests/isa/riscv64/ushr-const.clif @@ -391,14 +391,13 @@ block0(v0: i128): ; li a2,64 ; sub a2,a2,a4 ; sll a5,a1,a2 -; select_reg a2,zero,a5##condition=(a4 eq zero) +; select a2,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a2,a5 ; li t0,64 ; srl a2,a1,a4 ; andi a4,a3,127 -; select_reg a0,a2,a5##condition=(a4 uge t0) -; select_reg a1,zero,a2##condition=(a4 uge t0) +; select [a0,a1],[a2,zero],[a5,a2]##condition=(a4 uge t0) ; ret ; ; Disassembled: @@ -408,23 +407,21 @@ block0(v0: i128): ; addi a2, zero, 0x40 ; sub a2, a2, a4 ; sll a5, a1, a2 -; beqz a4, 0xc -; mv a2, a5 -; j 8 +; bnez a4, 0xc ; mv a2, zero +; j 8 +; mv a2, a5 ; srl a5, a0, a4 ; or a5, a2, a5 ; addi t0, zero, 0x40 ; srl a2, a1, a4 ; andi a4, a3, 0x7f -; bgeu a4, t0, 0xc -; mv a0, a5 -; j 8 +; bltu a4, t0, 0x10 ; mv a0, a2 -; bgeu a4, t0, 0xc -; mv a1, a2 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, a5 +; mv a1, a2 ; ret function %ushr_i128_const_i16(i128) -> i128 { @@ -441,14 +438,13 @@ block0(v0: i128): ; li a2,64 ; sub a2,a2,a4 ; sll a5,a1,a2 -; select_reg a2,zero,a5##condition=(a4 eq zero) +; select a2,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a2,a5 ; li t0,64 ; srl a2,a1,a4 ; andi a4,a3,127 -; select_reg a0,a2,a5##condition=(a4 uge t0) -; select_reg a1,zero,a2##condition=(a4 uge t0) +; select [a0,a1],[a2,zero],[a5,a2]##condition=(a4 uge t0) ; ret ; ; Disassembled: @@ -458,23 +454,21 @@ block0(v0: i128): ; addi a2, zero, 0x40 ; sub a2, a2, a4 ; sll a5, a1, a2 -; beqz a4, 0xc -; mv a2, a5 -; j 8 +; bnez a4, 0xc ; mv a2, zero +; j 8 +; mv a2, a5 ; srl a5, a0, a4 ; or a5, a2, a5 ; addi t0, zero, 0x40 ; srl a2, a1, a4 ; andi a4, a3, 0x7f -; bgeu a4, t0, 0xc -; mv a0, a5 -; j 8 +; bltu a4, t0, 0x10 ; mv a0, a2 -; bgeu a4, t0, 0xc -; mv a1, a2 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, a5 +; mv a1, a2 ; ret function %ushr_i128_const_i32(i128) -> i128 { @@ -491,14 +485,13 @@ block0(v0: i128): ; li a2,64 ; sub a2,a2,a4 ; sll a5,a1,a2 -; select_reg a2,zero,a5##condition=(a4 eq zero) +; select a2,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a2,a5 ; li t0,64 ; srl a2,a1,a4 ; andi a4,a3,127 -; select_reg a0,a2,a5##condition=(a4 uge t0) -; select_reg a1,zero,a2##condition=(a4 uge t0) +; select [a0,a1],[a2,zero],[a5,a2]##condition=(a4 uge t0) ; ret ; ; Disassembled: @@ -508,23 +501,21 @@ block0(v0: i128): ; addi a2, zero, 0x40 ; sub a2, a2, a4 ; sll a5, a1, a2 -; beqz a4, 0xc -; mv a2, a5 -; j 8 +; bnez a4, 0xc ; mv a2, zero +; j 8 +; mv a2, a5 ; srl a5, a0, a4 ; or a5, a2, a5 ; addi t0, zero, 0x40 ; srl a2, a1, a4 ; andi a4, a3, 0x7f -; bgeu a4, t0, 0xc -; mv a0, a5 -; j 8 +; bltu a4, t0, 0x10 ; mv a0, a2 -; bgeu a4, t0, 0xc -; mv a1, a2 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, a5 +; mv a1, a2 ; ret function %ushr_i128_const_i64(i128) -> i128 { @@ -541,14 +532,13 @@ block0(v0: i128): ; li a2,64 ; sub a2,a2,a4 ; sll a5,a1,a2 -; select_reg a2,zero,a5##condition=(a4 eq zero) +; select a2,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a2,a5 ; li t0,64 ; srl a2,a1,a4 ; andi a4,a3,127 -; select_reg a0,a2,a5##condition=(a4 uge t0) -; select_reg a1,zero,a2##condition=(a4 uge t0) +; select [a0,a1],[a2,zero],[a5,a2]##condition=(a4 uge t0) ; ret ; ; Disassembled: @@ -558,23 +548,21 @@ block0(v0: i128): ; addi a2, zero, 0x40 ; sub a2, a2, a4 ; sll a5, a1, a2 -; beqz a4, 0xc -; mv a2, a5 -; j 8 +; bnez a4, 0xc ; mv a2, zero +; j 8 +; mv a2, a5 ; srl a5, a0, a4 ; or a5, a2, a5 ; addi t0, zero, 0x40 ; srl a2, a1, a4 ; andi a4, a3, 0x7f -; bgeu a4, t0, 0xc -; mv a0, a5 -; j 8 +; bltu a4, t0, 0x10 ; mv a0, a2 -; bgeu a4, t0, 0xc -; mv a1, a2 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, a5 +; mv a1, a2 ; ret function %ushr_i128_const_i128(i128) -> i128 { @@ -586,6 +574,12 @@ block0(v0: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s11,-8(sp) +; add sp,-16 ; block0: ; li a4,5 ; li a5,0 @@ -593,38 +587,52 @@ block0(v0: i128): ; li a2,64 ; sub a3,a2,a5 ; sll a2,a1,a3 -; select_reg a2,zero,a2##condition=(a5 eq zero) +; select a2,zero,a2##condition=(a5 eq zero) ; srl a3,a0,a5 -; or a0,a2,a3 +; or s11,a2,a3 ; li a2,64 ; srl a3,a1,a5 ; andi a5,a4,127 -; select_reg a0,a3,a0##condition=(a5 uge a2) -; select_reg a1,zero,a3##condition=(a5 uge a2) +; select [a0,a1],[a3,zero],[s11,a3]##condition=(a5 uge a2) +; add sp,+16 +; ld s11,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s11, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 ; addi a4, zero, 5 ; mv a5, zero ; andi a5, a4, 0x3f ; addi a2, zero, 0x40 ; sub a3, a2, a5 ; sll a2, a1, a3 -; beqz a5, 8 -; j 8 +; bnez a5, 8 ; mv a2, zero ; srl a3, a0, a5 -; or a0, a2, a3 +; or s11, a2, a3 ; addi a2, zero, 0x40 ; srl a3, a1, a5 ; andi a5, a4, 0x7f -; bgeu a5, a2, 8 -; j 8 +; bltu a5, a2, 0x10 ; mv a0, a3 -; bgeu a5, a2, 0xc -; mv a1, a3 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, s11 +; mv a1, a3 +; addi sp, sp, 0x10 +; ld s11, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/ushr.clif b/cranelift/filetests/filetests/isa/riscv64/ushr.clif index 6d16f2db8054..a9faa81c617e 100644 --- a/cranelift/filetests/filetests/isa/riscv64/ushr.clif +++ b/cranelift/filetests/filetests/isa/riscv64/ushr.clif @@ -385,14 +385,13 @@ block0(v0: i128, v1: i8): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li t0,64 ; srl a3,a1,a4 ; andi a4,a2,127 -; select_reg a0,a3,a5##condition=(a4 uge t0) -; select_reg a1,zero,a3##condition=(a4 uge t0) +; select [a0,a1],[a3,zero],[a5,a3]##condition=(a4 uge t0) ; ret ; ; Disassembled: @@ -401,23 +400,21 @@ block0(v0: i128, v1: i8): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi t0, zero, 0x40 ; srl a3, a1, a4 ; andi a4, a2, 0x7f -; bgeu a4, t0, 0xc -; mv a0, a5 -; j 8 +; bltu a4, t0, 0x10 ; mv a0, a3 -; bgeu a4, t0, 0xc -; mv a1, a3 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, a5 +; mv a1, a3 ; ret function %ushr_i128_i16(i128, i16) -> i128 { @@ -432,14 +429,13 @@ block0(v0: i128, v1: i16): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li t0,64 ; srl a3,a1,a4 ; andi a4,a2,127 -; select_reg a0,a3,a5##condition=(a4 uge t0) -; select_reg a1,zero,a3##condition=(a4 uge t0) +; select [a0,a1],[a3,zero],[a5,a3]##condition=(a4 uge t0) ; ret ; ; Disassembled: @@ -448,23 +444,21 @@ block0(v0: i128, v1: i16): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi t0, zero, 0x40 ; srl a3, a1, a4 ; andi a4, a2, 0x7f -; bgeu a4, t0, 0xc -; mv a0, a5 -; j 8 +; bltu a4, t0, 0x10 ; mv a0, a3 -; bgeu a4, t0, 0xc -; mv a1, a3 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, a5 +; mv a1, a3 ; ret function %ushr_i128_i32(i128, i32) -> i128 { @@ -479,14 +473,13 @@ block0(v0: i128, v1: i32): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li t0,64 ; srl a3,a1,a4 ; andi a4,a2,127 -; select_reg a0,a3,a5##condition=(a4 uge t0) -; select_reg a1,zero,a3##condition=(a4 uge t0) +; select [a0,a1],[a3,zero],[a5,a3]##condition=(a4 uge t0) ; ret ; ; Disassembled: @@ -495,23 +488,21 @@ block0(v0: i128, v1: i32): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi t0, zero, 0x40 ; srl a3, a1, a4 ; andi a4, a2, 0x7f -; bgeu a4, t0, 0xc -; mv a0, a5 -; j 8 +; bltu a4, t0, 0x10 ; mv a0, a3 -; bgeu a4, t0, 0xc -; mv a1, a3 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, a5 +; mv a1, a3 ; ret function %ushr_i128_i64(i128, i64) -> i128 { @@ -526,14 +517,13 @@ block0(v0: i128, v1: i64): ; li a3,64 ; sub a3,a3,a4 ; sll a5,a1,a3 -; select_reg a3,zero,a5##condition=(a4 eq zero) +; select a3,zero,a5##condition=(a4 eq zero) ; srl a5,a0,a4 ; or a5,a3,a5 ; li t0,64 ; srl a3,a1,a4 ; andi a4,a2,127 -; select_reg a0,a3,a5##condition=(a4 uge t0) -; select_reg a1,zero,a3##condition=(a4 uge t0) +; select [a0,a1],[a3,zero],[a5,a3]##condition=(a4 uge t0) ; ret ; ; Disassembled: @@ -542,23 +532,21 @@ block0(v0: i128, v1: i64): ; addi a3, zero, 0x40 ; sub a3, a3, a4 ; sll a5, a1, a3 -; beqz a4, 0xc -; mv a3, a5 -; j 8 +; bnez a4, 0xc ; mv a3, zero +; j 8 +; mv a3, a5 ; srl a5, a0, a4 ; or a5, a3, a5 ; addi t0, zero, 0x40 ; srl a3, a1, a4 ; andi a4, a2, 0x7f -; bgeu a4, t0, 0xc -; mv a0, a5 -; j 8 +; bltu a4, t0, 0x10 ; mv a0, a3 -; bgeu a4, t0, 0xc -; mv a1, a3 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, a5 +; mv a1, a3 ; ret function %ushr_i128_i128(i128, i128) -> i128 { @@ -568,41 +556,61 @@ block0(v0: i128, v1: i128): } ; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s11,-8(sp) +; add sp,-16 ; block0: ; andi a5,a2,63 ; li a3,64 ; sub a3,a3,a5 ; sll a3,a1,a3 -; select_reg a3,zero,a3##condition=(a5 eq zero) +; select a3,zero,a3##condition=(a5 eq zero) ; srl a4,a0,a5 -; or a0,a3,a4 +; or s11,a3,a4 ; li a3,64 ; srl a4,a1,a5 ; andi a5,a2,127 -; select_reg a0,a4,a0##condition=(a5 uge a3) -; select_reg a1,zero,a4##condition=(a5 uge a3) +; select [a0,a1],[a4,zero],[s11,a4]##condition=(a5 uge a3) +; add sp,+16 +; ld s11,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 ; ret ; ; Disassembled: ; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s11, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 ; andi a5, a2, 0x3f ; addi a3, zero, 0x40 ; sub a3, a3, a5 ; sll a3, a1, a3 -; beqz a5, 8 -; j 8 +; bnez a5, 8 ; mv a3, zero ; srl a4, a0, a5 -; or a0, a3, a4 +; or s11, a3, a4 ; addi a3, zero, 0x40 ; srl a4, a1, a5 ; andi a5, a2, 0x7f -; bgeu a5, a3, 8 -; j 8 +; bltu a5, a3, 0x10 ; mv a0, a4 -; bgeu a5, a3, 0xc -; mv a1, a4 -; j 8 ; mv a1, zero +; j 0xc +; mv a0, s11 +; mv a1, a4 +; addi sp, sp, 0x10 +; ld s11, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat index 7938e65a4c6f..d72cfed5a124 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat @@ -42,15 +42,14 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a2) -;; addi a4,a4,-4 -;; ugt a4,a5,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; srli a4,a3,32 +;; ld a3,8(a2) +;; addi a3,a3,-4 +;; bgtu a4,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a2) -;; add a5,a0,a5 -;; sw a1,0(a5) +;; ld a5,0(a2) +;; add a4,a5,a4 +;; sw a1,0(a4) ;; j label2 ;; block2: ;; ret @@ -59,16 +58,15 @@ ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a1) -;; addi a4,a4,-4 -;; ugt a4,a5,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,8(a1) +;; addi a3,a3,-4 +;; bgtu a4,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a1) -;; add a5,a0,a5 -;; lw a0,0(a5) +;; ld a5,0(a1) +;; add a4,a5,a4 +;; lw a0,0(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index d27c4fd77cb6..f877835d280c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -41,18 +41,17 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; ld a0,8(a2) -;; lui a5,-1 -;; addi a4,a5,-4 -;; add a0,a0,a4 -;; ugt a0,a3,a0##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a2) +;; lui a4,-1 +;; addi a3,a4,-4 +;; add a5,a5,a3 +;; bgtu a0,a5,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) -;; add a2,a2,a3 -;; sw a1,4096(a2) +;; add a0,a2,a0 +;; sw a1,4096(a0) ;; j label2 ;; block2: ;; ret @@ -61,18 +60,17 @@ ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; ld a0,8(a1) -;; lui a5,-1 -;; addi a3,a5,-4 -;; add a0,a0,a3 -;; ugt a0,a2,a0##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a1) +;; lui a4,-1 +;; addi a2,a4,-4 +;; add a5,a5,a2 +;; bgtu a0,a5,taken(label3),not_taken(label1) ;; block1: ;; ld a1,0(a1) -;; add a1,a1,a2 -;; lw a0,4096(a1) +;; add a0,a1,a0 +;; lw a0,4096(a0) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index e99ca727f9b8..16a262f3b76f 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -42,22 +42,21 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 +;; srli a4,a3,32 ;; lui a3,262140 -;; addi a4,a3,1 -;; slli a0,a4,2 -;; add a4,a5,a0 -;; trap_if heap_oob##(a4 ult a5) -;; ld a0,8(a2) -;; ugt a0,a4,a0##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; addi a3,a3,1 +;; slli a5,a3,2 +;; add a3,a4,a5 +;; trap_if heap_oob##(a3 ult a4) +;; ld a5,8(a2) +;; bgtu a3,a5,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a2) -;; add a0,a0,a5 -;; lui a5,65535 -;; slli a2,a5,4 -;; add a0,a0,a2 -;; sw a1,0(a0) +;; ld a5,0(a2) +;; add a5,a5,a4 +;; lui a4,65535 +;; slli a0,a4,4 +;; add a5,a5,a0 +;; sw a1,0(a5) ;; j label2 ;; block2: ;; ret @@ -66,23 +65,22 @@ ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 +;; slli a2,a0,32 +;; srli a4,a2,32 ;; lui a2,262140 -;; addi a4,a2,1 -;; slli a0,a4,2 -;; add a4,a5,a0 -;; trap_if heap_oob##(a4 ult a5) -;; ld a0,8(a1) -;; ugt a0,a4,a0##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; addi a3,a2,1 +;; slli a5,a3,2 +;; add a3,a4,a5 +;; trap_if heap_oob##(a3 ult a4) +;; ld a5,8(a1) +;; bgtu a3,a5,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a1) -;; add a0,a0,a5 -;; lui a5,65535 -;; slli a1,a5,4 -;; add a0,a0,a1 -;; lw a0,0(a0) +;; ld a5,0(a1) +;; add a5,a5,a4 +;; lui a4,65535 +;; slli a0,a4,4 +;; add a5,a5,a0 +;; lw a0,0(a5) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat index ee9fb69ef2e0..9a41cc69378c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat @@ -42,14 +42,13 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,8(a2) -;; uge a3,a4,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; srli a3,a3,32 +;; ld a4,8(a2) +;; bgeu a3,a4,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a4,a5,a4 -;; sb a1,0(a4) +;; ld a4,0(a2) +;; add a3,a4,a3 +;; sb a1,0(a3) ;; j label2 ;; block2: ;; ret @@ -59,14 +58,13 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,8(a1) -;; uge a3,a4,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; srli a3,a2,32 +;; ld a2,8(a1) +;; bgeu a3,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a4,a5,a4 -;; lbu a0,0(a4) +;; ld a4,0(a1) +;; add a3,a4,a3 +;; lbu a0,0(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index 789410db035a..f7870c3feb68 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -41,18 +41,17 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; ld a0,8(a2) -;; lui a5,-1 -;; addi a4,a5,-1 -;; add a0,a0,a4 -;; ugt a0,a3,a0##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a2) +;; lui a4,-1 +;; addi a3,a4,-1 +;; add a5,a5,a3 +;; bgtu a0,a5,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) -;; add a2,a2,a3 -;; sb a1,4096(a2) +;; add a0,a2,a0 +;; sb a1,4096(a0) ;; j label2 ;; block2: ;; ret @@ -61,18 +60,17 @@ ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; ld a0,8(a1) -;; lui a5,-1 -;; addi a3,a5,-1 -;; add a0,a0,a3 -;; ugt a0,a2,a0##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a1) +;; lui a4,-1 +;; addi a2,a4,-1 +;; add a5,a5,a2 +;; bgtu a0,a5,taken(label3),not_taken(label1) ;; block1: ;; ld a1,0(a1) -;; add a1,a1,a2 -;; lbu a0,4096(a1) +;; add a0,a1,a0 +;; lbu a0,4096(a0) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index 15ca642762a1..43443094f4b7 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,21 +41,20 @@ ;; function u0:0: ;; block0: -;; slli a3,a0,32 -;; srli a3,a3,32 +;; slli a0,a0,32 +;; srli a3,a0,32 ;; ld a4,[const(0)] ;; add a4,a3,a4 ;; trap_if heap_oob##(a4 ult a3) ;; ld a5,8(a2) -;; ugt a4,a4,a5##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; bgtu a4,a5,taken(label3),not_taken(label1) ;; block1: ;; ld a4,0(a2) -;; add a4,a4,a3 -;; lui a3,65535 -;; slli a5,a3,4 -;; add a4,a4,a5 -;; sb a1,0(a4) +;; add a3,a4,a3 +;; lui a2,65535 +;; slli a4,a2,4 +;; add a3,a3,a4 +;; sb a1,0(a3) ;; j label2 ;; block2: ;; ret @@ -64,21 +63,20 @@ ;; ;; function u0:1: ;; block0: -;; slli a2,a0,32 -;; srli a3,a2,32 -;; ld a2,[const(0)] -;; add a2,a3,a2 -;; trap_if heap_oob##(a2 ult a3) +;; slli a0,a0,32 +;; srli a2,a0,32 +;; ld a3,[const(0)] +;; add a3,a2,a3 +;; trap_if heap_oob##(a3 ult a2) ;; ld a4,8(a1) -;; ugt a4,a2,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; bgtu a3,a4,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a1) -;; add a4,a4,a3 -;; lui a3,65535 -;; slli a5,a3,4 -;; add a4,a4,a5 -;; lbu a0,0(a4) +;; ld a3,0(a1) +;; add a3,a3,a2 +;; lui a2,65535 +;; slli a4,a2,4 +;; add a3,a3,a4 +;; lbu a0,0(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat index c0978bf98749..d30b15cce958 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat @@ -42,14 +42,13 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,8(a2) -;; ugt a3,a4,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; srli a3,a3,32 +;; ld a4,8(a2) +;; bgtu a3,a4,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a4,a5,a4 -;; sw a1,0(a4) +;; ld a4,0(a2) +;; add a3,a4,a3 +;; sw a1,0(a3) ;; j label2 ;; block2: ;; ret @@ -59,14 +58,13 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,8(a1) -;; ugt a3,a4,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; srli a3,a2,32 +;; ld a2,8(a1) +;; bgtu a3,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a4,a5,a4 -;; lw a0,0(a4) +;; ld a4,0(a1) +;; add a3,a4,a3 +;; lw a0,0(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index 533e1999b405..fa671f933dab 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -42,14 +42,13 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,8(a2) -;; ugt a3,a4,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; srli a3,a3,32 +;; ld a4,8(a2) +;; bgtu a3,a4,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a4,a5,a4 -;; sw a1,4096(a4) +;; ld a4,0(a2) +;; add a3,a4,a3 +;; sw a1,4096(a3) ;; j label2 ;; block2: ;; ret @@ -59,14 +58,13 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,8(a1) -;; ugt a3,a4,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; srli a3,a2,32 +;; ld a2,8(a1) +;; bgtu a3,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a4,a5,a4 -;; lw a0,4096(a4) +;; ld a4,0(a1) +;; add a3,a4,a3 +;; lw a0,4096(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index a9fbcf7ce77e..2abf0fb9a8ba 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,18 +41,17 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; ld a0,8(a2) -;; ugt a0,a3,a0##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a2) +;; bgtu a0,a5,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) -;; add a2,a2,a3 -;; lui a0,65535 -;; slli a3,a0,4 -;; add a2,a2,a3 -;; sw a1,0(a2) +;; add a0,a2,a0 +;; lui a5,65535 +;; slli a2,a5,4 +;; add a0,a0,a2 +;; sw a1,0(a0) ;; j label2 ;; block2: ;; ret @@ -61,18 +60,17 @@ ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; ld a0,8(a1) -;; ugt a0,a2,a0##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a1) +;; bgtu a0,a5,taken(label3),not_taken(label1) ;; block1: ;; ld a1,0(a1) -;; add a1,a1,a2 -;; lui a0,65535 -;; slli a2,a0,4 -;; add a1,a1,a2 -;; lw a0,0(a1) +;; add a0,a1,a0 +;; lui a5,65535 +;; slli a1,a5,4 +;; add a0,a0,a1 +;; lw a0,0(a0) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat index 7798803bd437..0137cdbbbc0c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat @@ -42,14 +42,13 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,8(a2) -;; uge a3,a4,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; srli a3,a3,32 +;; ld a4,8(a2) +;; bgeu a3,a4,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a4,a5,a4 -;; sb a1,0(a4) +;; ld a4,0(a2) +;; add a3,a4,a3 +;; sb a1,0(a3) ;; j label2 ;; block2: ;; ret @@ -59,14 +58,13 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,8(a1) -;; uge a3,a4,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; srli a3,a2,32 +;; ld a2,8(a1) +;; bgeu a3,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a4,a5,a4 -;; lbu a0,0(a4) +;; ld a4,0(a1) +;; add a3,a4,a3 +;; lbu a0,0(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index 62e15263099d..3e9a20ee72d1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -42,14 +42,13 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,8(a2) -;; ugt a3,a4,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; srli a3,a3,32 +;; ld a4,8(a2) +;; bgtu a3,a4,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a4,a5,a4 -;; sb a1,4096(a4) +;; ld a4,0(a2) +;; add a3,a4,a3 +;; sb a1,4096(a3) ;; j label2 ;; block2: ;; ret @@ -59,14 +58,13 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,8(a1) -;; ugt a3,a4,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; srli a3,a2,32 +;; ld a2,8(a1) +;; bgtu a3,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a4,a5,a4 -;; lbu a0,4096(a4) +;; ld a4,0(a1) +;; add a3,a4,a3 +;; lbu a0,4096(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index b34ee036abbd..3064723e08ae 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,18 +41,17 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; ld a0,8(a2) -;; ugt a0,a3,a0##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a2) +;; bgtu a0,a5,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) -;; add a2,a2,a3 -;; lui a0,65535 -;; slli a3,a0,4 -;; add a2,a2,a3 -;; sb a1,0(a2) +;; add a0,a2,a0 +;; lui a5,65535 +;; slli a2,a5,4 +;; add a0,a0,a2 +;; sb a1,0(a0) ;; j label2 ;; block2: ;; ret @@ -61,18 +60,17 @@ ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; ld a0,8(a1) -;; ugt a0,a2,a0##ty=i64 -;; bne a0,zero,taken(label3),not_taken(label1) +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a1) +;; bgtu a0,a5,taken(label3),not_taken(label1) ;; block1: ;; ld a1,0(a1) -;; add a1,a1,a2 -;; lui a0,65535 -;; slli a2,a0,4 -;; add a1,a1,a2 -;; lbu a0,0(a1) +;; add a0,a1,a0 +;; lui a5,65535 +;; slli a1,a5,4 +;; add a0,a0,a1 +;; lbu a0,0(a0) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat index c7ad7f0c8218..f21969e0c25e 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat @@ -43,12 +43,11 @@ ;; block0: ;; ld a3,8(a2) ;; addi a3,a3,-4 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a2) -;; add a3,a3,a0 -;; sw a1,0(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sw a1,0(a2) ;; j label2 ;; block2: ;; ret @@ -59,12 +58,11 @@ ;; block0: ;; ld a2,8(a1) ;; addi a2,a2,-4 -;; ugt a2,a0,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lw a0,0(a3) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lw a0,0(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index 7dcefcb76636..025f33dde63e 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -41,16 +41,15 @@ ;; function u0:0: ;; block0: -;; ld a4,8(a2) -;; lui a3,-1 -;; addi a5,a3,-4 -;; add a4,a4,a5 -;; ugt a4,a0,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ld a3,8(a2) +;; lui a4,-1 +;; addi a4,a4,-4 +;; add a3,a3,a4 +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a5,a5,a0 -;; sw a1,4096(a5) +;; ld a4,0(a2) +;; add a4,a4,a0 +;; sw a1,4096(a4) ;; j label2 ;; block2: ;; ret @@ -59,16 +58,15 @@ ;; ;; function u0:1: ;; block0: -;; ld a4,8(a1) -;; lui a3,-1 -;; addi a5,a3,-4 -;; add a4,a4,a5 -;; ugt a4,a0,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ld a3,8(a1) +;; lui a2,-1 +;; addi a4,a2,-4 +;; add a3,a3,a4 +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lw a0,4096(a5) +;; ld a4,0(a1) +;; add a4,a4,a0 +;; lw a0,4096(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index d374fc15791c..fa4768c25dfa 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,21 +41,20 @@ ;; function u0:0: ;; block0: -;; lui a3,262140 -;; addi a3,a3,1 -;; slli a4,a3,2 -;; add a3,a0,a4 +;; lui a5,262140 +;; addi a3,a5,1 +;; slli a3,a3,2 +;; add a3,a0,a3 ;; trap_if heap_oob##(a3 ult a0) ;; ld a4,8(a2) -;; ugt a3,a3,a4##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a3,a4,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a2) -;; add a4,a4,a0 -;; lui a3,65535 -;; slli a5,a3,4 -;; add a4,a4,a5 -;; sw a1,0(a4) +;; ld a3,0(a2) +;; add a3,a3,a0 +;; lui a2,65535 +;; slli a4,a2,4 +;; add a3,a3,a4 +;; sw a1,0(a3) ;; j label2 ;; block2: ;; ret @@ -64,21 +63,20 @@ ;; ;; function u0:1: ;; block0: -;; lui a2,262140 -;; addi a2,a2,1 -;; slli a4,a2,2 -;; add a2,a0,a4 +;; lui a5,262140 +;; addi a2,a5,1 +;; slli a3,a2,2 +;; add a2,a0,a3 ;; trap_if heap_oob##(a2 ult a0) ;; ld a3,8(a1) -;; ugt a3,a2,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a2,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a4,0(a1) -;; add a4,a4,a0 -;; lui a3,65535 -;; slli a5,a3,4 -;; add a4,a4,a5 -;; lw a0,0(a4) +;; ld a3,0(a1) +;; add a3,a3,a0 +;; lui a2,65535 +;; slli a4,a2,4 +;; add a3,a3,a4 +;; lw a0,0(a3) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat index e46cd968548f..3a323cef4580 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat @@ -42,8 +42,7 @@ ;; function u0:0: ;; block0: ;; ld a3,8(a2) -;; uge a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgeu a0,a3,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) ;; add a2,a2,a0 @@ -57,12 +56,11 @@ ;; function u0:1: ;; block0: ;; ld a2,8(a1) -;; uge a2,a0,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; bgeu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a1) -;; add a2,a2,a0 -;; lbu a0,0(a2) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lbu a0,0(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index 1d0c8afc3d5a..ee26ad7f0626 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -41,16 +41,15 @@ ;; function u0:0: ;; block0: -;; ld a4,8(a2) -;; lui a3,-1 -;; addi a5,a3,-1 -;; add a4,a4,a5 -;; ugt a4,a0,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ld a3,8(a2) +;; lui a4,-1 +;; addi a4,a4,-1 +;; add a3,a3,a4 +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a5,a5,a0 -;; sb a1,4096(a5) +;; ld a4,0(a2) +;; add a4,a4,a0 +;; sb a1,4096(a4) ;; j label2 ;; block2: ;; ret @@ -59,16 +58,15 @@ ;; ;; function u0:1: ;; block0: -;; ld a4,8(a1) -;; lui a3,-1 -;; addi a5,a3,-1 -;; add a4,a4,a5 -;; ugt a4,a0,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ld a3,8(a1) +;; lui a2,-1 +;; addi a4,a2,-1 +;; add a3,a3,a4 +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lbu a0,4096(a5) +;; ld a4,0(a1) +;; add a4,a4,a0 +;; lbu a0,4096(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index 9127fa86c181..445a2b6d9885 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,17 +41,16 @@ ;; function u0:0: ;; block0: -;; ld a3,[const(0)] -;; add a3,a0,a3 -;; trap_if heap_oob##(a3 ult a0) -;; ld a4,8(a2) -;; ugt a3,a3,a4##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; ld a5,[const(0)] +;; add a5,a0,a5 +;; trap_if heap_oob##(a5 ult a0) +;; ld a3,8(a2) +;; bgtu a5,a3,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) ;; add a2,a2,a0 -;; lui a3,65535 -;; slli a3,a3,4 +;; lui a0,65535 +;; slli a3,a0,4 ;; add a2,a2,a3 ;; sb a1,0(a2) ;; j label2 @@ -62,19 +61,18 @@ ;; ;; function u0:1: ;; block0: -;; ld a2,[const(0)] -;; add a2,a0,a2 -;; trap_if heap_oob##(a2 ult a0) -;; ld a3,8(a1) -;; ugt a2,a2,a3##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; ld a5,[const(0)] +;; add a5,a0,a5 +;; trap_if heap_oob##(a5 ult a0) +;; ld a2,8(a1) +;; bgtu a5,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a1) -;; add a2,a2,a0 -;; lui a1,65535 -;; slli a3,a1,4 -;; add a2,a2,a3 -;; lbu a0,0(a2) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lui a0,65535 +;; slli a2,a0,4 +;; add a1,a1,a2 +;; lbu a0,0(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat index 75f0ba62c4e5..216eea7f715d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat @@ -42,8 +42,7 @@ ;; function u0:0: ;; block0: ;; ld a3,8(a2) -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) ;; add a2,a2,a0 @@ -57,12 +56,11 @@ ;; function u0:1: ;; block0: ;; ld a2,8(a1) -;; ugt a2,a0,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a1) -;; add a2,a2,a0 -;; lw a0,0(a2) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lw a0,0(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index 8fa233529260..f7784defa517 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -42,8 +42,7 @@ ;; function u0:0: ;; block0: ;; ld a3,8(a2) -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) ;; add a2,a2,a0 @@ -57,12 +56,11 @@ ;; function u0:1: ;; block0: ;; ld a2,8(a1) -;; ugt a2,a0,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a1) -;; add a2,a2,a0 -;; lw a0,4096(a2) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lw a0,4096(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index bc0a82b1a9fa..3a8325aa3646 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -41,16 +41,15 @@ ;; function u0:0: ;; block0: -;; ld a4,8(a2) -;; ugt a4,a0,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ld a3,8(a2) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a5,a5,a0 -;; lui a4,65535 -;; slli a0,a4,4 -;; add a5,a5,a0 -;; sw a1,0(a5) +;; ld a4,0(a2) +;; add a4,a4,a0 +;; lui a3,65535 +;; slli a5,a3,4 +;; add a4,a4,a5 +;; sw a1,0(a4) ;; j label2 ;; block2: ;; ret @@ -59,16 +58,15 @@ ;; ;; function u0:1: ;; block0: -;; ld a4,8(a1) -;; ugt a4,a0,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ld a3,8(a1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lui a4,65535 -;; slli a0,a4,4 -;; add a5,a5,a0 -;; lw a0,0(a5) +;; ld a4,0(a1) +;; add a4,a4,a0 +;; lui a3,65535 +;; slli a5,a3,4 +;; add a4,a4,a5 +;; lw a0,0(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat index f860593c77d7..598727d97962 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat @@ -42,8 +42,7 @@ ;; function u0:0: ;; block0: ;; ld a3,8(a2) -;; uge a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgeu a0,a3,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) ;; add a2,a2,a0 @@ -57,12 +56,11 @@ ;; function u0:1: ;; block0: ;; ld a2,8(a1) -;; uge a2,a0,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; bgeu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a1) -;; add a2,a2,a0 -;; lbu a0,0(a2) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lbu a0,0(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index d793c28abcb6..10beda2e0206 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -42,8 +42,7 @@ ;; function u0:0: ;; block0: ;; ld a3,8(a2) -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: ;; ld a2,0(a2) ;; add a2,a2,a0 @@ -57,12 +56,11 @@ ;; function u0:1: ;; block0: ;; ld a2,8(a1) -;; ugt a2,a0,a2##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a2,0(a1) -;; add a2,a2,a0 -;; lbu a0,4096(a2) +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lbu a0,4096(a1) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index f2e476f13c60..6ac50e3591b9 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -41,16 +41,15 @@ ;; function u0:0: ;; block0: -;; ld a4,8(a2) -;; ugt a4,a0,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ld a3,8(a2) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a2) -;; add a5,a5,a0 -;; lui a4,65535 -;; slli a0,a4,4 -;; add a5,a5,a0 -;; sb a1,0(a5) +;; ld a4,0(a2) +;; add a4,a4,a0 +;; lui a3,65535 +;; slli a5,a3,4 +;; add a4,a4,a5 +;; sb a1,0(a4) ;; j label2 ;; block2: ;; ret @@ -59,16 +58,15 @@ ;; ;; function u0:1: ;; block0: -;; ld a4,8(a1) -;; ugt a4,a0,a4##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; ld a3,8(a1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lui a4,65535 -;; slli a0,a4,4 -;; add a5,a5,a0 -;; lbu a0,0(a5) +;; ld a4,0(a1) +;; add a4,a4,a0 +;; lui a3,65535 +;; slli a5,a3,4 +;; add a4,a4,a5 +;; lbu a0,0(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat index b37cdad54916..be756e1e6b5a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0_offset.wat @@ -40,15 +40,14 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 +;; srli a4,a3,32 ;; lui a3,65536 -;; addi a0,a3,-4 -;; ugt a4,a5,a0##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a5,a3,-4 +;; bgtu a4,a5,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a2) -;; add a5,a0,a5 -;; sw a1,0(a5) +;; ld a5,0(a2) +;; add a4,a5,a4 +;; sw a1,0(a4) ;; j label2 ;; block2: ;; ret @@ -57,16 +56,15 @@ ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; lui a3,65536 -;; addi a0,a3,-4 -;; ugt a4,a5,a0##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; lui a2,65536 +;; addi a5,a2,-4 +;; bgtu a4,a5,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a1) -;; add a5,a0,a5 -;; lw a0,0(a5) +;; ld a5,0(a1) +;; add a4,a5,a4 +;; lw a0,0(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index d8c6b3db0004..bb58c0d19bf0 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -40,15 +40,14 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 +;; srli a4,a3,32 ;; lui a3,65535 -;; addi a0,a3,-4 -;; ugt a4,a5,a0##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a5,a3,-4 +;; bgtu a4,a5,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a2) -;; add a5,a0,a5 -;; sw a1,4096(a5) +;; ld a5,0(a2) +;; add a4,a5,a4 +;; sw a1,4096(a4) ;; j label2 ;; block2: ;; ret @@ -57,16 +56,15 @@ ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; lui a3,65535 -;; addi a0,a3,-4 -;; ugt a4,a5,a0##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; lui a2,65535 +;; addi a5,a2,-4 +;; bgtu a4,a5,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a1) -;; add a5,a0,a5 -;; lw a0,4096(a5) +;; ld a5,0(a1) +;; add a4,a5,a4 +;; lw a0,4096(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat index a537a70f068e..10a641f261a1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0_offset.wat @@ -40,15 +40,14 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 +;; srli a4,a3,32 ;; lui a3,65536 -;; addi a0,a3,-1 -;; ugt a4,a5,a0##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a5,a3,-1 +;; bgtu a4,a5,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a2) -;; add a5,a0,a5 -;; sb a1,0(a5) +;; ld a5,0(a2) +;; add a4,a5,a4 +;; sb a1,0(a4) ;; j label2 ;; block2: ;; ret @@ -57,16 +56,15 @@ ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; lui a3,65536 -;; addi a0,a3,-1 -;; ugt a4,a5,a0##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; lui a2,65536 +;; addi a5,a2,-1 +;; bgtu a4,a5,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a1) -;; add a5,a0,a5 -;; lbu a0,0(a5) +;; ld a5,0(a1) +;; add a4,a5,a4 +;; lbu a0,0(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index 54bfd5069289..3c3968591466 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -40,15 +40,14 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 +;; srli a4,a3,32 ;; lui a3,65535 -;; addi a0,a3,-1 -;; ugt a4,a5,a0##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; addi a5,a3,-1 +;; bgtu a4,a5,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a2) -;; add a5,a0,a5 -;; sb a1,4096(a5) +;; ld a5,0(a2) +;; add a4,a5,a4 +;; sb a1,4096(a4) ;; j label2 ;; block2: ;; ret @@ -57,16 +56,15 @@ ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; lui a3,65535 -;; addi a0,a3,-1 -;; ugt a4,a5,a0##ty=i64 -;; bne a4,zero,taken(label3),not_taken(label1) +;; slli a2,a0,32 +;; srli a4,a2,32 +;; lui a2,65535 +;; addi a5,a2,-1 +;; bgtu a4,a5,taken(label3),not_taken(label1) ;; block1: -;; ld a0,0(a1) -;; add a5,a0,a5 -;; lbu a0,4096(a5) +;; ld a5,0(a1) +;; add a4,a5,a4 +;; lbu a0,4096(a4) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat index ecd6829c5d7d..2ccf3332dafe 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0_offset.wat @@ -41,12 +41,11 @@ ;; block0: ;; lui a3,65536 ;; addi a3,a3,-4 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a2) -;; add a3,a3,a0 -;; sw a1,0(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sw a1,0(a2) ;; j label2 ;; block2: ;; ret @@ -56,13 +55,12 @@ ;; function u0:1: ;; block0: ;; lui a2,65536 -;; addi a3,a2,-4 -;; ugt a2,a0,a3##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; addi a2,a2,-4 +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lw a0,0(a3) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lw a0,0(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index 9f15ae9dd19f..1e526bff3979 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -41,12 +41,11 @@ ;; block0: ;; lui a3,65535 ;; addi a3,a3,-4 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a2) -;; add a3,a3,a0 -;; sw a1,4096(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sw a1,4096(a2) ;; j label2 ;; block2: ;; ret @@ -56,13 +55,12 @@ ;; function u0:1: ;; block0: ;; lui a2,65535 -;; addi a3,a2,-4 -;; ugt a2,a0,a3##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; addi a2,a2,-4 +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lw a0,4096(a3) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lw a0,4096(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat index d20b6c5773f7..0b54080054b9 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0_offset.wat @@ -41,12 +41,11 @@ ;; block0: ;; lui a3,65536 ;; addi a3,a3,-1 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a2) -;; add a3,a3,a0 -;; sb a1,0(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sb a1,0(a2) ;; j label2 ;; block2: ;; ret @@ -56,13 +55,12 @@ ;; function u0:1: ;; block0: ;; lui a2,65536 -;; addi a3,a2,-1 -;; ugt a2,a0,a3##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; addi a2,a2,-1 +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lbu a0,0(a3) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lbu a0,0(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index bf67a6fe8b65..f85b1a0bd8fe 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -41,12 +41,11 @@ ;; block0: ;; lui a3,65535 ;; addi a3,a3,-1 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a2) -;; add a3,a3,a0 -;; sb a1,4096(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sb a1,4096(a2) ;; j label2 ;; block2: ;; ret @@ -56,13 +55,12 @@ ;; function u0:1: ;; block0: ;; lui a2,65535 -;; addi a3,a2,-1 -;; ugt a2,a0,a3##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; addi a2,a2,-1 +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lbu a0,4096(a3) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lbu a0,4096(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat index d9741e0bd334..7ee1cabd23a5 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0_offset.wat @@ -41,12 +41,11 @@ ;; block0: ;; lui a3,65536 ;; addi a3,a3,-4 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a2) -;; add a3,a3,a0 -;; sw a1,0(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sw a1,0(a2) ;; j label2 ;; block2: ;; ret @@ -56,13 +55,12 @@ ;; function u0:1: ;; block0: ;; lui a2,65536 -;; addi a3,a2,-4 -;; ugt a2,a0,a3##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; addi a2,a2,-4 +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lw a0,0(a3) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lw a0,0(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index 5cd4036a9196..27c1f657371c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -41,12 +41,11 @@ ;; block0: ;; lui a3,65535 ;; addi a3,a3,-4 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a2) -;; add a3,a3,a0 -;; sw a1,4096(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sw a1,4096(a2) ;; j label2 ;; block2: ;; ret @@ -56,13 +55,12 @@ ;; function u0:1: ;; block0: ;; lui a2,65535 -;; addi a3,a2,-4 -;; ugt a2,a0,a3##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; addi a2,a2,-4 +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lw a0,4096(a3) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lw a0,4096(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat index 645e8aaa6203..60b63137691f 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0_offset.wat @@ -41,12 +41,11 @@ ;; block0: ;; lui a3,65536 ;; addi a3,a3,-1 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a2) -;; add a3,a3,a0 -;; sb a1,0(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sb a1,0(a2) ;; j label2 ;; block2: ;; ret @@ -56,13 +55,12 @@ ;; function u0:1: ;; block0: ;; lui a2,65536 -;; addi a3,a2,-1 -;; ugt a2,a0,a3##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; addi a2,a2,-1 +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lbu a0,0(a3) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lbu a0,0(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index 1c81770d8647..8568f99897bc 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -41,12 +41,11 @@ ;; block0: ;; lui a3,65535 ;; addi a3,a3,-1 -;; ugt a3,a0,a3##ty=i64 -;; bne a3,zero,taken(label3),not_taken(label1) +;; bgtu a0,a3,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a2) -;; add a3,a3,a0 -;; sb a1,4096(a3) +;; ld a2,0(a2) +;; add a2,a2,a0 +;; sb a1,4096(a2) ;; j label2 ;; block2: ;; ret @@ -56,13 +55,12 @@ ;; function u0:1: ;; block0: ;; lui a2,65535 -;; addi a3,a2,-1 -;; ugt a2,a0,a3##ty=i64 -;; bne a2,zero,taken(label3),not_taken(label1) +;; addi a2,a2,-1 +;; bgtu a0,a2,taken(label3),not_taken(label1) ;; block1: -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lbu a0,4096(a3) +;; ld a2,0(a1) +;; add a2,a2,a0 +;; lbu a0,4096(a2) ;; j label2 ;; block2: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/zca.clif b/cranelift/filetests/filetests/isa/riscv64/zca.clif index cc8a16140c49..4726ec5d488d 100644 --- a/cranelift/filetests/filetests/isa/riscv64/zca.clif +++ b/cranelift/filetests/filetests/isa/riscv64/zca.clif @@ -157,7 +157,7 @@ block0(v0: i8, v1: i8, v2: i8): ; VCode: ; block0: ; andi a4,a0,255 -; select_i8 a0,a1,a2##condition=a4 +; select a0,a1,a2##condition=(a4 ne zero) ; ret ; ; Disassembled: From 7ef5094a0eabf38af18a164f672d649b8a5e1c4b Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Wed, 4 Oct 2023 23:41:40 +0100 Subject: [PATCH 059/199] riscv64: Add support for min/max instructions (#7146) --- cranelift/codegen/src/isa/riscv64/inst.isle | 55 +++++-- cranelift/codegen/src/isa/riscv64/lower.isle | 2 +- .../codegen/src/isa/riscv64/lower/isle.rs | 5 + cranelift/codegen/src/isa/x64/inst.isle | 9 -- cranelift/codegen/src/isa/x64/lower/isle.rs | 5 - cranelift/codegen/src/isle_prelude.rs | 5 + cranelift/codegen/src/prelude.isle | 6 + .../filetests/isa/riscv64/smax-zbb.clif | 135 +++++++++++++++++ .../filetests/isa/riscv64/smin-zbb.clif | 135 +++++++++++++++++ .../filetests/isa/riscv64/umax-zbb.clif | 139 ++++++++++++++++++ .../filetests/isa/riscv64/umin-zbb.clif | 139 ++++++++++++++++++ 11 files changed, 608 insertions(+), 27 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/riscv64/smax-zbb.clif create mode 100644 cranelift/filetests/filetests/isa/riscv64/smin-zbb.clif create mode 100644 cranelift/filetests/filetests/isa/riscv64/umax-zbb.clif create mode 100644 cranelift/filetests/filetests/isa/riscv64/umin-zbb.clif diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 32968393af10..569d5c26b865 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -1599,6 +1599,21 @@ (rule (rv_max rs1 rs2) (alu_rrr (AluOPRRR.Max) rs1 rs2)) +;; Helper for emitting the `maxu` instruction. +(decl rv_maxu (XReg XReg) XReg) +(rule (rv_maxu rs1 rs2) + (alu_rrr (AluOPRRR.Maxu) rs1 rs2)) + +;; Helper for emitting the `min` instruction. +(decl rv_min (XReg XReg) XReg) +(rule (rv_min rs1 rs2) + (alu_rrr (AluOPRRR.Max) rs1 rs2)) + +;; Helper for emitting the `minu` instruction. +(decl rv_minu (XReg XReg) XReg) +(rule (rv_minu rs1 rs2) + (alu_rrr (AluOPRRR.Minu) rs1 rs2)) + ;; Helper for emitting the `sext.b` instruction. (decl rv_sextb (XReg) XReg) (rule (rv_sextb rs1) @@ -2546,11 +2561,34 @@ (extern constructor gen_stack_addr gen_stack_addr) (decl gen_select_xreg (IntegerCompare XReg XReg) XReg) -(rule (gen_select_xreg c x y) + +(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y) + (if-let (IntCC.UnsignedLessThan) (intcc_without_eq cc)) + (if-let $true (has_zbb)) + (rv_minu x y)) + +(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y) + (if-let (IntCC.SignedLessThan) (intcc_without_eq cc)) + (if-let $true (has_zbb)) + (rv_min x y)) + +(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y) + (if-let (IntCC.UnsignedGreaterThan) (intcc_without_eq cc)) + (if-let $true (has_zbb)) + (rv_maxu x y)) + +(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y) + (if-let (IntCC.SignedGreaterThan) (intcc_without_eq cc)) + (if-let $true (has_zbb)) + (rv_max x y)) + +(rule 0 (gen_select_xreg c x y) (let ((dst WritableReg (temp_writable_xreg)) (_ Unit (emit (MInst.Select dst c x y)))) (writable_reg_to_reg dst))) + + (decl gen_select_vreg (IntegerCompare VReg VReg) VReg) (rule (gen_select_vreg c x y) (let @@ -2649,6 +2687,10 @@ (decl int_compare (IntCC XReg XReg) IntegerCompare) (extern constructor int_compare int_compare) +;; Extract the components of an `IntegerCompare` +(decl int_compare_decompose (IntCC XReg XReg) IntegerCompare) +(extern extractor infallible int_compare_decompose int_compare_decompose) + (decl label_to_br_target (MachLabel) CondBrTarget) (extern constructor label_to_br_target label_to_br_target) (convert MachLabel CondBrTarget label_to_br_target) @@ -2826,17 +2868,6 @@ (i128_sub (value_regs_zero) val)) -;; Selects the greatest of two registers as signed values. -(decl max (Type XReg XReg) XReg) -(rule (max (fits_in_64 (ty_int ty)) x y) - (if-let $true (has_zbb)) - (rv_max x y)) - -(rule (max (fits_in_64 (ty_int ty)) x y) - (if-let $false (has_zbb)) - (gen_select_xreg (cmp_gt x y) x y)) - - ;; Builds an instruction sequence that traps if the comparision succeeds. (decl gen_trapif (IntCC XReg XReg TrapCode) InstOutput) (rule (gen_trapif cc a b trap_code) diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index 0c6fbf248e87..03ee2a93d234 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -2044,7 +2044,7 @@ (rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (iabs x))) (let ((extended XReg (sext x)) (negated XReg (rv_neg extended))) - (max $I64 extended negated))) + (gen_select_xreg (cmp_gt extended negated) extended negated))) ;; For vectors we generate the same code, but with vector instructions ;; we can skip the sign extension, since the vector unit will only process diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs index 699ed57b0cec..b106cd77f7ed 100644 --- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs +++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs @@ -482,6 +482,11 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend> } } + #[inline] + fn int_compare_decompose(&mut self, cmp: IntegerCompare) -> (IntCC, XReg, XReg) { + (cmp.kind, self.xreg_new(cmp.rs1), self.xreg_new(cmp.rs2)) + } + #[inline] fn vstate_from_type(&mut self, ty: Type) -> VState { VState::from_type(ty) diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle index 281b665c7eec..e5cb338a40cf 100644 --- a/cranelift/codegen/src/isa/x64/inst.isle +++ b/cranelift/codegen/src/isa/x64/inst.isle @@ -1669,15 +1669,6 @@ (decl xmi_imm (u32) XmmMemImm) (extern constructor xmi_imm xmi_imm) -;;;; Helpers for Working With Integer Comparison Codes ;;;;;;;;;;;;;;;;;;;;;;;;; -;; - -;; This is a direct import of `IntCC::without_equal`. -;; Get the corresponding IntCC with the equal component removed. -;; For conditions without a zero component, this is a no-op. -(decl intcc_without_eq (IntCC) IntCC) -(extern constructor intcc_without_eq intcc_without_eq) - ;;;; Helpers for determining the register class of a value type ;;;;;;;;;;;;;;;; (type RegisterClass diff --git a/cranelift/codegen/src/isa/x64/lower/isle.rs b/cranelift/codegen/src/isa/x64/lower/isle.rs index 99d454a70cd6..5908ad58aa61 100644 --- a/cranelift/codegen/src/isa/x64/lower/isle.rs +++ b/cranelift/codegen/src/isa/x64/lower/isle.rs @@ -623,11 +623,6 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> { } } - #[inline] - fn intcc_without_eq(&mut self, x: &IntCC) -> IntCC { - x.without_equal() - } - #[inline] fn intcc_to_cc(&mut self, intcc: &IntCC) -> CC { CC::from_intcc(*intcc) diff --git a/cranelift/codegen/src/isle_prelude.rs b/cranelift/codegen/src/isle_prelude.rs index 26332de5a88b..4353ae388c6d 100644 --- a/cranelift/codegen/src/isle_prelude.rs +++ b/cranelift/codegen/src/isle_prelude.rs @@ -846,6 +846,11 @@ macro_rules! isle_common_prelude_methods { cc.complement() } + #[inline] + fn intcc_without_eq(&mut self, x: &IntCC) -> IntCC { + x.without_equal() + } + #[inline] fn floatcc_swap_args(&mut self, cc: &FloatCC) -> FloatCC { cc.swap_args() diff --git a/cranelift/codegen/src/prelude.isle b/cranelift/codegen/src/prelude.isle index da37334b1d12..5f389dbc9df4 100644 --- a/cranelift/codegen/src/prelude.isle +++ b/cranelift/codegen/src/prelude.isle @@ -337,6 +337,12 @@ (decl intcc_complement (IntCC) IntCC) (extern constructor intcc_complement intcc_complement) +;; This is a direct import of `IntCC::without_equal`. +;; Get the corresponding IntCC with the equal component removed. +;; For conditions without a zero component, this is a no-op. +(decl pure intcc_without_eq (IntCC) IntCC) +(extern constructor intcc_without_eq intcc_without_eq) + ;; Swap args of a FloatCC flag. (decl floatcc_swap_args (FloatCC) FloatCC) (extern constructor floatcc_swap_args floatcc_swap_args) diff --git a/cranelift/filetests/filetests/isa/riscv64/smax-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/smax-zbb.clif new file mode 100644 index 000000000000..25812fb06058 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/smax-zbb.clif @@ -0,0 +1,135 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_zbb + +function %smax_i8(i8, i8) -> i8{ +block0(v0: i8, v1: i8): + v2 = smax v0, v1 + return v2 +} + +; VCode: +; block0: +; sext.b a3,a0 +; sext.b a5,a1 +; max a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x93, 0x16, 0x45, 0x60 +; .byte 0x93, 0x97, 0x45, 0x60 +; .byte 0x33, 0xe5, 0xf6, 0x0a +; ret + +function %smax_i16(i16, i16) -> i16{ +block0(v0: i16, v1: i16): + v2 = smax v0, v1 + return v2 +} + +; VCode: +; block0: +; sext.h a3,a0 +; sext.h a5,a1 +; max a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x93, 0x16, 0x55, 0x60 +; .byte 0x93, 0x97, 0x55, 0x60 +; .byte 0x33, 0xe5, 0xf6, 0x0a +; ret + +function %smax_i32(i32, i32) -> i32{ +block0(v0: i32, v1: i32): + v2 = smax v0, v1 + return v2 +} + +; VCode: +; block0: +; sext.w a3,a0 +; sext.w a5,a1 +; max a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; sext.w a3, a0 +; sext.w a5, a1 +; .byte 0x33, 0xe5, 0xf6, 0x0a +; ret + +function %smax_i64(i64, i64) -> i64{ +block0(v0: i64, v1: i64): + v2 = smax v0, v1 + return v2 +} + +; VCode: +; block0: +; max a0,a0,a1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x33, 0x65, 0xb5, 0x0a +; ret + +function %smax_i128(i128, i128) -> i128{ +block0(v0: i128, v1: i128): + v2 = smax v0, v1 + return v2 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s3,-8(sp) +; add sp,-16 +; block0: +; sgt a5,[a0,a1],[a2,a3]##ty=i128 +; mv a4,a0 +; mv s3,a1 +; select [a0,a1],[a4,s3],[a2,a3]##condition=(a5 ne zero) +; add sp,+16 +; ld s3,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s3, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 +; blt a3, a1, 0xc +; bne a1, a3, 0x10 +; bgeu a2, a0, 0xc +; addi a5, zero, 1 +; j 8 +; mv a5, zero +; mv a4, a0 +; mv s3, a1 +; beqz a5, 0x10 +; mv a0, a4 +; mv a1, s3 +; j 0xc +; mv a0, a2 +; mv a1, a3 +; addi sp, sp, 0x10 +; ld s3, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/smin-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/smin-zbb.clif new file mode 100644 index 000000000000..fa67cb0d998d --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/smin-zbb.clif @@ -0,0 +1,135 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_zbb + +function %smin_i8(i8, i8) -> i8{ +block0(v0: i8, v1: i8): + v2 = smin v0, v1 + return v2 +} + +; VCode: +; block0: +; sext.b a3,a0 +; sext.b a5,a1 +; max a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x93, 0x16, 0x45, 0x60 +; .byte 0x93, 0x97, 0x45, 0x60 +; .byte 0x33, 0xe5, 0xf6, 0x0a +; ret + +function %smin_i16(i16, i16) -> i16{ +block0(v0: i16, v1: i16): + v2 = smin v0, v1 + return v2 +} + +; VCode: +; block0: +; sext.h a3,a0 +; sext.h a5,a1 +; max a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x93, 0x16, 0x55, 0x60 +; .byte 0x93, 0x97, 0x55, 0x60 +; .byte 0x33, 0xe5, 0xf6, 0x0a +; ret + +function %smin_i32(i32, i32) -> i32{ +block0(v0: i32, v1: i32): + v2 = smin v0, v1 + return v2 +} + +; VCode: +; block0: +; sext.w a3,a0 +; sext.w a5,a1 +; max a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; sext.w a3, a0 +; sext.w a5, a1 +; .byte 0x33, 0xe5, 0xf6, 0x0a +; ret + +function %smin_i64(i64, i64) -> i64{ +block0(v0: i64, v1: i64): + v2 = smin v0, v1 + return v2 +} + +; VCode: +; block0: +; max a0,a0,a1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x33, 0x65, 0xb5, 0x0a +; ret + +function %smin_i128(i128, i128) -> i128{ +block0(v0: i128, v1: i128): + v2 = smin v0, v1 + return v2 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s3,-8(sp) +; add sp,-16 +; block0: +; slt a5,[a0,a1],[a2,a3]##ty=i128 +; mv a4,a0 +; mv s3,a1 +; select [a0,a1],[a4,s3],[a2,a3]##condition=(a5 ne zero) +; add sp,+16 +; ld s3,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s3, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 +; blt a1, a3, 0xc +; bne a1, a3, 0x10 +; bgeu a0, a2, 0xc +; addi a5, zero, 1 +; j 8 +; mv a5, zero +; mv a4, a0 +; mv s3, a1 +; beqz a5, 0x10 +; mv a0, a4 +; mv a1, s3 +; j 0xc +; mv a0, a2 +; mv a1, a3 +; addi sp, sp, 0x10 +; ld s3, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/umax-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/umax-zbb.clif new file mode 100644 index 000000000000..1eb8f37baa6b --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/umax-zbb.clif @@ -0,0 +1,139 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_zbb + +function %umax_i8(i8, i8) -> i8{ +block0(v0: i8, v1: i8): + v2 = umax v0, v1 + return v2 +} + +; VCode: +; block0: +; andi a3,a0,255 +; andi a5,a1,255 +; maxu a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; andi a3, a0, 0xff +; andi a5, a1, 0xff +; .byte 0x33, 0xf5, 0xf6, 0x0a +; ret + +function %umax_i16(i16, i16) -> i16{ +block0(v0: i16, v1: i16): + v2 = umax v0, v1 + return v2 +} + +; VCode: +; block0: +; zext.h a3,a0 +; zext.h a5,a1 +; maxu a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0xbb, 0x46, 0x05, 0x08 +; .byte 0xbb, 0xc7, 0x05, 0x08 +; .byte 0x33, 0xf5, 0xf6, 0x0a +; ret + +function %umax_i32(i32, i32) -> i32{ +block0(v0: i32, v1: i32): + v2 = umax v0, v1 + return v2 +} + +; VCode: +; block0: +; slli a3,a0,32 +; srli a5,a3,32 +; slli a1,a1,32 +; srli a3,a1,32 +; maxu a0,a5,a3 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a3, a0, 0x20 +; srli a5, a3, 0x20 +; slli a1, a1, 0x20 +; srli a3, a1, 0x20 +; .byte 0x33, 0xf5, 0xd7, 0x0a +; ret + +function %umax_i64(i64, i64) -> i64{ +block0(v0: i64, v1: i64): + v2 = umax v0, v1 + return v2 +} + +; VCode: +; block0: +; maxu a0,a0,a1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x33, 0x75, 0xb5, 0x0a +; ret + +function %umax_i128(i128, i128) -> i128{ +block0(v0: i128, v1: i128): + v2 = umax v0, v1 + return v2 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s3,-8(sp) +; add sp,-16 +; block0: +; ugt a5,[a0,a1],[a2,a3]##ty=i128 +; mv a4,a0 +; mv s3,a1 +; select [a0,a1],[a4,s3],[a2,a3]##condition=(a5 ne zero) +; add sp,+16 +; ld s3,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s3, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 +; bltu a3, a1, 0xc +; bne a1, a3, 0x10 +; bgeu a2, a0, 0xc +; addi a5, zero, 1 +; j 8 +; mv a5, zero +; mv a4, a0 +; mv s3, a1 +; beqz a5, 0x10 +; mv a0, a4 +; mv a1, s3 +; j 0xc +; mv a0, a2 +; mv a1, a3 +; addi sp, sp, 0x10 +; ld s3, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/umin-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/umin-zbb.clif new file mode 100644 index 000000000000..2a2d522b5632 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/umin-zbb.clif @@ -0,0 +1,139 @@ +test compile precise-output +set unwind_info=false +target riscv64 has_zbb + +function %umin_i8(i8, i8) -> i8{ +block0(v0: i8, v1: i8): + v2 = umin v0, v1 + return v2 +} + +; VCode: +; block0: +; andi a3,a0,255 +; andi a5,a1,255 +; minu a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; andi a3, a0, 0xff +; andi a5, a1, 0xff +; .byte 0x33, 0xd5, 0xf6, 0x0a +; ret + +function %umin_i16(i16, i16) -> i16{ +block0(v0: i16, v1: i16): + v2 = umin v0, v1 + return v2 +} + +; VCode: +; block0: +; zext.h a3,a0 +; zext.h a5,a1 +; minu a0,a3,a5 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0xbb, 0x46, 0x05, 0x08 +; .byte 0xbb, 0xc7, 0x05, 0x08 +; .byte 0x33, 0xd5, 0xf6, 0x0a +; ret + +function %umin_i32(i32, i32) -> i32{ +block0(v0: i32, v1: i32): + v2 = umin v0, v1 + return v2 +} + +; VCode: +; block0: +; slli a3,a0,32 +; srli a5,a3,32 +; slli a1,a1,32 +; srli a3,a1,32 +; minu a0,a5,a3 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; slli a3, a0, 0x20 +; srli a5, a3, 0x20 +; slli a1, a1, 0x20 +; srli a3, a1, 0x20 +; .byte 0x33, 0xd5, 0xd7, 0x0a +; ret + +function %umin_i64(i64, i64) -> i64{ +block0(v0: i64, v1: i64): + v2 = umin v0, v1 + return v2 +} + +; VCode: +; block0: +; minu a0,a0,a1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; .byte 0x33, 0x55, 0xb5, 0x0a +; ret + +function %umin_i128(i128, i128) -> i128{ +block0(v0: i128, v1: i128): + v2 = umin v0, v1 + return v2 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; sd s3,-8(sp) +; add sp,-16 +; block0: +; ult a5,[a0,a1],[a2,a3]##ty=i128 +; mv a4,a0 +; mv s3,a1 +; select [a0,a1],[a4,s3],[a2,a3]##condition=(a5 ne zero) +; add sp,+16 +; ld s3,-8(sp) +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; sd s3, -8(sp) +; addi sp, sp, -0x10 +; block1: ; offset 0x18 +; bltu a1, a3, 0xc +; bne a1, a3, 0x10 +; bgeu a0, a2, 0xc +; addi a5, zero, 1 +; j 8 +; mv a5, zero +; mv a4, a0 +; mv s3, a1 +; beqz a5, 0x10 +; mv a0, a4 +; mv a1, s3 +; j 0xc +; mv a0, a2 +; mv a1, a3 +; addi sp, sp, 0x10 +; ld s3, -8(sp) +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + From 946c21d9c01c3584761896b32cae4f74c4206ba4 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Wed, 4 Oct 2023 23:42:20 +0100 Subject: [PATCH 060/199] riscv64: Mark `fcmp` as already extended (#7151) --- cranelift/codegen/src/isa/riscv64/inst.isle | 5 +++-- .../filetests/isa/riscv64/extend.clif | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 569d5c26b865..e004d5dfaeb4 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -2154,9 +2154,10 @@ (rule 1 (val_already_extended (uextend _)) $true) (rule 1 (val_already_extended (sextend _)) $true) -;; The result of `icmp` is zero or one, meaning that it's already sign extended -;; to the full register width. +;; The result of `icmp`/`fcmp` is zero or one, meaning that it's already sign +;; extended to the full register width. (rule 1 (val_already_extended (icmp _ _ _)) $true) +(rule 1 (val_already_extended (fcmp _ _ _)) $true) (type ExtendOp (enum diff --git a/cranelift/filetests/filetests/isa/riscv64/extend.clif b/cranelift/filetests/filetests/isa/riscv64/extend.clif index 604e31ea5fe9..445671f2c8af 100644 --- a/cranelift/filetests/filetests/isa/riscv64/extend.clif +++ b/cranelift/filetests/filetests/isa/riscv64/extend.clif @@ -258,3 +258,21 @@ block0(v0: i8, v1: i64): ; add a0, a0, a1 ; ret + +function %extend_fcmp(f64, f64) -> i64 { +block0(v0: f64, v1: f64): + v3 = fcmp.f64 lt v0, v1 + v4 = sextend.i64 v3 + return v4 +} + +; VCode: +; block0: +; flt.d a0,fa0,fa1 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; flt.d a0, fa0, fa1 +; ret + From 7ef957a1ff9fdc5c542569aa2ebf8edde0360c0e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 4 Oct 2023 19:55:28 -0500 Subject: [PATCH 061/199] Fix printing error information in sync http tests (#7153) The usage of `?` accidentally caused the extra information to not get printed, so avoid the use of `?` until the extra error info has been attached. --- crates/test-programs/tests/wasi-http-components-sync.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/test-programs/tests/wasi-http-components-sync.rs b/crates/test-programs/tests/wasi-http-components-sync.rs index 1f5a196c361c..c54b98f7f421 100644 --- a/crates/test-programs/tests/wasi-http-components-sync.rs +++ b/crates/test-programs/tests/wasi-http-components-sync.rs @@ -87,11 +87,7 @@ fn run(name: &str, server: &Server) -> Result<()> { let http = WasiHttpCtx {}; let (mut store, command) = instantiate_component(component, Ctx { table, wasi, http })?; - command - .wasi_cli_run() - .call_run(&mut store)? - .map_err(|()| anyhow::anyhow!("run returned a failure"))?; - Ok(()) + command.wasi_cli_run().call_run(&mut store) }; r.map_err(move |trap: anyhow::Error| { let stdout = stdout.try_into_inner().expect("single ref to stdout"); @@ -106,7 +102,8 @@ fn run(name: &str, server: &Server) -> Result<()> { "error while testing wasi-tests {} with http-components-sync", name )) - })?; + })? + .map_err(|()| anyhow::anyhow!("run returned an error"))?; Ok(()) } From 157b4318df99651a0439289fa690b6f7554c0e3e Mon Sep 17 00:00:00 2001 From: wasmtime-publish <59749941+wasmtime-publish@users.noreply.github.com> Date: Thu, 5 Oct 2023 07:21:17 -0700 Subject: [PATCH 062/199] Bump Wasmtime to 15.0.0 (#7154) Co-authored-by: Wasmtime Publish --- Cargo.lock | 110 +++---- Cargo.toml | 92 +++--- RELEASES.md | 10 + cranelift/bforest/Cargo.toml | 2 +- cranelift/codegen/Cargo.toml | 8 +- cranelift/codegen/meta/Cargo.toml | 4 +- cranelift/codegen/shared/Cargo.toml | 2 +- cranelift/control/Cargo.toml | 2 +- cranelift/entity/Cargo.toml | 2 +- cranelift/frontend/Cargo.toml | 2 +- cranelift/interpreter/Cargo.toml | 2 +- cranelift/isle/isle/Cargo.toml | 2 +- cranelift/jit/Cargo.toml | 2 +- cranelift/module/Cargo.toml | 2 +- cranelift/native/Cargo.toml | 2 +- cranelift/object/Cargo.toml | 2 +- cranelift/reader/Cargo.toml | 2 +- cranelift/serde/Cargo.toml | 2 +- cranelift/umbrella/Cargo.toml | 2 +- cranelift/wasm/Cargo.toml | 2 +- crates/c-api/include/wasmtime.h | 4 +- supply-chain/imports.lock | 484 ++++++++++++++++++++++++++++ winch/codegen/Cargo.toml | 2 +- 23 files changed, 619 insertions(+), 125 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebde4e29ce0a..2cc0cbd7a9a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -226,7 +226,7 @@ checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-array-literals" -version = "14.0.0" +version = "15.0.0" [[package]] name = "byteorder" @@ -566,7 +566,7 @@ dependencies = [ [[package]] name = "cranelift" -version = "0.101.0" +version = "0.102.0" dependencies = [ "cranelift-codegen", "cranelift-frontend", @@ -574,14 +574,14 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.101.0" +version = "0.102.0" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.101.0" +version = "0.102.0" dependencies = [ "anyhow", "bincode", @@ -609,25 +609,25 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.101.0" +version = "0.102.0" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.101.0" +version = "0.102.0" [[package]] name = "cranelift-control" -version = "0.101.0" +version = "0.102.0" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.101.0" +version = "0.102.0" dependencies = [ "serde", "serde_derive", @@ -665,7 +665,7 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.101.0" +version = "0.102.0" dependencies = [ "cranelift-codegen", "hashbrown 0.14.0", @@ -689,7 +689,7 @@ dependencies = [ [[package]] name = "cranelift-interpreter" -version = "0.101.0" +version = "0.102.0" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -703,7 +703,7 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.101.0" +version = "0.102.0" dependencies = [ "codespan-reporting", "log", @@ -712,7 +712,7 @@ dependencies = [ [[package]] name = "cranelift-jit" -version = "0.101.0" +version = "0.102.0" dependencies = [ "anyhow", "cranelift", @@ -733,7 +733,7 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.101.0" +version = "0.102.0" dependencies = [ "anyhow", "cranelift-codegen", @@ -745,7 +745,7 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.101.0" +version = "0.102.0" dependencies = [ "cranelift-codegen", "libc", @@ -754,7 +754,7 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.101.0" +version = "0.102.0" dependencies = [ "anyhow", "cranelift-codegen", @@ -769,7 +769,7 @@ dependencies = [ [[package]] name = "cranelift-reader" -version = "0.101.0" +version = "0.102.0" dependencies = [ "anyhow", "cranelift-codegen", @@ -779,7 +779,7 @@ dependencies = [ [[package]] name = "cranelift-serde" -version = "0.101.0" +version = "0.102.0" dependencies = [ "clap", "cranelift-codegen", @@ -826,7 +826,7 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.101.0" +version = "0.102.0" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -2963,7 +2963,7 @@ dependencies = [ [[package]] name = "verify-component-adapter" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "wasmparser", @@ -3013,7 +3013,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi-cap-std-sync" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "async-trait", @@ -3035,7 +3035,7 @@ dependencies = [ [[package]] name = "wasi-common" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "bitflags 2.3.3", @@ -3068,7 +3068,7 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter" -version = "14.0.0" +version = "15.0.0" dependencies = [ "byte-array-literals", "object", @@ -3097,7 +3097,7 @@ dependencies = [ [[package]] name = "wasi-tokio" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "cap-std", @@ -3287,7 +3287,7 @@ dependencies = [ [[package]] name = "wasmtime" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "async-trait", @@ -3328,14 +3328,14 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" -version = "14.0.0" +version = "15.0.0" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-bench-api" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "cap-std", @@ -3352,7 +3352,7 @@ dependencies = [ [[package]] name = "wasmtime-c-api" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "cap-std", @@ -3377,7 +3377,7 @@ dependencies = [ [[package]] name = "wasmtime-cache" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "base64", @@ -3399,7 +3399,7 @@ dependencies = [ [[package]] name = "wasmtime-cli" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "async-trait", @@ -3451,7 +3451,7 @@ dependencies = [ [[package]] name = "wasmtime-cli-flags" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "clap", @@ -3464,7 +3464,7 @@ dependencies = [ [[package]] name = "wasmtime-component-macro" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "component-macro-test-helpers", @@ -3480,11 +3480,11 @@ dependencies = [ [[package]] name = "wasmtime-component-util" -version = "14.0.0" +version = "15.0.0" [[package]] name = "wasmtime-cranelift" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "cfg-if", @@ -3507,7 +3507,7 @@ dependencies = [ [[package]] name = "wasmtime-cranelift-shared" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "cranelift-codegen", @@ -3521,7 +3521,7 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "clap", @@ -3559,7 +3559,7 @@ dependencies = [ [[package]] name = "wasmtime-explorer" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "capstone", @@ -3573,7 +3573,7 @@ dependencies = [ [[package]] name = "wasmtime-fiber" -version = "14.0.0" +version = "15.0.0" dependencies = [ "backtrace", "cc", @@ -3641,7 +3641,7 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "14.0.0" +version = "15.0.0" dependencies = [ "addr2line", "anyhow", @@ -3666,7 +3666,7 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "14.0.0" +version = "15.0.0" dependencies = [ "object", "once_cell", @@ -3676,7 +3676,7 @@ dependencies = [ [[package]] name = "wasmtime-jit-icache-coherence" -version = "14.0.0" +version = "15.0.0" dependencies = [ "cfg-if", "libc", @@ -3685,7 +3685,7 @@ dependencies = [ [[package]] name = "wasmtime-runtime" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "cc", @@ -3715,7 +3715,7 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "14.0.0" +version = "15.0.0" dependencies = [ "cranelift-entity", "serde", @@ -3726,7 +3726,7 @@ dependencies = [ [[package]] name = "wasmtime-versioned-export-macros" -version = "14.0.0" +version = "15.0.0" dependencies = [ "proc-macro2", "quote", @@ -3735,7 +3735,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "async-trait", @@ -3771,7 +3771,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi-http" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "async-trait", @@ -3792,7 +3792,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi-nn" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "openvino", @@ -3805,7 +3805,7 @@ dependencies = [ [[package]] name = "wasmtime-wasi-threads" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "log", @@ -3817,7 +3817,7 @@ dependencies = [ [[package]] name = "wasmtime-wast" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "log", @@ -3827,7 +3827,7 @@ dependencies = [ [[package]] name = "wasmtime-winch" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "cranelift-codegen", @@ -3842,7 +3842,7 @@ dependencies = [ [[package]] name = "wasmtime-wit-bindgen" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "heck", @@ -3852,7 +3852,7 @@ dependencies = [ [[package]] name = "wasmtime-wmemcheck" -version = "14.0.0" +version = "15.0.0" [[package]] name = "wast" @@ -3913,7 +3913,7 @@ dependencies = [ [[package]] name = "wiggle" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "async-trait", @@ -3930,7 +3930,7 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "14.0.0" +version = "15.0.0" dependencies = [ "anyhow", "heck", @@ -3943,7 +3943,7 @@ dependencies = [ [[package]] name = "wiggle-macro" -version = "14.0.0" +version = "15.0.0" dependencies = [ "proc-macro2", "quote", @@ -3998,7 +3998,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "0.12.0" +version = "0.13.0" dependencies = [ "anyhow", "cranelift-codegen", diff --git a/Cargo.toml b/Cargo.toml index d4cc8fc06a95..fd17a28ef37d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,7 +128,7 @@ exclude = [ ] [workspace.package] -version = "14.0.0" +version = "15.0.0" authors = ["The Wasmtime Project Developers"] edition = "2021" # Wasmtime's current policy is that this number can be no larger than the @@ -136,58 +136,58 @@ edition = "2021" rust-version = "1.70.0" [workspace.dependencies] -wasmtime-wmemcheck = { path = "crates/wmemcheck", version = "=14.0.0" } -wasmtime = { path = "crates/wasmtime", version = "14.0.0", default-features = false } -wasmtime-cache = { path = "crates/cache", version = "=14.0.0" } -wasmtime-cli-flags = { path = "crates/cli-flags", version = "=14.0.0" } -wasmtime-cranelift = { path = "crates/cranelift", version = "=14.0.0" } -wasmtime-cranelift-shared = { path = "crates/cranelift-shared", version = "=14.0.0" } -wasmtime-winch = { path = "crates/winch", version = "=14.0.0" } -wasmtime-environ = { path = "crates/environ", version = "=14.0.0" } -wasmtime-explorer = { path = "crates/explorer", version = "=14.0.0" } -wasmtime-fiber = { path = "crates/fiber", version = "=14.0.0" } -wasmtime-types = { path = "crates/types", version = "14.0.0" } -wasmtime-jit = { path = "crates/jit", version = "=14.0.0" } -wasmtime-jit-debug = { path = "crates/jit-debug", version = "=14.0.0" } -wasmtime-runtime = { path = "crates/runtime", version = "=14.0.0" } -wasmtime-wast = { path = "crates/wast", version = "=14.0.0" } -wasmtime-wasi = { path = "crates/wasi", version = "14.0.0", default-features = false } -wasmtime-wasi-http = { path = "crates/wasi-http", version = "=14.0.0", default-features = false } -wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "14.0.0" } -wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "14.0.0" } -wasmtime-component-util = { path = "crates/component-util", version = "=14.0.0" } -wasmtime-component-macro = { path = "crates/component-macro", version = "=14.0.0" } -wasmtime-asm-macros = { path = "crates/asm-macros", version = "=14.0.0" } -wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", version = "=14.0.0" } +wasmtime-wmemcheck = { path = "crates/wmemcheck", version = "=15.0.0" } +wasmtime = { path = "crates/wasmtime", version = "15.0.0", default-features = false } +wasmtime-cache = { path = "crates/cache", version = "=15.0.0" } +wasmtime-cli-flags = { path = "crates/cli-flags", version = "=15.0.0" } +wasmtime-cranelift = { path = "crates/cranelift", version = "=15.0.0" } +wasmtime-cranelift-shared = { path = "crates/cranelift-shared", version = "=15.0.0" } +wasmtime-winch = { path = "crates/winch", version = "=15.0.0" } +wasmtime-environ = { path = "crates/environ", version = "=15.0.0" } +wasmtime-explorer = { path = "crates/explorer", version = "=15.0.0" } +wasmtime-fiber = { path = "crates/fiber", version = "=15.0.0" } +wasmtime-types = { path = "crates/types", version = "15.0.0" } +wasmtime-jit = { path = "crates/jit", version = "=15.0.0" } +wasmtime-jit-debug = { path = "crates/jit-debug", version = "=15.0.0" } +wasmtime-runtime = { path = "crates/runtime", version = "=15.0.0" } +wasmtime-wast = { path = "crates/wast", version = "=15.0.0" } +wasmtime-wasi = { path = "crates/wasi", version = "15.0.0", default-features = false } +wasmtime-wasi-http = { path = "crates/wasi-http", version = "=15.0.0", default-features = false } +wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "15.0.0" } +wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "15.0.0" } +wasmtime-component-util = { path = "crates/component-util", version = "=15.0.0" } +wasmtime-component-macro = { path = "crates/component-macro", version = "=15.0.0" } +wasmtime-asm-macros = { path = "crates/asm-macros", version = "=15.0.0" } +wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", version = "=15.0.0" } component-test-util = { path = "crates/misc/component-test-util" } component-fuzz-util = { path = "crates/misc/component-fuzz-util" } -wiggle = { path = "crates/wiggle", version = "=14.0.0", default-features = false } -wiggle-macro = { path = "crates/wiggle/macro", version = "=14.0.0" } -wiggle-generate = { path = "crates/wiggle/generate", version = "=14.0.0" } -wasi-common = { path = "crates/wasi-common", version = "=14.0.0" } -wasi-tokio = { path = "crates/wasi-common/tokio", version = "=14.0.0" } -wasi-cap-std-sync = { path = "crates/wasi-common/cap-std-sync", version = "=14.0.0" } +wiggle = { path = "crates/wiggle", version = "=15.0.0", default-features = false } +wiggle-macro = { path = "crates/wiggle/macro", version = "=15.0.0" } +wiggle-generate = { path = "crates/wiggle/generate", version = "=15.0.0" } +wasi-common = { path = "crates/wasi-common", version = "=15.0.0" } +wasi-tokio = { path = "crates/wasi-common/tokio", version = "=15.0.0" } +wasi-cap-std-sync = { path = "crates/wasi-common/cap-std-sync", version = "=15.0.0" } wasmtime-fuzzing = { path = "crates/fuzzing" } -wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=14.0.0" } -wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=14.0.0" } +wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=15.0.0" } +wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=15.0.0" } -cranelift-wasm = { path = "cranelift/wasm", version = "0.101.0" } -cranelift-codegen = { path = "cranelift/codegen", version = "0.101.0" } -cranelift-frontend = { path = "cranelift/frontend", version = "0.101.0" } -cranelift-entity = { path = "cranelift/entity", version = "0.101.0" } -cranelift-native = { path = "cranelift/native", version = "0.101.0" } -cranelift-module = { path = "cranelift/module", version = "0.101.0" } -cranelift-interpreter = { path = "cranelift/interpreter", version = "0.101.0" } -cranelift-reader = { path = "cranelift/reader", version = "0.101.0" } +cranelift-wasm = { path = "cranelift/wasm", version = "0.102.0" } +cranelift-codegen = { path = "cranelift/codegen", version = "0.102.0" } +cranelift-frontend = { path = "cranelift/frontend", version = "0.102.0" } +cranelift-entity = { path = "cranelift/entity", version = "0.102.0" } +cranelift-native = { path = "cranelift/native", version = "0.102.0" } +cranelift-module = { path = "cranelift/module", version = "0.102.0" } +cranelift-interpreter = { path = "cranelift/interpreter", version = "0.102.0" } +cranelift-reader = { path = "cranelift/reader", version = "0.102.0" } cranelift-filetests = { path = "cranelift/filetests" } -cranelift-object = { path = "cranelift/object", version = "0.101.0" } -cranelift-jit = { path = "cranelift/jit", version = "0.101.0" } +cranelift-object = { path = "cranelift/object", version = "0.102.0" } +cranelift-jit = { path = "cranelift/jit", version = "0.102.0" } cranelift-fuzzgen = { path = "cranelift/fuzzgen" } -cranelift-bforest = { path = "cranelift/bforest", version = "0.101.0" } -cranelift-control = { path = "cranelift/control", version = "0.101.0" } -cranelift = { path = "cranelift/umbrella", version = "0.101.0" } +cranelift-bforest = { path = "cranelift/bforest", version = "0.102.0" } +cranelift-control = { path = "cranelift/control", version = "0.102.0" } +cranelift = { path = "cranelift/umbrella", version = "0.102.0" } -winch-codegen = { path = "winch/codegen", version = "=0.12.0" } +winch-codegen = { path = "winch/codegen", version = "=0.13.0" } winch-filetests = { path = "winch/filetests" } winch-test-macros = { path = "winch/test-macros" } diff --git a/RELEASES.md b/RELEASES.md index cc89c3f2231c..da9c72e82d86 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,15 @@ -------------------------------------------------------------------------------- +## 15.0.0 + +Unreleased. + +### Added + +### Changed + +-------------------------------------------------------------------------------- + ## 14.0.0 Unreleased. diff --git a/cranelift/bforest/Cargo.toml b/cranelift/bforest/Cargo.toml index d55fdad495ee..f620eb757029 100644 --- a/cranelift/bforest/Cargo.toml +++ b/cranelift/bforest/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-bforest" -version = "0.101.0" +version = "0.102.0" description = "A forest of B+-trees" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-bforest" diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml index 640444c9c959..3402550d0757 100644 --- a/cranelift/codegen/Cargo.toml +++ b/cranelift/codegen/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-codegen" -version = "0.101.0" +version = "0.102.0" description = "Low-level code generator library" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-codegen" @@ -16,7 +16,7 @@ edition.workspace = true anyhow = { workspace = true, optional = true } bumpalo = "3" capstone = { workspace = true, optional = true } -cranelift-codegen-shared = { path = "./shared", version = "0.101.0" } +cranelift-codegen-shared = { path = "./shared", version = "0.102.0" } cranelift-entity = { workspace = true } cranelift-bforest = { workspace = true } cranelift-control = { workspace = true } @@ -41,8 +41,8 @@ criterion = { version = "0.5.0", features = ["html_reports"] } similar = "2.1.0" [build-dependencies] -cranelift-codegen-meta = { path = "meta", version = "0.101.0" } -cranelift-isle = { path = "../isle/isle", version = "=0.101.0" } +cranelift-codegen-meta = { path = "meta", version = "0.102.0" } +cranelift-isle = { path = "../isle/isle", version = "=0.102.0" } [features] default = ["std", "unwind", "host-arch"] diff --git a/cranelift/codegen/meta/Cargo.toml b/cranelift/codegen/meta/Cargo.toml index 4b930571018d..f507cc873bbe 100644 --- a/cranelift/codegen/meta/Cargo.toml +++ b/cranelift/codegen/meta/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cranelift-codegen-meta" authors = ["The Cranelift Project Developers"] -version = "0.101.0" +version = "0.102.0" description = "Metaprogram for cranelift-codegen code generator library" license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" @@ -12,4 +12,4 @@ edition.workspace = true rustdoc-args = [ "--document-private-items" ] [dependencies] -cranelift-codegen-shared = { path = "../shared", version = "0.101.0" } +cranelift-codegen-shared = { path = "../shared", version = "0.102.0" } diff --git a/cranelift/codegen/shared/Cargo.toml b/cranelift/codegen/shared/Cargo.toml index 3575420fa117..d68f66091609 100644 --- a/cranelift/codegen/shared/Cargo.toml +++ b/cranelift/codegen/shared/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-codegen-shared" -version = "0.101.0" +version = "0.102.0" description = "For code shared between cranelift-codegen-meta and cranelift-codegen" license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/control/Cargo.toml b/cranelift/control/Cargo.toml index 006afd14420d..594d4de67ec2 100644 --- a/cranelift/control/Cargo.toml +++ b/cranelift/control/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-control" -version = "0.101.0" +version = "0.102.0" description = "White-box fuzz testing framework" license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/entity/Cargo.toml b/cranelift/entity/Cargo.toml index 5740ce49538f..c0f08dde9847 100644 --- a/cranelift/entity/Cargo.toml +++ b/cranelift/entity/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-entity" -version = "0.101.0" +version = "0.102.0" description = "Data structures using entity references as mapping keys" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-entity" diff --git a/cranelift/frontend/Cargo.toml b/cranelift/frontend/Cargo.toml index fffa87065d7b..7c804f44ccb7 100644 --- a/cranelift/frontend/Cargo.toml +++ b/cranelift/frontend/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-frontend" -version = "0.101.0" +version = "0.102.0" description = "Cranelift IR builder helper" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-frontend" diff --git a/cranelift/interpreter/Cargo.toml b/cranelift/interpreter/Cargo.toml index 01da71ed791e..728148e49ef0 100644 --- a/cranelift/interpreter/Cargo.toml +++ b/cranelift/interpreter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-interpreter" -version = "0.101.0" +version = "0.102.0" authors = ["The Cranelift Project Developers"] description = "Interpret Cranelift IR" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/isle/isle/Cargo.toml b/cranelift/isle/isle/Cargo.toml index db802bf0f06d..44d555583118 100644 --- a/cranelift/isle/isle/Cargo.toml +++ b/cranelift/isle/isle/Cargo.toml @@ -6,7 +6,7 @@ license = "Apache-2.0 WITH LLVM-exception" name = "cranelift-isle" readme = "../README.md" repository = "https://github.com/bytecodealliance/wasmtime/tree/main/cranelift/isle" -version = "0.101.0" +version = "0.102.0" [dependencies] codespan-reporting = { version = "0.11.1", optional = true } diff --git a/cranelift/jit/Cargo.toml b/cranelift/jit/Cargo.toml index 0d367c7a32cd..eb137461b220 100644 --- a/cranelift/jit/Cargo.toml +++ b/cranelift/jit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-jit" -version = "0.101.0" +version = "0.102.0" authors = ["The Cranelift Project Developers"] description = "A JIT library backed by Cranelift" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/module/Cargo.toml b/cranelift/module/Cargo.toml index e89a1e20ed61..ad92ca3e5465 100644 --- a/cranelift/module/Cargo.toml +++ b/cranelift/module/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-module" -version = "0.101.0" +version = "0.102.0" authors = ["The Cranelift Project Developers"] description = "Support for linking functions and data with Cranelift" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/native/Cargo.toml b/cranelift/native/Cargo.toml index b9556a8c589d..e9410c36e125 100644 --- a/cranelift/native/Cargo.toml +++ b/cranelift/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-native" -version = "0.101.0" +version = "0.102.0" authors = ["The Cranelift Project Developers"] description = "Support for targeting the host with Cranelift" documentation = "https://docs.rs/cranelift-native" diff --git a/cranelift/object/Cargo.toml b/cranelift/object/Cargo.toml index 5b3260dc950f..33acc617c6ae 100644 --- a/cranelift/object/Cargo.toml +++ b/cranelift/object/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-object" -version = "0.101.0" +version = "0.102.0" authors = ["The Cranelift Project Developers"] description = "Emit Cranelift output to native object files with `object`" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/reader/Cargo.toml b/cranelift/reader/Cargo.toml index 36f77d6cb7fb..b45218fa0122 100644 --- a/cranelift/reader/Cargo.toml +++ b/cranelift/reader/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift-reader" -version = "0.101.0" +version = "0.102.0" description = "Cranelift textual IR reader" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift-reader" diff --git a/cranelift/serde/Cargo.toml b/cranelift/serde/Cargo.toml index e19cdfaa6826..c2e10d7ce7ec 100644 --- a/cranelift/serde/Cargo.toml +++ b/cranelift/serde/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-serde" -version = "0.101.0" +version = "0.102.0" authors = ["The Cranelift Project Developers"] description = "Serializer/Deserializer for Cranelift IR" repository = "https://github.com/bytecodealliance/wasmtime" diff --git a/cranelift/umbrella/Cargo.toml b/cranelift/umbrella/Cargo.toml index b3ed56f1404c..4c84099717ab 100644 --- a/cranelift/umbrella/Cargo.toml +++ b/cranelift/umbrella/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cranelift Project Developers"] name = "cranelift" -version = "0.101.0" +version = "0.102.0" description = "Umbrella for commonly-used cranelift crates" license = "Apache-2.0 WITH LLVM-exception" documentation = "https://docs.rs/cranelift" diff --git a/cranelift/wasm/Cargo.toml b/cranelift/wasm/Cargo.toml index fcac5021bcc5..b4b98d5a68c6 100644 --- a/cranelift/wasm/Cargo.toml +++ b/cranelift/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cranelift-wasm" -version = "0.101.0" +version = "0.102.0" authors = ["The Cranelift Project Developers"] description = "Translator from WebAssembly to Cranelift IR" documentation = "https://docs.rs/cranelift-wasm" diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 32c0478e5753..44e4e3d2c3c6 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -200,11 +200,11 @@ /** * \brief Wasmtime version string. */ -#define WASMTIME_VERSION "14.0.0" +#define WASMTIME_VERSION "15.0.0" /** * \brief Wasmtime major version number. */ -#define WASMTIME_VERSION_MAJOR 14 +#define WASMTIME_VERSION_MAJOR 15 /** * \brief Wasmtime minor version number. */ diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index c1301305ab8d..7857d0a0651b 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -9,6 +9,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-bforest]] version = "0.100.0" audited_as = "0.98.1" @@ -17,6 +21,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-bforest]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-codegen]] version = "0.100.0" audited_as = "0.98.1" @@ -25,6 +33,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-codegen]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-codegen-meta]] version = "0.100.0" audited_as = "0.98.1" @@ -33,6 +45,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-codegen-meta]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-codegen-shared]] version = "0.100.0" audited_as = "0.98.1" @@ -41,6 +57,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-codegen-shared]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-control]] version = "0.100.0" audited_as = "0.98.1" @@ -49,6 +69,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-control]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-entity]] version = "0.100.0" audited_as = "0.98.1" @@ -57,6 +81,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-entity]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-frontend]] version = "0.100.0" audited_as = "0.98.1" @@ -65,6 +93,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-frontend]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-interpreter]] version = "0.100.0" audited_as = "0.98.1" @@ -73,6 +105,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-interpreter]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-isle]] version = "0.100.0" audited_as = "0.98.1" @@ -81,6 +117,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-isle]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-jit]] version = "0.100.0" audited_as = "0.98.1" @@ -89,6 +129,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-jit]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-module]] version = "0.100.0" audited_as = "0.98.1" @@ -97,6 +141,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-module]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-native]] version = "0.100.0" audited_as = "0.98.1" @@ -105,6 +153,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-native]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-object]] version = "0.100.0" audited_as = "0.98.1" @@ -113,6 +165,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-object]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-reader]] version = "0.100.0" audited_as = "0.98.1" @@ -121,6 +177,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-reader]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-serde]] version = "0.100.0" audited_as = "0.98.1" @@ -129,6 +189,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-serde]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.cranelift-wasm]] version = "0.100.0" audited_as = "0.98.1" @@ -137,6 +201,10 @@ audited_as = "0.98.1" version = "0.101.0" audited_as = "0.99.1" +[[unpublished.cranelift-wasm]] +version = "0.102.0" +audited_as = "0.100.0" + [[unpublished.wasi-cap-std-sync]] version = "13.0.0" audited_as = "11.0.1" @@ -145,6 +213,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasi-cap-std-sync]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasi-common]] version = "13.0.0" audited_as = "11.0.1" @@ -153,6 +225,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasi-common]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasi-tokio]] version = "13.0.0" audited_as = "11.0.1" @@ -161,6 +237,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasi-tokio]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime]] version = "13.0.0" audited_as = "11.0.1" @@ -169,6 +249,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-asm-macros]] version = "13.0.0" audited_as = "11.0.1" @@ -177,6 +261,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-asm-macros]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-cache]] version = "13.0.0" audited_as = "11.0.1" @@ -185,6 +273,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-cache]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-cli]] version = "13.0.0" audited_as = "11.0.1" @@ -193,6 +285,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-cli]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-cli-flags]] version = "13.0.0" audited_as = "11.0.1" @@ -201,6 +297,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-cli-flags]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-component-macro]] version = "13.0.0" audited_as = "11.0.1" @@ -209,6 +309,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-component-macro]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-component-util]] version = "13.0.0" audited_as = "11.0.1" @@ -217,6 +321,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-component-util]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-cranelift]] version = "13.0.0" audited_as = "11.0.1" @@ -225,6 +333,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-cranelift]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-cranelift-shared]] version = "13.0.0" audited_as = "11.0.1" @@ -233,6 +345,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-cranelift-shared]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-environ]] version = "13.0.0" audited_as = "11.0.1" @@ -241,6 +357,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-environ]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-explorer]] version = "13.0.0" audited_as = "11.0.1" @@ -249,6 +369,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-explorer]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-fiber]] version = "13.0.0" audited_as = "11.0.1" @@ -257,6 +381,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-fiber]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-jit]] version = "13.0.0" audited_as = "11.0.1" @@ -265,6 +393,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-jit]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-jit-debug]] version = "13.0.0" audited_as = "11.0.1" @@ -273,6 +405,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-jit-debug]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-jit-icache-coherence]] version = "13.0.0" audited_as = "11.0.1" @@ -281,6 +417,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-jit-icache-coherence]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-runtime]] version = "13.0.0" audited_as = "11.0.1" @@ -289,6 +429,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-runtime]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-types]] version = "13.0.0" audited_as = "11.0.1" @@ -297,6 +441,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-types]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-wasi]] version = "13.0.0" audited_as = "11.0.1" @@ -305,6 +453,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-wasi]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-wasi-http]] version = "13.0.0" audited_as = "11.0.1" @@ -313,6 +465,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-wasi-http]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-wasi-nn]] version = "13.0.0" audited_as = "11.0.1" @@ -321,6 +477,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-wasi-nn]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-wasi-threads]] version = "13.0.0" audited_as = "11.0.1" @@ -329,6 +489,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-wasi-threads]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-wast]] version = "13.0.0" audited_as = "11.0.1" @@ -337,6 +501,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-wast]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-winch]] version = "13.0.0" audited_as = "11.0.1" @@ -345,6 +513,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-winch]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-wit-bindgen]] version = "13.0.0" audited_as = "11.0.1" @@ -353,10 +525,18 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wasmtime-wit-bindgen]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wasmtime-wmemcheck]] version = "14.0.0" audited_as = "13.0.0" +[[unpublished.wasmtime-wmemcheck]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wiggle]] version = "13.0.0" audited_as = "11.0.1" @@ -365,6 +545,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wiggle]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wiggle-generate]] version = "13.0.0" audited_as = "11.0.1" @@ -373,6 +557,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wiggle-generate]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wiggle-macro]] version = "13.0.0" audited_as = "11.0.1" @@ -381,6 +569,10 @@ audited_as = "11.0.1" version = "14.0.0" audited_as = "12.0.1" +[[unpublished.wiggle-macro]] +version = "15.0.0" +audited_as = "13.0.0" + [[unpublished.wiggle-test]] version = "0.0.0" audited_as = "0.1.0" @@ -393,6 +585,10 @@ audited_as = "0.9.1" version = "0.12.0" audited_as = "0.10.1" +[[unpublished.winch-codegen]] +version = "0.13.0" +audited_as = "0.11.0" + [[publisher.aho-corasick]] version = "1.0.2" when = "2023-06-04" @@ -580,6 +776,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-bforest]] version = "0.98.1" when = "2023-07-24" @@ -592,6 +794,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-bforest]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-codegen]] version = "0.98.1" when = "2023-07-24" @@ -604,6 +812,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-codegen]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-codegen-meta]] version = "0.98.1" when = "2023-07-24" @@ -616,6 +830,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-codegen-meta]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-codegen-shared]] version = "0.98.1" when = "2023-07-24" @@ -628,6 +848,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-codegen-shared]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-control]] version = "0.98.1" when = "2023-07-24" @@ -640,6 +866,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-control]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-entity]] version = "0.98.1" when = "2023-07-24" @@ -652,6 +884,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-entity]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-frontend]] version = "0.98.1" when = "2023-07-24" @@ -664,6 +902,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-frontend]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-interpreter]] version = "0.98.1" when = "2023-07-24" @@ -676,6 +920,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-interpreter]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-isle]] version = "0.98.1" when = "2023-07-24" @@ -688,6 +938,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-isle]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-jit]] version = "0.98.1" when = "2023-07-24" @@ -700,6 +956,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-jit]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-module]] version = "0.98.1" when = "2023-07-24" @@ -712,6 +974,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-module]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-native]] version = "0.98.1" when = "2023-07-24" @@ -724,6 +992,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-native]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-object]] version = "0.98.1" when = "2023-07-24" @@ -736,6 +1010,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-object]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-reader]] version = "0.98.1" when = "2023-07-24" @@ -748,6 +1028,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-reader]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-serde]] version = "0.98.1" when = "2023-07-24" @@ -760,6 +1046,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-serde]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.cranelift-wasm]] version = "0.98.1" when = "2023-07-24" @@ -772,6 +1064,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.cranelift-wasm]] +version = "0.100.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.derive_arbitrary]] version = "1.3.0" when = "2023-03-13" @@ -1119,6 +1417,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasi-cap-std-sync]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasi-common]] version = "11.0.1" when = "2023-07-24" @@ -1131,6 +1435,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasi-common]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasi-tokio]] version = "11.0.1" when = "2023-07-24" @@ -1143,6 +1453,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasi-tokio]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasm-bindgen]] version = "0.2.87" when = "2023-06-12" @@ -1414,6 +1730,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-asm-macros]] version = "11.0.1" when = "2023-07-24" @@ -1426,6 +1748,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-asm-macros]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-cache]] version = "11.0.1" when = "2023-07-24" @@ -1438,6 +1766,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-cache]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-cli]] version = "11.0.1" when = "2023-07-24" @@ -1450,6 +1784,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-cli]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-cli-flags]] version = "11.0.1" when = "2023-07-24" @@ -1462,6 +1802,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-cli-flags]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-component-macro]] version = "11.0.1" when = "2023-07-24" @@ -1474,6 +1820,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-component-macro]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-component-util]] version = "11.0.1" when = "2023-07-24" @@ -1486,6 +1838,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-component-util]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-cranelift]] version = "11.0.1" when = "2023-07-24" @@ -1498,6 +1856,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-cranelift]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-cranelift-shared]] version = "11.0.1" when = "2023-07-24" @@ -1510,6 +1874,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-cranelift-shared]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-environ]] version = "11.0.1" when = "2023-07-24" @@ -1522,6 +1892,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-environ]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-explorer]] version = "11.0.1" when = "2023-07-24" @@ -1534,6 +1910,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-explorer]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-fiber]] version = "11.0.1" when = "2023-07-24" @@ -1546,6 +1928,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-fiber]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-jit]] version = "11.0.1" when = "2023-07-24" @@ -1558,6 +1946,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-jit]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-jit-debug]] version = "11.0.1" when = "2023-07-24" @@ -1570,6 +1964,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-jit-debug]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-jit-icache-coherence]] version = "11.0.1" when = "2023-07-24" @@ -1582,6 +1982,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-jit-icache-coherence]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-runtime]] version = "11.0.1" when = "2023-07-24" @@ -1594,6 +2000,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-runtime]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-types]] version = "11.0.1" when = "2023-07-24" @@ -1606,6 +2018,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-types]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-wasi]] version = "11.0.1" when = "2023-07-24" @@ -1618,6 +2036,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-wasi]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-wasi-http]] version = "11.0.1" when = "2023-07-24" @@ -1630,6 +2054,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-wasi-http]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-wasi-nn]] version = "11.0.1" when = "2023-07-24" @@ -1642,6 +2072,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-wasi-nn]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-wasi-threads]] version = "11.0.1" when = "2023-07-24" @@ -1654,6 +2090,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-wasi-threads]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-wast]] version = "11.0.1" when = "2023-07-24" @@ -1666,6 +2108,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-wast]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-winch]] version = "11.0.1" when = "2023-07-24" @@ -1678,6 +2126,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-winch]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-wit-bindgen]] version = "11.0.1" when = "2023-07-24" @@ -1690,6 +2144,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wasmtime-wit-bindgen]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wasmtime-wmemcheck]] version = "13.0.0" when = "2023-09-20" @@ -1778,6 +2238,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wiggle]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wiggle-generate]] version = "11.0.1" when = "2023-07-24" @@ -1790,6 +2256,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wiggle-generate]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wiggle-macro]] version = "11.0.1" when = "2023-07-24" @@ -1802,6 +2274,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.wiggle-macro]] +version = "13.0.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.wiggle-test]] version = "0.1.0" when = "2020-03-12" @@ -1828,6 +2306,12 @@ when = "2023-08-24" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.winch-codegen]] +version = "0.11.0" +when = "2023-09-20" +user-id = 73222 +user-login = "wasmtime-publish" + [[publisher.windows-sys]] version = "0.48.0" when = "2023-03-31" diff --git a/winch/codegen/Cargo.toml b/winch/codegen/Cargo.toml index 63f186fe71d7..db0365ebb944 100644 --- a/winch/codegen/Cargo.toml +++ b/winch/codegen/Cargo.toml @@ -4,7 +4,7 @@ name = "winch-codegen" description = "Winch code generation library" license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" -version = "0.12.0" +version = "0.13.0" edition.workspace = true [dependencies] From 41cff15c50a77f3909cf9e8764d617b9f1d22282 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 5 Oct 2023 09:56:57 -0500 Subject: [PATCH 063/199] riscv64: Move `sextend` optimizations into `sext` (#7156) This commit moves the optimizations from `sextend` for various shapes of instructions into the `sext` helper. This enables these optimizations to kick in when generating other instructions which require sign-extensions such as `icmp` and `brif`. This additionally fixes an issue introduced in #7121 where uextend-of-sextend wasn't translated correctly and the outer `uextend` was mistakenly omitted. --- cranelift/codegen/src/isa/riscv64/inst.isle | 27 +- cranelift/codegen/src/isa/riscv64/lower.isle | 46 +- .../filetests/isa/riscv64/arithmetic.clif | 4 +- .../filetests/isa/riscv64/br_table.clif | 4 +- .../filetests/isa/riscv64/issue-6954.clif | 584 ++++++++---------- .../isa/riscv64/narrow-arithmetic.clif | 16 +- .../filetests/filetests/runtests/extend.clif | 18 + 7 files changed, 322 insertions(+), 377 deletions(-) diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index e004d5dfaeb4..7ea81927dad5 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -2110,7 +2110,7 @@ ;; Ignore sign extensions for values whose representation is already the full ;; register width. (rule 3 (zext val) - (if (val_already_extended val)) + (if (val_already_extended (ExtendOp.Zero) val)) val) ;; Performs a signed extension of the given value @@ -2139,25 +2139,36 @@ ;; Ignore sign extensions for values whose representation is already the full ;; register width. (rule 2 (sext val) - (if (val_already_extended val)) + (if (val_already_extended (ExtendOp.Signed) val)) val) ;; Helper matcher for when a value's representation is already sign or zero ;; extended to the full 64-bit register representation. This is used by `zext` ;; and `sext` above to skip the extension instruction entirely in some ;; circumstances. -(decl pure partial val_already_extended (Value) bool) -(rule 0 (val_already_extended v @ (value_type $I64)) $true) +(decl pure partial val_already_extended (ExtendOp Value) bool) +(rule 0 (val_already_extended _ v @ (value_type $I64)) $true) ;; When extending our backend always extends to the full register width, so ;; there's no need to extend-an-extend. -(rule 1 (val_already_extended (uextend _)) $true) -(rule 1 (val_already_extended (sextend _)) $true) +(rule 1 (val_already_extended (ExtendOp.Zero) (uextend _)) $true) +(rule 1 (val_already_extended (ExtendOp.Signed) (sextend _)) $true) ;; The result of `icmp`/`fcmp` is zero or one, meaning that it's already sign ;; extended to the full register width. -(rule 1 (val_already_extended (icmp _ _ _)) $true) -(rule 1 (val_already_extended (fcmp _ _ _)) $true) +(rule 1 (val_already_extended _ (icmp _ _ _)) $true) +(rule 1 (val_already_extended _ (fcmp _ _ _)) $true) + +;; The lowering for these operations always sign-extend their results due to the +;; use of the `*w` instructions in RV64I. Note that this requires that the +;; extension is from 32 to 64, 16/8-bit operations are explicitly excluded here. +;; There are no native instructions for the 16/8 bit operations so they must +;; fall through to actual sign extension above. +(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (ishl _ _))) $true) +(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (ushr _ _))) $true) +(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (sshr _ _))) $true) +(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (iadd _ _))) $true) +(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (isub _ _))) $true) (type ExtendOp (enum diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index 03ee2a93d234..53a2d65f6a05 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -33,7 +33,10 @@ ;;;; Rules for `iadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Base case, simply adding things in registers. -(rule 0 (lower (has_type (ty_int_ref_scalar_64 ty) (iadd x y))) +(rule -1 (lower (has_type (fits_in_32 (ty_int ty)) (iadd x y))) + (rv_addw x y)) + +(rule 0 (lower (has_type $I64 (iadd x y))) (rv_add x y)) ;; Special cases for when one operand is an immediate that fits in 12 bits. @@ -312,12 +315,12 @@ ;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Base case, simply subtracting things in registers. -(rule (lower (has_type (ty_int_ref_scalar_64 ty) (isub x y))) - (rv_sub x y)) - -(rule 1 (lower (has_type (fits_in_32 (ty_int ty)) (isub x y))) +(rule 0 (lower (has_type (fits_in_32 (ty_int ty)) (isub x y))) (rv_subw x y)) +(rule 1 (lower (has_type $I64 (isub x y))) + (rv_sub x y)) + (rule 2 (lower (has_type $I128 (isub x y))) (i128_sub x y)) @@ -962,39 +965,6 @@ (let ((lo XReg (sext val))) (value_regs lo (rv_srai lo (imm12_const 63))))) -;; The instructions below are present in RV64I and sign-extend the result to 64 bits. - -(rule 1 (lower (has_type $I64 (sextend (has_type $I32 (iadd x y))))) - (rv_addw x y)) - -(rule 1 (lower (has_type $I64 (sextend (has_type $I32 (isub x y))))) - (rv_subw x y)) - -(rule 1 (lower (has_type $I64 (sextend (has_type $I32 (ishl x y))))) - (rv_sllw x (value_regs_get y 0))) - -(rule 1 (lower (has_type $I64 (sextend (has_type $I32 (ushr x y))))) - (rv_srlw x (value_regs_get y 0))) - -(rule 1 (lower (has_type $I64 (sextend (has_type $I32 (sshr x y))))) - (rv_sraw x (value_regs_get y 0))) - - -(rule 2 (lower (has_type $I64 (sextend (has_type $I32 (iadd x (imm12_from_value y)))))) - (rv_addiw x y)) - -(rule 3 (lower (has_type $I64 (sextend (has_type $I32 (iadd (imm12_from_value x) y))))) - (rv_addiw y x)) - -(rule 2 (lower (has_type $I64 (sextend (has_type $I32 (ishl x (imm12_from_value y)))))) - (rv_slliw x y)) - -(rule 2 (lower (has_type $I64 (sextend (has_type $I32 (ushr x (imm12_from_value y)))))) - (rv_srliw x y)) - -(rule 2 (lower (has_type $I64 (sextend (has_type $I32 (sshr x (imm12_from_value y)))))) - (rv_sraiw x y)) - ;;;; Rules for `popcnt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule 0 (lower (has_type (fits_in_64 _) (popcnt x))) diff --git a/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif b/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif index a34749817fd3..070d7e178b0e 100644 --- a/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif +++ b/cranelift/filetests/filetests/isa/riscv64/arithmetic.clif @@ -618,13 +618,13 @@ block0(v0: i32, v1: i32, v2: i32): ; VCode: ; block0: ; mulw a5,a1,a2 -; add a0,a5,a0 +; addw a0,a5,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; mulw a5, a1, a2 -; add a0, a5, a0 +; addw a0, a5, a0 ; ret function %msub_i32(i32, i32, i32) -> i32 { diff --git a/cranelift/filetests/filetests/isa/riscv64/br_table.clif b/cranelift/filetests/filetests/isa/riscv64/br_table.clif index 8c5bde3ef46a..38bf96b51498 100644 --- a/cranelift/filetests/filetests/isa/riscv64/br_table.clif +++ b/cranelift/filetests/filetests/isa/riscv64/br_table.clif @@ -47,7 +47,7 @@ block5(v5: i32): ; li a5,4 ; j label7 ; block7: -; add a0,a0,a5 +; addw a0,a0,a5 ; ret ; ; Disassembled: @@ -84,6 +84,6 @@ block5(v5: i32): ; block5: ; offset 0x64 ; addi a5, zero, 4 ; block6: ; offset 0x68 -; add a0, a0, a5 +; addw a0, a0, a5 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif b/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif index 9aa772b2539b..027e65a55e0a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif +++ b/cranelift/filetests/filetests/isa/riscv64/issue-6954.clif @@ -129,191 +129,154 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; vle8.v v15,32(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v10,48(fp) #avl=16, #vtype=(e8, m1, ta, ma) ; vle8.v v12,64(fp) #avl=16, #vtype=(e8, m1, ta, ma) -; li a0,0 ; li a2,0 ; li a3,0 ; li a4,0 -; sd a3,0(nominal_sp) -; sd a4,8(nominal_sp) -; sd a3,16(nominal_sp) -; sd a4,24(nominal_sp) -; sd a3,32(nominal_sp) -; sd a4,40(nominal_sp) -; sd a3,48(nominal_sp) -; sd a4,56(nominal_sp) -; sd a3,64(nominal_sp) -; sd a4,72(nominal_sp) -; sd a3,80(nominal_sp) -; sd a4,88(nominal_sp) -; sd a3,96(nominal_sp) -; sd a4,104(nominal_sp) -; sd a3,112(nominal_sp) -; sw a2,120(nominal_sp) -; sh a0,124(nominal_sp) -; sd a3,128(nominal_sp) -; sd a4,136(nominal_sp) -; sd a3,144(nominal_sp) -; sd a4,152(nominal_sp) -; sd a3,160(nominal_sp) -; sd a4,168(nominal_sp) -; sd a3,176(nominal_sp) -; sd a4,184(nominal_sp) -; sd a3,192(nominal_sp) -; sd a4,200(nominal_sp) -; sd a3,208(nominal_sp) -; sd a4,216(nominal_sp) -; sd a3,224(nominal_sp) -; sd a4,232(nominal_sp) -; sd a3,240(nominal_sp) -; sw a2,248(nominal_sp) -; sh a0,252(nominal_sp) -; sd a3,256(nominal_sp) -; sd a4,264(nominal_sp) -; sd a3,272(nominal_sp) -; sd a4,280(nominal_sp) -; sd a3,288(nominal_sp) -; sd a4,296(nominal_sp) -; sd a3,304(nominal_sp) -; sd a4,312(nominal_sp) -; sd a3,320(nominal_sp) -; sd a4,328(nominal_sp) -; sd a3,336(nominal_sp) -; sd a4,344(nominal_sp) -; sd a3,352(nominal_sp) -; sd a4,360(nominal_sp) -; sd a3,368(nominal_sp) -; sw a2,376(nominal_sp) -; sh a0,380(nominal_sp) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; vfsqrt.v v10,v11 #avl=2, #vtype=(e64, m1, ta, ma) -; lui a0,4095 -; slli a2,a0,39 -; fmv.d.x fa4,a2 -; vfmv.v.f v11,fa4 #avl=2, #vtype=(e64, m1, ta, ma) -; vmfne.vv v0,v10,v10 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v13,v10,v11,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; vfsqrt.v v10,v13 #avl=2, #vtype=(e64, m1, ta, ma) -; lui a0,4095 -; slli a2,a0,39 -; fmv.d.x fa4,a2 -; vfmv.v.f v13,fa4 #avl=2, #vtype=(e64, m1, ta, ma) -; vmfne.vv v0,v10,v10 #avl=2, #vtype=(e64, m1, ta, ma) -; vmerge.vvm v11,v10,v13,v0.t #avl=2, #vtype=(e64, m1, ta, ma) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; sext.w a2,a1 -; select v12,v12,v12##condition=(a2 ne zero) -; add a2,a1,a1 -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; vmax.vv v10,v15,v15 #avl=2, #vtype=(e64, m1, ta, ma) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; load_addr a3,3(nominal_sp) -; addi a3,a3,0 -; andi a0,a3,3 -; slli a4,a0,3 -; andi a1,a3,-4 -; atomic_rmw.i8 and a0,a5,(a1)##t0=a3 offset=a4 -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; vse64.v v10,33(nominal_sp) #avl=2, #vtype=(e64, m1, ta, ma) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; sext.w a1,a2 -; select v12,v12,v12##condition=(a1 ne zero) -; vse8.v v11,0(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v12,16(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v11,32(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v12,48(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v12,64(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v12,80(a6) #avl=16, #vtype=(e8, m1, ta, ma) -; vse8.v v12,96(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; li a0,0 +; sd a4,0(nominal_sp) +; sd a0,8(nominal_sp) +; sd a4,16(nominal_sp) +; sd a0,24(nominal_sp) +; sd a4,32(nominal_sp) +; sd a0,40(nominal_sp) +; sd a4,48(nominal_sp) +; sd a0,56(nominal_sp) +; sd a4,64(nominal_sp) +; sd a0,72(nominal_sp) +; sd a4,80(nominal_sp) +; sd a0,88(nominal_sp) +; sd a4,96(nominal_sp) +; sd a0,104(nominal_sp) +; sd a4,112(nominal_sp) +; sw a3,120(nominal_sp) +; sh a2,124(nominal_sp) +; sd a4,128(nominal_sp) +; sd a0,136(nominal_sp) +; sd a4,144(nominal_sp) +; sd a0,152(nominal_sp) +; sd a4,160(nominal_sp) +; sd a0,168(nominal_sp) +; sd a4,176(nominal_sp) +; sd a0,184(nominal_sp) +; sd a4,192(nominal_sp) +; sd a0,200(nominal_sp) +; sd a4,208(nominal_sp) +; sd a0,216(nominal_sp) +; sd a4,224(nominal_sp) +; sd a0,232(nominal_sp) +; sd a4,240(nominal_sp) +; sw a3,248(nominal_sp) +; sh a2,252(nominal_sp) +; sd a4,256(nominal_sp) +; sd a0,264(nominal_sp) +; sd a4,272(nominal_sp) +; sd a0,280(nominal_sp) +; sd a4,288(nominal_sp) +; sd a0,296(nominal_sp) +; sd a4,304(nominal_sp) +; sd a0,312(nominal_sp) +; sd a4,320(nominal_sp) +; sd a0,328(nominal_sp) +; sd a4,336(nominal_sp) +; sd a0,344(nominal_sp) +; sd a4,352(nominal_sp) +; sd a0,360(nominal_sp) +; sd a4,368(nominal_sp) +; sw a3,376(nominal_sp) +; sh a2,380(nominal_sp) +; sext.w a4,a1 +; select v12,v12,v12##condition=(a4 ne zero) +; sext.w a4,a1 +; select v12,v12,v12##condition=(a4 ne zero) +; sext.w a4,a1 +; select v13,v12,v12##condition=(a4 ne zero) +; vfsqrt.v v11,v11 #avl=2, #vtype=(e64, m1, ta, ma) +; lui a3,4095 +; slli a0,a3,39 +; fmv.d.x fa1,a0 +; vfmv.v.f v12,fa1 #avl=2, #vtype=(e64, m1, ta, ma) +; vmfne.vv v0,v11,v11 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v14,v11,v12,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; vfsqrt.v v11,v14 #avl=2, #vtype=(e64, m1, ta, ma) +; lui a3,4095 +; slli a0,a3,39 +; fmv.d.x fa1,a0 +; vfmv.v.f v14,fa1 #avl=2, #vtype=(e64, m1, ta, ma) +; vmfne.vv v0,v11,v11 #avl=2, #vtype=(e64, m1, ta, ma) +; vmerge.vvm v12,v11,v14,v0.t #avl=2, #vtype=(e64, m1, ta, ma) +; sext.w a4,a1 +; select v13,v13,v13##condition=(a4 ne zero) +; sext.w a4,a1 +; select v13,v13,v13##condition=(a4 ne zero) +; sext.w a4,a1 +; select v13,v13,v13##condition=(a4 ne zero) +; sext.w a4,a1 +; select v13,v13,v13##condition=(a4 ne zero) +; sext.w a4,a1 +; select v13,v13,v13##condition=(a4 ne zero) +; sext.w a4,a1 +; select v13,v13,v13##condition=(a4 ne zero) +; sext.w a4,a1 +; select v13,v13,v13##condition=(a4 ne zero) +; sext.w a4,a1 +; select v13,v13,v13##condition=(a4 ne zero) +; addw a0,a1,a1 +; select v11,v13,v13##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v13,v11,v11##condition=(a0 ne zero) +; vmax.vv v11,v15,v15 #avl=2, #vtype=(e64, m1, ta, ma) +; select v13,v13,v13##condition=(a0 ne zero) +; load_addr a1,3(nominal_sp) +; addi a1,a1,0 +; andi a3,a1,3 +; slli a2,a3,3 +; andi a1,a1,-4 +; atomic_rmw.i8 and a4,a5,(a1)##t0=a3 offset=a2 +; mv a5,a4 +; select v10,v13,v13##condition=(a0 ne zero) +; select v10,v10,v10##condition=(a0 ne zero) +; select v10,v10,v10##condition=(a0 ne zero) +; select v10,v10,v10##condition=(a0 ne zero) +; select v10,v10,v10##condition=(a0 ne zero) +; select v10,v10,v10##condition=(a0 ne zero) +; select v10,v10,v10##condition=(a0 ne zero) +; vse64.v v11,33(nominal_sp) #avl=2, #vtype=(e64, m1, ta, ma) +; select v11,v10,v10##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; select v11,v11,v11##condition=(a0 ne zero) +; vse8.v v12,0(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v11,16(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v12,32(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v11,48(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v11,64(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v11,80(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; vse8.v v11,96(a6) #avl=16, #vtype=(e8, m1, ta, ma) +; mv a0,a5 ; add sp,+384 ; ld ra,8(sp) ; ld fp,0(sp) @@ -337,163 +300,146 @@ block0(v0: i16, v1: f32, v2: f64x2, v3: i32, v4: i8, v5: i64x2, v6: i8, v7: f32x ; .byte 0x07, 0x85, 0x0f, 0x02 ; addi t6, s0, 0x40 ; .byte 0x07, 0x86, 0x0f, 0x02 -; mv a0, zero ; mv a2, zero ; mv a3, zero ; mv a4, zero -; sd a3, 0(sp) -; sd a4, 8(sp) -; sd a3, 0x10(sp) -; sd a4, 0x18(sp) -; sd a3, 0x20(sp) -; sd a4, 0x28(sp) -; sd a3, 0x30(sp) -; sd a4, 0x38(sp) -; sd a3, 0x40(sp) -; sd a4, 0x48(sp) -; sd a3, 0x50(sp) -; sd a4, 0x58(sp) -; sd a3, 0x60(sp) -; sd a4, 0x68(sp) -; sd a3, 0x70(sp) -; sw a2, 0x78(sp) -; sh a0, 0x7c(sp) -; sd a3, 0x80(sp) -; sd a4, 0x88(sp) -; sd a3, 0x90(sp) -; sd a4, 0x98(sp) -; sd a3, 0xa0(sp) -; sd a4, 0xa8(sp) -; sd a3, 0xb0(sp) -; sd a4, 0xb8(sp) -; sd a3, 0xc0(sp) -; sd a4, 0xc8(sp) -; sd a3, 0xd0(sp) -; sd a4, 0xd8(sp) -; sd a3, 0xe0(sp) -; sd a4, 0xe8(sp) -; sd a3, 0xf0(sp) -; sw a2, 0xf8(sp) -; sh a0, 0xfc(sp) -; sd a3, 0x100(sp) -; sd a4, 0x108(sp) -; sd a3, 0x110(sp) -; sd a4, 0x118(sp) -; sd a3, 0x120(sp) -; sd a4, 0x128(sp) -; sd a3, 0x130(sp) -; sd a4, 0x138(sp) -; sd a3, 0x140(sp) -; sd a4, 0x148(sp) -; sd a3, 0x150(sp) -; sd a4, 0x158(sp) -; sd a3, 0x160(sp) -; sd a4, 0x168(sp) -; sd a3, 0x170(sp) -; sw a2, 0x178(sp) -; sh a0, 0x17c(sp) -; sext.w a2, a1 -; sext.w a2, a1 -; sext.w a2, a1 +; mv a0, zero +; sd a4, 0(sp) +; sd a0, 8(sp) +; sd a4, 0x10(sp) +; sd a0, 0x18(sp) +; sd a4, 0x20(sp) +; sd a0, 0x28(sp) +; sd a4, 0x30(sp) +; sd a0, 0x38(sp) +; sd a4, 0x40(sp) +; sd a0, 0x48(sp) +; sd a4, 0x50(sp) +; sd a0, 0x58(sp) +; sd a4, 0x60(sp) +; sd a0, 0x68(sp) +; sd a4, 0x70(sp) +; sw a3, 0x78(sp) +; sh a2, 0x7c(sp) +; sd a4, 0x80(sp) +; sd a0, 0x88(sp) +; sd a4, 0x90(sp) +; sd a0, 0x98(sp) +; sd a4, 0xa0(sp) +; sd a0, 0xa8(sp) +; sd a4, 0xb0(sp) +; sd a0, 0xb8(sp) +; sd a4, 0xc0(sp) +; sd a0, 0xc8(sp) +; sd a4, 0xd0(sp) +; sd a0, 0xd8(sp) +; sd a4, 0xe0(sp) +; sd a0, 0xe8(sp) +; sd a4, 0xf0(sp) +; sw a3, 0xf8(sp) +; sh a2, 0xfc(sp) +; sd a4, 0x100(sp) +; sd a0, 0x108(sp) +; sd a4, 0x110(sp) +; sd a0, 0x118(sp) +; sd a4, 0x120(sp) +; sd a0, 0x128(sp) +; sd a4, 0x130(sp) +; sd a0, 0x138(sp) +; sd a4, 0x140(sp) +; sd a0, 0x148(sp) +; sd a4, 0x150(sp) +; sd a0, 0x158(sp) +; sd a4, 0x160(sp) +; sd a0, 0x168(sp) +; sd a4, 0x170(sp) +; sw a3, 0x178(sp) +; sh a2, 0x17c(sp) +; sext.w a4, a1 +; sext.w a4, a1 +; sext.w a4, a1 +; beqz a4, 0xc +; .byte 0xd7, 0x36, 0xc0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xc0, 0x9e ; .byte 0x57, 0x70, 0x81, 0xcd -; .byte 0x57, 0x15, 0xb0, 0x4e -; lui a0, 0xfff -; slli a2, a0, 0x27 -; fmv.d.x fa4, a2 -; .byte 0xd7, 0x55, 0x07, 0x5e -; .byte 0x57, 0x10, 0xa5, 0x72 -; .byte 0xd7, 0x86, 0xa5, 0x5c -; .byte 0x57, 0x15, 0xd0, 0x4e -; lui a0, 0xfff -; slli a2, a0, 0x27 -; fmv.d.x fa4, a2 -; .byte 0xd7, 0x56, 0x07, 0x5e -; .byte 0x57, 0x10, 0xa5, 0x72 -; .byte 0xd7, 0x85, 0xa6, 0x5c -; sext.w a2, a1 -; sext.w a2, a1 -; sext.w a2, a1 -; sext.w a2, a1 -; sext.w a2, a1 -; sext.w a2, a1 -; sext.w a2, a1 -; sext.w a2, a1 -; add a2, a1, a1 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; .byte 0x57, 0x85, 0xf7, 0x1e -; sext.w a1, a2 -; addi a3, sp, 3 -; mv a3, a3 -; andi a0, a3, 3 -; slli a4, a0, 3 -; andi a1, a3, -4 -; lr.w.aqrl a0, (a1) -; srl a0, a0, a4 -; andi a0, a0, 0xff -; and a3, a0, a5 +; .byte 0xd7, 0x15, 0xb0, 0x4e +; lui a3, 0xfff +; slli a0, a3, 0x27 +; fmv.d.x fa1, a0 +; .byte 0x57, 0xd6, 0x05, 0x5e +; .byte 0x57, 0x90, 0xb5, 0x72 +; .byte 0x57, 0x07, 0xb6, 0x5c +; .byte 0xd7, 0x15, 0xe0, 0x4e +; lui a3, 0xfff +; slli a0, a3, 0x27 +; fmv.d.x fa1, a0 +; .byte 0x57, 0xd7, 0x05, 0x5e +; .byte 0x57, 0x90, 0xb5, 0x72 +; .byte 0x57, 0x06, 0xb7, 0x5c +; sext.w a4, a1 +; sext.w a4, a1 +; sext.w a4, a1 +; sext.w a4, a1 +; sext.w a4, a1 +; sext.w a4, a1 +; sext.w a4, a1 +; sext.w a4, a1 +; addw a0, a1, a1 +; beqz a0, 0xc +; .byte 0xd7, 0x35, 0xd0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xd0, 0x9e +; beqz a0, 0xc +; .byte 0xd7, 0x36, 0xb0, 0x9e +; j 8 +; .byte 0xd7, 0x36, 0xb0, 0x9e +; .byte 0xd7, 0x85, 0xf7, 0x1e +; addi a1, sp, 3 +; mv a1, a1 +; andi a3, a1, 3 +; slli a2, a3, 3 +; andi a1, a1, -4 +; lr.w.aqrl a4, (a1) +; srl a4, a4, a2 +; andi a4, a4, 0xff +; and a3, a4, a5 ; lr.w.aqrl t5, (a1) ; addi t6, zero, 0xff -; sll t6, t6, a4 +; sll t6, t6, a2 ; not t6, t6 ; and t5, t5, t6 ; andi t6, a3, 0xff -; sll t6, t6, a4 +; sll t6, t6, a2 ; or t5, t5, t6 ; sc.w.aqrl a3, t5, (a1) ; bnez a3, -0x34 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 +; mv a5, a4 +; beqz a0, 0xc +; .byte 0x57, 0x35, 0xd0, 0x9e +; j 8 +; .byte 0x57, 0x35, 0xd0, 0x9e ; addi t6, sp, 0x21 -; .byte 0x27, 0xf5, 0x0f, 0x02 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 -; sext.w a1, a2 +; .byte 0xa7, 0xf5, 0x0f, 0x02 +; beqz a0, 0xc +; .byte 0xd7, 0x35, 0xa0, 0x9e +; j 8 +; .byte 0xd7, 0x35, 0xa0, 0x9e ; .byte 0x57, 0x70, 0x08, 0xcc -; .byte 0xa7, 0x05, 0x08, 0x02 +; .byte 0x27, 0x06, 0x08, 0x02 ; addi t6, a6, 0x10 -; .byte 0x27, 0x86, 0x0f, 0x02 -; addi t6, a6, 0x20 ; .byte 0xa7, 0x85, 0x0f, 0x02 -; addi t6, a6, 0x30 +; addi t6, a6, 0x20 ; .byte 0x27, 0x86, 0x0f, 0x02 +; addi t6, a6, 0x30 +; .byte 0xa7, 0x85, 0x0f, 0x02 ; addi t6, a6, 0x40 -; .byte 0x27, 0x86, 0x0f, 0x02 +; .byte 0xa7, 0x85, 0x0f, 0x02 ; addi t6, a6, 0x50 -; .byte 0x27, 0x86, 0x0f, 0x02 +; .byte 0xa7, 0x85, 0x0f, 0x02 ; addi t6, a6, 0x60 -; .byte 0x27, 0x86, 0x0f, 0x02 +; .byte 0xa7, 0x85, 0x0f, 0x02 +; mv a0, a5 ; addi sp, sp, 0x180 ; ld ra, 8(sp) ; ld s0, 0(sp) diff --git a/cranelift/filetests/filetests/isa/riscv64/narrow-arithmetic.clif b/cranelift/filetests/filetests/isa/riscv64/narrow-arithmetic.clif index 6223052f8bff..05b39aeea943 100644 --- a/cranelift/filetests/filetests/isa/riscv64/narrow-arithmetic.clif +++ b/cranelift/filetests/filetests/isa/riscv64/narrow-arithmetic.clif @@ -10,12 +10,12 @@ block0(v0: i8, v1: i8): ; VCode: ; block0: -; add a0,a0,a1 +; addw a0,a0,a1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; add a0, a0, a1 +; addw a0, a0, a1 ; ret function %add16(i16, i16) -> i16 { @@ -26,12 +26,12 @@ block0(v0: i16, v1: i16): ; VCode: ; block0: -; add a0,a0,a1 +; addw a0,a0,a1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; add a0, a0, a1 +; addw a0, a0, a1 ; ret function %add32(i32, i32) -> i32 { @@ -42,12 +42,12 @@ block0(v0: i32, v1: i32): ; VCode: ; block0: -; add a0,a0,a1 +; addw a0,a0,a1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; add a0, a0, a1 +; addw a0, a0, a1 ; ret function %add32_8(i32, i8) -> i32 { @@ -61,14 +61,14 @@ block0(v0: i32, v1: i8): ; block0: ; slli a4,a1,56 ; srai a1,a4,56 -; add a0,a0,a1 +; addw a0,a0,a1 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; slli a4, a1, 0x38 ; srai a1, a4, 0x38 -; add a0, a0, a1 +; addw a0, a0, a1 ; ret function %add64_32(i64, i32) -> i64 { diff --git a/cranelift/filetests/filetests/runtests/extend.clif b/cranelift/filetests/filetests/runtests/extend.clif index bc02c2d23bad..710c33d9a8e9 100644 --- a/cranelift/filetests/filetests/runtests/extend.clif +++ b/cranelift/filetests/filetests/runtests/extend.clif @@ -216,3 +216,21 @@ block0(v0: i32, v1: i32): } ; run: %add_sextend32_64(0xffff_ee00, 0x1000_0001) == 0x0000_0000_0fff_ee01 ; run: %add_sextend32_64(0xffff_ee00, 0x9000_0001) == 0xffff_ffff_8fff_ee01 + +function %sext16_zext32(i8) -> i32 { +block0(v0: i8): + v1 = sextend.i16 v0 + v2 = uextend.i32 v1 + return v2 +} +; run: %sext16_zext32(0xff) == 0xffff +; run: %sext16_zext32(0x7f) == 0x7f + +function %zext16_sext32(i8) -> i32 { +block0(v0: i8): + v1 = uextend.i16 v0 + v2 = sextend.i32 v1 + return v2 +} +; run: %zext16_sext32(0xff) == 0xff +; run: %zext16_sext32(0x7f) == 0x7f From 88f54c5a3889dd96c66d20768882f7606d9e528f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 5 Oct 2023 11:44:56 -0500 Subject: [PATCH 064/199] Update release notes for 14.0.0 (#7158) * Update release notes for 14.0.0 Wow quite a bit happened! * Review comments --- RELEASES.md | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index da9c72e82d86..62e446cc8a9e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -14,17 +14,158 @@ Unreleased. Unreleased. +One of the larger changes in this release is a redesign of Wasmtime's CLI +arguments and where arguments are passed. This means that previous invocations +of the `wasmtime` CLI executable will need to be updated. No functionality was +removed but most of it is behind new flags. One major change is that Wasmtime +CLI flags are now grouped behind short options like `-O`. For example + + wasmtime run --opt-level 2 foo.wasm + +is now: + + wasmtime run -O opt-level=2 foo.wasm + +Additionally options prefixed with `--enable-*` or `--disable-*` now +consistently are considered boolean setters. For example: + + wasmtime run --disable-cache foo.wasm + +is now: + + wasmtime run -C cache=n foo.wasm + +Options can be explored with `wasmtime -C help` for example, and `wasmtime -h` +will show all option groups that can be expanded. + +Another major change in the CLI is that any CLI argument which positionally +comes after the wasm file specified will be passed as an argument to the guest +module. For example this invocations + + wasmtime run foo.wasm --epoch-interruption + +was previously accepted as enabling epoch interruption for the `foo.wasm` file. +This is now interpreted as if it were `./foo.wasm --epoch-interruption`, +however, passing the flag to the wasm file itself. Flags to Wasmtime must now +come after Wasmtime's subcommand (in this case `run`) and before the wasm file +that's being run, for example: + + wasmtime run -W epoch-interruption foo.wasm + +More information about this change can be found on +[#6925](https://github.com/bytecodealliance/wasmtime/pull/6925) and +[#6946](https://github.com/bytecodealliance/wasmtime/pull/6946). + ### Added * Added the `wasmtime::FrameInfo::module` method, which returns the `wasmtime::Module` associated with the stack frame. +* The `wasmtime::component::Linker` type now implements `Clone`. + [#7032](https://github.com/bytecodealliance/wasmtime/pull/7032) + +* Wasmtime's `TypedFunc` API now supports the `v128` WebAssembly type on x86\_64 + and aarch64. + [#7010](https://github.com/bytecodealliance/wasmtime/pull/7010) + +* Support for resources exported from a WebAssembly guest has been added to the + component `bindgen!` macro. + [#7050](https://github.com/bytecodealliance/wasmtime/pull/7050) + +* The C API now supports learning about a module's `image_range`. + [#7064](https://github.com/bytecodealliance/wasmtime/pull/7064) + +* Passing values between components is now possible with a more complete + implementation of type-checking of values. + [#7065](https://github.com/bytecodealliance/wasmtime/pull/7065) + +* Types representing resources can now be customized with `bindgen!`. + [#7069](https://github.com/bytecodealliance/wasmtime/pull/7069) + +* Wasm-defined globals and memories are now included in core dumps, and the + `wasmtime::WasmCoreDump` type is now serializable. + [#6935](https://github.com/bytecodealliance/wasmtime/pull/6935) + [#7078](https://github.com/bytecodealliance/wasmtime/pull/7078) + +* Initial experimental support for Intel MPK has been added to support running + more instances concurrently. + [#7072](https://github.com/bytecodealliance/wasmtime/pull/7072) + +* The implementation of `wasi:http` now supports inbound requests in addition to + outbound requests. A new `wasmtime serve` command is an example way of + handling http requests with wasm files. + [#7091](https://github.com/bytecodealliance/wasmtime/pull/7091) + +* The C API now supports Wasmtime's "host memory creation" API to customize the + allocation of linear memories. + [#7115](https://github.com/bytecodealliance/wasmtime/pull/7115) + +* The C API now supports asynchronous invocation of WebAssembly programs. + [#7106](https://github.com/bytecodealliance/wasmtime/pull/7106) + +* The C API now supports Wasmtime's `InstancePre` type. + [#7140](https://github.com/bytecodealliance/wasmtime/pull/7140) + +* The `wasi:sockets/ip-name-lookup` interface is now implemented by Wasmtime. + [#7109](https://github.com/bytecodealliance/wasmtime/pull/7109) + ### Changed +* Wasmtime's CLI has been significantly overhauled. See the note above. + [#6925](https://github.com/bytecodealliance/wasmtime/pull/6925) + [#6946](https://github.com/bytecodealliance/wasmtime/pull/6946) + * The `wasmtime::FrameInfo::module_name` has been removed, however you can now get identical results by chaining `wasmtime::FrameInfo::module` and `wasmtime::Module::name`: `my_frame.module().name()`. +* WASI interfaces have seen significant work since the previous release. Streams + for example have a new backpressure and flushing design. Additionally WIT + `resource`s are now used ubiquitously throughout the specification and + implementation. + [#6877](https://github.com/bytecodealliance/wasmtime/pull/6877) + [#7029](https://github.com/bytecodealliance/wasmtime/pull/7029) + [#7090](https://github.com/bytecodealliance/wasmtime/pull/7090) + +* The implementation of `wasi:http` now uses `{input,output}-stream` from the + `wasi:io/streams` interface. + [#7056](https://github.com/bytecodealliance/wasmtime/pull/7056) + +* Lifting and lowering of the `list` component values has been significantly + optimized. + [#6971](https://github.com/bytecodealliance/wasmtime/pull/6971) + +* The `wasmtime-c-api` crate is now additionally built as an rlib as well as the + previous cdylib/staticlib combo. + [#6765](https://github.com/bytecodealliance/wasmtime/pull/6765) + +### Fixed + +* Support referencing stack slots in the DWARF debug info. + [#6960](https://github.com/bytecodealliance/wasmtime/pull/6960) + +* Printing unicode to stdio on Windows has been fixed. + [#6825](https://github.com/bytecodealliance/wasmtime/pull/6825) + +* Building for x86\_64-linux-android has been fixed. + [#7055](https://github.com/bytecodealliance/wasmtime/pull/7055) + +* Fixed stdout/stderr becoming nonblocking by accident with WASI preview2 on + macOS. + [#7058](https://github.com/bytecodealliance/wasmtime/pull/7058) + +* Fixed some character boundary-related panics in the preview2 implementation of + preview1. + [#7011](https://github.com/bytecodealliance/wasmtime/pull/7011) + +* Fixed an issue of guests sleeping for an incorrect amount of time with + preview2. + [#6993](https://github.com/bytecodealliance/wasmtime/pull/6993) + +* Cranelift will now return an error when running out of temporaries in a very + large function instead of panicking. + [#7114](https://github.com/bytecodealliance/wasmtime/pull/7114) + -------------------------------------------------------------------------------- ## 13.0.0 From be4dbb2951bce20bae06291f3c333c861ece2d27 Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Thu, 5 Oct 2023 10:02:32 -0700 Subject: [PATCH 065/199] Bump regalloc2 to 0.9.3 (#7160) * Bump regalloc2 to 0.9.3 * Update filetests for the regalloc bump --- Cargo.lock | 4 +- Cargo.toml | 2 +- .../filetests/filetests/isa/aarch64/call.clif | 8 +- .../filetests/isa/aarch64/reftypes.clif | 4 +- .../filetests/isa/aarch64/simd-narrow.clif | 60 +- .../filetests/isa/aarch64/stack.clif | 8 +- .../filetests/filetests/isa/riscv64/call.clif | 24 +- .../filetests/isa/riscv64/reftypes.clif | 8 +- .../filetests/isa/riscv64/return-call.clif | 16 +- .../filetests/isa/riscv64/select.clif | 4 +- .../filetests/isa/riscv64/stack.clif | 12 +- .../filetests/isa/riscv64/tail-call-conv.clif | 16 +- .../isa/s390x/atomic_rmw-arch13.clif | 84 ++- .../isa/s390x/atomic_rmw-little.clif | 574 ++++++++---------- .../filetests/isa/s390x/atomic_rmw.clif | 534 ++++++++-------- .../filetests/filetests/isa/s390x/bitops.clif | 52 +- .../filetests/isa/s390x/bitwise.clif | 44 +- .../filetests/isa/s390x/reftypes.clif | 4 +- .../filetests/isa/s390x/shift-rotate.clif | 50 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 10 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 12 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 10 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 12 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 14 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 14 +- ...rd_no_spectre_i32_access_0x1000_offset.wat | 14 +- ...ard_no_spectre_i8_access_0x1000_offset.wat | 14 +- .../filetests/isa/x64/amode-opt.clif | 10 +- .../filetests/filetests/isa/x64/bmask.clif | 80 +-- .../filetests/filetests/isa/x64/bmi2.clif | 30 +- .../filetests/isa/x64/call-conv.clif | 4 +- .../filetests/isa/x64/conditional-values.clif | 16 +- .../filetests/filetests/isa/x64/fcvt.clif | 36 +- .../filetests/filetests/isa/x64/fma-call.clif | 56 +- .../filetests/filetests/isa/x64/i128.clif | 364 +++++------ .../filetests/filetests/isa/x64/ishl.clif | 184 +++--- .../filetests/isa/x64/narrowing.clif | 12 +- .../filetests/filetests/isa/x64/popcnt.clif | 96 ++- .../filetests/isa/x64/return-call.clif | 36 +- .../filetests/isa/x64/select-i128.clif | 8 +- .../filetests/filetests/isa/x64/select.clif | 4 +- .../filetests/isa/x64/simd-arith-avx.clif | 140 ++--- .../filetests/isa/x64/simd-bitselect.clif | 34 +- .../isa/x64/simd-bitwise-compile.clif | 171 +++--- .../isa/x64/simd-i64x2-shift-avx512.clif | 10 +- .../isa/x64/simd-logical-compile.clif | 10 +- .../filetests/isa/x64/simd-widen-mul.clif | 102 ++-- .../filetests/filetests/isa/x64/sshr.clif | 174 +++--- .../filetests/isa/x64/struct-arg.clif | 4 +- .../filetests/isa/x64/tail-call-conv.clif | 84 +-- .../filetests/filetests/isa/x64/traps.clif | 8 +- .../filetests/filetests/isa/x64/ushr.clif | 200 +++--- .../filetests/filetests/isa/x64/uunarrow.clif | 18 +- .../filetests/isa/x64/vhigh_bits.clif | 10 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 32 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 32 +- ...o_spectre_i32_access_0xffff0000_offset.wat | 14 +- ...no_spectre_i8_access_0xffff0000_offset.wat | 14 +- .../filetests/filetests/isa/x64/widening.clif | 20 +- .../filetests/filetests/wasm/x64-bmi2.wat | 10 +- .../filetests/wasm/x64-relaxed-simd.wat | 19 +- supply-chain/imports.lock | 7 + 62 files changed, 1651 insertions(+), 2006 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2cc0cbd7a9a9..027c50eae36a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2196,9 +2196,9 @@ dependencies = [ [[package]] name = "regalloc2" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b4dcbd3a2ae7fb94b5813fa0e957c6ab51bf5d0a8ee1b69e0c2d0f1e6eb8485" +checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" dependencies = [ "hashbrown 0.13.2", "log", diff --git a/Cargo.toml b/Cargo.toml index fd17a28ef37d..d60179bfbbe4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -196,7 +196,7 @@ byte-array-literals = { path = "crates/wasi-preview1-component-adapter/byte-arra # Bytecode Alliance maintained dependencies: # --------------------------- -regalloc2 = "0.9.2" +regalloc2 = "0.9.3" # cap-std family: target-lexicon = { version = "0.12.3", default-features = false, features = ["std"] } diff --git a/cranelift/filetests/filetests/isa/aarch64/call.clif b/cranelift/filetests/filetests/isa/aarch64/call.clif index 7f371334e244..649feef71613 100644 --- a/cranelift/filetests/filetests/isa/aarch64/call.clif +++ b/cranelift/filetests/filetests/isa/aarch64/call.clif @@ -707,8 +707,8 @@ block0(v0: i128, v1: i64): ; mov x5, x1 ; load_ext_name x10, TestCase(%f14)+0 ; mov x0, x4 -; mov x2, x4 ; mov x1, x5 +; mov x2, x4 ; mov x3, x5 ; blr x10 ; add sp, sp, #16 @@ -732,8 +732,8 @@ block0(v0: i128, v1: i64): ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %f14 0 ; .byte 0x00, 0x00, 0x00, 0x00 ; mov x0, x4 -; mov x2, x4 ; mov x1, x5 +; mov x2, x4 ; mov x3, x5 ; blr x10 ; add sp, sp, #0x10 @@ -785,8 +785,8 @@ block0(v0: i128, v1: i64): ; mov x5, x1 ; load_ext_name x10, TestCase(%f15)+0 ; mov x0, x4 -; mov x2, x4 ; mov x1, x5 +; mov x2, x4 ; mov x3, x5 ; blr x10 ; add sp, sp, #16 @@ -810,8 +810,8 @@ block0(v0: i128, v1: i64): ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %f15 0 ; .byte 0x00, 0x00, 0x00, 0x00 ; mov x0, x4 -; mov x2, x4 ; mov x1, x5 +; mov x2, x4 ; mov x3, x5 ; blr x10 ; add sp, sp, #0x10 diff --git a/cranelift/filetests/filetests/isa/aarch64/reftypes.clif b/cranelift/filetests/filetests/isa/aarch64/reftypes.clif index 55bc5678e184..f35508a3df87 100644 --- a/cranelift/filetests/filetests/isa/aarch64/reftypes.clif +++ b/cranelift/filetests/filetests/isa/aarch64/reftypes.clif @@ -106,8 +106,8 @@ block3(v7: r64, v8: r64): ; uxtb w12, w0 ; cbnz x12, label2 ; b label1 ; block1: -; mov x1, x24 ; ldr x0, [sp, #16] +; mov x1, x24 ; b label3 ; block2: ; mov x0, x24 @@ -144,8 +144,8 @@ block3(v7: r64, v8: r64): ; uxtb w12, w0 ; cbnz x12, #0x58 ; block2: ; offset 0x4c -; mov x1, x24 ; ldur x0, [sp, #0x10] +; mov x1, x24 ; b #0x60 ; block3: ; offset 0x58 ; mov x0, x24 diff --git a/cranelift/filetests/filetests/isa/aarch64/simd-narrow.clif b/cranelift/filetests/filetests/isa/aarch64/simd-narrow.clif index f8bb1410c2a5..da19dff51b0f 100644 --- a/cranelift/filetests/filetests/isa/aarch64/simd-narrow.clif +++ b/cranelift/filetests/filetests/isa/aarch64/simd-narrow.clif @@ -10,16 +10,14 @@ block0(v0: i16x4, v1: i16x4): ; VCode: ; block0: -; mov v3.16b, v0.16b -; mov v3.d[1], v3.d[1], v1.d[0] -; sqxtn v0.8b, v3.8h +; mov v0.d[1], v0.d[1], v1.d[0] +; sqxtn v0.8b, v0.8h ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mov v3.16b, v0.16b -; mov v3.d[1], v1.d[0] -; sqxtn v0.8b, v3.8h +; mov v0.d[1], v1.d[0] +; sqxtn v0.8b, v0.8h ; ret function %snarrow_i16x8(i16x8, i16x8) -> i8x16 { @@ -48,16 +46,14 @@ block0(v0: i32x2, v1: i32x2): ; VCode: ; block0: -; mov v3.16b, v0.16b -; mov v3.d[1], v3.d[1], v1.d[0] -; sqxtn v0.4h, v3.4s +; mov v0.d[1], v0.d[1], v1.d[0] +; sqxtn v0.4h, v0.4s ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mov v3.16b, v0.16b -; mov v3.d[1], v1.d[0] -; sqxtn v0.4h, v3.4s +; mov v0.d[1], v1.d[0] +; sqxtn v0.4h, v0.4s ; ret function %snarrow_i32x4(i32x4, i32x4) -> i16x8 { @@ -104,16 +100,14 @@ block0(v0: i16x4, v1: i16x4): ; VCode: ; block0: -; mov v3.16b, v0.16b -; mov v3.d[1], v3.d[1], v1.d[0] -; sqxtun v0.8b, v3.8h +; mov v0.d[1], v0.d[1], v1.d[0] +; sqxtun v0.8b, v0.8h ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mov v3.16b, v0.16b -; mov v3.d[1], v1.d[0] -; sqxtun v0.8b, v3.8h +; mov v0.d[1], v1.d[0] +; sqxtun v0.8b, v0.8h ; ret function %unarrow_i16x8(i16x8, i16x8) -> i8x16 { @@ -142,16 +136,14 @@ block0(v0: i32x2, v1: i32x2): ; VCode: ; block0: -; mov v3.16b, v0.16b -; mov v3.d[1], v3.d[1], v1.d[0] -; sqxtun v0.4h, v3.4s +; mov v0.d[1], v0.d[1], v1.d[0] +; sqxtun v0.4h, v0.4s ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mov v3.16b, v0.16b -; mov v3.d[1], v1.d[0] -; sqxtun v0.4h, v3.4s +; mov v0.d[1], v1.d[0] +; sqxtun v0.4h, v0.4s ; ret function %unarrow_i32x4(i32x4, i32x4) -> i16x8 { @@ -198,16 +190,14 @@ block0(v0: i16x4, v1: i16x4): ; VCode: ; block0: -; mov v3.16b, v0.16b -; mov v3.d[1], v3.d[1], v1.d[0] -; uqxtn v0.8b, v3.8h +; mov v0.d[1], v0.d[1], v1.d[0] +; uqxtn v0.8b, v0.8h ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mov v3.16b, v0.16b -; mov v3.d[1], v1.d[0] -; uqxtn v0.8b, v3.8h +; mov v0.d[1], v1.d[0] +; uqxtn v0.8b, v0.8h ; ret function %uunarrow_i16x8(i16x8, i16x8) -> i8x16 { @@ -236,16 +226,14 @@ block0(v0: i32x2, v1: i32x2): ; VCode: ; block0: -; mov v3.16b, v0.16b -; mov v3.d[1], v3.d[1], v1.d[0] -; uqxtn v0.4h, v3.4s +; mov v0.d[1], v0.d[1], v1.d[0] +; uqxtn v0.4h, v0.4s ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; mov v3.16b, v0.16b -; mov v3.d[1], v1.d[0] -; uqxtn v0.4h, v3.4s +; mov v0.d[1], v1.d[0] +; uqxtn v0.4h, v0.4s ; ret function %uunarrow_i32x4(i32x4, i32x4) -> i16x8 { diff --git a/cranelift/filetests/filetests/isa/aarch64/stack.clif b/cranelift/filetests/filetests/isa/aarch64/stack.clif index 8369c5efc419..cd2fd150b9cb 100644 --- a/cranelift/filetests/filetests/isa/aarch64/stack.clif +++ b/cranelift/filetests/filetests/isa/aarch64/stack.clif @@ -479,8 +479,8 @@ block0(v0: i8): ; add x14, x14, #37 ; ldr x15, [sp, #1144] ; add x15, x15, #39 -; ldr x3, [sp, #1128] ; ldr x1, [sp, #1136] +; ldr x3, [sp, #1128] ; add x1, x1, x3 ; ldr x2, [sp, #1112] ; ldr x6, [sp, #1120] @@ -500,8 +500,8 @@ block0(v0: i8): ; ldr x23, [sp, #1032] ; ldr x24, [sp, #1040] ; add x23, x24, x23 -; ldr x25, [sp, #1016] ; ldr x24, [sp, #1024] +; ldr x25, [sp, #1016] ; add x24, x24, x25 ; ldr x25, [sp, #1008] ; add x25, x25, x26 @@ -657,8 +657,8 @@ block0(v0: i8): ; add x14, x14, #0x25 ; ldr x15, [sp, #0x478] ; add x15, x15, #0x27 -; ldr x3, [sp, #0x468] ; ldr x1, [sp, #0x470] +; ldr x3, [sp, #0x468] ; add x1, x1, x3 ; ldr x2, [sp, #0x458] ; ldr x6, [sp, #0x460] @@ -678,8 +678,8 @@ block0(v0: i8): ; ldr x23, [sp, #0x408] ; ldr x24, [sp, #0x410] ; add x23, x24, x23 -; ldr x25, [sp, #0x3f8] ; ldr x24, [sp, #0x400] +; ldr x25, [sp, #0x3f8] ; add x24, x24, x25 ; ldr x25, [sp, #0x3f0] ; add x25, x25, x26 diff --git a/cranelift/filetests/filetests/isa/riscv64/call.clif b/cranelift/filetests/filetests/isa/riscv64/call.clif index 8fa3cad65681..c97543c7214f 100644 --- a/cranelift/filetests/filetests/isa/riscv64/call.clif +++ b/cranelift/filetests/filetests/isa/riscv64/call.clif @@ -633,17 +633,17 @@ block0(v0: i128, v1: i64): ; sd s3,-8(sp) ; add sp,-16 ; block0: -; mv a7,a0 ; mv a6,a2 +; mv a7,a0 ; add sp,-16 ; virtual_sp_offset_adj +16 ; sd a1,0(sp) ; mv a5,a1 ; load_sym s3,%f14+0 -; mv a1,a5 -; mv a3,a5 ; mv a0,a7 +; mv a1,a5 ; mv a2,a7 +; mv a3,a5 ; mv a4,a7 ; callind s3 ; add sp,+16 @@ -664,8 +664,8 @@ block0(v0: i128, v1: i64): ; sd s3, -8(sp) ; addi sp, sp, -0x10 ; block1: ; offset 0x18 -; mv a7, a0 ; mv a6, a2 +; mv a7, a0 ; addi sp, sp, -0x10 ; sd a1, 0(sp) ; mv a5, a1 @@ -674,10 +674,10 @@ block0(v0: i128, v1: i64): ; j 0xc ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %f14 0 ; .byte 0x00, 0x00, 0x00, 0x00 -; mv a1, a5 -; mv a3, a5 ; mv a0, a7 +; mv a1, a5 ; mv a2, a7 +; mv a3, a5 ; mv a4, a7 ; jalr s3 ; addi sp, sp, 0x10 @@ -736,17 +736,17 @@ block0(v0: i128, v1: i64): ; sd s3,-8(sp) ; add sp,-16 ; block0: -; mv a7,a0 ; mv a6,a2 +; mv a7,a0 ; add sp,-16 ; virtual_sp_offset_adj +16 ; sd a1,0(sp) ; mv a5,a1 ; load_sym s3,%f15+0 -; mv a1,a5 -; mv a3,a5 ; mv a0,a7 +; mv a1,a5 ; mv a2,a7 +; mv a3,a5 ; mv a4,a7 ; callind s3 ; add sp,+16 @@ -767,8 +767,8 @@ block0(v0: i128, v1: i64): ; sd s3, -8(sp) ; addi sp, sp, -0x10 ; block1: ; offset 0x18 -; mv a7, a0 ; mv a6, a2 +; mv a7, a0 ; addi sp, sp, -0x10 ; sd a1, 0(sp) ; mv a5, a1 @@ -777,10 +777,10 @@ block0(v0: i128, v1: i64): ; j 0xc ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %f15 0 ; .byte 0x00, 0x00, 0x00, 0x00 -; mv a1, a5 -; mv a3, a5 ; mv a0, a7 +; mv a1, a5 ; mv a2, a7 +; mv a3, a5 ; mv a4, a7 ; jalr s3 ; addi sp, sp, 0x10 diff --git a/cranelift/filetests/filetests/isa/riscv64/reftypes.clif b/cranelift/filetests/filetests/isa/riscv64/reftypes.clif index 85bcfdadfce9..8865924d54a5 100644 --- a/cranelift/filetests/filetests/isa/riscv64/reftypes.clif +++ b/cranelift/filetests/filetests/isa/riscv64/reftypes.clif @@ -95,8 +95,8 @@ block3(v7: r64, v8: r64): ; add sp,-48 ; block0: ; mv a3,a0 -; sd a1,16(nominal_sp) ; mv s5,a2 +; sd a1,16(nominal_sp) ; mv a3,a0 ; mv s9,a3 ; load_sym a5,%f+0 @@ -107,8 +107,8 @@ block3(v7: r64, v8: r64): ; andi a5,a0,255 ; bne a5,zero,taken(label2),not_taken(label1) ; block1: -; mv a1,s9 ; ld a0,16(nominal_sp) +; mv a1,s9 ; j label3 ; block2: ; mv a0,s9 @@ -137,8 +137,8 @@ block3(v7: r64, v8: r64): ; addi sp, sp, -0x30 ; block1: ; offset 0x1c ; mv a3, a0 -; sd a1, 0x10(sp) ; mv s5, a2 +; sd a1, 0x10(sp) ; mv a0, a3 ; mv s9, a3 ; auipc a5, 0 @@ -153,8 +153,8 @@ block3(v7: r64, v8: r64): ; andi a5, a0, 0xff ; bnez a5, 0x10 ; block2: ; offset 0x5c -; mv a1, s9 ; ld a0, 0x10(sp) +; mv a1, s9 ; j 0xc ; block3: ; offset 0x68 ; mv a0, s9 diff --git a/cranelift/filetests/filetests/isa/riscv64/return-call.clif b/cranelift/filetests/filetests/isa/riscv64/return-call.clif index ae7d8951717f..a119160e1f08 100644 --- a/cranelift/filetests/filetests/isa/riscv64/return-call.clif +++ b/cranelift/filetests/filetests/isa/riscv64/return-call.clif @@ -324,8 +324,8 @@ block0: ; sd s1,24(sp) ; sd a0,32(sp) ; load_sym t0,%tail_callee_stack_args+0 -; ld a0,0(nominal_sp) ; ld s1,8(nominal_sp) +; ld a0,0(nominal_sp) ; return_call_ind t0 old_stack_arg_size:0 new_stack_arg_size:48 s1=s1 a0=a0 a1=a1 a2=a2 a3=a3 a4=a4 a5=a5 a6=a6 a7=a7 s2=s2 s3=s3 s4=s4 s5=s5 s6=s6 s7=s7 s8=s8 s9=s9 s10=s10 s11=s11 t3=t3 t4=t4 ; ; Disassembled: @@ -375,8 +375,8 @@ block0: ; j 0xc ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %tail_callee_stack_args 0 ; .byte 0x00, 0x00, 0x00, 0x00 -; ld a0, 0x30(sp) ; ld s1, 0x38(sp) +; ld a0, 0x30(sp) ; ld ra, 8(s0) ; ld t6, 0(s0) ; ld t5, 0x28(sp) @@ -568,9 +568,9 @@ block2: ; sd t0,32(sp) ; sd s1,40(sp) ; load_sym t0,%different_callee2+0 -; ld a1,0(nominal_sp) -; ld a0,8(nominal_sp) ; ld s1,16(nominal_sp) +; ld a0,8(nominal_sp) +; ld a1,0(nominal_sp) ; return_call_ind t0 old_stack_arg_size:0 new_stack_arg_size:48 s1=s1 a0=a0 a1=a1 a2=a2 a3=a3 a4=a4 a5=a5 a6=a6 a7=a7 s2=s2 s3=s3 s4=s4 s5=s5 s6=s6 s7=s7 s8=s8 s9=s9 s10=s10 s11=s11 t3=t3 t4=t4 ; block2: ; ld s1,16(nominal_sp) @@ -582,8 +582,8 @@ block2: ; sd t1,24(sp) ; sd t0,32(sp) ; load_sym t0,%different_callee1+0 -; ld a1,0(nominal_sp) ; ld a0,8(nominal_sp) +; ld a1,0(nominal_sp) ; return_call_ind t0 old_stack_arg_size:0 new_stack_arg_size:48 s1=s1 a0=a0 a1=a1 a2=a2 a3=a3 a4=a4 a5=a5 a6=a6 a7=a7 s2=s2 s3=s3 s4=s4 s5=s5 s6=s6 s7=s7 s8=s8 s9=s9 s10=s10 s11=s11 t3=t3 t4=t4 ; ; Disassembled: @@ -638,9 +638,9 @@ block2: ; j 0xc ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %different_callee2 0 ; .byte 0x00, 0x00, 0x00, 0x00 -; ld a1, 0x30(sp) -; ld a0, 0x38(sp) ; ld s1, 0x40(sp) +; ld a0, 0x38(sp) +; ld a1, 0x30(sp) ; ld ra, 8(s0) ; ld t6, 0(s0) ; ld t5, 0x28(sp) @@ -671,8 +671,8 @@ block2: ; j 0xc ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %different_callee1 0 ; .byte 0x00, 0x00, 0x00, 0x00 -; ld a1, 0x30(sp) ; ld a0, 0x38(sp) +; ld a1, 0x30(sp) ; ld ra, 8(s0) ; ld t6, 0(s0) ; ld t5, 0x28(sp) diff --git a/cranelift/filetests/filetests/isa/riscv64/select.clif b/cranelift/filetests/filetests/isa/riscv64/select.clif index 57b9ec24c866..1a7a6b98cb7a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/select.clif +++ b/cranelift/filetests/filetests/isa/riscv64/select.clif @@ -629,8 +629,8 @@ block0(v0: i64, v1: i128, v2: i128): ; sd s6,-16(sp) ; add sp,-16 ; block0: -; mv s6,a0 ; mv s4,a1 +; mv s6,a0 ; li a5,42 ; select [a0,a1],[s4,a2],[a3,a4]##condition=(s6 eq a5) ; add sp,+16 @@ -651,8 +651,8 @@ block0(v0: i64, v1: i128, v2: i128): ; sd s6, -0x10(sp) ; addi sp, sp, -0x10 ; block1: ; offset 0x1c -; mv s6, a0 ; mv s4, a1 +; mv s6, a0 ; addi a5, zero, 0x2a ; bne s6, a5, 0x10 ; mv a0, s4 diff --git a/cranelift/filetests/filetests/isa/riscv64/stack.clif b/cranelift/filetests/filetests/isa/riscv64/stack.clif index 08a73c80436c..a5215b51f4c2 100644 --- a/cranelift/filetests/filetests/isa/riscv64/stack.clif +++ b/cranelift/filetests/filetests/isa/riscv64/stack.clif @@ -560,11 +560,11 @@ block0(v0: i8): ; ld t1,1160(nominal_sp) ; ld t2,1168(nominal_sp) ; add t1,t2,t1 -; ld t4,1144(nominal_sp) ; ld a7,1152(nominal_sp) +; ld t4,1144(nominal_sp) ; add t2,a7,t4 -; ld s5,1128(nominal_sp) ; ld s3,1136(nominal_sp) +; ld s5,1128(nominal_sp) ; add a6,s3,s5 ; ld a7,1112(nominal_sp) ; ld t3,1120(nominal_sp) @@ -584,8 +584,8 @@ block0(v0: i8): ; ld s3,1032(nominal_sp) ; ld s4,1040(nominal_sp) ; add s3,s4,s3 -; ld s5,1016(nominal_sp) ; ld s4,1024(nominal_sp) +; ld s5,1016(nominal_sp) ; add s4,s4,s5 ; ld s5,1008(nominal_sp) ; add s5,s5,s6 @@ -766,11 +766,11 @@ block0(v0: i8): ; ld t1, 0x488(sp) ; ld t2, 0x490(sp) ; add t1, t2, t1 -; ld t4, 0x478(sp) ; ld a7, 0x480(sp) +; ld t4, 0x478(sp) ; add t2, a7, t4 -; ld s5, 0x468(sp) ; ld s3, 0x470(sp) +; ld s5, 0x468(sp) ; add a6, s3, s5 ; ld a7, 0x458(sp) ; ld t3, 0x460(sp) @@ -790,8 +790,8 @@ block0(v0: i8): ; ld s3, 0x408(sp) ; ld s4, 0x410(sp) ; add s3, s4, s3 -; ld s5, 0x3f8(sp) ; ld s4, 0x400(sp) +; ld s5, 0x3f8(sp) ; add s4, s4, s5 ; ld s5, 0x3f0(sp) ; add s5, s5, s6 diff --git a/cranelift/filetests/filetests/isa/riscv64/tail-call-conv.clif b/cranelift/filetests/filetests/isa/riscv64/tail-call-conv.clif index f8f17dea411a..d927079c460a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/tail-call-conv.clif +++ b/cranelift/filetests/filetests/isa/riscv64/tail-call-conv.clif @@ -120,8 +120,8 @@ block0: ; sd s1,24(sp) ; sd a0,32(sp) ; load_sym t0,%tail_callee_stack_args+0 -; ld a0,0(nominal_sp) ; ld s1,8(nominal_sp) +; ld a0,0(nominal_sp) ; callind t0 ; add sp,+16 ; ld ra,8(sp) @@ -176,8 +176,8 @@ block0: ; j 0xc ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %tail_callee_stack_args 0 ; .byte 0x00, 0x00, 0x00, 0x00 -; ld a0, 0x30(sp) ; ld s1, 0x38(sp) +; ld a0, 0x30(sp) ; jalr t0 ; addi sp, sp, 0x10 ; ld ra, 8(sp) @@ -259,9 +259,9 @@ block0: ; sd t2,16(s1) ; sd a0,24(s1) ; sd a1,32(s1) -; ld a1,0(nominal_sp) -; ld a0,8(nominal_sp) ; ld s1,16(nominal_sp) +; ld a0,8(nominal_sp) +; ld a1,0(nominal_sp) ; add sp,+32 ; ld ra,8(sp) ; ld fp,0(sp) @@ -310,9 +310,9 @@ block0: ; sd t2, 0x10(s1) ; sd a0, 0x18(s1) ; sd a1, 0x20(s1) -; ld a1, 0(sp) -; ld a0, 8(sp) ; ld s1, 0x10(sp) +; ld a0, 8(sp) +; ld a1, 0(sp) ; addi sp, sp, 0x20 ; ld ra, 8(sp) ; ld s0, 0(sp) @@ -526,8 +526,8 @@ block0: ; load_addr a0,48(sp) ; sd a0,40(sp) ; load_sym t0,%tail_callee_stack_args_and_rets+0 -; ld a0,0(nominal_sp) ; ld s1,8(nominal_sp) +; ld a0,0(nominal_sp) ; callind t0 ; ld a4,0(sp) ; ld a0,8(sp) @@ -591,8 +591,8 @@ block0: ; j 0xc ; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %tail_callee_stack_args_and_rets 0 ; .byte 0x00, 0x00, 0x00, 0x00 -; ld a0, 0x60(sp) ; ld s1, 0x68(sp) +; ld a0, 0x60(sp) ; jalr t0 ; ld a4, 0(sp) ; ld a0, 8(sp) diff --git a/cranelift/filetests/filetests/isa/s390x/atomic_rmw-arch13.clif b/cranelift/filetests/filetests/isa/s390x/atomic_rmw-arch13.clif index 3aa194f547e4..7864a67bb2bf 100644 --- a/cranelift/filetests/filetests/isa/s390x/atomic_rmw-arch13.clif +++ b/cranelift/filetests/filetests/isa/s390x/atomic_rmw-arch13.clif @@ -57,25 +57,23 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 48, 16 ; xilf %r1, 4294901760 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 48, 16 ; xilf %r1, 4294901760 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 0x20, 0x30, 0x10 ; xilf %r1, 0xffff0000 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r5) -; jglh 0x12 +; cs %r0, %r1, 0(%r3) +; jglh 0xe ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -88,27 +86,25 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; xilf %r1, 4278190080 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; xilf %r1, 4278190080 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 0x20, 0x28, 0x18 ; xilf %r1, 0xff000000 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -170,31 +166,27 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; lrvr %r3, %r5 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; rnsbg %r1, %r3, 48, 64, 48 ; xilf %r1, 65535 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; lrvr %r4, %r4 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; rnsbg %r1, %r4, 48, 64, 48 ; xilf %r1, 65535 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; lrvr %r3, %r5 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; lrvr %r4, %r4 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) -; rnsbg %r1, %r3, 0x30, 0x40, 0x30 +; rnsbg %r1, %r4, 0x30, 0x40, 0x30 ; xilf %r1, 0xffff ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1a +; cs %r0, %r1, 0(%r3) +; jglh 0x12 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -208,27 +200,25 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; xilf %r1, 4278190080 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; xilf %r1, 4278190080 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 0x20, 0x28, 0x18 ; xilf %r1, 0xff000000 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/atomic_rmw-little.clif b/cranelift/filetests/filetests/isa/s390x/atomic_rmw-little.clif index b113d1c94c9e..d8bbbecddf3b 100644 --- a/cranelift/filetests/filetests/isa/s390x/atomic_rmw-little.clif +++ b/cranelift/filetests/filetests/isa/s390x/atomic_rmw-little.clif @@ -59,30 +59,26 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; lrvr %r3, %r5 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; risbgn %r1, %r3, 48, 64, 48 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; lrvr %r4, %r4 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; risbgn %r1, %r4, 48, 64, 48 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; lrvr %r3, %r5 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; lrvr %r4, %r4 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) -; risbgn %r1, %r3, 0x30, 0x40, 0x30 +; risbgn %r1, %r4, 0x30, 0x40, 0x30 ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1a +; cs %r0, %r1, 0(%r3) +; jglh 0x12 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -96,26 +92,24 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; risbgn %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; risbgn %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; risbgn %r1, %r4, 0x20, 0x28, 0x18 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -175,32 +169,28 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; ar %r1, %r3 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; ar %r1, %r4 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) ; lrvr %r1, %r1 -; ar %r1, %r3 +; ar %r1, %r4 ; lrvr %r1, %r1 ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -214,28 +204,26 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; ar %r1, %r3 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; ar %r1, %r4 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; ar %r1, %r3 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; ar %r1, %r4 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -295,32 +283,28 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; sr %r1, %r3 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; sr %r1, %r4 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) ; lrvr %r1, %r1 -; sr %r1, %r3 +; sr %r1, %r4 ; lrvr %r1, %r1 ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -334,28 +318,26 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; sr %r1, %r3 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; sr %r1, %r4 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; sr %r1, %r3 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; sr %r1, %r4 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -407,30 +389,26 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; lrvr %r3, %r5 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; rnsbg %r1, %r3, 48, 64, 48 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; lrvr %r4, %r4 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; rnsbg %r1, %r4, 48, 64, 48 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; lrvr %r3, %r5 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; lrvr %r4, %r4 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) -; rnsbg %r1, %r3, 0x30, 0x40, 0x30 +; rnsbg %r1, %r4, 0x30, 0x40, 0x30 ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1a +; cs %r0, %r1, 0(%r3) +; jglh 0x12 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -444,26 +422,24 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 0x20, 0x28, 0x18 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -515,30 +491,26 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; lrvr %r3, %r5 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; rosbg %r1, %r3, 48, 64, 48 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; lrvr %r4, %r4 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; rosbg %r1, %r4, 48, 64, 48 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; lrvr %r3, %r5 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; lrvr %r4, %r4 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) -; rosbg %r1, %r3, 0x30, 0x40, 0x30 +; rosbg %r1, %r4, 0x30, 0x40, 0x30 ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1a +; cs %r0, %r1, 0(%r3) +; jglh 0x12 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -552,26 +524,24 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rosbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rosbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rosbg %r1, %r4, 0x20, 0x28, 0x18 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -623,30 +593,26 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; lrvr %r3, %r5 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; rxsbg %r1, %r3, 48, 64, 48 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; lrvr %r4, %r4 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; rxsbg %r1, %r4, 48, 64, 48 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; lrvr %r3, %r5 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; lrvr %r4, %r4 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) -; rxsbg %r1, %r3, 0x30, 0x40, 0x30 +; rxsbg %r1, %r4, 0x30, 0x40, 0x30 ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1a +; cs %r0, %r1, 0(%r3) +; jglh 0x12 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -660,26 +626,24 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rxsbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rxsbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rxsbg %r1, %r4, 0x20, 0x28, 0x18 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -742,31 +706,27 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; lrvr %r3, %r5 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; rnsbg %r1, %r3, 48, 64, 48 ; xilf %r1, 65535 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; lrvr %r4, %r4 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; rnsbg %r1, %r4, 48, 64, 48 ; xilf %r1, 65535 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; lrvr %r3, %r5 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; lrvr %r4, %r4 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) -; rnsbg %r1, %r3, 0x30, 0x40, 0x30 +; rnsbg %r1, %r4, 0x30, 0x40, 0x30 ; xilf %r1, 0xffff ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1a +; cs %r0, %r1, 0(%r3) +; jglh 0x12 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -780,27 +740,25 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; xilf %r1, 4278190080 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; xilf %r1, 4278190080 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 0x20, 0x28, 0x18 ; xilf %r1, 0xff000000 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -862,34 +820,30 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; cr %r3, %r1 ; jgnl 1f ; risbgn %r1, %r3, 32, 48, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; cr %r4, %r1 ; jgnl 1f ; risbgn %r1, %r4, 32, 48, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) ; lrvr %r1, %r1 -; cr %r3, %r1 -; jgnl 0x48 -; risbgn %r1, %r3, 0x20, 0x30, 0 +; cr %r4, %r1 +; jgnl 0x40 +; risbgn %r1, %r4, 0x20, 0x30, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -903,30 +857,28 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; cr %r3, %r1 ; jgnl 1f ; risbgn %r1, %r3, 32, 40, 0 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; cr %r4, %r1 ; jgnl 1f ; risbgn %r1, %r4, 32, 40, 0 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; cr %r3, %r1 -; jgnl 0x3e -; risbgn %r1, %r3, 0x20, 0x28, 0 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; cr %r4, %r1 +; jgnl 0x3a +; risbgn %r1, %r4, 0x20, 0x28, 0 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -988,34 +940,30 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; cr %r3, %r1 ; jgnh 1f ; risbgn %r1, %r3, 32, 48, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; cr %r4, %r1 ; jgnh 1f ; risbgn %r1, %r4, 32, 48, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) ; lrvr %r1, %r1 -; cr %r3, %r1 -; jgnh 0x48 -; risbgn %r1, %r3, 0x20, 0x30, 0 +; cr %r4, %r1 +; jgnh 0x40 +; risbgn %r1, %r4, 0x20, 0x30, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -1029,30 +977,28 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; cr %r3, %r1 ; jgnh 1f ; risbgn %r1, %r3, 32, 40, 0 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; cr %r4, %r1 ; jgnh 1f ; risbgn %r1, %r4, 32, 40, 0 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; cr %r3, %r1 -; jgnh 0x3e -; risbgn %r1, %r3, 0x20, 0x28, 0 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; cr %r4, %r1 +; jgnh 0x3a +; risbgn %r1, %r4, 0x20, 0x28, 0 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -1114,34 +1060,30 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; clr %r3, %r1 ; jgnl 1f ; risbgn %r1, %r3, 32, 48, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; clr %r4, %r1 ; jgnl 1f ; risbgn %r1, %r4, 32, 48, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) ; lrvr %r1, %r1 -; clr %r3, %r1 -; jgnl 0x48 -; risbgn %r1, %r3, 0x20, 0x30, 0 +; clr %r4, %r1 +; jgnl 0x40 +; risbgn %r1, %r4, 0x20, 0x30, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -1155,30 +1097,28 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; clr %r3, %r1 ; jgnl 1f ; risbgn %r1, %r3, 32, 40, 0 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; clr %r4, %r1 ; jgnl 1f ; risbgn %r1, %r4, 32, 40, 0 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; clr %r3, %r1 -; jgnl 0x3e -; risbgn %r1, %r3, 0x20, 0x28, 0 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; clr %r4, %r1 +; jgnl 0x3a +; risbgn %r1, %r4, 0x20, 0x28, 0 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -1240,34 +1180,30 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; clr %r3, %r1 ; jgnh 1f ; risbgn %r1, %r3, 32, 48, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 16(%r2) ; lrvr %r1, %r1 ; clr %r4, %r1 ; jgnh 1f ; risbgn %r1, %r4, 32, 48, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 16(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0x10(%r2) ; lrvr %r1, %r1 -; clr %r3, %r1 -; jgnh 0x48 -; risbgn %r1, %r3, 0x20, 0x30, 0 +; clr %r4, %r1 +; jgnh 0x40 +; risbgn %r1, %r4, 0x20, 0x30, 0 ; lrvr %r1, %r1 ; rll %r1, %r1, 0x10(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0(%r2) ; lrvr %r2, %r2 ; br %r14 @@ -1281,30 +1217,28 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; clr %r3, %r1 ; jgnh 1f ; risbgn %r1, %r3, 32, 40, 0 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; clr %r4, %r1 ; jgnh 1f ; risbgn %r1, %r4, 32, 40, 0 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; clr %r3, %r1 -; jgnh 0x3e -; risbgn %r1, %r3, 0x20, 0x28, 0 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; clr %r4, %r1 +; jgnh 0x3a +; risbgn %r1, %r4, 0x20, 0x28, 0 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/atomic_rmw.clif b/cranelift/filetests/filetests/isa/s390x/atomic_rmw.clif index 50366a46feff..3ab94f59f473 100644 --- a/cranelift/filetests/filetests/isa/s390x/atomic_rmw.clif +++ b/cranelift/filetests/filetests/isa/s390x/atomic_rmw.clif @@ -56,24 +56,22 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; risbgn %r1, %r4, 32, 48, 16 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; risbgn %r1, %r4, 32, 48, 16 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; risbgn %r1, %r4, 0x20, 0x30, 0x10 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r5) -; jglh 0x12 +; cs %r0, %r1, 0(%r3) +; jglh 0xe ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -86,26 +84,24 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; risbgn %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; risbgn %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; risbgn %r1, %r4, 0x20, 0x28, 0x18 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -149,29 +145,25 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 0(%r2) ; ar %r1, %r3 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; ar %r1, %r4 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; ar %r1, %r3 +; ar %r1, %r4 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -184,28 +176,26 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; ar %r1, %r3 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; ar %r1, %r4 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; ar %r1, %r3 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; ar %r1, %r4 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -253,29 +243,25 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 0(%r2) ; sr %r1, %r3 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; sr %r1, %r4 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; sr %r1, %r3 +; sr %r1, %r4 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -288,28 +274,26 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; sr %r1, %r3 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; sr %r1, %r4 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; sr %r1, %r3 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; sr %r1, %r4 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -354,24 +338,22 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 48, 16 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 48, 16 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 0x20, 0x30, 0x10 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r5) -; jglh 0x12 +; cs %r0, %r1, 0(%r3) +; jglh 0xe ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -384,26 +366,24 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 0x20, 0x28, 0x18 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -448,24 +428,22 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rosbg %r1, %r4, 32, 48, 16 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rosbg %r1, %r4, 32, 48, 16 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rosbg %r1, %r4, 0x20, 0x30, 0x10 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r5) -; jglh 0x12 +; cs %r0, %r1, 0(%r3) +; jglh 0xe ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -478,26 +456,24 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rosbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rosbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rosbg %r1, %r4, 0x20, 0x28, 0x18 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -542,24 +518,22 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rxsbg %r1, %r4, 32, 48, 16 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rxsbg %r1, %r4, 32, 48, 16 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rxsbg %r1, %r4, 0x20, 0x30, 0x10 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r5) -; jglh 0x12 +; cs %r0, %r1, 0(%r3) +; jglh 0xe ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -572,26 +546,24 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rxsbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rxsbg %r1, %r4, 32, 40, 24 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rxsbg %r1, %r4, 0x20, 0x28, 0x18 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -651,25 +623,23 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 48, 16 ; xilf %r1, 4294901760 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 48, 16 ; xilf %r1, 4294901760 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 0x20, 0x30, 0x10 ; xilf %r1, 0xffff0000 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r5) -; jglh 0x12 +; cs %r0, %r1, 0(%r3) +; jglh 0xe ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -682,27 +652,25 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; lcr %r3, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; xilf %r1, 4278190080 ; rll %r1, %r1, 0(%r3) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 32, 40, 24 ; xilf %r1, 4278190080 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; lcr %r3, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) ; rnsbg %r1, %r4, 0x20, 0x28, 0x18 ; xilf %r1, 0xff000000 -; rll %r1, %r1, 0(%r3) -; cs %r0, %r1, 0(%r5) -; jglh 0x14 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x10 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -760,31 +728,27 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 0(%r2) ; cr %r3, %r1 ; jgnl 1f ; risbgn %r1, %r3, 32, 48, 0 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; cr %r4, %r1 ; jgnl 1f ; risbgn %r1, %r4, 32, 48, 0 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; cr %r3, %r1 -; jgnl 0x40 -; risbgn %r1, %r3, 0x20, 0x30, 0 +; cr %r4, %r1 +; jgnl 0x38 +; risbgn %r1, %r4, 0x20, 0x30, 0 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -797,30 +761,28 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; cr %r3, %r1 ; jgnl 1f ; risbgn %r1, %r3, 32, 40, 0 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; cr %r4, %r1 ; jgnl 1f ; risbgn %r1, %r4, 32, 40, 0 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; cr %r3, %r1 -; jgnl 0x3e -; risbgn %r1, %r3, 0x20, 0x28, 0 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; cr %r4, %r1 +; jgnl 0x3a +; risbgn %r1, %r4, 0x20, 0x28, 0 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -878,31 +840,27 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 0(%r2) ; cr %r3, %r1 ; jgnh 1f ; risbgn %r1, %r3, 32, 48, 0 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; cr %r4, %r1 ; jgnh 1f ; risbgn %r1, %r4, 32, 48, 0 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; cr %r3, %r1 -; jgnh 0x40 -; risbgn %r1, %r3, 0x20, 0x30, 0 +; cr %r4, %r1 +; jgnh 0x38 +; risbgn %r1, %r4, 0x20, 0x30, 0 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -915,30 +873,28 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; cr %r3, %r1 ; jgnh 1f ; risbgn %r1, %r3, 32, 40, 0 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; cr %r4, %r1 ; jgnh 1f ; risbgn %r1, %r4, 32, 40, 0 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; cr %r3, %r1 -; jgnh 0x3e -; risbgn %r1, %r3, 0x20, 0x28, 0 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; cr %r4, %r1 +; jgnh 0x3a +; risbgn %r1, %r4, 0x20, 0x28, 0 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -996,31 +952,27 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 0(%r2) ; clr %r3, %r1 ; jgnl 1f ; risbgn %r1, %r3, 32, 48, 0 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; clr %r4, %r1 ; jgnl 1f ; risbgn %r1, %r4, 32, 48, 0 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; clr %r3, %r1 -; jgnl 0x40 -; risbgn %r1, %r3, 0x20, 0x30, 0 +; clr %r4, %r1 +; jgnl 0x38 +; risbgn %r1, %r4, 0x20, 0x30, 0 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -1033,30 +985,28 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; clr %r3, %r1 ; jgnl 1f ; risbgn %r1, %r3, 32, 40, 0 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; clr %r4, %r1 ; jgnl 1f ; risbgn %r1, %r4, 32, 40, 0 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; clr %r3, %r1 -; jgnl 0x3e -; risbgn %r1, %r3, 0x20, 0x28, 0 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; clr %r4, %r1 +; jgnl 0x3a +; risbgn %r1, %r4, 0x20, 0x28, 0 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 @@ -1114,31 +1064,27 @@ block0(v0: i64, v1: i64, v2: i16): ; VCode: ; block0: -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 65532 -; sllk %r3, %r5, 16 -; l %r0, 0(%r4) -; 0: rll %r1, %r0, 0(%r2) ; clr %r3, %r1 ; jgnh 1f ; risbgn %r1, %r3, 32, 48, 0 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r4) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 16 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; clr %r4, %r1 ; jgnh 1f ; risbgn %r1, %r4, 32, 48, 0 ; rll %r1, %r1, 0(%r2) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 16(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r4 ; sllk %r2, %r3, 3 -; lgr %r4, %r3 -; nill %r4, 0xfffc -; sllk %r3, %r5, 0x10 -; l %r0, 0(%r4) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x10 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; clr %r3, %r1 -; jgnh 0x40 -; risbgn %r1, %r3, 0x20, 0x30, 0 +; clr %r4, %r1 +; jgnh 0x38 +; risbgn %r1, %r4, 0x20, 0x30, 0 ; rll %r1, %r1, 0(%r2) -; cs %r0, %r1, 0(%r4) -; jglh 0x1c +; cs %r0, %r1, 0(%r3) +; jglh 0x14 ; rll %r2, %r0, 0x10(%r2) ; br %r14 @@ -1151,30 +1097,28 @@ block0(v0: i64, v1: i64, v2: i8): ; VCode: ; block0: ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 65532 -; sllk %r3, %r4, 24 -; lcr %r4, %r2 -; l %r0, 0(%r5) -; 0: rll %r1, %r0, 0(%r2) ; clr %r3, %r1 ; jgnh 1f ; risbgn %r1, %r3, 32, 40, 0 ; rll %r1, %r1, 0(%r4) ; cs %r0, %r1, 0(%r5) ; jglh 0b ; 1: +; nill %r3, 65532 +; sllk %r4, %r4, 24 +; lcr %r5, %r2 +; l %r0, 0(%r3) +; 0: rll %r1, %r0, 0(%r2) ; clr %r4, %r1 ; jgnh 1f ; risbgn %r1, %r4, 32, 40, 0 ; rll %r1, %r1, 0(%r5) ; cs %r0, %r1, 0(%r3) ; jglh 0b ; 1: ; rll %r2, %r0, 8(%r2) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; sllk %r2, %r3, 3 -; lgr %r5, %r3 -; nill %r5, 0xfffc -; sllk %r3, %r4, 0x18 -; lcr %r4, %r2 -; l %r0, 0(%r5) +; nill %r3, 0xfffc +; sllk %r4, %r4, 0x18 +; lcr %r5, %r2 +; l %r0, 0(%r3) ; rll %r1, %r0, 0(%r2) -; clr %r3, %r1 -; jgnh 0x3e -; risbgn %r1, %r3, 0x20, 0x28, 0 -; rll %r1, %r1, 0(%r4) -; cs %r0, %r1, 0(%r5) -; jglh 0x1a +; clr %r4, %r1 +; jgnh 0x3a +; risbgn %r1, %r4, 0x20, 0x28, 0 +; rll %r1, %r1, 0(%r5) +; cs %r0, %r1, 0(%r3) +; jglh 0x16 ; rll %r2, %r0, 8(%r2) ; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/bitops.clif b/cranelift/filetests/filetests/isa/s390x/bitops.clif index eda5849401a2..62fc004022bd 100644 --- a/cranelift/filetests/filetests/isa/s390x/bitops.clif +++ b/cranelift/filetests/filetests/isa/s390x/bitops.clif @@ -603,10 +603,9 @@ block0(v0: i64): ; lcgr %r4, %r2 ; ngr %r2, %r4 ; flogr %r2, %r2 -; lgr %r3, %r2 -; locghie %r3, -1 +; locghie %r2, -1 ; lghi %r5, 63 -; sgrk %r2, %r5, %r3 +; sgrk %r2, %r5, %r2 ; br %r14 ; ; Disassembled: @@ -614,10 +613,9 @@ block0(v0: i64): ; lcgr %r4, %r2 ; ngr %r2, %r4 ; flogr %r2, %r2 -; lgr %r3, %r2 -; locghie %r3, -1 +; locghie %r2, -1 ; lghi %r5, 0x3f -; sgrk %r2, %r5, %r3 +; sgrk %r2, %r5, %r2 ; br %r14 function %ctz_i32(i32) -> i32 { @@ -628,10 +626,9 @@ block0(v0: i32): ; VCode: ; block0: -; lgr %r4, %r2 -; oihl %r4, 1 -; lcgr %r2, %r4 -; ngr %r4, %r2 +; oihl %r2, 1 +; lcgr %r3, %r2 +; ngrk %r4, %r2, %r3 ; flogr %r2, %r4 ; lhi %r5, 63 ; srk %r2, %r5, %r2 @@ -639,10 +636,9 @@ block0(v0: i32): ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r4, %r2 -; oihl %r4, 1 -; lcgr %r2, %r4 -; ngr %r4, %r2 +; oihl %r2, 1 +; lcgr %r3, %r2 +; ngrk %r4, %r2, %r3 ; flogr %r2, %r4 ; lhi %r5, 0x3f ; srk %r2, %r5, %r2 @@ -656,10 +652,9 @@ block0(v0: i16): ; VCode: ; block0: -; lgr %r4, %r2 -; oilh %r4, 1 -; lcgr %r2, %r4 -; ngr %r4, %r2 +; oilh %r2, 1 +; lcgr %r3, %r2 +; ngrk %r4, %r2, %r3 ; flogr %r2, %r4 ; lhi %r5, 63 ; srk %r2, %r5, %r2 @@ -667,10 +662,9 @@ block0(v0: i16): ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r4, %r2 -; oilh %r4, 1 -; lcgr %r2, %r4 -; ngr %r4, %r2 +; oilh %r2, 1 +; lcgr %r3, %r2 +; ngrk %r4, %r2, %r3 ; flogr %r2, %r4 ; lhi %r5, 0x3f ; srk %r2, %r5, %r2 @@ -684,10 +678,9 @@ block0(v0: i8): ; VCode: ; block0: -; lgr %r4, %r2 -; oill %r4, 256 -; lcgr %r2, %r4 -; ngr %r4, %r2 +; oill %r2, 256 +; lcgr %r3, %r2 +; ngrk %r4, %r2, %r3 ; flogr %r2, %r4 ; lhi %r5, 63 ; srk %r2, %r5, %r2 @@ -695,10 +688,9 @@ block0(v0: i8): ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r4, %r2 -; oill %r4, 0x100 -; lcgr %r2, %r4 -; ngr %r4, %r2 +; oill %r2, 0x100 +; lcgr %r3, %r2 +; ngrk %r4, %r2, %r3 ; flogr %r2, %r4 ; lhi %r5, 0x3f ; srk %r2, %r5, %r2 diff --git a/cranelift/filetests/filetests/isa/s390x/bitwise.clif b/cranelift/filetests/filetests/isa/s390x/bitwise.clif index cee8bfd674d3..54506197e4ea 100644 --- a/cranelift/filetests/filetests/isa/s390x/bitwise.clif +++ b/cranelift/filetests/filetests/isa/s390x/bitwise.clif @@ -936,20 +936,18 @@ block0(v0: i64, v1: i64, v2: i64): ; VCode: ; block0: ; ngr %r3, %r2 -; lgr %r5, %r2 -; xilf %r5, 4294967295 -; xihf %r5, 4294967295 -; ngr %r4, %r5 +; xilf %r2, 4294967295 +; xihf %r2, 4294967295 +; ngr %r4, %r2 ; ogrk %r2, %r4, %r3 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; ngr %r3, %r2 -; lgr %r5, %r2 -; xilf %r5, 0xffffffff -; xihf %r5, 0xffffffff -; ngr %r4, %r5 +; xilf %r2, 0xffffffff +; xihf %r2, 0xffffffff +; ngr %r4, %r2 ; ogrk %r2, %r4, %r3 ; br %r14 @@ -962,18 +960,16 @@ block0(v0: i32, v1: i32, v2: i32): ; VCode: ; block0: ; nr %r3, %r2 -; lgr %r5, %r2 -; xilf %r5, 4294967295 -; nrk %r2, %r4, %r5 +; xilf %r2, 4294967295 +; nrk %r2, %r4, %r2 ; or %r2, %r3 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; nr %r3, %r2 -; lgr %r5, %r2 -; xilf %r5, 0xffffffff -; nrk %r2, %r4, %r5 +; xilf %r2, 0xffffffff +; nrk %r2, %r4, %r2 ; or %r2, %r3 ; br %r14 @@ -986,18 +982,16 @@ block0(v0: i16, v1: i16, v2: i16): ; VCode: ; block0: ; nr %r3, %r2 -; lgr %r5, %r2 -; xilf %r5, 4294967295 -; nrk %r2, %r4, %r5 +; xilf %r2, 4294967295 +; nrk %r2, %r4, %r2 ; or %r2, %r3 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; nr %r3, %r2 -; lgr %r5, %r2 -; xilf %r5, 0xffffffff -; nrk %r2, %r4, %r5 +; xilf %r2, 0xffffffff +; nrk %r2, %r4, %r2 ; or %r2, %r3 ; br %r14 @@ -1010,18 +1004,16 @@ block0(v0: i8, v1: i8, v2: i8): ; VCode: ; block0: ; nr %r3, %r2 -; lgr %r5, %r2 -; xilf %r5, 4294967295 -; nrk %r2, %r4, %r5 +; xilf %r2, 4294967295 +; nrk %r2, %r4, %r2 ; or %r2, %r3 ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 ; nr %r3, %r2 -; lgr %r5, %r2 -; xilf %r5, 0xffffffff -; nrk %r2, %r4, %r5 +; xilf %r2, 0xffffffff +; nrk %r2, %r4, %r2 ; or %r2, %r3 ; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/reftypes.clif b/cranelift/filetests/filetests/isa/s390x/reftypes.clif index a879eb37c9c4..b2dc9a4aadfa 100644 --- a/cranelift/filetests/filetests/isa/s390x/reftypes.clif +++ b/cranelift/filetests/filetests/isa/s390x/reftypes.clif @@ -111,8 +111,8 @@ block3(v7: r64, v8: r64): ; chi %r2, 0 ; jglh label2 ; jg label1 ; block1: -; lgr %r3, %r11 ; lg %r2, 176(%r15) +; lgr %r3, %r11 ; jg label3 ; block2: ; lgr %r2, %r11 @@ -148,8 +148,8 @@ block3(v7: r64, v8: r64): ; chi %r2, 0 ; jglh 0x62 ; block2: ; offset 0x52 -; lgr %r3, %r11 ; lg %r2, 0xb0(%r15) +; lgr %r3, %r11 ; jg 0x6c ; block3: ; offset 0x62 ; lgr %r2, %r11 diff --git a/cranelift/filetests/filetests/isa/s390x/shift-rotate.clif b/cranelift/filetests/filetests/isa/s390x/shift-rotate.clif index 044a454395b3..c11ea0659971 100644 --- a/cranelift/filetests/filetests/isa/s390x/shift-rotate.clif +++ b/cranelift/filetests/filetests/isa/s390x/shift-rotate.clif @@ -915,16 +915,14 @@ block0(v0: i32, v1: i32): ; VCode: ; block0: -; lgr %r5, %r3 -; nill %r5, 31 -; srlk %r2, %r2, 0(%r5) +; nill %r3, 31 +; srlk %r2, %r2, 0(%r3) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r3 -; nill %r5, 0x1f -; srlk %r2, %r2, 0(%r5) +; nill %r3, 0x1f +; srlk %r2, %r2, 0(%r3) ; br %r14 function %ushr_i32_imm(i32) -> i32 { @@ -1230,16 +1228,14 @@ block0(v0: i32, v1: i32): ; VCode: ; block0: -; lgr %r5, %r3 -; nill %r5, 31 -; sllk %r2, %r2, 0(%r5) +; nill %r3, 31 +; sllk %r2, %r2, 0(%r3) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r3 -; nill %r5, 0x1f -; sllk %r2, %r2, 0(%r5) +; nill %r3, 0x1f +; sllk %r2, %r2, 0(%r3) ; br %r14 function %ishl_i32_imm(i32) -> i32 { @@ -1289,16 +1285,14 @@ block0(v0: i16, v1: i16): ; VCode: ; block0: -; lgr %r5, %r3 -; nill %r5, 15 -; sllk %r2, %r2, 0(%r5) +; nill %r3, 15 +; sllk %r2, %r2, 0(%r3) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r3 -; nill %r5, 0xf -; sllk %r2, %r2, 0(%r5) +; nill %r3, 0xf +; sllk %r2, %r2, 0(%r3) ; br %r14 function %ishl_i16_imm(i16) -> i16 { @@ -1348,16 +1342,14 @@ block0(v0: i8, v1: i8): ; VCode: ; block0: -; lgr %r5, %r3 -; nill %r5, 7 -; sllk %r2, %r2, 0(%r5) +; nill %r3, 7 +; sllk %r2, %r2, 0(%r3) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r3 -; nill %r5, 7 -; sllk %r2, %r2, 0(%r5) +; nill %r3, 7 +; sllk %r2, %r2, 0(%r3) ; br %r14 function %ishl_i8_imm(i8) -> i8 { @@ -1537,16 +1529,14 @@ block0(v0: i32, v1: i32): ; VCode: ; block0: -; lgr %r5, %r3 -; nill %r5, 31 -; srak %r2, %r2, 0(%r5) +; nill %r3, 31 +; srak %r2, %r2, 0(%r3) ; br %r14 ; ; Disassembled: ; block0: ; offset 0x0 -; lgr %r5, %r3 -; nill %r5, 0x1f -; srak %r2, %r2, 0(%r5) +; nill %r3, 0x1f +; srak %r2, %r2, 0(%r3) ; br %r14 function %sshr_i32_imm(i32) -> i32 { diff --git a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index daeb205fa0ba..a7b6dc32a222 100644 --- a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -48,10 +48,9 @@ ;; clgr %r2, %r5 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r4) +;; ag %r2, 0(%r4) ;; lghi %r4, 4096 -;; strv %r3, 0(%r4,%r5) +;; strv %r3, 0(%r4,%r2) ;; jg label2 ;; block2: ;; br %r14 @@ -67,10 +66,9 @@ ;; clgr %r2, %r4 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r4, %r2 -;; ag %r4, 0(%r3) +;; ag %r2, 0(%r3) ;; lghi %r3, 4096 -;; lrv %r2, 0(%r3,%r4) +;; lrv %r2, 0(%r3,%r2) ;; jg label2 ;; block2: ;; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat index 3c280d330546..50f1dc5ade66 100644 --- a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -53,10 +53,9 @@ ;; clgr %r5, %r14 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r4) +;; ag %r2, 0(%r4) ;; llilh %r4, 65535 -;; strv %r3, 0(%r4,%r5) +;; strv %r3, 0(%r4,%r2) ;; jg label2 ;; block2: ;; lmg %r14, %r15, 112(%r15) @@ -75,10 +74,9 @@ ;; clgr %r5, %r4 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r4, %r2 -;; ag %r4, 0(%r3) -;; llilh %r5, 65535 -;; lrv %r2, 0(%r5,%r4) +;; ag %r2, 0(%r3) +;; llilh %r4, 65535 +;; lrv %r2, 0(%r4,%r2) ;; jg label2 ;; block2: ;; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index d3f740891bc0..e25817dc31a9 100644 --- a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -48,10 +48,9 @@ ;; clgr %r2, %r5 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r4) +;; ag %r2, 0(%r4) ;; lghi %r4, 4096 -;; stc %r3, 0(%r4,%r5) +;; stc %r3, 0(%r4,%r2) ;; jg label2 ;; block2: ;; br %r14 @@ -67,10 +66,9 @@ ;; clgr %r2, %r4 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r4, %r2 -;; ag %r4, 0(%r3) +;; ag %r2, 0(%r3) ;; lghi %r3, 4096 -;; llc %r2, 0(%r3,%r4) +;; llc %r2, 0(%r3,%r2) ;; jg label2 ;; block2: ;; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat index 631727f67640..be59a9689720 100644 --- a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_dynamic_kind_i64_index_0_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -53,10 +53,9 @@ ;; clgr %r5, %r14 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r4) +;; ag %r2, 0(%r4) ;; llilh %r4, 65535 -;; stc %r3, 0(%r4,%r5) +;; stc %r3, 0(%r4,%r2) ;; jg label2 ;; block2: ;; lmg %r14, %r15, 112(%r15) @@ -75,10 +74,9 @@ ;; clgr %r5, %r4 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r4, %r2 -;; ag %r4, 0(%r3) -;; llilh %r5, 65535 -;; llc %r2, 0(%r5,%r4) +;; ag %r2, 0(%r3) +;; llilh %r4, 65535 +;; llc %r2, 0(%r4,%r2) ;; jg label2 ;; block2: ;; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat index 0d1ed849d45f..56cce5cd1cef 100644 --- a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i32_access_0x1000_offset.wat @@ -44,10 +44,9 @@ ;; clgfi %r2, 268431356 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r4) -;; lghi %r2, 4096 -;; strv %r3, 0(%r2,%r5) +;; ag %r2, 0(%r4) +;; lghi %r5, 4096 +;; strv %r3, 0(%r5,%r2) ;; jg label2 ;; block2: ;; br %r14 @@ -61,10 +60,9 @@ ;; clgfi %r2, 268431356 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r3) -;; lghi %r2, 4096 -;; lrv %r2, 0(%r2,%r5) +;; ag %r2, 0(%r3) +;; lghi %r5, 4096 +;; lrv %r2, 0(%r5,%r2) ;; jg label2 ;; block2: ;; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat index f9ed4aab9b37..99d2a21705f1 100644 --- a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0_guard_no_spectre_i8_access_0x1000_offset.wat @@ -44,10 +44,9 @@ ;; clgfi %r2, 268431359 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r4) -;; lghi %r2, 4096 -;; stc %r3, 0(%r2,%r5) +;; ag %r2, 0(%r4) +;; lghi %r5, 4096 +;; stc %r3, 0(%r5,%r2) ;; jg label2 ;; block2: ;; br %r14 @@ -61,10 +60,9 @@ ;; clgfi %r2, 268431359 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r3) -;; lghi %r2, 4096 -;; llc %r2, 0(%r2,%r5) +;; ag %r2, 0(%r3) +;; lghi %r5, 4096 +;; llc %r2, 0(%r5,%r2) ;; jg label2 ;; block2: ;; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat index 5e17082f889a..b74f04d06f9b 100644 --- a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0x1000_offset.wat @@ -44,10 +44,9 @@ ;; clgfi %r2, 268431356 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r4) -;; lghi %r2, 4096 -;; strv %r3, 0(%r2,%r5) +;; ag %r2, 0(%r4) +;; lghi %r5, 4096 +;; strv %r3, 0(%r5,%r2) ;; jg label2 ;; block2: ;; br %r14 @@ -61,10 +60,9 @@ ;; clgfi %r2, 268431356 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r3) -;; lghi %r2, 4096 -;; lrv %r2, 0(%r2,%r5) +;; ag %r2, 0(%r3) +;; lghi %r5, 4096 +;; lrv %r2, 0(%r5,%r2) ;; jg label2 ;; block2: ;; br %r14 diff --git a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat index d9442a3a1f20..1cc7a766fae8 100644 --- a/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/s390x/wasm/load_store_static_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0x1000_offset.wat @@ -44,10 +44,9 @@ ;; clgfi %r2, 268431359 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r4) -;; lghi %r2, 4096 -;; stc %r3, 0(%r2,%r5) +;; ag %r2, 0(%r4) +;; lghi %r5, 4096 +;; stc %r3, 0(%r5,%r2) ;; jg label2 ;; block2: ;; br %r14 @@ -61,10 +60,9 @@ ;; clgfi %r2, 268431359 ;; jgh label3 ; jg label1 ;; block1: -;; lgr %r5, %r2 -;; ag %r5, 0(%r3) -;; lghi %r2, 4096 -;; llc %r2, 0(%r2,%r5) +;; ag %r2, 0(%r3) +;; lghi %r5, 4096 +;; llc %r2, 0(%r5,%r2) ;; jg label2 ;; block2: ;; br %r14 diff --git a/cranelift/filetests/filetests/isa/x64/amode-opt.clif b/cranelift/filetests/filetests/isa/x64/amode-opt.clif index 2c8b00c744ec..1188f022bb0d 100644 --- a/cranelift/filetests/filetests/isa/x64/amode-opt.clif +++ b/cranelift/filetests/filetests/isa/x64/amode-opt.clif @@ -210,9 +210,8 @@ block0(v0: i64, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rsi, %rdx -; shll $3, %edx, %edx -; movq -1(%rdi,%rdx,1), %rax +; shll $3, %esi, %esi +; movq -1(%rdi,%rsi,1), %rax ; movq %rbp, %rsp ; popq %rbp ; ret @@ -222,9 +221,8 @@ block0(v0: i64, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rsi, %rdx -; shll $3, %edx -; movq -1(%rdi, %rdx), %rax ; trap: heap_oob +; shll $3, %esi +; movq -1(%rdi, %rsi), %rax ; trap: heap_oob ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/bmask.clif b/cranelift/filetests/filetests/isa/x64/bmask.clif index 8184c444cd66..3871aaec8c59 100644 --- a/cranelift/filetests/filetests/isa/x64/bmask.clif +++ b/cranelift/filetests/filetests/isa/x64/bmask.clif @@ -509,11 +509,11 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rdx -; orq %rdx, %rsi, %rdx -; movq %rdx, %r8 +; orq %rdi, %rsi, %rdi +; movq %rdi, %r8 ; negq %r8, %r8 -; sbbq %rdx, %rdx, %rdx +; movq %rdi, %rdx +; sbbq %rdx, %rdi, %rdx ; movq %rdx, %rax ; movq %rbp, %rsp ; popq %rbp @@ -524,11 +524,11 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rdx -; orq %rsi, %rdx -; movq %rdx, %r8 +; orq %rsi, %rdi +; movq %rdi, %r8 ; negq %r8 -; sbbq %rdx, %rdx +; movq %rdi, %rdx +; sbbq %rdi, %rdx ; movq %rdx, %rax ; movq %rbp, %rsp ; popq %rbp @@ -544,11 +544,11 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rax -; orq %rax, %rsi, %rax -; movq %rax, %r8 +; orq %rdi, %rsi, %rdi +; movq %rdi, %r8 ; negq %r8, %r8 -; sbbq %rax, %rax, %rax +; movq %rdi, %rax +; sbbq %rax, %rdi, %rax ; movq %rbp, %rsp ; popq %rbp ; ret @@ -558,11 +558,11 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rax -; orq %rsi, %rax -; movq %rax, %r8 +; orq %rsi, %rdi +; movq %rdi, %r8 ; negq %r8 -; sbbq %rax, %rax +; movq %rdi, %rax +; sbbq %rdi, %rax ; movq %rbp, %rsp ; popq %rbp ; retq @@ -577,11 +577,11 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rax -; orq %rax, %rsi, %rax -; movq %rax, %r8 +; orq %rdi, %rsi, %rdi +; movq %rdi, %r8 ; negq %r8, %r8 -; sbbl %eax, %eax, %eax +; movq %rdi, %rax +; sbbl %eax, %edi, %eax ; movq %rbp, %rsp ; popq %rbp ; ret @@ -591,11 +591,11 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rax -; orq %rsi, %rax -; movq %rax, %r8 +; orq %rsi, %rdi +; movq %rdi, %r8 ; negq %r8 -; sbbl %eax, %eax +; movq %rdi, %rax +; sbbl %edi, %eax ; movq %rbp, %rsp ; popq %rbp ; retq @@ -610,11 +610,11 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rax -; orq %rax, %rsi, %rax -; movq %rax, %r8 +; orq %rdi, %rsi, %rdi +; movq %rdi, %r8 ; negq %r8, %r8 -; sbbl %eax, %eax, %eax +; movq %rdi, %rax +; sbbl %eax, %edi, %eax ; movq %rbp, %rsp ; popq %rbp ; ret @@ -624,11 +624,11 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rax -; orq %rsi, %rax -; movq %rax, %r8 +; orq %rsi, %rdi +; movq %rdi, %r8 ; negq %r8 -; sbbl %eax, %eax +; movq %rdi, %rax +; sbbl %edi, %eax ; movq %rbp, %rsp ; popq %rbp ; retq @@ -643,11 +643,11 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rax -; orq %rax, %rsi, %rax -; movq %rax, %r8 +; orq %rdi, %rsi, %rdi +; movq %rdi, %r8 ; negq %r8, %r8 -; sbbl %eax, %eax, %eax +; movq %rdi, %rax +; sbbl %eax, %edi, %eax ; movq %rbp, %rsp ; popq %rbp ; ret @@ -657,11 +657,11 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rax -; orq %rsi, %rax -; movq %rax, %r8 +; orq %rsi, %rdi +; movq %rdi, %r8 ; negq %r8 -; sbbl %eax, %eax +; movq %rdi, %rax +; sbbl %edi, %eax ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/bmi2.clif b/cranelift/filetests/filetests/isa/x64/bmi2.clif index b65825742732..134853ed393e 100644 --- a/cranelift/filetests/filetests/isa/x64/bmi2.clif +++ b/cranelift/filetests/filetests/isa/x64/bmi2.clif @@ -268,9 +268,8 @@ block0(v0: i32, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rsi, %rcx -; andl %ecx, $31, %ecx -; bzhi %edi, %ecx, %eax +; andl %esi, $31, %esi +; bzhi %edi, %esi, %eax ; movq %rbp, %rsp ; popq %rbp ; ret @@ -280,9 +279,8 @@ block0(v0: i32, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rsi, %rcx -; andl $0x1f, %ecx -; bzhil %ecx, %edi, %eax +; andl $0x1f, %esi +; bzhil %esi, %edi, %eax ; movq %rbp, %rsp ; popq %rbp ; retq @@ -300,9 +298,8 @@ block0(v0: i64, v1: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rsi, %rcx -; andq %rcx, $63, %rcx -; bzhi %rdi, %rcx, %rax +; andq %rsi, $63, %rsi +; bzhi %rdi, %rsi, %rax ; movq %rbp, %rsp ; popq %rbp ; ret @@ -312,9 +309,8 @@ block0(v0: i64, v1: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rsi, %rcx -; andq $0x3f, %rcx -; bzhiq %rcx, %rdi, %rax +; andq $0x3f, %rsi +; bzhiq %rsi, %rdi, %rax ; movq %rbp, %rsp ; popq %rbp ; retq @@ -333,9 +329,8 @@ block0(v0: i64, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rsi, %rcx -; andl %ecx, $31, %ecx -; bzhi 20(%rdi), %ecx, %eax +; andl %esi, $31, %esi +; bzhi 20(%rdi), %esi, %eax ; movq %rbp, %rsp ; popq %rbp ; ret @@ -345,9 +340,8 @@ block0(v0: i64, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rsi, %rcx -; andl $0x1f, %ecx -; bzhil %ecx, 0x14(%rdi), %eax ; trap: heap_oob +; andl $0x1f, %esi +; bzhil %esi, 0x14(%rdi), %eax ; trap: heap_oob ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/call-conv.clif b/cranelift/filetests/filetests/isa/x64/call-conv.clif index 1e0cede45e3b..33d93d834604 100644 --- a/cranelift/filetests/filetests/isa/x64/call-conv.clif +++ b/cranelift/filetests/filetests/isa/x64/call-conv.clif @@ -425,8 +425,8 @@ block0(v0: i32, v1: i32, v2: i8x16, v3: i64, v4: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdx, %r8 ; movq %rsi, %rcx +; movq %rdx, %r8 ; subq %rsp, $64, %rsp ; virtual_sp_offset_adjust 64 ; lea 32(%rsp), %rdx @@ -446,8 +446,8 @@ block0(v0: i32, v1: i32, v2: i8x16, v3: i64, v4: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdx, %r8 ; movq %rsi, %rcx +; movq %rdx, %r8 ; subq $0x40, %rsp ; leaq 0x20(%rsp), %rdx ; movdqu %xmm0, (%rdx) diff --git a/cranelift/filetests/filetests/isa/x64/conditional-values.clif b/cranelift/filetests/filetests/isa/x64/conditional-values.clif index d54ebd4f34a8..8cba952ac3d7 100644 --- a/cranelift/filetests/filetests/isa/x64/conditional-values.clif +++ b/cranelift/filetests/filetests/isa/x64/conditional-values.clif @@ -347,8 +347,8 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: +; notq %rdi, %rdi ; movq %rdi, %rax -; notq %rax, %rax ; shrq $63, %rax, %rax ; movq %rbp, %rsp ; popq %rbp @@ -359,8 +359,8 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 +; notq %rdi ; movq %rdi, %rax -; notq %rax ; shrq $0x3f, %rax ; movq %rbp, %rsp ; popq %rbp @@ -377,8 +377,8 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: +; notq %rdi, %rdi ; movq %rdi, %rax -; notq %rax, %rax ; shrl $31, %eax, %eax ; movq %rbp, %rsp ; popq %rbp @@ -389,8 +389,8 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 +; notq %rdi ; movq %rdi, %rax -; notq %rax ; shrl $0x1f, %eax ; movq %rbp, %rsp ; popq %rbp @@ -407,8 +407,8 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: +; notq %rdi, %rdi ; movq %rdi, %rax -; notq %rax, %rax ; shrq $63, %rax, %rax ; movq %rbp, %rsp ; popq %rbp @@ -419,8 +419,8 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 +; notq %rdi ; movq %rdi, %rax -; notq %rax ; shrq $0x3f, %rax ; movq %rbp, %rsp ; popq %rbp @@ -437,8 +437,8 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: +; notq %rdi, %rdi ; movq %rdi, %rax -; notq %rax, %rax ; shrl $31, %eax, %eax ; movq %rbp, %rsp ; popq %rbp @@ -449,8 +449,8 @@ block0(v0: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 +; notq %rdi ; movq %rdi, %rax -; notq %rax ; shrl $0x1f, %eax ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/fcvt.clif b/cranelift/filetests/filetests/isa/x64/fcvt.clif index 1a04881cf022..2533ccb85fa8 100644 --- a/cranelift/filetests/filetests/isa/x64/fcvt.clif +++ b/cranelift/filetests/filetests/isa/x64/fcvt.clif @@ -1066,15 +1066,14 @@ block0(v0: f32x4): ; block0: ; uninit %xmm6 ; xorps %xmm6, %xmm6, %xmm6 -; movdqa %xmm0, %xmm3 -; maxps %xmm3, %xmm6, %xmm3 +; maxps %xmm0, %xmm6, %xmm0 ; pcmpeqd %xmm6, %xmm6, %xmm6 ; psrld %xmm6, $1, %xmm6 ; cvtdq2ps %xmm6, %xmm7 -; cvttps2dq %xmm3, %xmm6 -; subps %xmm3, %xmm7, %xmm3 -; cmpps $2, %xmm7, %xmm3, %xmm7 -; cvttps2dq %xmm3, %xmm0 +; cvttps2dq %xmm0, %xmm6 +; subps %xmm0, %xmm7, %xmm0 +; cmpps $2, %xmm7, %xmm0, %xmm7 +; cvttps2dq %xmm0, %xmm0 ; pxor %xmm0, %xmm7, %xmm0 ; uninit %xmm1 ; pxor %xmm1, %xmm1, %xmm1 @@ -1090,15 +1089,14 @@ block0(v0: f32x4): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; xorps %xmm6, %xmm6 -; movdqa %xmm0, %xmm3 -; maxps %xmm6, %xmm3 +; maxps %xmm6, %xmm0 ; pcmpeqd %xmm6, %xmm6 ; psrld $1, %xmm6 ; cvtdq2ps %xmm6, %xmm7 -; cvttps2dq %xmm3, %xmm6 -; subps %xmm7, %xmm3 -; cmpleps %xmm3, %xmm7 -; cvttps2dq %xmm3, %xmm0 +; cvttps2dq %xmm0, %xmm6 +; subps %xmm7, %xmm0 +; cmpleps %xmm0, %xmm7 +; cvttps2dq %xmm0, %xmm0 ; pxor %xmm7, %xmm0 ; pxor %xmm1, %xmm1 ; pmaxsd %xmm1, %xmm0 @@ -1119,10 +1117,9 @@ block0(v0: f32x4): ; block0: ; movdqa %xmm0, %xmm4 ; cmpps $0, %xmm4, %xmm0, %xmm4 -; movdqa %xmm0, %xmm5 -; andps %xmm5, %xmm4, %xmm5 -; pxor %xmm4, %xmm5, %xmm4 -; cvttps2dq %xmm5, %xmm1 +; andps %xmm0, %xmm4, %xmm0 +; pxor %xmm4, %xmm0, %xmm4 +; cvttps2dq %xmm0, %xmm1 ; movdqa %xmm1, %xmm0 ; pand %xmm0, %xmm4, %xmm0 ; psrad %xmm0, $31, %xmm0 @@ -1138,10 +1135,9 @@ block0(v0: f32x4): ; block1: ; offset 0x4 ; movdqa %xmm0, %xmm4 ; cmpeqps %xmm0, %xmm4 -; movdqa %xmm0, %xmm5 -; andps %xmm4, %xmm5 -; pxor %xmm5, %xmm4 -; cvttps2dq %xmm5, %xmm1 +; andps %xmm4, %xmm0 +; pxor %xmm0, %xmm4 +; cvttps2dq %xmm0, %xmm1 ; movdqa %xmm1, %xmm0 ; pand %xmm4, %xmm0 ; psrad $0x1f, %xmm0 diff --git a/cranelift/filetests/filetests/isa/x64/fma-call.clif b/cranelift/filetests/filetests/isa/x64/fma-call.clif index 3bb595a4fe26..79175599d00d 100644 --- a/cranelift/filetests/filetests/isa/x64/fma-call.clif +++ b/cranelift/filetests/filetests/isa/x64/fma-call.clif @@ -74,26 +74,26 @@ block0(v0: f32x4, v1: f32x4, v2: f32x4): ; movdqu rsp(16 + virtual offset), %xmm1 ; movdqu rsp(32 + virtual offset), %xmm2 ; call *%r8 -; movdqu %xmm0, rsp(48 + virtual offset) ; movdqu rsp(0 + virtual offset), %xmm4 +; movdqu %xmm0, rsp(80 + virtual offset) ; pshufd $1, %xmm4, %xmm0 ; movdqu rsp(16 + virtual offset), %xmm2 ; pshufd $1, %xmm2, %xmm1 -; movdqu rsp(32 + virtual offset), %xmm4 -; pshufd $1, %xmm4, %xmm2 +; movdqu rsp(32 + virtual offset), %xmm5 +; pshufd $1, %xmm5, %xmm2 ; load_ext_name %FmaF32+0, %r9 ; call *%r9 -; movdqu %xmm0, rsp(64 + virtual offset) ; movdqu rsp(0 + virtual offset), %xmm4 +; movdqu %xmm0, rsp(48 + virtual offset) ; pshufd $2, %xmm4, %xmm0 -; movdqu rsp(16 + virtual offset), %xmm6 -; pshufd $2, %xmm6, %xmm1 +; movdqu rsp(16 + virtual offset), %xmm7 +; pshufd $2, %xmm7, %xmm1 ; movdqu rsp(32 + virtual offset), %xmm3 ; pshufd $2, %xmm3, %xmm2 ; load_ext_name %FmaF32+0, %r10 ; call *%r10 -; movdqu %xmm0, rsp(80 + virtual offset) ; movdqu rsp(0 + virtual offset), %xmm4 +; movdqu %xmm0, rsp(64 + virtual offset) ; pshufd $3, %xmm4, %xmm0 ; movdqu rsp(16 + virtual offset), %xmm1 ; pshufd $3, %xmm1, %xmm1 @@ -101,12 +101,12 @@ block0(v0: f32x4, v1: f32x4, v2: f32x4): ; pshufd $3, %xmm2, %xmm2 ; load_ext_name %FmaF32+0, %r11 ; call *%r11 -; movdqu rsp(64 + virtual offset), %xmm4 ; movdqa %xmm0, %xmm2 -; movdqu rsp(48 + virtual offset), %xmm0 -; insertps $16, %xmm0, %xmm4, %xmm0 -; movdqu rsp(80 + virtual offset), %xmm1 -; insertps $32, %xmm0, %xmm1, %xmm0 +; movdqu rsp(48 + virtual offset), %xmm1 +; movdqu rsp(80 + virtual offset), %xmm0 +; insertps $16, %xmm0, %xmm1, %xmm0 +; movdqu rsp(64 + virtual offset), %xmm6 +; insertps $32, %xmm0, %xmm6, %xmm0 ; movdqa %xmm2, %xmm3 ; insertps $48, %xmm0, %xmm3, %xmm0 ; addq %rsp, $96, %rsp @@ -128,26 +128,26 @@ block0(v0: f32x4, v1: f32x4, v2: f32x4): ; movdqu 0x10(%rsp), %xmm1 ; movdqu 0x20(%rsp), %xmm2 ; callq *%r8 -; movdqu %xmm0, 0x30(%rsp) ; movdqu (%rsp), %xmm4 +; movdqu %xmm0, 0x50(%rsp) ; pshufd $1, %xmm4, %xmm0 ; movdqu 0x10(%rsp), %xmm2 ; pshufd $1, %xmm2, %xmm1 -; movdqu 0x20(%rsp), %xmm4 -; pshufd $1, %xmm4, %xmm2 +; movdqu 0x20(%rsp), %xmm5 +; pshufd $1, %xmm5, %xmm2 ; movabsq $0, %r9 ; reloc_external Abs8 %FmaF32 0 ; callq *%r9 -; movdqu %xmm0, 0x40(%rsp) ; movdqu (%rsp), %xmm4 +; movdqu %xmm0, 0x30(%rsp) ; pshufd $2, %xmm4, %xmm0 -; movdqu 0x10(%rsp), %xmm6 -; pshufd $2, %xmm6, %xmm1 +; movdqu 0x10(%rsp), %xmm7 +; pshufd $2, %xmm7, %xmm1 ; movdqu 0x20(%rsp), %xmm3 ; pshufd $2, %xmm3, %xmm2 ; movabsq $0, %r10 ; reloc_external Abs8 %FmaF32 0 ; callq *%r10 -; movdqu %xmm0, 0x50(%rsp) ; movdqu (%rsp), %xmm4 +; movdqu %xmm0, 0x40(%rsp) ; pshufd $3, %xmm4, %xmm0 ; movdqu 0x10(%rsp), %xmm1 ; pshufd $3, %xmm1, %xmm1 @@ -155,12 +155,12 @@ block0(v0: f32x4, v1: f32x4, v2: f32x4): ; pshufd $3, %xmm2, %xmm2 ; movabsq $0, %r11 ; reloc_external Abs8 %FmaF32 0 ; callq *%r11 -; movdqu 0x40(%rsp), %xmm4 ; movdqa %xmm0, %xmm2 -; movdqu 0x30(%rsp), %xmm0 -; insertps $0x10, %xmm4, %xmm0 -; movdqu 0x50(%rsp), %xmm1 -; insertps $0x20, %xmm1, %xmm0 +; movdqu 0x30(%rsp), %xmm1 +; movdqu 0x50(%rsp), %xmm0 +; insertps $0x10, %xmm1, %xmm0 +; movdqu 0x40(%rsp), %xmm6 +; insertps $0x20, %xmm6, %xmm0 ; movdqa %xmm2, %xmm3 ; insertps $0x30, %xmm3, %xmm0 ; addq $0x60, %rsp @@ -196,9 +196,9 @@ block0(v0: f64x2, v1: f64x2, v2: f64x2): ; pshufd $238, %xmm2, %xmm2 ; load_ext_name %FmaF64+0, %r9 ; call *%r9 -; movdqa %xmm0, %xmm6 +; movdqa %xmm0, %xmm7 ; movdqu rsp(48 + virtual offset), %xmm0 -; movlhps %xmm0, %xmm6, %xmm0 +; movlhps %xmm0, %xmm7, %xmm0 ; addq %rsp, $64, %rsp ; movq %rbp, %rsp ; popq %rbp @@ -227,9 +227,9 @@ block0(v0: f64x2, v1: f64x2, v2: f64x2): ; pshufd $0xee, %xmm2, %xmm2 ; movabsq $0, %r9 ; reloc_external Abs8 %FmaF64 0 ; callq *%r9 -; movdqa %xmm0, %xmm6 +; movdqa %xmm0, %xmm7 ; movdqu 0x30(%rsp), %xmm0 -; movlhps %xmm6, %xmm0 +; movlhps %xmm7, %xmm0 ; addq $0x40, %rsp ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/i128.clif b/cranelift/filetests/filetests/isa/x64/i128.clif index 74048b0a8115..abc78be6005d 100644 --- a/cranelift/filetests/filetests/isa/x64/i128.clif +++ b/cranelift/filetests/filetests/isa/x64/i128.clif @@ -203,9 +203,8 @@ block0(v0: i128, v1: i128): ; imulq %rdx, %rcx, %rdx ; movq %rax, %rcx ; movq %rdi, %rax -; movq %rsi, %r10 -; imulq %r10, %rcx, %r10 -; addq %rdx, %r10, %rdx +; imulq %rsi, %rcx, %rsi +; addq %rdx, %rsi, %rdx ; movq %rdx, %r9 ; mul %rax, %rcx, %rax, %rdx ; movq %rdx, %rcx @@ -225,9 +224,8 @@ block0(v0: i128, v1: i128): ; imulq %rcx, %rdx ; movq %rax, %rcx ; movq %rdi, %rax -; movq %rsi, %r10 -; imulq %rcx, %r10 -; addq %r10, %rdx +; imulq %rcx, %rsi +; addq %rsi, %rdx ; movq %rdx, %r9 ; mulq %rcx ; movq %rdx, %rcx @@ -247,8 +245,8 @@ block0(v0: i64, v1: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rsi, %rdx ; movq %rdi, %rax +; movq %rsi, %rdx ; movq %rbp, %rsp ; popq %rbp ; ret @@ -258,8 +256,8 @@ block0(v0: i64, v1: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rsi, %rdx ; movq %rdi, %rax +; movq %rsi, %rdx ; movq %rbp, %rsp ; popq %rbp ; retq @@ -274,8 +272,8 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rsi, %rdx ; movq %rdi, %rax +; movq %rsi, %rdx ; movq %rbp, %rsp ; popq %rbp ; ret @@ -285,8 +283,8 @@ block0(v0: i128): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rsi, %rdx ; movq %rdi, %rax +; movq %rsi, %rdx ; movq %rbp, %rsp ; popq %rbp ; retq @@ -865,17 +863,16 @@ block0(v0: i128): ; shrq $1, %rax, %rax ; movabsq $8608480567731124087, %r8 ; andq %rax, %r8, %rax -; movq %rdi, %r9 -; subq %r9, %rax, %r9 +; subq %rdi, %rax, %rdi ; shrq $1, %rax, %rax ; andq %rax, %r8, %rax -; subq %r9, %rax, %r9 +; subq %rdi, %rax, %rdi ; shrq $1, %rax, %rax ; andq %rax, %r8, %rax -; subq %r9, %rax, %r9 -; movq %r9, %rax +; subq %rdi, %rax, %rdi +; movq %rdi, %rax ; shrq $4, %rax, %rax -; addq %rax, %r9, %rax +; addq %rax, %rdi, %rax ; movabsq $1085102592571150095, %rdi ; andq %rax, %rdi, %rax ; movabsq $72340172838076673, %rdx @@ -885,23 +882,22 @@ block0(v0: i128): ; shrq $1, %rdi, %rdi ; movabsq $8608480567731124087, %rcx ; andq %rdi, %rcx, %rdi -; movq %rsi, %rdx -; subq %rdx, %rdi, %rdx +; subq %rsi, %rdi, %rsi ; shrq $1, %rdi, %rdi ; andq %rdi, %rcx, %rdi -; subq %rdx, %rdi, %rdx +; subq %rsi, %rdi, %rsi ; shrq $1, %rdi, %rdi ; andq %rdi, %rcx, %rdi -; subq %rdx, %rdi, %rdx -; movq %rdx, %rsi -; shrq $4, %rsi, %rsi -; addq %rsi, %rdx, %rsi +; subq %rsi, %rdi, %rsi +; movq %rsi, %rdi +; shrq $4, %rdi, %rdi +; addq %rdi, %rsi, %rdi ; movabsq $1085102592571150095, %r10 -; andq %rsi, %r10, %rsi +; andq %rdi, %r10, %rdi ; movabsq $72340172838076673, %rcx -; imulq %rsi, %rcx, %rsi -; shrq $56, %rsi, %rsi -; addq %rax, %rsi, %rax +; imulq %rdi, %rcx, %rdi +; shrq $56, %rdi, %rdi +; addq %rax, %rdi, %rax ; xorq %rdx, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -916,17 +912,16 @@ block0(v0: i128): ; shrq $1, %rax ; movabsq $0x7777777777777777, %r8 ; andq %r8, %rax -; movq %rdi, %r9 -; subq %rax, %r9 +; subq %rax, %rdi ; shrq $1, %rax ; andq %r8, %rax -; subq %rax, %r9 +; subq %rax, %rdi ; shrq $1, %rax ; andq %r8, %rax -; subq %rax, %r9 -; movq %r9, %rax +; subq %rax, %rdi +; movq %rdi, %rax ; shrq $4, %rax -; addq %r9, %rax +; addq %rdi, %rax ; movabsq $0xf0f0f0f0f0f0f0f, %rdi ; andq %rdi, %rax ; movabsq $0x101010101010101, %rdx @@ -936,23 +931,22 @@ block0(v0: i128): ; shrq $1, %rdi ; movabsq $0x7777777777777777, %rcx ; andq %rcx, %rdi -; movq %rsi, %rdx -; subq %rdi, %rdx +; subq %rdi, %rsi ; shrq $1, %rdi ; andq %rcx, %rdi -; subq %rdi, %rdx +; subq %rdi, %rsi ; shrq $1, %rdi ; andq %rcx, %rdi -; subq %rdi, %rdx -; movq %rdx, %rsi -; shrq $4, %rsi -; addq %rdx, %rsi +; subq %rdi, %rsi +; movq %rsi, %rdi +; shrq $4, %rdi +; addq %rsi, %rdi ; movabsq $0xf0f0f0f0f0f0f0f, %r10 -; andq %r10, %rsi +; andq %r10, %rdi ; movabsq $0x101010101010101, %rcx -; imulq %rcx, %rsi -; shrq $0x38, %rsi -; addq %rsi, %rax +; imulq %rcx, %rdi +; shrq $0x38, %rdi +; addq %rdi, %rax ; xorq %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -971,11 +965,10 @@ block0(v0: i128): ; movabsq $6148914691236517205, %rcx ; movq %rsi, %rdx ; andq %rdx, %rcx, %rdx -; movq %rsi, %r11 -; shrq $1, %r11, %r11 -; andq %r11, %rcx, %r11 +; shrq $1, %rsi, %rsi +; andq %rsi, %rcx, %rsi ; shlq $1, %rdx, %rdx -; orq %rdx, %r11, %rdx +; orq %rdx, %rsi, %rdx ; movabsq $3689348814741910323, %r9 ; movq %rdx, %r10 ; andq %r10, %r9, %r10 @@ -1013,11 +1006,10 @@ block0(v0: i128): ; movabsq $6148914691236517205, %rdx ; movq %rdi, %rcx ; andq %rcx, %rdx, %rcx -; movq %rdi, %r9 -; shrq $1, %r9, %r9 -; andq %r9, %rdx, %r9 +; shrq $1, %rdi, %rdi +; andq %rdi, %rdx, %rdi ; shlq $1, %rcx, %rcx -; orq %rcx, %r9, %rcx +; orq %rcx, %rdi, %rcx ; movabsq $3689348814741910323, %rdx ; movq %rcx, %r8 ; andq %r8, %rdx, %r8 @@ -1064,11 +1056,10 @@ block0(v0: i128): ; movabsq $0x5555555555555555, %rcx ; movq %rsi, %rdx ; andq %rcx, %rdx -; movq %rsi, %r11 -; shrq $1, %r11 -; andq %rcx, %r11 +; shrq $1, %rsi +; andq %rcx, %rsi ; shlq $1, %rdx -; orq %r11, %rdx +; orq %rsi, %rdx ; movabsq $0x3333333333333333, %r9 ; movq %rdx, %r10 ; andq %r9, %r10 @@ -1106,11 +1097,10 @@ block0(v0: i128): ; movabsq $0x5555555555555555, %rdx ; movq %rdi, %rcx ; andq %rdx, %rcx -; movq %rdi, %r9 -; shrq $1, %r9 -; andq %rdx, %r9 +; shrq $1, %rdi +; andq %rdx, %rdi ; shlq $1, %rcx -; orq %r9, %rcx +; orq %rdi, %rcx ; movabsq $0x3333333333333333, %rdx ; movq %rcx, %r8 ; andq %rdx, %r8 @@ -1296,36 +1286,31 @@ block0(v0: i128, v1: i128, v2: i64, v3: i128, v4: i128, v5: i128): ; movq %rsp, %rbp ; subq %rsp, $32, %rsp ; movq %rbx, 0(%rsp) -; movq %r12, 8(%rsp) +; movq %r13, 8(%rsp) ; movq %r14, 16(%rsp) ; movq %r15, 24(%rsp) ; block0: -; movq %r8, %r14 -; movq %rcx, %rbx -; movq %rdx, %rcx -; movq 16(%rbp), %r15 +; movq %rdx, %rbx +; movq %rcx, %r14 +; movq 16(%rbp), %rcx ; movq 24(%rbp), %rax ; movq 32(%rbp), %rdx ; movq 40(%rbp), %r11 ; movq 48(%rbp), %r10 -; movq %rdi, %r8 -; addq %r8, %rcx, %r8 -; movq %rbx, %rdi -; movq %rsi, %rcx -; adcq %rcx, %rdi, %rcx -; xorq %rdi, %rdi, %rdi -; movq %r14, %r12 -; movq %r9, %rsi -; addq %rsi, %r12, %rsi -; adcq %r15, %rdi, %r15 +; addq %rdi, %rbx, %rdi +; movq %r14, %r15 +; adcq %rsi, %r15, %rsi +; xorq %r13, %r13, %r13 +; addq %r9, %r8, %r9 +; adcq %rcx, %r13, %rcx ; addq %rax, %r11, %rax ; adcq %rdx, %r10, %rdx -; addq %r8, %rsi, %r8 -; adcq %rcx, %r15, %rcx -; addq %rax, %r8, %rax -; adcq %rdx, %rcx, %rdx +; addq %rdi, %r9, %rdi +; adcq %rsi, %rcx, %rsi +; addq %rax, %rdi, %rax +; adcq %rdx, %rsi, %rdx ; movq 0(%rsp), %rbx -; movq 8(%rsp), %r12 +; movq 8(%rsp), %r13 ; movq 16(%rsp), %r14 ; movq 24(%rsp), %r15 ; addq %rsp, $32, %rsp @@ -1339,36 +1324,31 @@ block0(v0: i128, v1: i128, v2: i64, v3: i128, v4: i128, v5: i128): ; movq %rsp, %rbp ; subq $0x20, %rsp ; movq %rbx, (%rsp) -; movq %r12, 8(%rsp) +; movq %r13, 8(%rsp) ; movq %r14, 0x10(%rsp) ; movq %r15, 0x18(%rsp) ; block1: ; offset 0x1b -; movq %r8, %r14 -; movq %rcx, %rbx -; movq %rdx, %rcx -; movq 0x10(%rbp), %r15 +; movq %rdx, %rbx +; movq %rcx, %r14 +; movq 0x10(%rbp), %rcx ; movq 0x18(%rbp), %rax ; movq 0x20(%rbp), %rdx ; movq 0x28(%rbp), %r11 ; movq 0x30(%rbp), %r10 -; movq %rdi, %r8 -; addq %rcx, %r8 -; movq %rbx, %rdi -; movq %rsi, %rcx -; adcq %rdi, %rcx -; xorq %rdi, %rdi -; movq %r14, %r12 -; movq %r9, %rsi -; addq %r12, %rsi -; adcq %rdi, %r15 +; addq %rbx, %rdi +; movq %r14, %r15 +; adcq %r15, %rsi +; xorq %r13, %r13 +; addq %r8, %r9 +; adcq %r13, %rcx ; addq %r11, %rax ; adcq %r10, %rdx -; addq %rsi, %r8 -; adcq %r15, %rcx -; addq %r8, %rax -; adcq %rcx, %rdx +; addq %r9, %rdi +; adcq %rcx, %rsi +; addq %rdi, %rax +; adcq %rsi, %rdx ; movq (%rsp), %rbx -; movq 8(%rsp), %r12 +; movq 8(%rsp), %r13 ; movq 0x10(%rsp), %r14 ; movq 0x18(%rsp), %r15 ; addq $0x20, %rsp @@ -1618,21 +1598,19 @@ block0(v0: i128, v1: i128): ; movq %rdx, %r10 ; movq %rdi, %rdx ; shlq %cl, %rdx, %rdx -; movq %rsi, %r11 -; shlq %cl, %r11, %r11 +; shlq %cl, %rsi, %rsi ; movq %rcx, %r10 ; movl $64, %ecx ; movq %r10, %r8 ; subq %rcx, %r8, %rcx -; movq %rdi, %r10 -; shrq %cl, %r10, %r10 +; shrq %cl, %rdi, %rdi ; xorq %rax, %rax, %rax ; testq $127, %r8 -; cmovzq %rax, %r10, %r10 -; orq %r10, %r11, %r10 +; cmovzq %rax, %rdi, %rdi +; orq %rdi, %rsi, %rdi ; testq $64, %r8 ; cmovzq %rdx, %rax, %rax -; cmovzq %r10, %rdx, %rdx +; cmovzq %rdi, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp ; ret @@ -1646,21 +1624,19 @@ block0(v0: i128, v1: i128): ; movq %rdx, %r10 ; movq %rdi, %rdx ; shlq %cl, %rdx -; movq %rsi, %r11 -; shlq %cl, %r11 +; shlq %cl, %rsi ; movq %rcx, %r10 ; movl $0x40, %ecx ; movq %r10, %r8 ; subq %r8, %rcx -; movq %rdi, %r10 -; shrq %cl, %r10 +; shrq %cl, %rdi ; xorq %rax, %rax ; testq $0x7f, %r8 -; cmoveq %rax, %r10 -; orq %r11, %r10 +; cmoveq %rax, %rdi +; orq %rsi, %rdi ; testq $0x40, %r8 ; cmoveq %rdx, %rax -; cmoveq %r10, %rdx +; cmoveq %rdi, %rdx ; movq %rbp, %rsp ; popq %rbp ; retq @@ -1677,23 +1653,21 @@ block0(v0: i128, v1: i128): ; block0: ; movq %rdx, %rcx ; movq %rdx, %r11 -; movq %rdi, %r8 -; shrq %cl, %r8, %r8 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r10 ; shrq %cl, %r10, %r10 ; movq %rcx, %r11 ; movl $64, %ecx -; movq %r11, %rdi -; subq %rcx, %rdi, %rcx -; movq %rsi, %r11 -; shlq %cl, %r11, %r11 +; movq %r11, %rax +; subq %rcx, %rax, %rcx +; shlq %cl, %rsi, %rsi ; xorq %rdx, %rdx, %rdx -; testq $127, %rdi -; cmovzq %rdx, %r11, %r11 -; orq %r11, %r8, %r11 -; testq $64, %rdi +; testq $127, %rax +; cmovzq %rdx, %rsi, %rsi +; orq %rsi, %rdi, %rsi +; testq $64, %rax ; movq %r10, %rax -; cmovzq %r11, %rax, %rax +; cmovzq %rsi, %rax, %rax ; cmovzq %r10, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -1706,23 +1680,21 @@ block0(v0: i128, v1: i128): ; block1: ; offset 0x4 ; movq %rdx, %rcx ; movq %rdx, %r11 -; movq %rdi, %r8 -; shrq %cl, %r8 +; shrq %cl, %rdi ; movq %rsi, %r10 ; shrq %cl, %r10 ; movq %rcx, %r11 ; movl $0x40, %ecx -; movq %r11, %rdi -; subq %rdi, %rcx -; movq %rsi, %r11 -; shlq %cl, %r11 +; movq %r11, %rax +; subq %rax, %rcx +; shlq %cl, %rsi ; xorq %rdx, %rdx -; testq $0x7f, %rdi -; cmoveq %rdx, %r11 -; orq %r8, %r11 -; testq $0x40, %rdi +; testq $0x7f, %rax +; cmoveq %rdx, %rsi +; orq %rdi, %rsi +; testq $0x40, %rax ; movq %r10, %rax -; cmoveq %r11, %rax +; cmoveq %rsi, %rax ; cmoveq %r10, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -1740,8 +1712,7 @@ block0(v0: i128, v1: i128): ; block0: ; movq %rdx, %rcx ; movq %rdx, %r11 -; movq %rdi, %r8 -; shrq %cl, %r8, %r8 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r10 ; sarq %cl, %r10, %r10 ; movq %rcx, %r11 @@ -1753,12 +1724,12 @@ block0(v0: i128, v1: i128): ; xorq %r11, %r11, %r11 ; testq $127, %rax ; cmovzq %r11, %r9, %r9 -; orq %r8, %r9, %r8 -; movq %rsi, %rdx -; sarq $63, %rdx, %rdx +; orq %rdi, %r9, %rdi +; sarq $63, %rsi, %rsi ; testq $64, %rax ; movq %r10, %rax -; cmovzq %r8, %rax, %rax +; cmovzq %rdi, %rax, %rax +; movq %rsi, %rdx ; cmovzq %r10, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -1771,8 +1742,7 @@ block0(v0: i128, v1: i128): ; block1: ; offset 0x4 ; movq %rdx, %rcx ; movq %rdx, %r11 -; movq %rdi, %r8 -; shrq %cl, %r8 +; shrq %cl, %rdi ; movq %rsi, %r10 ; sarq %cl, %r10 ; movq %rcx, %r11 @@ -1784,12 +1754,12 @@ block0(v0: i128, v1: i128): ; xorq %r11, %r11 ; testq $0x7f, %rax ; cmoveq %r11, %r9 -; orq %r9, %r8 -; movq %rsi, %rdx -; sarq $0x3f, %rdx +; orq %r9, %rdi +; sarq $0x3f, %rsi ; testq $0x40, %rax ; movq %r10, %rax -; cmoveq %r8, %rax +; cmoveq %rdi, %rax +; movq %rsi, %rdx ; cmoveq %r10, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -1828,26 +1798,24 @@ block0(v0: i128, v1: i128): ; movl $128, %ecx ; movq %r8, %r10 ; subq %rcx, %r10, %rcx -; movq %rdi, %r8 -; shrq %cl, %r8, %r8 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r9 ; shrq %cl, %r9, %r9 -; movq %rcx, %r10 +; movq %rcx, %r8 ; movl $64, %ecx -; movq %r10, %r11 -; subq %rcx, %r11, %rcx -; movq %rsi, %r10 -; shlq %cl, %r10, %r10 -; xorq %rsi, %rsi, %rsi -; testq $127, %r11 +; movq %r8, %r10 +; subq %rcx, %r10, %rcx +; shlq %cl, %rsi, %rsi +; xorq %r8, %r8, %r8 +; testq $127, %r10 +; cmovzq %r8, %rsi, %rsi +; orq %rsi, %rdi, %rsi +; testq $64, %r10 +; movq %r9, %r10 ; cmovzq %rsi, %r10, %r10 -; orq %r10, %r8, %r10 -; testq $64, %r11 -; movq %r9, %r8 -; cmovzq %r10, %r8, %r8 -; cmovzq %r9, %rsi, %rsi -; orq %rax, %r8, %rax -; orq %rdx, %rsi, %rdx +; cmovzq %r9, %r8, %r8 +; orq %rax, %r10, %rax +; orq %rdx, %r8, %rdx ; movq %rbp, %rsp ; popq %rbp ; ret @@ -1880,26 +1848,24 @@ block0(v0: i128, v1: i128): ; movl $0x80, %ecx ; movq %r8, %r10 ; subq %r10, %rcx -; movq %rdi, %r8 -; shrq %cl, %r8 +; shrq %cl, %rdi ; movq %rsi, %r9 ; shrq %cl, %r9 -; movq %rcx, %r10 +; movq %rcx, %r8 ; movl $0x40, %ecx -; movq %r10, %r11 -; subq %r11, %rcx -; movq %rsi, %r10 -; shlq %cl, %r10 -; xorq %rsi, %rsi -; testq $0x7f, %r11 +; movq %r8, %r10 +; subq %r10, %rcx +; shlq %cl, %rsi +; xorq %r8, %r8 +; testq $0x7f, %r10 +; cmoveq %r8, %rsi +; orq %rdi, %rsi +; testq $0x40, %r10 +; movq %r9, %r10 ; cmoveq %rsi, %r10 -; orq %r8, %r10 -; testq $0x40, %r11 -; movq %r9, %r8 -; cmoveq %r10, %r8 -; cmoveq %r9, %rsi -; orq %r8, %rax -; orq %rsi, %rdx +; cmoveq %r9, %r8 +; orq %r10, %rax +; orq %r8, %rdx ; movq %rbp, %rsp ; popq %rbp ; retq @@ -1940,21 +1906,18 @@ block0(v0: i128, v1: i128): ; subq %rcx, %r10, %rcx ; movq %rdi, %r8 ; shlq %cl, %r8, %r8 -; movq %rsi, %r10 -; shlq %cl, %r10, %r10 +; shlq %cl, %rsi, %rsi ; movq %rcx, %r9 ; movl $64, %ecx -; movq %r9, %rsi -; subq %rcx, %rsi, %rcx -; movq %rdi, %r9 -; shrq %cl, %r9, %r9 +; subq %rcx, %r9, %rcx +; shrq %cl, %rdi, %rdi ; xorq %r11, %r11, %r11 -; testq $127, %rsi -; cmovzq %r11, %r9, %r9 -; orq %r9, %r10, %r9 -; testq $64, %rsi +; testq $127, %r9 +; cmovzq %r11, %rdi, %rdi +; orq %rdi, %rsi, %rdi +; testq $64, %r9 ; cmovzq %r8, %r11, %r11 -; cmovzq %r9, %r8, %r8 +; cmovzq %rdi, %r8, %r8 ; orq %rax, %r11, %rax ; orq %rdx, %r8, %rdx ; movq %rbp, %rsp @@ -1992,21 +1955,18 @@ block0(v0: i128, v1: i128): ; subq %r10, %rcx ; movq %rdi, %r8 ; shlq %cl, %r8 -; movq %rsi, %r10 -; shlq %cl, %r10 +; shlq %cl, %rsi ; movq %rcx, %r9 ; movl $0x40, %ecx -; movq %r9, %rsi -; subq %rsi, %rcx -; movq %rdi, %r9 -; shrq %cl, %r9 +; subq %r9, %rcx +; shrq %cl, %rdi ; xorq %r11, %r11 -; testq $0x7f, %rsi -; cmoveq %r11, %r9 -; orq %r10, %r9 -; testq $0x40, %rsi +; testq $0x7f, %r9 +; cmoveq %r11, %rdi +; orq %rsi, %rdi +; testq $0x40, %r9 ; cmoveq %r8, %r11 -; cmoveq %r9, %r8 +; cmoveq %rdi, %r8 ; orq %r11, %rax ; orq %r8, %rdx ; movq %rbp, %rsp diff --git a/cranelift/filetests/filetests/isa/x64/ishl.clif b/cranelift/filetests/filetests/isa/x64/ishl.clif index 743ea813cc49..32ab2f796270 100644 --- a/cranelift/filetests/filetests/isa/x64/ishl.clif +++ b/cranelift/filetests/filetests/isa/x64/ishl.clif @@ -21,21 +21,19 @@ block0(v0: i128, v1: i8): ; movzbq %dl, %rcx ; movq %rdi, %rdx ; shlq %cl, %rdx, %rdx -; movq %rsi, %r11 -; shlq %cl, %r11, %r11 +; shlq %cl, %rsi, %rsi ; movq %rcx, %r9 ; movl $64, %ecx ; movq %r9, %r8 ; subq %rcx, %r8, %rcx -; movq %rdi, %r10 -; shrq %cl, %r10, %r10 +; shrq %cl, %rdi, %rdi ; xorq %rax, %rax, %rax ; testq $127, %r8 -; cmovzq %rax, %r10, %r10 -; orq %r10, %r11, %r10 +; cmovzq %rax, %rdi, %rdi +; orq %rdi, %rsi, %rdi ; testq $64, %r8 ; cmovzq %rdx, %rax, %rax -; cmovzq %r10, %rdx, %rdx +; cmovzq %rdi, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp ; ret @@ -48,21 +46,19 @@ block0(v0: i128, v1: i8): ; movzbq %dl, %rcx ; movq %rdi, %rdx ; shlq %cl, %rdx -; movq %rsi, %r11 -; shlq %cl, %r11 +; shlq %cl, %rsi ; movq %rcx, %r9 ; movl $0x40, %ecx ; movq %r9, %r8 ; subq %r8, %rcx -; movq %rdi, %r10 -; shrq %cl, %r10 +; shrq %cl, %rdi ; xorq %rax, %rax ; testq $0x7f, %r8 -; cmoveq %rax, %r10 -; orq %r11, %r10 +; cmoveq %rax, %rdi +; orq %rsi, %rdi ; testq $0x40, %r8 ; cmoveq %rdx, %rax -; cmoveq %r10, %rdx +; cmoveq %rdi, %rdx ; movq %rbp, %rsp ; popq %rbp ; retq @@ -81,21 +77,19 @@ block0(v0: i128, v1: i64): ; movq %rdx, %r9 ; movq %rdi, %rdx ; shlq %cl, %rdx, %rdx -; movq %rsi, %r10 -; shlq %cl, %r10, %r10 +; shlq %cl, %rsi, %rsi ; movq %rcx, %r9 ; movl $64, %ecx -; movq %r9, %rsi -; subq %rcx, %rsi, %rcx -; movq %rdi, %r9 -; shrq %cl, %r9, %r9 +; movq %r9, %r8 +; subq %rcx, %r8, %rcx +; shrq %cl, %rdi, %rdi ; xorq %rax, %rax, %rax -; testq $127, %rsi -; cmovzq %rax, %r9, %r9 -; orq %r9, %r10, %r9 -; testq $64, %rsi +; testq $127, %r8 +; cmovzq %rax, %rdi, %rdi +; orq %rdi, %rsi, %rdi +; testq $64, %r8 ; cmovzq %rdx, %rax, %rax -; cmovzq %r9, %rdx, %rdx +; cmovzq %rdi, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp ; ret @@ -109,21 +103,19 @@ block0(v0: i128, v1: i64): ; movq %rdx, %r9 ; movq %rdi, %rdx ; shlq %cl, %rdx -; movq %rsi, %r10 -; shlq %cl, %r10 +; shlq %cl, %rsi ; movq %rcx, %r9 ; movl $0x40, %ecx -; movq %r9, %rsi -; subq %rsi, %rcx -; movq %rdi, %r9 -; shrq %cl, %r9 +; movq %r9, %r8 +; subq %r8, %rcx +; shrq %cl, %rdi ; xorq %rax, %rax -; testq $0x7f, %rsi -; cmoveq %rax, %r9 -; orq %r10, %r9 -; testq $0x40, %rsi +; testq $0x7f, %r8 +; cmoveq %rax, %rdi +; orq %rsi, %rdi +; testq $0x40, %r8 ; cmoveq %rdx, %rax -; cmoveq %r9, %rdx +; cmoveq %rdi, %rdx ; movq %rbp, %rsp ; popq %rbp ; retq @@ -142,21 +134,19 @@ block0(v0: i128, v1: i32): ; movq %rdx, %r9 ; movq %rdi, %rdx ; shlq %cl, %rdx, %rdx -; movq %rsi, %r10 -; shlq %cl, %r10, %r10 +; shlq %cl, %rsi, %rsi ; movq %rcx, %r9 ; movl $64, %ecx -; movq %r9, %rsi -; subq %rcx, %rsi, %rcx -; movq %rdi, %r9 -; shrq %cl, %r9, %r9 +; movq %r9, %r8 +; subq %rcx, %r8, %rcx +; shrq %cl, %rdi, %rdi ; xorq %rax, %rax, %rax -; testq $127, %rsi -; cmovzq %rax, %r9, %r9 -; orq %r9, %r10, %r9 -; testq $64, %rsi +; testq $127, %r8 +; cmovzq %rax, %rdi, %rdi +; orq %rdi, %rsi, %rdi +; testq $64, %r8 ; cmovzq %rdx, %rax, %rax -; cmovzq %r9, %rdx, %rdx +; cmovzq %rdi, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp ; ret @@ -170,21 +160,19 @@ block0(v0: i128, v1: i32): ; movq %rdx, %r9 ; movq %rdi, %rdx ; shlq %cl, %rdx -; movq %rsi, %r10 -; shlq %cl, %r10 +; shlq %cl, %rsi ; movq %rcx, %r9 ; movl $0x40, %ecx -; movq %r9, %rsi -; subq %rsi, %rcx -; movq %rdi, %r9 -; shrq %cl, %r9 +; movq %r9, %r8 +; subq %r8, %rcx +; shrq %cl, %rdi ; xorq %rax, %rax -; testq $0x7f, %rsi -; cmoveq %rax, %r9 -; orq %r10, %r9 -; testq $0x40, %rsi +; testq $0x7f, %r8 +; cmoveq %rax, %rdi +; orq %rsi, %rdi +; testq $0x40, %r8 ; cmoveq %rdx, %rax -; cmoveq %r9, %rdx +; cmoveq %rdi, %rdx ; movq %rbp, %rsp ; popq %rbp ; retq @@ -203,21 +191,19 @@ block0(v0: i128, v1: i16): ; movq %rdx, %r9 ; movq %rdi, %rdx ; shlq %cl, %rdx, %rdx -; movq %rsi, %r10 -; shlq %cl, %r10, %r10 +; shlq %cl, %rsi, %rsi ; movq %rcx, %r9 ; movl $64, %ecx -; movq %r9, %rsi -; subq %rcx, %rsi, %rcx -; movq %rdi, %r9 -; shrq %cl, %r9, %r9 +; movq %r9, %r8 +; subq %rcx, %r8, %rcx +; shrq %cl, %rdi, %rdi ; xorq %rax, %rax, %rax -; testq $127, %rsi -; cmovzq %rax, %r9, %r9 -; orq %r9, %r10, %r9 -; testq $64, %rsi +; testq $127, %r8 +; cmovzq %rax, %rdi, %rdi +; orq %rdi, %rsi, %rdi +; testq $64, %r8 ; cmovzq %rdx, %rax, %rax -; cmovzq %r9, %rdx, %rdx +; cmovzq %rdi, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp ; ret @@ -231,21 +217,19 @@ block0(v0: i128, v1: i16): ; movq %rdx, %r9 ; movq %rdi, %rdx ; shlq %cl, %rdx -; movq %rsi, %r10 -; shlq %cl, %r10 +; shlq %cl, %rsi ; movq %rcx, %r9 ; movl $0x40, %ecx -; movq %r9, %rsi -; subq %rsi, %rcx -; movq %rdi, %r9 -; shrq %cl, %r9 +; movq %r9, %r8 +; subq %r8, %rcx +; shrq %cl, %rdi ; xorq %rax, %rax -; testq $0x7f, %rsi -; cmoveq %rax, %r9 -; orq %r10, %r9 -; testq $0x40, %rsi +; testq $0x7f, %r8 +; cmoveq %rax, %rdi +; orq %rsi, %rdi +; testq $0x40, %r8 ; cmoveq %rdx, %rax -; cmoveq %r9, %rdx +; cmoveq %rdi, %rdx ; movq %rbp, %rsp ; popq %rbp ; retq @@ -264,21 +248,19 @@ block0(v0: i128, v1: i8): ; movq %rdx, %r9 ; movq %rdi, %rdx ; shlq %cl, %rdx, %rdx -; movq %rsi, %r10 -; shlq %cl, %r10, %r10 +; shlq %cl, %rsi, %rsi ; movq %rcx, %r9 ; movl $64, %ecx -; movq %r9, %rsi -; subq %rcx, %rsi, %rcx -; movq %rdi, %r9 -; shrq %cl, %r9, %r9 +; movq %r9, %r8 +; subq %rcx, %r8, %rcx +; shrq %cl, %rdi, %rdi ; xorq %rax, %rax, %rax -; testq $127, %rsi -; cmovzq %rax, %r9, %r9 -; orq %r9, %r10, %r9 -; testq $64, %rsi +; testq $127, %r8 +; cmovzq %rax, %rdi, %rdi +; orq %rdi, %rsi, %rdi +; testq $64, %r8 ; cmovzq %rdx, %rax, %rax -; cmovzq %r9, %rdx, %rdx +; cmovzq %rdi, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp ; ret @@ -292,21 +274,19 @@ block0(v0: i128, v1: i8): ; movq %rdx, %r9 ; movq %rdi, %rdx ; shlq %cl, %rdx -; movq %rsi, %r10 -; shlq %cl, %r10 +; shlq %cl, %rsi ; movq %rcx, %r9 ; movl $0x40, %ecx -; movq %r9, %rsi -; subq %rsi, %rcx -; movq %rdi, %r9 -; shrq %cl, %r9 +; movq %r9, %r8 +; subq %r8, %rcx +; shrq %cl, %rdi ; xorq %rax, %rax -; testq $0x7f, %rsi -; cmoveq %rax, %r9 -; orq %r10, %r9 -; testq $0x40, %rsi +; testq $0x7f, %r8 +; cmoveq %rax, %rdi +; orq %rsi, %rdi +; testq $0x40, %r8 ; cmoveq %rdx, %rax -; cmoveq %r9, %rdx +; cmoveq %rdi, %rdx ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/narrowing.clif b/cranelift/filetests/filetests/isa/x64/narrowing.clif index 44dc282d7006..d46d1c9980da 100644 --- a/cranelift/filetests/filetests/isa/x64/narrowing.clif +++ b/cranelift/filetests/filetests/isa/x64/narrowing.clif @@ -66,9 +66,8 @@ block0(v0: f64x2): ; movdqa %xmm0, %xmm3 ; cmppd $0, %xmm3, %xmm0, %xmm3 ; andps %xmm3, const(0), %xmm3 -; movdqa %xmm0, %xmm6 -; minpd %xmm6, %xmm3, %xmm6 -; cvttpd2dq %xmm6, %xmm0 +; minpd %xmm0, %xmm3, %xmm0 +; cvttpd2dq %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -81,9 +80,8 @@ block0(v0: f64x2): ; movdqa %xmm0, %xmm3 ; cmpeqpd %xmm0, %xmm3 ; andps 0x1c(%rip), %xmm3 -; movdqa %xmm0, %xmm6 -; minpd %xmm3, %xmm6 -; cvttpd2dq %xmm6, %xmm0 +; minpd %xmm3, %xmm0 +; cvttpd2dq %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -93,6 +91,8 @@ block0(v0: f64x2): ; addb %al, (%rax) ; addb %al, (%rax) ; addb %al, (%rax) +; addb %al, (%rax) +; addb %al, (%rax) ; addb %al, %al function %f4(i16x8, i16x8) -> i8x16 { diff --git a/cranelift/filetests/filetests/isa/x64/popcnt.clif b/cranelift/filetests/filetests/isa/x64/popcnt.clif index 41ecc069c2a4..3aecd486fd12 100644 --- a/cranelift/filetests/filetests/isa/x64/popcnt.clif +++ b/cranelift/filetests/filetests/isa/x64/popcnt.clif @@ -11,21 +11,20 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; shrq $1, %rdi, %rdi -; movq %rcx, %r8 +; movq %rdi, %rax +; shrq $1, %rax, %rax ; movabsq $8608480567731124087, %rdx -; andq %rdi, %rdx, %rdi -; subq %r8, %rdi, %r8 -; shrq $1, %rdi, %rdi -; andq %rdi, %rdx, %rdi -; subq %r8, %rdi, %r8 -; shrq $1, %rdi, %rdi -; andq %rdi, %rdx, %rdi -; subq %r8, %rdi, %r8 -; movq %r8, %rax +; andq %rax, %rdx, %rax +; subq %rdi, %rax, %rdi +; shrq $1, %rax, %rax +; andq %rax, %rdx, %rax +; subq %rdi, %rax, %rdi +; shrq $1, %rax, %rax +; andq %rax, %rdx, %rax +; subq %rdi, %rax, %rdi +; movq %rdi, %rax ; shrq $4, %rax, %rax -; addq %rax, %r8, %rax +; addq %rax, %rdi, %rax ; movabsq $1085102592571150095, %r11 ; andq %rax, %r11, %rax ; movabsq $72340172838076673, %rcx @@ -40,21 +39,20 @@ block0(v0: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; shrq $1, %rdi -; movq %rcx, %r8 +; movq %rdi, %rax +; shrq $1, %rax ; movabsq $0x7777777777777777, %rdx -; andq %rdx, %rdi -; subq %rdi, %r8 -; shrq $1, %rdi -; andq %rdx, %rdi -; subq %rdi, %r8 -; shrq $1, %rdi -; andq %rdx, %rdi -; subq %rdi, %r8 -; movq %r8, %rax +; andq %rdx, %rax +; subq %rax, %rdi +; shrq $1, %rax +; andq %rdx, %rax +; subq %rax, %rdi +; shrq $1, %rax +; andq %rdx, %rax +; subq %rax, %rdi +; movq %rdi, %rax ; shrq $4, %rax -; addq %r8, %rax +; addq %rdi, %rax ; movabsq $0xf0f0f0f0f0f0f0f, %r11 ; andq %r11, %rax ; movabsq $0x101010101010101, %rcx @@ -139,20 +137,19 @@ block0(v0: i32): ; movq %rsp, %rbp ; block0: ; movq %rdi, %rax -; shrl $1, %edi, %edi +; shrl $1, %eax, %eax ; movl $2004318071, %edx -; andl %edi, %edx, %edi -; movq %rax, %r8 -; subl %r8d, %edi, %r8d -; shrl $1, %edi, %edi -; andl %edi, %edx, %edi -; subl %r8d, %edi, %r8d -; shrl $1, %edi, %edi -; andl %edi, %edx, %edi -; subl %r8d, %edi, %r8d -; movq %r8, %rax +; andl %eax, %edx, %eax +; subl %edi, %eax, %edi +; shrl $1, %eax, %eax +; andl %eax, %edx, %eax +; subl %edi, %eax, %edi +; shrl $1, %eax, %eax +; andl %eax, %edx, %eax +; subl %edi, %eax, %edi +; movq %rdi, %rax ; shrl $4, %eax, %eax -; addl %eax, %r8d, %eax +; addl %eax, %edi, %eax ; andl %eax, $252645135, %eax ; imull %eax, $16843009, %eax ; shrl $24, %eax, %eax @@ -166,20 +163,19 @@ block0(v0: i32): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; movq %rdi, %rax -; shrl $1, %edi +; shrl $1, %eax ; movl $0x77777777, %edx -; andl %edx, %edi -; movq %rax, %r8 -; subl %edi, %r8d -; shrl $1, %edi -; andl %edx, %edi -; subl %edi, %r8d -; shrl $1, %edi -; andl %edx, %edi -; subl %edi, %r8d -; movq %r8, %rax +; andl %edx, %eax +; subl %eax, %edi +; shrl $1, %eax +; andl %edx, %eax +; subl %eax, %edi +; shrl $1, %eax +; andl %edx, %eax +; subl %eax, %edi +; movq %rdi, %rax ; shrl $4, %eax -; addl %r8d, %eax +; addl %edi, %eax ; andl $0xf0f0f0f, %eax ; imull $0x1010101, %eax, %eax ; shrl $0x18, %eax diff --git a/cranelift/filetests/filetests/isa/x64/return-call.clif b/cranelift/filetests/filetests/isa/x64/return-call.clif index 18b9dce2db26..3276c7678894 100644 --- a/cranelift/filetests/filetests/isa/x64/return-call.clif +++ b/cranelift/filetests/filetests/isa/x64/return-call.clif @@ -363,16 +363,16 @@ block0: ; movq %rbp, %r15 ; movq 8(%r15), %r13 ; load_ext_name %tail_callee_stack_args+0, %r12 -; movq rsp(24 + virtual offset), %r11 -; movq rsp(32 + virtual offset), %r10 -; movq rsp(40 + virtual offset), %r9 -; movq rsp(48 + virtual offset), %r8 -; movq rsp(56 + virtual offset), %rdi -; movq rsp(64 + virtual offset), %rsi -; movq rsp(72 + virtual offset), %rbx -; movq rsp(80 + virtual offset), %rdx -; movq rsp(88 + virtual offset), %rcx ; movq rsp(96 + virtual offset), %rax +; movq rsp(88 + virtual offset), %rcx +; movq rsp(80 + virtual offset), %rdx +; movq rsp(72 + virtual offset), %rbx +; movq rsp(64 + virtual offset), %rsi +; movq rsp(56 + virtual offset), %rdi +; movq rsp(48 + virtual offset), %r8 +; movq rsp(40 + virtual offset), %r9 +; movq rsp(32 + virtual offset), %r10 +; movq rsp(24 + virtual offset), %r11 ; return_call_unknown %r12 new_stack_arg_size:128 old_stack_arg_size:0 ret_addr:Some("%v219") fp:%v218 tmp:%v220 %rax=%rax %rcx=%rcx %rdx=%rdx %rbx=%rbx %rsi=%rsi %rdi=%rdi %r8=%r8 %r9=%r9 %r10=%r10 %r11=%r11 ; ; Disassembled: @@ -443,16 +443,16 @@ block0: ; movq %rbp, %r15 ; movq 8(%r15), %r13 ; movabsq $0, %r12 ; reloc_external Abs8 %tail_callee_stack_args 0 -; movq 0x98(%rsp), %r11 -; movq 0xa0(%rsp), %r10 -; movq 0xa8(%rsp), %r9 -; movq 0xb0(%rsp), %r8 -; movq 0xb8(%rsp), %rdi -; movq 0xc0(%rsp), %rsi -; movq 0xc8(%rsp), %rbx -; movq 0xd0(%rsp), %rdx -; movq 0xd8(%rsp), %rcx ; movq 0xe0(%rsp), %rax +; movq 0xd8(%rsp), %rcx +; movq 0xd0(%rsp), %rdx +; movq 0xc8(%rsp), %rbx +; movq 0xc0(%rsp), %rsi +; movq 0xb8(%rsp), %rdi +; movq 0xb0(%rsp), %r8 +; movq 0xa8(%rsp), %r9 +; movq 0xa0(%rsp), %r10 +; movq 0x98(%rsp), %r11 ; movq (%r15), %rbp ; movq 0x78(%rsp), %r14 ; movq %r14, 8(%r15) diff --git a/cranelift/filetests/filetests/isa/x64/select-i128.clif b/cranelift/filetests/filetests/isa/x64/select-i128.clif index 3586dc7523e8..74eae44aec8c 100644 --- a/cranelift/filetests/filetests/isa/x64/select-i128.clif +++ b/cranelift/filetests/filetests/isa/x64/select-i128.clif @@ -51,11 +51,11 @@ block0(v0: f32, v1: i128, v2: i128): ; movq %rsp, %rbp ; block0: ; ucomiss %xmm0, %xmm0 +; cmovpq %rdx, %rdi, %rdi ; movq %rdi, %rax -; cmovpq %rdx, %rax, %rax ; cmovnzq %rdx, %rax, %rax +; cmovpq %rcx, %rsi, %rsi ; movq %rsi, %rdx -; cmovpq %rcx, %rdx, %rdx ; cmovnzq %rcx, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -67,11 +67,11 @@ block0(v0: f32, v1: i128, v2: i128): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; ucomiss %xmm0, %xmm0 +; cmovpq %rdx, %rdi ; movq %rdi, %rax -; cmovpq %rdx, %rax ; cmovneq %rdx, %rax +; cmovpq %rcx, %rsi ; movq %rsi, %rdx -; cmovpq %rcx, %rdx ; cmovneq %rcx, %rdx ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/select.clif b/cranelift/filetests/filetests/isa/x64/select.clif index a348b34a6e9e..4951bce58d10 100644 --- a/cranelift/filetests/filetests/isa/x64/select.clif +++ b/cranelift/filetests/filetests/isa/x64/select.clif @@ -45,8 +45,8 @@ block0(v0: f32, v1: f32, v2: i64, v3: i64): ; movq %rsp, %rbp ; block0: ; ucomiss %xmm1, %xmm0 +; cmovpq %rsi, %rdi, %rdi ; movq %rdi, %rax -; cmovpq %rsi, %rax, %rax ; cmovnzq %rsi, %rax, %rax ; movq %rbp, %rsp ; popq %rbp @@ -58,8 +58,8 @@ block0(v0: f32, v1: f32, v2: i64, v3: i64): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; ucomiss %xmm1, %xmm0 +; cmovpq %rsi, %rdi ; movq %rdi, %rax -; cmovpq %rsi, %rax ; cmovneq %rsi, %rax ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif b/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif index 8cd260a3f536..12e3ce7c3f5d 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif @@ -914,12 +914,11 @@ block0(v0: i8x16, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %r9 -; andq %r9, $7, %r9 +; andq %rdi, $7, %rdi ; vpunpcklbw %xmm0, %xmm0, %xmm5 ; vpunpckhbw %xmm0, %xmm0, %xmm7 -; addl %r9d, $8, %r9d -; vmovd %r9d, %xmm3 +; addl %edi, $8, %edi +; vmovd %edi, %xmm3 ; vpsraw %xmm5, %xmm3, %xmm5 ; vpsraw %xmm7, %xmm3, %xmm7 ; vpacksswb %xmm5, %xmm7, %xmm0 @@ -932,12 +931,11 @@ block0(v0: i8x16, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %r9 -; andq $7, %r9 +; andq $7, %rdi ; vpunpcklbw %xmm0, %xmm0, %xmm5 ; vpunpckhbw %xmm0, %xmm0, %xmm7 -; addl $8, %r9d -; vmovd %r9d, %xmm3 +; addl $8, %edi +; vmovd %edi, %xmm3 ; vpsraw %xmm3, %xmm5, %xmm5 ; vpsraw %xmm3, %xmm7, %xmm7 ; vpacksswb %xmm7, %xmm5, %xmm0 @@ -989,9 +987,8 @@ block0(v0: i16x8, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; andq %rcx, $15, %rcx -; vmovd %ecx, %xmm5 +; andq %rdi, $15, %rdi +; vmovd %edi, %xmm5 ; vpsraw %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1002,9 +999,8 @@ block0(v0: i16x8, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; andq $0xf, %rcx -; vmovd %ecx, %xmm5 +; andq $0xf, %rdi +; vmovd %edi, %xmm5 ; vpsraw %xmm5, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1046,9 +1042,8 @@ block0(v0: i32x4, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; andq %rcx, $31, %rcx -; vmovd %ecx, %xmm5 +; andq %rdi, $31, %rdi +; vmovd %edi, %xmm5 ; vpsrad %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1059,9 +1054,8 @@ block0(v0: i32x4, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; andq $0x1f, %rcx -; vmovd %ecx, %xmm5 +; andq $0x1f, %rdi +; vmovd %edi, %xmm5 ; vpsrad %xmm5, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1387,13 +1381,12 @@ block0(v0: i8x16, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %r10 -; andq %r10, $7, %r10 -; vmovd %r10d, %xmm5 +; andq %rdi, $7, %rdi +; vmovd %edi, %xmm5 ; vpsllw %xmm0, %xmm5, %xmm7 ; lea const(0), %rsi -; shlq $4, %r10, %r10 -; vmovdqu 0(%rsi,%r10,1), %xmm5 +; shlq $4, %rdi, %rdi +; vmovdqu 0(%rsi,%rdi,1), %xmm5 ; vpand %xmm7, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1404,18 +1397,20 @@ block0(v0: i8x16, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %r10 -; andq $7, %r10 -; vmovd %r10d, %xmm5 +; andq $7, %rdi +; vmovd %edi, %xmm5 ; vpsllw %xmm5, %xmm0, %xmm7 -; leaq 0x15(%rip), %rsi -; shlq $4, %r10 -; vmovdqu (%rsi, %r10), %xmm5 +; leaq 0x19(%rip), %rsi +; shlq $4, %rdi +; vmovdqu (%rsi, %rdi), %xmm5 ; vpand %xmm5, %xmm7, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq ; addb %al, (%rax) +; addb %al, (%rax) +; addb %al, (%rax) +; addb %bh, %bh function %i8x16_shl_imm(i8x16) -> i8x16 { block0(v0: i8x16): @@ -1460,9 +1455,8 @@ block0(v0: i16x8, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; andq %rcx, $15, %rcx -; vmovd %ecx, %xmm5 +; andq %rdi, $15, %rdi +; vmovd %edi, %xmm5 ; vpsllw %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1473,9 +1467,8 @@ block0(v0: i16x8, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; andq $0xf, %rcx -; vmovd %ecx, %xmm5 +; andq $0xf, %rdi +; vmovd %edi, %xmm5 ; vpsllw %xmm5, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1517,9 +1510,8 @@ block0(v0: i32x4, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; andq %rcx, $31, %rcx -; vmovd %ecx, %xmm5 +; andq %rdi, $31, %rdi +; vmovd %edi, %xmm5 ; vpslld %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1530,9 +1522,8 @@ block0(v0: i32x4, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; andq $0x1f, %rcx -; vmovd %ecx, %xmm5 +; andq $0x1f, %rdi +; vmovd %edi, %xmm5 ; vpslld %xmm5, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1574,9 +1565,8 @@ block0(v0: i64x2, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; andq %rcx, $63, %rcx -; vmovd %ecx, %xmm5 +; andq %rdi, $63, %rdi +; vmovd %edi, %xmm5 ; vpsllq %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1587,9 +1577,8 @@ block0(v0: i64x2, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; andq $0x3f, %rcx -; vmovd %ecx, %xmm5 +; andq $0x3f, %rdi +; vmovd %edi, %xmm5 ; vpsllq %xmm5, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1631,13 +1620,12 @@ block0(v0: i8x16, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %r10 -; andq %r10, $7, %r10 -; vmovd %r10d, %xmm5 +; andq %rdi, $7, %rdi +; vmovd %edi, %xmm5 ; vpsrlw %xmm0, %xmm5, %xmm7 ; lea const(0), %rsi -; shlq $4, %r10, %r10 -; vpand %xmm7, 0(%rsi,%r10,1), %xmm0 +; shlq $4, %rdi, %rdi +; vpand %xmm7, 0(%rsi,%rdi,1), %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -1647,19 +1635,21 @@ block0(v0: i8x16, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %r10 -; andq $7, %r10 -; vmovd %r10d, %xmm5 +; andq $7, %rdi +; vmovd %edi, %xmm5 ; vpsrlw %xmm5, %xmm0, %xmm7 -; leaq 0x15(%rip), %rsi -; shlq $4, %r10 -; vpand (%rsi, %r10), %xmm7, %xmm0 +; leaq 0x19(%rip), %rsi +; shlq $4, %rdi +; vpand (%rsi, %rdi), %xmm7, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq ; addb %al, (%rax) ; addb %al, (%rax) ; addb %al, (%rax) +; addb %al, (%rax) +; addb %al, (%rax) +; addb %bh, %bh function %i8x16_ushr_imm(i8x16) -> i8x16 { block0(v0: i8x16): @@ -1712,9 +1702,8 @@ block0(v0: i16x8, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; andq %rcx, $15, %rcx -; vmovd %ecx, %xmm5 +; andq %rdi, $15, %rdi +; vmovd %edi, %xmm5 ; vpsrlw %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1725,9 +1714,8 @@ block0(v0: i16x8, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; andq $0xf, %rcx -; vmovd %ecx, %xmm5 +; andq $0xf, %rdi +; vmovd %edi, %xmm5 ; vpsrlw %xmm5, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1769,9 +1757,8 @@ block0(v0: i32x4, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; andq %rcx, $31, %rcx -; vmovd %ecx, %xmm5 +; andq %rdi, $31, %rdi +; vmovd %edi, %xmm5 ; vpsrld %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1782,9 +1769,8 @@ block0(v0: i32x4, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; andq $0x1f, %rcx -; vmovd %ecx, %xmm5 +; andq $0x1f, %rdi +; vmovd %edi, %xmm5 ; vpsrld %xmm5, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1826,9 +1812,8 @@ block0(v0: i64x2, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; andq %rcx, $63, %rcx -; vmovd %ecx, %xmm5 +; andq %rdi, $63, %rdi +; vmovd %edi, %xmm5 ; vpsrlq %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -1839,9 +1824,8 @@ block0(v0: i64x2, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; andq $0x3f, %rcx -; vmovd %ecx, %xmm5 +; andq $0x3f, %rdi +; vmovd %edi, %xmm5 ; vpsrlq %xmm5, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/simd-bitselect.clif b/cranelift/filetests/filetests/isa/x64/simd-bitselect.clif index 6ffc922ee764..842fceb3bf1c 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-bitselect.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-bitselect.clif @@ -82,11 +82,12 @@ block0(v0: i8x16, v1: i8x16, v2: i32x4): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqa %xmm0, %xmm4 -; pand %xmm4, %xmm2, %xmm4 +; pand %xmm0, %xmm2, %xmm0 +; movdqa %xmm0, %xmm7 +; pandn %xmm2, %xmm1, %xmm2 +; movdqa %xmm7, %xmm1 ; movdqa %xmm2, %xmm0 -; pandn %xmm0, %xmm1, %xmm0 -; por %xmm0, %xmm4, %xmm0 +; por %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -96,11 +97,12 @@ block0(v0: i8x16, v1: i8x16, v2: i32x4): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqa %xmm0, %xmm4 -; pand %xmm2, %xmm4 +; pand %xmm2, %xmm0 +; movdqa %xmm0, %xmm7 +; pandn %xmm1, %xmm2 +; movdqa %xmm7, %xmm1 ; movdqa %xmm2, %xmm0 -; pandn %xmm1, %xmm0 -; por %xmm4, %xmm0 +; por %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -211,12 +213,12 @@ block0(v0: i8x16, v1: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqa %xmm0, %xmm2 +; movdqa %xmm0, %xmm7 ; movdqu const(0), %xmm0 -; movdqa %xmm2, %xmm4 -; pand %xmm4, %xmm0, %xmm4 +; movdqa %xmm7, %xmm2 +; pand %xmm2, %xmm0, %xmm2 ; pandn %xmm0, %xmm1, %xmm0 -; por %xmm0, %xmm4, %xmm0 +; por %xmm0, %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -226,12 +228,12 @@ block0(v0: i8x16, v1: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqa %xmm0, %xmm2 +; movdqa %xmm0, %xmm7 ; movdqu 0x20(%rip), %xmm0 -; movdqa %xmm2, %xmm4 -; pand %xmm0, %xmm4 +; movdqa %xmm7, %xmm2 +; pand %xmm0, %xmm2 ; pandn %xmm1, %xmm0 -; por %xmm4, %xmm0 +; por %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif b/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif index b1ed1466069f..f026d0a6e809 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif @@ -236,10 +236,9 @@ block0(v0: i16x8, v1: i16x8, v2: i16x8): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqa %xmm1, %xmm4 -; pand %xmm4, %xmm0, %xmm4 +; pand %xmm1, %xmm0, %xmm1 ; pandn %xmm0, %xmm2, %xmm0 -; por %xmm0, %xmm4, %xmm0 +; por %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -249,10 +248,9 @@ block0(v0: i16x8, v1: i16x8, v2: i16x8): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqa %xmm1, %xmm4 -; pand %xmm0, %xmm4 +; pand %xmm0, %xmm1 ; pandn %xmm2, %xmm0 -; por %xmm4, %xmm0 +; por %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -267,10 +265,9 @@ block0(v0: f32x4, v1: f32x4, v2: f32x4): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqa %xmm1, %xmm4 -; andps %xmm4, %xmm0, %xmm4 +; andps %xmm1, %xmm0, %xmm1 ; andnps %xmm0, %xmm2, %xmm0 -; orps %xmm0, %xmm4, %xmm0 +; orps %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -280,10 +277,9 @@ block0(v0: f32x4, v1: f32x4, v2: f32x4): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqa %xmm1, %xmm4 -; andps %xmm0, %xmm4 +; andps %xmm0, %xmm1 ; andnps %xmm2, %xmm0 -; orps %xmm4, %xmm0 +; orps %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -298,10 +294,9 @@ block0(v0: f64x2, v1: f64x2, v2: f64x2): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqa %xmm1, %xmm4 -; andpd %xmm4, %xmm0, %xmm4 +; andpd %xmm1, %xmm0, %xmm1 ; andnpd %xmm0, %xmm2, %xmm0 -; orpd %xmm0, %xmm4, %xmm0 +; orpd %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -311,10 +306,9 @@ block0(v0: f64x2, v1: f64x2, v2: f64x2): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqa %xmm1, %xmm4 -; andpd %xmm0, %xmm4 +; andpd %xmm0, %xmm1 ; andnpd %xmm2, %xmm0 -; orpd %xmm4, %xmm0 +; orpd %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -331,13 +325,12 @@ block0(v0: i32): ; movq %rsp, %rbp ; block0: ; movdqu const(1), %xmm0 -; movq %rdi, %r10 -; andq %r10, $7, %r10 -; movd %r10d, %xmm5 +; andq %rdi, $7, %rdi +; movd %edi, %xmm5 ; psllw %xmm0, %xmm5, %xmm0 ; lea const(0), %rsi -; shlq $4, %r10, %r10 -; movdqu 0(%rsi,%r10,1), %xmm5 +; shlq $4, %rdi, %rdi +; movdqu 0(%rsi,%rdi,1), %xmm5 ; pand %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -349,13 +342,12 @@ block0(v0: i32): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; movdqu 0x34(%rip), %xmm0 -; movq %rdi, %r10 -; andq $7, %r10 -; movd %r10d, %xmm5 +; andq $7, %rdi +; movd %edi, %xmm5 ; psllw %xmm5, %xmm0 -; leaq 0x2d(%rip), %rsi -; shlq $4, %r10 -; movdqu (%rsi, %r10), %xmm5 +; leaq 0x31(%rip), %rsi +; shlq $4, %rdi +; movdqu (%rsi, %rdi), %xmm5 ; pand %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -365,9 +357,13 @@ block0(v0: i32): ; addb %al, (%rax) ; addb %al, (%rax) ; addb %al, (%rax) -; addb %al, (%rcx) -; addb (%rbx), %al -; addb $5, %al +; addb %al, (%rax) +; addb %al, (%rax) +; addb %al, (%rax) +; addl %eax, (%rdx) +; addl 0x9080706(, %rax), %eax +; orb (%rbx), %cl +; orb $0xd, %al function %ishl_i8x16_imm(i8x16) -> i8x16 { block0(v0: i8x16): @@ -605,13 +601,12 @@ block0(v0: i32): ; movq %rsp, %rbp ; block0: ; movdqu const(0), %xmm1 -; movq %rdi, %r9 -; andq %r9, $7, %r9 +; andq %rdi, $7, %rdi ; movdqa %xmm1, %xmm0 ; punpcklbw %xmm0, %xmm1, %xmm0 ; punpckhbw %xmm1, %xmm1, %xmm1 -; addl %r9d, $8, %r9d -; movd %r9d, %xmm3 +; addl %edi, $8, %edi +; movd %edi, %xmm3 ; psraw %xmm0, %xmm3, %xmm0 ; psraw %xmm1, %xmm3, %xmm1 ; packsswb %xmm0, %xmm1, %xmm0 @@ -625,13 +620,12 @@ block0(v0: i32): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; movdqu 0x34(%rip), %xmm1 -; movq %rdi, %r9 -; andq $7, %r9 +; andq $7, %rdi ; movdqa %xmm1, %xmm0 ; punpcklbw %xmm1, %xmm0 ; punpckhbw %xmm1, %xmm1 -; addl $8, %r9d -; movd %r9d, %xmm3 +; addl $8, %edi +; movd %edi, %xmm3 ; psraw %xmm3, %xmm0 ; psraw %xmm3, %xmm1 ; packsswb %xmm1, %xmm0 @@ -642,10 +636,11 @@ block0(v0: i32): ; addb %al, (%rax) ; addb %al, (%rax) ; addb %al, (%rax) -; addl %eax, (%rdx) -; addl 0x9080706(, %rax), %eax -; orb (%rbx), %cl -; orb $0xd, %al +; addb %al, (%rax) +; addb %al, (%rax) +; addb %al, (%rcx) +; addb (%rbx), %al +; addb $5, %al function %sshr_i8x16_imm(i8x16, i32) -> i8x16 { block0(v0: i8x16, v1: i32): @@ -659,13 +654,12 @@ block0(v0: i8x16, v1: i32): ; block0: ; movdqa %xmm0, %xmm7 ; punpcklbw %xmm7, %xmm0, %xmm7 -; movdqa %xmm7, %xmm1 -; movdqa %xmm0, %xmm7 -; punpckhbw %xmm7, %xmm0, %xmm7 -; movdqa %xmm1, %xmm0 +; punpckhbw %xmm0, %xmm0, %xmm0 +; movdqa %xmm0, %xmm5 +; movdqa %xmm7, %xmm0 ; psraw %xmm0, $11, %xmm0 -; psraw %xmm7, $11, %xmm7 -; packsswb %xmm0, %xmm7, %xmm0 +; psraw %xmm5, $11, %xmm5 +; packsswb %xmm0, %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -677,13 +671,12 @@ block0(v0: i8x16, v1: i32): ; block1: ; offset 0x4 ; movdqa %xmm0, %xmm7 ; punpcklbw %xmm0, %xmm7 -; movdqa %xmm7, %xmm1 -; movdqa %xmm0, %xmm7 -; punpckhbw %xmm0, %xmm7 -; movdqa %xmm1, %xmm0 +; punpckhbw %xmm0, %xmm0 +; movdqa %xmm0, %xmm5 +; movdqa %xmm7, %xmm0 ; psraw $0xb, %xmm0 -; psraw $0xb, %xmm7 -; packsswb %xmm7, %xmm0 +; psraw $0xb, %xmm5 +; packsswb %xmm5, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -754,9 +747,8 @@ block0(v0: i64x2): ; movdqa %xmm0, %xmm2 ; psrad %xmm2, $1, %xmm2 ; pshufd $237, %xmm2, %xmm4 -; movdqa %xmm0, %xmm6 -; psrlq %xmm6, $1, %xmm6 -; pshufd $232, %xmm6, %xmm0 +; psrlq %xmm0, $1, %xmm0 +; pshufd $232, %xmm0, %xmm0 ; punpckldq %xmm0, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -770,9 +762,8 @@ block0(v0: i64x2): ; movdqa %xmm0, %xmm2 ; psrad $1, %xmm2 ; pshufd $0xed, %xmm2, %xmm4 -; movdqa %xmm0, %xmm6 -; psrlq $1, %xmm6 -; pshufd $0xe8, %xmm6, %xmm0 +; psrlq $1, %xmm0 +; pshufd $0xe8, %xmm0, %xmm0 ; punpckldq %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -790,9 +781,8 @@ block0(v0: i64x2): ; movq %rsp, %rbp ; block0: ; pshufd $237, %xmm0, %xmm5 -; movdqa %xmm0, %xmm4 -; psrad %xmm4, $31, %xmm4 -; pshufd $237, %xmm4, %xmm6 +; psrad %xmm0, $31, %xmm0 +; pshufd $237, %xmm0, %xmm6 ; movdqa %xmm5, %xmm0 ; punpckldq %xmm0, %xmm6, %xmm0 ; movq %rbp, %rsp @@ -805,9 +795,8 @@ block0(v0: i64x2): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; pshufd $0xed, %xmm0, %xmm5 -; movdqa %xmm0, %xmm4 -; psrad $0x1f, %xmm4 -; pshufd $0xed, %xmm4, %xmm6 +; psrad $0x1f, %xmm0 +; pshufd $0xed, %xmm0, %xmm6 ; movdqa %xmm5, %xmm0 ; punpckldq %xmm6, %xmm0 ; movq %rbp, %rsp @@ -828,9 +817,8 @@ block0(v0: i64x2): ; movdqa %xmm0, %xmm2 ; psrad %xmm2, $31, %xmm2 ; pshufd $237, %xmm2, %xmm4 -; movdqa %xmm0, %xmm6 -; psrad %xmm6, $22, %xmm6 -; pshufd $237, %xmm6, %xmm0 +; psrad %xmm0, $22, %xmm0 +; pshufd $237, %xmm0, %xmm0 ; punpckldq %xmm0, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -844,9 +832,8 @@ block0(v0: i64x2): ; movdqa %xmm0, %xmm2 ; psrad $0x1f, %xmm2 ; pshufd $0xed, %xmm2, %xmm4 -; movdqa %xmm0, %xmm6 -; psrad $0x16, %xmm6 -; pshufd $0xed, %xmm6, %xmm0 +; psrad $0x16, %xmm0 +; pshufd $0xed, %xmm0, %xmm0 ; punpckldq %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -866,9 +853,8 @@ block0(v0: i64x2): ; movdqa %xmm0, %xmm2 ; psrad %xmm2, $31, %xmm2 ; pshufd $237, %xmm2, %xmm4 -; movdqa %xmm0, %xmm6 -; psrad %xmm6, $4, %xmm6 -; pshufd $237, %xmm6, %xmm0 +; psrad %xmm0, $4, %xmm0 +; pshufd $237, %xmm0, %xmm0 ; punpckldq %xmm0, %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -882,9 +868,8 @@ block0(v0: i64x2): ; movdqa %xmm0, %xmm2 ; psrad $0x1f, %xmm2 ; pshufd $0xed, %xmm2, %xmm4 -; movdqa %xmm0, %xmm6 -; psrad $4, %xmm6 -; pshufd $0xed, %xmm6, %xmm0 +; psrad $4, %xmm0 +; pshufd $0xed, %xmm0, %xmm0 ; punpckldq %xmm4, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -900,15 +885,14 @@ block0(v0: i64x2, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; andq %rcx, $63, %rcx -; movq %rcx, %xmm5 +; andq %rdi, $63, %rdi +; movq %rdi, %xmm5 ; movdqu const(0), %xmm1 ; psrlq %xmm1, %xmm5, %xmm1 -; movdqa %xmm0, %xmm3 -; psrlq %xmm3, %xmm5, %xmm3 +; psrlq %xmm0, %xmm5, %xmm0 +; movdqa %xmm0, %xmm7 ; movdqa %xmm1, %xmm0 -; pxor %xmm0, %xmm3, %xmm0 +; pxor %xmm0, %xmm7, %xmm0 ; psubq %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -919,15 +903,14 @@ block0(v0: i64x2, v1: i32): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; andq $0x3f, %rcx -; movq %rcx, %xmm5 -; movdqu 0x28(%rip), %xmm1 +; andq $0x3f, %rdi +; movq %rdi, %xmm5 +; movdqu 0x2b(%rip), %xmm1 ; psrlq %xmm5, %xmm1 -; movdqa %xmm0, %xmm3 -; psrlq %xmm5, %xmm3 +; psrlq %xmm5, %xmm0 +; movdqa %xmm0, %xmm7 ; movdqa %xmm1, %xmm0 -; pxor %xmm3, %xmm0 +; pxor %xmm7, %xmm0 ; psubq %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -941,7 +924,7 @@ block0(v0: i64x2, v1: i32): ; addb %al, (%rax) ; addb %al, (%rax) ; addb %al, (%rax) -; addb $0, (%rax) +; addb %al, (%rax) ; addb %al, (%rax) ; addb %al, (%rax) diff --git a/cranelift/filetests/filetests/isa/x64/simd-i64x2-shift-avx512.clif b/cranelift/filetests/filetests/isa/x64/simd-i64x2-shift-avx512.clif index 8730dee0e2d5..22210f479675 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-i64x2-shift-avx512.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-i64x2-shift-avx512.clif @@ -11,9 +11,8 @@ block0(v0: i64x2, v1: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; andq %rcx, $63, %rcx -; vmovd %ecx, %xmm5 +; andq %rdi, $63, %rdi +; vmovd %edi, %xmm5 ; vpsraq %xmm5, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -24,9 +23,8 @@ block0(v0: i64x2, v1: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; andq $0x3f, %rcx -; vmovd %ecx, %xmm5 +; andq $0x3f, %rdi +; vmovd %edi, %xmm5 ; vpsraq %xmm5, %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/simd-logical-compile.clif b/cranelift/filetests/filetests/isa/x64/simd-logical-compile.clif index e9df78d5fd82..0e95a11aa374 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-logical-compile.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-logical-compile.clif @@ -68,9 +68,8 @@ block0(v0: i64x2): ; block0: ; uninit %xmm3 ; pxor %xmm3, %xmm3, %xmm3 -; movdqa %xmm0, %xmm6 -; pcmpeqq %xmm6, %xmm3, %xmm6 -; ptest %xmm6, %xmm6 +; pcmpeqq %xmm0, %xmm3, %xmm0 +; ptest %xmm0, %xmm0 ; setz %al ; movq %rbp, %rsp ; popq %rbp @@ -82,9 +81,8 @@ block0(v0: i64x2): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; pxor %xmm3, %xmm3 -; movdqa %xmm0, %xmm6 -; pcmpeqq %xmm3, %xmm6 -; ptest %xmm6, %xmm6 +; pcmpeqq %xmm3, %xmm0 +; ptest %xmm0, %xmm0 ; sete %al ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/simd-widen-mul.clif b/cranelift/filetests/filetests/isa/x64/simd-widen-mul.clif index 4890d8bc4f75..69a43c63bc7f 100644 --- a/cranelift/filetests/filetests/isa/x64/simd-widen-mul.clif +++ b/cranelift/filetests/filetests/isa/x64/simd-widen-mul.clif @@ -13,12 +13,10 @@ block0(v0: i8x16, v1: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqa %xmm0, %xmm6 -; palignr $8, %xmm6, %xmm0, %xmm6 -; pmovsxbw %xmm6, %xmm0 -; movdqa %xmm1, %xmm6 -; palignr $8, %xmm6, %xmm1, %xmm6 -; pmovsxbw %xmm6, %xmm1 +; palignr $8, %xmm0, %xmm0, %xmm0 +; pmovsxbw %xmm0, %xmm0 +; palignr $8, %xmm1, %xmm1, %xmm1 +; pmovsxbw %xmm1, %xmm1 ; pmullw %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -29,12 +27,10 @@ block0(v0: i8x16, v1: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqa %xmm0, %xmm6 -; palignr $8, %xmm0, %xmm6 -; pmovsxbw %xmm6, %xmm0 -; movdqa %xmm1, %xmm6 -; palignr $8, %xmm1, %xmm6 -; pmovsxbw %xmm6, %xmm1 +; palignr $8, %xmm0, %xmm0 +; pmovsxbw %xmm0, %xmm0 +; palignr $8, %xmm1, %xmm1 +; pmovsxbw %xmm1, %xmm1 ; pmullw %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp @@ -54,11 +50,10 @@ block0(v0: i16x8, v1: i16x8): ; block0: ; movdqa %xmm0, %xmm5 ; pmullw %xmm5, %xmm1, %xmm5 -; movdqa %xmm5, %xmm6 -; movdqa %xmm0, %xmm5 -; pmulhw %xmm5, %xmm1, %xmm5 -; movdqa %xmm6, %xmm0 -; punpckhwd %xmm0, %xmm5, %xmm0 +; pmulhw %xmm0, %xmm1, %xmm0 +; movdqa %xmm0, %xmm2 +; movdqa %xmm5, %xmm0 +; punpckhwd %xmm0, %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -70,11 +65,10 @@ block0(v0: i16x8, v1: i16x8): ; block1: ; offset 0x4 ; movdqa %xmm0, %xmm5 ; pmullw %xmm1, %xmm5 -; movdqa %xmm5, %xmm6 -; movdqa %xmm0, %xmm5 -; pmulhw %xmm1, %xmm5 -; movdqa %xmm6, %xmm0 -; punpckhwd %xmm5, %xmm0 +; pmulhw %xmm1, %xmm0 +; movdqa %xmm0, %xmm2 +; movdqa %xmm5, %xmm0 +; punpckhwd %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -155,11 +149,10 @@ block0(v0: i16x8, v1: i16x8): ; block0: ; movdqa %xmm0, %xmm5 ; pmullw %xmm5, %xmm1, %xmm5 -; movdqa %xmm5, %xmm6 -; movdqa %xmm0, %xmm5 -; pmulhw %xmm5, %xmm1, %xmm5 -; movdqa %xmm6, %xmm0 -; punpcklwd %xmm0, %xmm5, %xmm0 +; pmulhw %xmm0, %xmm1, %xmm0 +; movdqa %xmm0, %xmm2 +; movdqa %xmm5, %xmm0 +; punpcklwd %xmm0, %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -171,11 +164,10 @@ block0(v0: i16x8, v1: i16x8): ; block1: ; offset 0x4 ; movdqa %xmm0, %xmm5 ; pmullw %xmm1, %xmm5 -; movdqa %xmm5, %xmm6 -; movdqa %xmm0, %xmm5 -; pmulhw %xmm1, %xmm5 -; movdqa %xmm6, %xmm0 -; punpcklwd %xmm5, %xmm0 +; pmulhw %xmm1, %xmm0 +; movdqa %xmm0, %xmm2 +; movdqa %xmm5, %xmm0 +; punpcklwd %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -228,9 +220,8 @@ block0(v0: i8x16, v1: i8x16): ; punpckhbw %xmm0, %xmm2, %xmm0 ; uninit %xmm2 ; pxor %xmm2, %xmm2, %xmm2 -; movdqa %xmm1, %xmm3 -; punpckhbw %xmm3, %xmm2, %xmm3 -; pmullw %xmm0, %xmm3, %xmm0 +; punpckhbw %xmm1, %xmm2, %xmm1 +; pmullw %xmm0, %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -243,9 +234,8 @@ block0(v0: i8x16, v1: i8x16): ; pxor %xmm2, %xmm2 ; punpckhbw %xmm2, %xmm0 ; pxor %xmm2, %xmm2 -; movdqa %xmm1, %xmm3 -; punpckhbw %xmm2, %xmm3 -; pmullw %xmm3, %xmm0 +; punpckhbw %xmm2, %xmm1 +; pmullw %xmm1, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -264,11 +254,10 @@ block0(v0: i16x8, v1: i16x8): ; block0: ; movdqa %xmm0, %xmm5 ; pmullw %xmm5, %xmm1, %xmm5 -; movdqa %xmm5, %xmm6 -; movdqa %xmm0, %xmm5 -; pmulhuw %xmm5, %xmm1, %xmm5 -; movdqa %xmm6, %xmm0 -; punpckhwd %xmm0, %xmm5, %xmm0 +; pmulhuw %xmm0, %xmm1, %xmm0 +; movdqa %xmm0, %xmm2 +; movdqa %xmm5, %xmm0 +; punpckhwd %xmm0, %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -280,11 +269,10 @@ block0(v0: i16x8, v1: i16x8): ; block1: ; offset 0x4 ; movdqa %xmm0, %xmm5 ; pmullw %xmm1, %xmm5 -; movdqa %xmm5, %xmm6 -; movdqa %xmm0, %xmm5 -; pmulhuw %xmm1, %xmm5 -; movdqa %xmm6, %xmm0 -; punpckhwd %xmm5, %xmm0 +; pmulhuw %xmm1, %xmm0 +; movdqa %xmm0, %xmm2 +; movdqa %xmm5, %xmm0 +; punpckhwd %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -365,11 +353,10 @@ block0(v0: i16x8, v1: i16x8): ; block0: ; movdqa %xmm0, %xmm5 ; pmullw %xmm5, %xmm1, %xmm5 -; movdqa %xmm5, %xmm6 -; movdqa %xmm0, %xmm5 -; pmulhuw %xmm5, %xmm1, %xmm5 -; movdqa %xmm6, %xmm0 -; punpcklwd %xmm0, %xmm5, %xmm0 +; pmulhuw %xmm0, %xmm1, %xmm0 +; movdqa %xmm0, %xmm2 +; movdqa %xmm5, %xmm0 +; punpcklwd %xmm0, %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -381,11 +368,10 @@ block0(v0: i16x8, v1: i16x8): ; block1: ; offset 0x4 ; movdqa %xmm0, %xmm5 ; pmullw %xmm1, %xmm5 -; movdqa %xmm5, %xmm6 -; movdqa %xmm0, %xmm5 -; pmulhuw %xmm1, %xmm5 -; movdqa %xmm6, %xmm0 -; punpcklwd %xmm5, %xmm0 +; pmulhuw %xmm1, %xmm0 +; movdqa %xmm0, %xmm2 +; movdqa %xmm5, %xmm0 +; punpcklwd %xmm2, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/sshr.clif b/cranelift/filetests/filetests/isa/x64/sshr.clif index 45d7d9af7624..b44ad0c35ed6 100644 --- a/cranelift/filetests/filetests/isa/x64/sshr.clif +++ b/cranelift/filetests/filetests/isa/x64/sshr.clif @@ -18,8 +18,7 @@ block0(v0: i128, v1: i8): ; movq %rsp, %rbp ; block0: ; movzbq %dl, %rcx -; movq %rdi, %r8 -; shrq %cl, %r8, %r8 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r10 ; sarq %cl, %r10, %r10 ; movq %rcx, %r11 @@ -31,12 +30,12 @@ block0(v0: i128, v1: i8): ; xorq %r11, %r11, %r11 ; testq $127, %rax ; cmovzq %r11, %r9, %r9 -; orq %r8, %r9, %r8 -; movq %rsi, %rdx -; sarq $63, %rdx, %rdx +; orq %rdi, %r9, %rdi +; sarq $63, %rsi, %rsi ; testq $64, %rax ; movq %r10, %rax -; cmovzq %r8, %rax, %rax +; cmovzq %rdi, %rax, %rax +; movq %rsi, %rdx ; cmovzq %r10, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -48,8 +47,7 @@ block0(v0: i128, v1: i8): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; movzbq %dl, %rcx -; movq %rdi, %r8 -; shrq %cl, %r8 +; shrq %cl, %rdi ; movq %rsi, %r10 ; sarq %cl, %r10 ; movq %rcx, %r11 @@ -61,12 +59,12 @@ block0(v0: i128, v1: i8): ; xorq %r11, %r11 ; testq $0x7f, %rax ; cmoveq %r11, %r9 -; orq %r9, %r8 -; movq %rsi, %rdx -; sarq $0x3f, %rdx +; orq %r9, %rdi +; sarq $0x3f, %rsi ; testq $0x40, %rax ; movq %r10, %rax -; cmoveq %r8, %rax +; cmoveq %rdi, %rax +; movq %rsi, %rdx ; cmoveq %r10, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -84,25 +82,24 @@ block0(v0: i128, v1: i64): ; block0: ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r11 -; shrq %cl, %r11, %r11 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r9 ; sarq %cl, %r9, %r9 ; movq %rcx, %r10 ; movl $64, %ecx -; movq %r10, %rdi -; subq %rcx, %rdi, %rcx +; movq %r10, %rax +; subq %rcx, %rax, %rcx ; movq %rsi, %r8 ; shlq %cl, %r8, %r8 ; xorq %r10, %r10, %r10 -; testq $127, %rdi +; testq $127, %rax ; cmovzq %r10, %r8, %r8 -; orq %r11, %r8, %r11 -; movq %rsi, %rdx -; sarq $63, %rdx, %rdx -; testq $64, %rdi +; orq %rdi, %r8, %rdi +; sarq $63, %rsi, %rsi +; testq $64, %rax ; movq %r9, %rax -; cmovzq %r11, %rax, %rax +; cmovzq %rdi, %rax, %rax +; movq %rsi, %rdx ; cmovzq %r9, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -115,25 +112,24 @@ block0(v0: i128, v1: i64): ; block1: ; offset 0x4 ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r11 -; shrq %cl, %r11 +; shrq %cl, %rdi ; movq %rsi, %r9 ; sarq %cl, %r9 ; movq %rcx, %r10 ; movl $0x40, %ecx -; movq %r10, %rdi -; subq %rdi, %rcx +; movq %r10, %rax +; subq %rax, %rcx ; movq %rsi, %r8 ; shlq %cl, %r8 ; xorq %r10, %r10 -; testq $0x7f, %rdi +; testq $0x7f, %rax ; cmoveq %r10, %r8 -; orq %r8, %r11 -; movq %rsi, %rdx -; sarq $0x3f, %rdx -; testq $0x40, %rdi +; orq %r8, %rdi +; sarq $0x3f, %rsi +; testq $0x40, %rax ; movq %r9, %rax -; cmoveq %r11, %rax +; cmoveq %rdi, %rax +; movq %rsi, %rdx ; cmoveq %r9, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -151,25 +147,24 @@ block0(v0: i128, v1: i32): ; block0: ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r11 -; shrq %cl, %r11, %r11 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r9 ; sarq %cl, %r9, %r9 ; movq %rcx, %r10 ; movl $64, %ecx -; movq %r10, %rdi -; subq %rcx, %rdi, %rcx +; movq %r10, %rax +; subq %rcx, %rax, %rcx ; movq %rsi, %r8 ; shlq %cl, %r8, %r8 ; xorq %r10, %r10, %r10 -; testq $127, %rdi +; testq $127, %rax ; cmovzq %r10, %r8, %r8 -; orq %r11, %r8, %r11 -; movq %rsi, %rdx -; sarq $63, %rdx, %rdx -; testq $64, %rdi +; orq %rdi, %r8, %rdi +; sarq $63, %rsi, %rsi +; testq $64, %rax ; movq %r9, %rax -; cmovzq %r11, %rax, %rax +; cmovzq %rdi, %rax, %rax +; movq %rsi, %rdx ; cmovzq %r9, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -182,25 +177,24 @@ block0(v0: i128, v1: i32): ; block1: ; offset 0x4 ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r11 -; shrq %cl, %r11 +; shrq %cl, %rdi ; movq %rsi, %r9 ; sarq %cl, %r9 ; movq %rcx, %r10 ; movl $0x40, %ecx -; movq %r10, %rdi -; subq %rdi, %rcx +; movq %r10, %rax +; subq %rax, %rcx ; movq %rsi, %r8 ; shlq %cl, %r8 ; xorq %r10, %r10 -; testq $0x7f, %rdi +; testq $0x7f, %rax ; cmoveq %r10, %r8 -; orq %r8, %r11 -; movq %rsi, %rdx -; sarq $0x3f, %rdx -; testq $0x40, %rdi +; orq %r8, %rdi +; sarq $0x3f, %rsi +; testq $0x40, %rax ; movq %r9, %rax -; cmoveq %r11, %rax +; cmoveq %rdi, %rax +; movq %rsi, %rdx ; cmoveq %r9, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -218,25 +212,24 @@ block0(v0: i128, v1: i16): ; block0: ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r11 -; shrq %cl, %r11, %r11 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r9 ; sarq %cl, %r9, %r9 ; movq %rcx, %r10 ; movl $64, %ecx -; movq %r10, %rdi -; subq %rcx, %rdi, %rcx +; movq %r10, %rax +; subq %rcx, %rax, %rcx ; movq %rsi, %r8 ; shlq %cl, %r8, %r8 ; xorq %r10, %r10, %r10 -; testq $127, %rdi +; testq $127, %rax ; cmovzq %r10, %r8, %r8 -; orq %r11, %r8, %r11 -; movq %rsi, %rdx -; sarq $63, %rdx, %rdx -; testq $64, %rdi +; orq %rdi, %r8, %rdi +; sarq $63, %rsi, %rsi +; testq $64, %rax ; movq %r9, %rax -; cmovzq %r11, %rax, %rax +; cmovzq %rdi, %rax, %rax +; movq %rsi, %rdx ; cmovzq %r9, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -249,25 +242,24 @@ block0(v0: i128, v1: i16): ; block1: ; offset 0x4 ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r11 -; shrq %cl, %r11 +; shrq %cl, %rdi ; movq %rsi, %r9 ; sarq %cl, %r9 ; movq %rcx, %r10 ; movl $0x40, %ecx -; movq %r10, %rdi -; subq %rdi, %rcx +; movq %r10, %rax +; subq %rax, %rcx ; movq %rsi, %r8 ; shlq %cl, %r8 ; xorq %r10, %r10 -; testq $0x7f, %rdi +; testq $0x7f, %rax ; cmoveq %r10, %r8 -; orq %r8, %r11 -; movq %rsi, %rdx -; sarq $0x3f, %rdx -; testq $0x40, %rdi +; orq %r8, %rdi +; sarq $0x3f, %rsi +; testq $0x40, %rax ; movq %r9, %rax -; cmoveq %r11, %rax +; cmoveq %rdi, %rax +; movq %rsi, %rdx ; cmoveq %r9, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -285,25 +277,24 @@ block0(v0: i128, v1: i8): ; block0: ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r11 -; shrq %cl, %r11, %r11 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r9 ; sarq %cl, %r9, %r9 ; movq %rcx, %r10 ; movl $64, %ecx -; movq %r10, %rdi -; subq %rcx, %rdi, %rcx +; movq %r10, %rax +; subq %rcx, %rax, %rcx ; movq %rsi, %r8 ; shlq %cl, %r8, %r8 ; xorq %r10, %r10, %r10 -; testq $127, %rdi +; testq $127, %rax ; cmovzq %r10, %r8, %r8 -; orq %r11, %r8, %r11 -; movq %rsi, %rdx -; sarq $63, %rdx, %rdx -; testq $64, %rdi +; orq %rdi, %r8, %rdi +; sarq $63, %rsi, %rsi +; testq $64, %rax ; movq %r9, %rax -; cmovzq %r11, %rax, %rax +; cmovzq %rdi, %rax, %rax +; movq %rsi, %rdx ; cmovzq %r9, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -316,25 +307,24 @@ block0(v0: i128, v1: i8): ; block1: ; offset 0x4 ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r11 -; shrq %cl, %r11 +; shrq %cl, %rdi ; movq %rsi, %r9 ; sarq %cl, %r9 ; movq %rcx, %r10 ; movl $0x40, %ecx -; movq %r10, %rdi -; subq %rdi, %rcx +; movq %r10, %rax +; subq %rax, %rcx ; movq %rsi, %r8 ; shlq %cl, %r8 ; xorq %r10, %r10 -; testq $0x7f, %rdi +; testq $0x7f, %rax ; cmoveq %r10, %r8 -; orq %r8, %r11 -; movq %rsi, %rdx -; sarq $0x3f, %rdx -; testq $0x40, %rdi +; orq %r8, %rdi +; sarq $0x3f, %rsi +; testq $0x40, %rax ; movq %r9, %rax -; cmoveq %r11, %rax +; cmoveq %rdi, %rax +; movq %rsi, %rdx ; cmoveq %r9, %rdx ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/struct-arg.clif b/cranelift/filetests/filetests/isa/x64/struct-arg.clif index cf1606a892e5..5de74242ec39 100644 --- a/cranelift/filetests/filetests/isa/x64/struct-arg.clif +++ b/cranelift/filetests/filetests/isa/x64/struct-arg.clif @@ -207,8 +207,8 @@ block0(v0: i64, v1: i64, v2: i64): ; movq %r12, 0(%rsp) ; movq %r14, 8(%rsp) ; block0: -; movq %rdx, %r14 ; movq %rdi, %r12 +; movq %rdx, %r14 ; subq %rsp, $192, %rsp ; virtual_sp_offset_adjust 192 ; lea 0(%rsp), %rdi @@ -239,8 +239,8 @@ block0(v0: i64, v1: i64, v2: i64): ; movq %r12, (%rsp) ; movq %r14, 8(%rsp) ; block1: ; offset 0x11 -; movq %rdx, %r14 ; movq %rdi, %r12 +; movq %rdx, %r14 ; subq $0xc0, %rsp ; leaq (%rsp), %rdi ; movl $0x80, %edx diff --git a/cranelift/filetests/filetests/isa/x64/tail-call-conv.clif b/cranelift/filetests/filetests/isa/x64/tail-call-conv.clif index 04012299b7b5..e3afe591e268 100644 --- a/cranelift/filetests/filetests/isa/x64/tail-call-conv.clif +++ b/cranelift/filetests/filetests/isa/x64/tail-call-conv.clif @@ -240,19 +240,19 @@ block0: ; movq %r12, 80(%rax) ; movq %r13, 88(%rax) ; movq %r14, 96(%rax) -; movq rsp(0 + virtual offset), %r14 -; movq rsp(8 + virtual offset), %r13 -; movq rsp(16 + virtual offset), %r12 -; movq rsp(24 + virtual offset), %r11 -; movq rsp(32 + virtual offset), %r10 -; movq rsp(40 + virtual offset), %r9 -; movq rsp(48 + virtual offset), %r8 -; movq rsp(56 + virtual offset), %rdi -; movq rsp(64 + virtual offset), %rsi -; movq rsp(72 + virtual offset), %rbx -; movq rsp(80 + virtual offset), %rdx -; movq rsp(88 + virtual offset), %rcx ; movq rsp(96 + virtual offset), %rax +; movq rsp(88 + virtual offset), %rcx +; movq rsp(80 + virtual offset), %rdx +; movq rsp(72 + virtual offset), %rbx +; movq rsp(64 + virtual offset), %rsi +; movq rsp(56 + virtual offset), %rdi +; movq rsp(48 + virtual offset), %r8 +; movq rsp(40 + virtual offset), %r9 +; movq rsp(32 + virtual offset), %r10 +; movq rsp(24 + virtual offset), %r11 +; movq rsp(16 + virtual offset), %r12 +; movq rsp(8 + virtual offset), %r13 +; movq rsp(0 + virtual offset), %r14 ; addq %rsp, $112, %rsp ; movq %rbp, %rsp ; popq %rbp @@ -316,19 +316,19 @@ block0: ; movq %r12, 0x50(%rax) ; movq %r13, 0x58(%rax) ; movq %r14, 0x60(%rax) -; movq (%rsp), %r14 -; movq 8(%rsp), %r13 -; movq 0x10(%rsp), %r12 -; movq 0x18(%rsp), %r11 -; movq 0x20(%rsp), %r10 -; movq 0x28(%rsp), %r9 -; movq 0x30(%rsp), %r8 -; movq 0x38(%rsp), %rdi -; movq 0x40(%rsp), %rsi -; movq 0x48(%rsp), %rbx -; movq 0x50(%rsp), %rdx -; movq 0x58(%rsp), %rcx ; movq 0x60(%rsp), %rax +; movq 0x58(%rsp), %rcx +; movq 0x50(%rsp), %rdx +; movq 0x48(%rsp), %rbx +; movq 0x40(%rsp), %rsi +; movq 0x38(%rsp), %rdi +; movq 0x30(%rsp), %r8 +; movq 0x28(%rsp), %r9 +; movq 0x20(%rsp), %r10 +; movq 0x18(%rsp), %r11 +; movq 0x10(%rsp), %r12 +; movq 8(%rsp), %r13 +; movq (%rsp), %r14 ; addq $0x70, %rsp ; movq %rbp, %rsp ; popq %rbp @@ -638,16 +638,16 @@ block0: ; lea 144(%rsp), %r10 ; movq %r10, 128(%rsp) ; load_ext_name %tail_callee_stack_args_and_rets+0, %r15 -; movq rsp(24 + virtual offset), %r11 -; movq rsp(32 + virtual offset), %r10 -; movq rsp(40 + virtual offset), %r9 -; movq rsp(48 + virtual offset), %r8 -; movq rsp(56 + virtual offset), %rdi -; movq rsp(64 + virtual offset), %rsi -; movq rsp(72 + virtual offset), %rbx -; movq rsp(80 + virtual offset), %rdx -; movq rsp(88 + virtual offset), %rcx ; movq rsp(96 + virtual offset), %rax +; movq rsp(88 + virtual offset), %rcx +; movq rsp(80 + virtual offset), %rdx +; movq rsp(72 + virtual offset), %rbx +; movq rsp(64 + virtual offset), %rsi +; movq rsp(56 + virtual offset), %rdi +; movq rsp(48 + virtual offset), %r8 +; movq rsp(40 + virtual offset), %r9 +; movq rsp(32 + virtual offset), %r10 +; movq rsp(24 + virtual offset), %r11 ; call *%r15 ; movq 0(%rsp), %r10 ; movq 8(%rsp), %rsi @@ -737,16 +737,16 @@ block0: ; leaq 0x90(%rsp), %r10 ; movq %r10, 0x80(%rsp) ; movabsq $0, %r15 ; reloc_external Abs8 %tail_callee_stack_args_and_rets 0 -; movq 0x118(%rsp), %r11 -; movq 0x120(%rsp), %r10 -; movq 0x128(%rsp), %r9 -; movq 0x130(%rsp), %r8 -; movq 0x138(%rsp), %rdi -; movq 0x140(%rsp), %rsi -; movq 0x148(%rsp), %rbx -; movq 0x150(%rsp), %rdx -; movq 0x158(%rsp), %rcx ; movq 0x160(%rsp), %rax +; movq 0x158(%rsp), %rcx +; movq 0x150(%rsp), %rdx +; movq 0x148(%rsp), %rbx +; movq 0x140(%rsp), %rsi +; movq 0x138(%rsp), %rdi +; movq 0x130(%rsp), %r8 +; movq 0x128(%rsp), %r9 +; movq 0x120(%rsp), %r10 +; movq 0x118(%rsp), %r11 ; callq *%r15 ; movq (%rsp), %r10 ; movq 8(%rsp), %rsi diff --git a/cranelift/filetests/filetests/isa/x64/traps.clif b/cranelift/filetests/filetests/isa/x64/traps.clif index bd3e81ee3bf0..6645ecb3bb77 100644 --- a/cranelift/filetests/filetests/isa/x64/traps.clif +++ b/cranelift/filetests/filetests/isa/x64/traps.clif @@ -29,8 +29,7 @@ block0(v0: i64, v1: i64): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movq %rdi, %rcx -; addq %rcx, %rsi, %rcx +; addq %rdi, %rsi, %rdi ; jb #trap=user0 ; movq %rbp, %rsp ; popq %rbp @@ -41,9 +40,8 @@ block0(v0: i64, v1: i64): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movq %rdi, %rcx -; addq %rsi, %rcx -; jb 0x15 +; addq %rsi, %rdi +; jb 0x12 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/isa/x64/ushr.clif b/cranelift/filetests/filetests/isa/x64/ushr.clif index 710f35a11f33..8c760f2d48f0 100644 --- a/cranelift/filetests/filetests/isa/x64/ushr.clif +++ b/cranelift/filetests/filetests/isa/x64/ushr.clif @@ -17,23 +17,21 @@ block0(v0: i128, v1: i8): ; movq %rsp, %rbp ; block0: ; movzbq %dl, %rcx -; movq %rdi, %r8 -; shrq %cl, %r8, %r8 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r10 ; shrq %cl, %r10, %r10 ; movq %rcx, %r9 ; movl $64, %ecx -; movq %r9, %rdi -; subq %rcx, %rdi, %rcx -; movq %rsi, %r11 -; shlq %cl, %r11, %r11 +; movq %r9, %rax +; subq %rcx, %rax, %rcx +; shlq %cl, %rsi, %rsi ; xorq %rdx, %rdx, %rdx -; testq $127, %rdi -; cmovzq %rdx, %r11, %r11 -; orq %r11, %r8, %r11 -; testq $64, %rdi +; testq $127, %rax +; cmovzq %rdx, %rsi, %rsi +; orq %rsi, %rdi, %rsi +; testq $64, %rax ; movq %r10, %rax -; cmovzq %r11, %rax, %rax +; cmovzq %rsi, %rax, %rax ; cmovzq %r10, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -45,23 +43,21 @@ block0(v0: i128, v1: i8): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; movzbq %dl, %rcx -; movq %rdi, %r8 -; shrq %cl, %r8 +; shrq %cl, %rdi ; movq %rsi, %r10 ; shrq %cl, %r10 ; movq %rcx, %r9 ; movl $0x40, %ecx -; movq %r9, %rdi -; subq %rdi, %rcx -; movq %rsi, %r11 -; shlq %cl, %r11 +; movq %r9, %rax +; subq %rax, %rcx +; shlq %cl, %rsi ; xorq %rdx, %rdx -; testq $0x7f, %rdi -; cmoveq %rdx, %r11 -; orq %r8, %r11 -; testq $0x40, %rdi +; testq $0x7f, %rax +; cmoveq %rdx, %rsi +; orq %rdi, %rsi +; testq $0x40, %rax ; movq %r10, %rax -; cmoveq %r11, %rax +; cmoveq %rsi, %rax ; cmoveq %r10, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -79,23 +75,21 @@ block0(v0: i128, v1: i64): ; block0: ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r8 -; shrq %cl, %r8, %r8 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r9 ; shrq %cl, %r9, %r9 ; movq %rcx, %r10 ; movl $64, %ecx -; movq %r10, %rdi -; subq %rcx, %rdi, %rcx -; movq %rsi, %r10 -; shlq %cl, %r10, %r10 +; movq %r10, %rax +; subq %rcx, %rax, %rcx +; shlq %cl, %rsi, %rsi ; xorq %rdx, %rdx, %rdx -; testq $127, %rdi -; cmovzq %rdx, %r10, %r10 -; orq %r10, %r8, %r10 -; testq $64, %rdi +; testq $127, %rax +; cmovzq %rdx, %rsi, %rsi +; orq %rsi, %rdi, %rsi +; testq $64, %rax ; movq %r9, %rax -; cmovzq %r10, %rax, %rax +; cmovzq %rsi, %rax, %rax ; cmovzq %r9, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -108,23 +102,21 @@ block0(v0: i128, v1: i64): ; block1: ; offset 0x4 ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r8 -; shrq %cl, %r8 +; shrq %cl, %rdi ; movq %rsi, %r9 ; shrq %cl, %r9 ; movq %rcx, %r10 ; movl $0x40, %ecx -; movq %r10, %rdi -; subq %rdi, %rcx -; movq %rsi, %r10 -; shlq %cl, %r10 +; movq %r10, %rax +; subq %rax, %rcx +; shlq %cl, %rsi ; xorq %rdx, %rdx -; testq $0x7f, %rdi -; cmoveq %rdx, %r10 -; orq %r8, %r10 -; testq $0x40, %rdi +; testq $0x7f, %rax +; cmoveq %rdx, %rsi +; orq %rdi, %rsi +; testq $0x40, %rax ; movq %r9, %rax -; cmoveq %r10, %rax +; cmoveq %rsi, %rax ; cmoveq %r9, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -142,23 +134,21 @@ block0(v0: i128, v1: i32): ; block0: ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r8 -; shrq %cl, %r8, %r8 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r9 ; shrq %cl, %r9, %r9 ; movq %rcx, %r10 ; movl $64, %ecx -; movq %r10, %rdi -; subq %rcx, %rdi, %rcx -; movq %rsi, %r10 -; shlq %cl, %r10, %r10 +; movq %r10, %rax +; subq %rcx, %rax, %rcx +; shlq %cl, %rsi, %rsi ; xorq %rdx, %rdx, %rdx -; testq $127, %rdi -; cmovzq %rdx, %r10, %r10 -; orq %r10, %r8, %r10 -; testq $64, %rdi +; testq $127, %rax +; cmovzq %rdx, %rsi, %rsi +; orq %rsi, %rdi, %rsi +; testq $64, %rax ; movq %r9, %rax -; cmovzq %r10, %rax, %rax +; cmovzq %rsi, %rax, %rax ; cmovzq %r9, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -171,23 +161,21 @@ block0(v0: i128, v1: i32): ; block1: ; offset 0x4 ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r8 -; shrq %cl, %r8 +; shrq %cl, %rdi ; movq %rsi, %r9 ; shrq %cl, %r9 ; movq %rcx, %r10 ; movl $0x40, %ecx -; movq %r10, %rdi -; subq %rdi, %rcx -; movq %rsi, %r10 -; shlq %cl, %r10 +; movq %r10, %rax +; subq %rax, %rcx +; shlq %cl, %rsi ; xorq %rdx, %rdx -; testq $0x7f, %rdi -; cmoveq %rdx, %r10 -; orq %r8, %r10 -; testq $0x40, %rdi +; testq $0x7f, %rax +; cmoveq %rdx, %rsi +; orq %rdi, %rsi +; testq $0x40, %rax ; movq %r9, %rax -; cmoveq %r10, %rax +; cmoveq %rsi, %rax ; cmoveq %r9, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -205,23 +193,21 @@ block0(v0: i128, v1: i16): ; block0: ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r8 -; shrq %cl, %r8, %r8 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r9 ; shrq %cl, %r9, %r9 ; movq %rcx, %r10 ; movl $64, %ecx -; movq %r10, %rdi -; subq %rcx, %rdi, %rcx -; movq %rsi, %r10 -; shlq %cl, %r10, %r10 +; movq %r10, %rax +; subq %rcx, %rax, %rcx +; shlq %cl, %rsi, %rsi ; xorq %rdx, %rdx, %rdx -; testq $127, %rdi -; cmovzq %rdx, %r10, %r10 -; orq %r10, %r8, %r10 -; testq $64, %rdi +; testq $127, %rax +; cmovzq %rdx, %rsi, %rsi +; orq %rsi, %rdi, %rsi +; testq $64, %rax ; movq %r9, %rax -; cmovzq %r10, %rax, %rax +; cmovzq %rsi, %rax, %rax ; cmovzq %r9, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -234,23 +220,21 @@ block0(v0: i128, v1: i16): ; block1: ; offset 0x4 ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r8 -; shrq %cl, %r8 +; shrq %cl, %rdi ; movq %rsi, %r9 ; shrq %cl, %r9 ; movq %rcx, %r10 ; movl $0x40, %ecx -; movq %r10, %rdi -; subq %rdi, %rcx -; movq %rsi, %r10 -; shlq %cl, %r10 +; movq %r10, %rax +; subq %rax, %rcx +; shlq %cl, %rsi ; xorq %rdx, %rdx -; testq $0x7f, %rdi -; cmoveq %rdx, %r10 -; orq %r8, %r10 -; testq $0x40, %rdi +; testq $0x7f, %rax +; cmoveq %rdx, %rsi +; orq %rdi, %rsi +; testq $0x40, %rax ; movq %r9, %rax -; cmoveq %r10, %rax +; cmoveq %rsi, %rax ; cmoveq %r9, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -268,23 +252,21 @@ block0(v0: i128, v1: i8): ; block0: ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r8 -; shrq %cl, %r8, %r8 +; shrq %cl, %rdi, %rdi ; movq %rsi, %r9 ; shrq %cl, %r9, %r9 ; movq %rcx, %r10 ; movl $64, %ecx -; movq %r10, %rdi -; subq %rcx, %rdi, %rcx -; movq %rsi, %r10 -; shlq %cl, %r10, %r10 +; movq %r10, %rax +; subq %rcx, %rax, %rcx +; shlq %cl, %rsi, %rsi ; xorq %rdx, %rdx, %rdx -; testq $127, %rdi -; cmovzq %rdx, %r10, %r10 -; orq %r10, %r8, %r10 -; testq $64, %rdi +; testq $127, %rax +; cmovzq %rdx, %rsi, %rsi +; orq %rsi, %rdi, %rsi +; testq $64, %rax ; movq %r9, %rax -; cmovzq %r10, %rax, %rax +; cmovzq %rsi, %rax, %rax ; cmovzq %r9, %rdx, %rdx ; movq %rbp, %rsp ; popq %rbp @@ -297,23 +279,21 @@ block0(v0: i128, v1: i8): ; block1: ; offset 0x4 ; movq %rdx, %rcx ; movq %rdx, %r10 -; movq %rdi, %r8 -; shrq %cl, %r8 +; shrq %cl, %rdi ; movq %rsi, %r9 ; shrq %cl, %r9 ; movq %rcx, %r10 ; movl $0x40, %ecx -; movq %r10, %rdi -; subq %rdi, %rcx -; movq %rsi, %r10 -; shlq %cl, %r10 +; movq %r10, %rax +; subq %rax, %rcx +; shlq %cl, %rsi ; xorq %rdx, %rdx -; testq $0x7f, %rdi -; cmoveq %rdx, %r10 -; orq %r8, %r10 -; testq $0x40, %rdi +; testq $0x7f, %rax +; cmoveq %rdx, %rsi +; orq %rdi, %rsi +; testq $0x40, %rax ; movq %r9, %rax -; cmoveq %r10, %rax +; cmoveq %rsi, %rax ; cmoveq %r9, %rdx ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/uunarrow.clif b/cranelift/filetests/filetests/isa/x64/uunarrow.clif index 0abe8d104ba6..57fdf011cbaa 100644 --- a/cranelift/filetests/filetests/isa/x64/uunarrow.clif +++ b/cranelift/filetests/filetests/isa/x64/uunarrow.clif @@ -15,10 +15,9 @@ block0(v0: f64x2): ; block0: ; uninit %xmm3 ; xorpd %xmm3, %xmm3, %xmm3 -; movdqa %xmm0, %xmm7 -; maxpd %xmm7, %xmm3, %xmm7 -; minpd %xmm7, const(0), %xmm7 -; roundpd $3, %xmm7, %xmm0 +; maxpd %xmm0, %xmm3, %xmm0 +; minpd %xmm0, const(0), %xmm0 +; roundpd $3, %xmm0, %xmm0 ; addpd %xmm0, const(1), %xmm0 ; shufps $136, %xmm0, %xmm3, %xmm0 ; movq %rbp, %rsp @@ -31,15 +30,16 @@ block0(v0: f64x2): ; movq %rsp, %rbp ; block1: ; offset 0x4 ; xorpd %xmm3, %xmm3 -; movdqa %xmm0, %xmm7 -; maxpd %xmm3, %xmm7 -; minpd 0x18(%rip), %xmm7 -; roundpd $3, %xmm7, %xmm0 -; addpd 0x1a(%rip), %xmm0 +; maxpd %xmm3, %xmm0 +; minpd 0x1c(%rip), %xmm0 +; roundpd $3, %xmm0, %xmm0 +; addpd 0x1e(%rip), %xmm0 ; shufps $0x88, %xmm3, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq ; addb %al, (%rax) +; addb %al, (%rax) +; addb %al, (%rax) ; addb %ah, %al diff --git a/cranelift/filetests/filetests/isa/x64/vhigh_bits.clif b/cranelift/filetests/filetests/isa/x64/vhigh_bits.clif index bf086f00c894..51fdff3a0bfc 100644 --- a/cranelift/filetests/filetests/isa/x64/vhigh_bits.clif +++ b/cranelift/filetests/filetests/isa/x64/vhigh_bits.clif @@ -61,9 +61,8 @@ block0(v0: i16x8): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqa %xmm0, %xmm2 -; packsswb %xmm2, %xmm0, %xmm2 -; pmovmskb %xmm2, %eax +; packsswb %xmm0, %xmm0, %xmm0 +; pmovmskb %xmm0, %eax ; shrq $8, %rax, %rax ; movq %rbp, %rsp ; popq %rbp @@ -74,9 +73,8 @@ block0(v0: i16x8): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqa %xmm0, %xmm2 -; packsswb %xmm0, %xmm2 -; pmovmskb %xmm2, %eax +; packsswb %xmm0, %xmm0 +; pmovmskb %xmm0, %eax ; shrq $8, %rax ; movq %rbp, %rsp ; popq %rbp diff --git a/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 98836b711f88..ca713ccc731b 100644 --- a/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -45,18 +45,17 @@ ;; movq %rsp, %rbp ;; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 } ;; block0: -;; movq %rdi, %r8 -;; addq %r8, const(0), %r8 +;; movq %rdi, %rax +;; addq %rax, const(0), %rax ;; jb #trap=heap_oob -;; movq 8(%rdx), %rax -;; movq %rdi, %rcx -;; addq %rcx, 0(%rdx), %rcx +;; movq 8(%rdx), %rcx +;; addq %rdi, 0(%rdx), %rdi ;; movl $-65536, %edx -;; lea 0(%rcx,%rdx,1), %rcx +;; lea 0(%rdi,%rdx,1), %rdi ;; xorq %rdx, %rdx, %rdx -;; cmpq %rax, %r8 -;; cmovnbeq %rdx, %rcx, %rcx -;; movl %esi, 0(%rcx) +;; cmpq %rcx, %rax +;; cmovnbeq %rdx, %rdi, %rdi +;; movl %esi, 0(%rdi) ;; jmp label1 ;; block1: ;; movq %rbp, %rsp @@ -69,16 +68,15 @@ ;; movq %rsp, %rbp ;; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 } ;; block0: -;; movq %rdi, %rcx -;; addq %rcx, const(0), %rcx +;; movq %rdi, %rax +;; addq %rax, const(0), %rax ;; jb #trap=heap_oob -;; movq 8(%rsi), %rax -;; movq %rdi, %rdx -;; addq %rdx, 0(%rsi), %rdx -;; movl $-65536, %r8d -;; lea 0(%rdx,%r8,1), %rsi +;; movq 8(%rsi), %rcx +;; addq %rdi, 0(%rsi), %rdi +;; movl $-65536, %edx +;; lea 0(%rdi,%rdx,1), %rsi ;; xorq %rdx, %rdx, %rdx -;; cmpq %rax, %rcx +;; cmpq %rcx, %rax ;; cmovnbeq %rdx, %rsi, %rsi ;; movl 0(%rsi), %eax ;; jmp label1 diff --git a/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index be22d8a42144..c0ae962583e7 100644 --- a/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -45,18 +45,17 @@ ;; movq %rsp, %rbp ;; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 } ;; block0: -;; movq %rdi, %r8 -;; addq %r8, const(0), %r8 +;; movq %rdi, %rax +;; addq %rax, const(0), %rax ;; jb #trap=heap_oob -;; movq 8(%rdx), %rax -;; movq %rdi, %rcx -;; addq %rcx, 0(%rdx), %rcx +;; movq 8(%rdx), %rcx +;; addq %rdi, 0(%rdx), %rdi ;; movl $-65536, %edx -;; lea 0(%rcx,%rdx,1), %rcx +;; lea 0(%rdi,%rdx,1), %rdi ;; xorq %rdx, %rdx, %rdx -;; cmpq %rax, %r8 -;; cmovnbeq %rdx, %rcx, %rcx -;; movb %sil, 0(%rcx) +;; cmpq %rcx, %rax +;; cmovnbeq %rdx, %rdi, %rdi +;; movb %sil, 0(%rdi) ;; jmp label1 ;; block1: ;; movq %rbp, %rsp @@ -69,16 +68,15 @@ ;; movq %rsp, %rbp ;; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 } ;; block0: -;; movq %rdi, %rcx -;; addq %rcx, const(0), %rcx +;; movq %rdi, %rax +;; addq %rax, const(0), %rax ;; jb #trap=heap_oob -;; movq 8(%rsi), %rax -;; movq %rdi, %rdx -;; addq %rdx, 0(%rsi), %rdx -;; movl $-65536, %r8d -;; lea 0(%rdx,%r8,1), %rsi +;; movq 8(%rsi), %rcx +;; addq %rdi, 0(%rsi), %rdi +;; movl $-65536, %edx +;; lea 0(%rdi,%rdx,1), %rsi ;; xorq %rdx, %rdx, %rdx -;; cmpq %rax, %rcx +;; cmpq %rcx, %rax ;; cmovnbeq %rdx, %rsi, %rsi ;; movzbq 0(%rsi), %rax ;; jmp label1 diff --git a/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat index 6d9c1df46417..e0f869ff951d 100644 --- a/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i32_access_0xffff0000_offset.wat @@ -49,10 +49,9 @@ ;; cmpq %r9, %rdi ;; jnbe label3; j label1 ;; block1: -;; movq %rdi, %r11 -;; addq %r11, 0(%rdx), %r11 -;; movl $-65536, %edi -;; movl %esi, 0(%r11,%rdi,1) +;; addq %rdi, 0(%rdx), %rdi +;; movl $-65536, %r11d +;; movl %esi, 0(%rdi,%r11,1) ;; jmp label2 ;; block2: ;; movq %rbp, %rsp @@ -71,10 +70,9 @@ ;; cmpq %r9, %rdi ;; jnbe label3; j label1 ;; block1: -;; movq %rdi, %r11 -;; addq %r11, 0(%rsi), %r11 -;; movl $-65536, %esi -;; movl 0(%r11,%rsi,1), %eax +;; addq %rdi, 0(%rsi), %rdi +;; movl $-65536, %r11d +;; movl 0(%rdi,%r11,1), %eax ;; jmp label2 ;; block2: ;; movq %rbp, %rsp diff --git a/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat index 33e0e97a68c0..0a8abfc3ceb3 100644 --- a/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/x64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_no_spectre_i8_access_0xffff0000_offset.wat @@ -49,10 +49,9 @@ ;; cmpq %r9, %rdi ;; jnbe label3; j label1 ;; block1: -;; movq %rdi, %r11 -;; addq %r11, 0(%rdx), %r11 -;; movl $-65536, %edi -;; movb %sil, 0(%r11,%rdi,1) +;; addq %rdi, 0(%rdx), %rdi +;; movl $-65536, %r11d +;; movb %sil, 0(%rdi,%r11,1) ;; jmp label2 ;; block2: ;; movq %rbp, %rsp @@ -71,10 +70,9 @@ ;; cmpq %r9, %rdi ;; jnbe label3; j label1 ;; block1: -;; movq %rdi, %r11 -;; addq %r11, 0(%rsi), %r11 -;; movl $-65536, %esi -;; movzbq 0(%r11,%rsi,1), %rax +;; addq %rdi, 0(%rsi), %rdi +;; movl $-65536, %r11d +;; movzbq 0(%rdi,%r11,1), %rax ;; jmp label2 ;; block2: ;; movq %rbp, %rsp diff --git a/cranelift/filetests/filetests/isa/x64/widening.clif b/cranelift/filetests/filetests/isa/x64/widening.clif index 65d32fd50665..1bd9ef170bcb 100644 --- a/cranelift/filetests/filetests/isa/x64/widening.clif +++ b/cranelift/filetests/filetests/isa/x64/widening.clif @@ -86,9 +86,8 @@ block0(v0: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqa %xmm0, %xmm2 -; palignr $8, %xmm2, %xmm0, %xmm2 -; pmovsxbw %xmm2, %xmm0 +; palignr $8, %xmm0, %xmm0, %xmm0 +; pmovsxbw %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -98,9 +97,8 @@ block0(v0: i8x16): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqa %xmm0, %xmm2 -; palignr $8, %xmm0, %xmm2 -; pmovsxbw %xmm2, %xmm0 +; palignr $8, %xmm0, %xmm0 +; pmovsxbw %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq @@ -115,9 +113,8 @@ block0(v0: i16x8): ; pushq %rbp ; movq %rsp, %rbp ; block0: -; movdqa %xmm0, %xmm2 -; palignr $8, %xmm2, %xmm0, %xmm2 -; pmovsxwd %xmm2, %xmm0 +; palignr $8, %xmm0, %xmm0, %xmm0 +; pmovsxwd %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; ret @@ -127,9 +124,8 @@ block0(v0: i16x8): ; pushq %rbp ; movq %rsp, %rbp ; block1: ; offset 0x4 -; movdqa %xmm0, %xmm2 -; palignr $8, %xmm0, %xmm2 -; pmovsxwd %xmm2, %xmm0 +; palignr $8, %xmm0, %xmm0 +; pmovsxwd %xmm0, %xmm0 ; movq %rbp, %rsp ; popq %rbp ; retq diff --git a/cranelift/filetests/filetests/wasm/x64-bmi2.wat b/cranelift/filetests/filetests/wasm/x64-bmi2.wat index 21418a017e82..c2ddd20370b8 100644 --- a/cranelift/filetests/filetests/wasm/x64-bmi2.wat +++ b/cranelift/filetests/filetests/wasm/x64-bmi2.wat @@ -50,9 +50,8 @@ ;; block0: ;; jmp label1 ;; block1: -;; movq %rsi, %r8 -;; andl %r8d, $31, %r8d -;; bzhi %edi, %r8d, %eax +;; andl %esi, $31, %esi +;; bzhi %edi, %esi, %eax ;; movq %rbp, %rsp ;; popq %rbp ;; ret @@ -65,9 +64,8 @@ ;; block0: ;; jmp label1 ;; block1: -;; movq %rsi, %r8 -;; andq %r8, $63, %r8 -;; bzhi %rdi, %r8, %rax +;; andq %rsi, $63, %rsi +;; bzhi %rdi, %rsi, %rax ;; movq %rbp, %rsp ;; popq %rbp ;; ret diff --git a/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat b/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat index d3bbfd4441e2..10e3433d61b1 100644 --- a/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat +++ b/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat @@ -58,15 +58,14 @@ ;; block0: ;; uninit %xmm7 ;; xorps %xmm7, %xmm7, %xmm7 -;; movdqa %xmm0, %xmm4 -;; maxps %xmm4, %xmm7, %xmm4 +;; maxps %xmm0, %xmm7, %xmm0 ;; pcmpeqd %xmm7, %xmm7, %xmm7 ;; psrld %xmm7, $1, %xmm7 ;; cvtdq2ps %xmm7, %xmm1 -;; cvttps2dq %xmm4, %xmm7 -;; subps %xmm4, %xmm1, %xmm4 -;; cmpps $2, %xmm1, %xmm4, %xmm1 -;; cvttps2dq %xmm4, %xmm0 +;; cvttps2dq %xmm0, %xmm7 +;; subps %xmm0, %xmm1, %xmm0 +;; cmpps $2, %xmm1, %xmm0, %xmm1 +;; cvttps2dq %xmm0, %xmm0 ;; pxor %xmm0, %xmm1, %xmm0 ;; uninit %xmm2 ;; pxor %xmm2, %xmm2, %xmm2 @@ -131,11 +130,9 @@ ;; movq %rsp, %rbp ;; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 } ;; block0: -;; movdqa %xmm1, %xmm3 -;; movdqa %xmm0, %xmm1 -;; movdqa %xmm3, %xmm0 -;; pmaddubsw %xmm0, %xmm1, %xmm0 -;; pmaddwd %xmm0, const(0), %xmm0 +;; pmaddubsw %xmm1, %xmm0, %xmm1 +;; pmaddwd %xmm1, const(0), %xmm1 +;; movdqa %xmm1, %xmm0 ;; paddd %xmm0, %xmm2, %xmm0 ;; jmp label1 ;; block1: diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 7857d0a0651b..3f9c4018ac05 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -1196,6 +1196,13 @@ user-id = 3726 user-login = "cfallin" user-name = "Chris Fallin" +[[publisher.regalloc2]] +version = "0.9.3" +when = "2023-10-05" +user-id = 3726 +user-login = "cfallin" +user-name = "Chris Fallin" + [[publisher.regex]] version = "1.9.1" when = "2023-07-07" From df150065b81bad2f252ebc92445888627282fe39 Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Thu, 5 Oct 2023 12:04:53 -0700 Subject: [PATCH 066/199] wasi-http: Use borrow syntax for borrowed resources (#7161) * Remove `own` and `borrow` annotations from wasi-http * Fix the tests, as borrows are now needed * Don't delete headers that we're borrowing --- .../wasi-http-proxy-tests/src/lib.rs | 2 +- crates/test-programs/wasi-http-tests/src/lib.rs | 2 +- crates/wasi-http/src/types_impl.rs | 1 - .../wasi-http/wit/deps/http/incoming-handler.wit | 4 ++-- .../wasi-http/wit/deps/http/outgoing-handler.wit | 2 +- crates/wasi-http/wit/deps/http/types.wit | 16 ++++++++-------- crates/wasi/wit/deps/http/incoming-handler.wit | 4 ++-- crates/wasi/wit/deps/http/outgoing-handler.wit | 2 +- crates/wasi/wit/deps/http/types.wit | 16 ++++++++-------- 9 files changed, 24 insertions(+), 25 deletions(-) diff --git a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs b/crates/test-programs/wasi-http-proxy-tests/src/lib.rs index 65c7db9168c6..5c25f34ddd3a 100644 --- a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-proxy-tests/src/lib.rs @@ -17,7 +17,7 @@ struct T; impl bindings::exports::wasi::http::incoming_handler::Guest for T { fn handle(_request: IncomingRequest, outparam: ResponseOutparam) { let hdrs = bindings::wasi::http::types::Headers::new(&[]); - let resp = bindings::wasi::http::types::OutgoingResponse::new(200, hdrs); + let resp = bindings::wasi::http::types::OutgoingResponse::new(200, &hdrs); let body = resp.write().expect("outgoing response"); bindings::wasi::http::types::ResponseOutparam::set(outparam, Ok(resp)); diff --git a/crates/test-programs/wasi-http-tests/src/lib.rs b/crates/test-programs/wasi-http-tests/src/lib.rs index 94b103414340..fd30fda53c4f 100644 --- a/crates/test-programs/wasi-http-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-tests/src/lib.rs @@ -67,7 +67,7 @@ pub fn request( Some(path_with_query), Some(&scheme), Some(authority), - headers, + &headers, ); let outgoing_body = request diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index bc2ae97f4ed5..0cbde9f5cd85 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -470,7 +470,6 @@ impl crate::bindings::http::types::HostOutgoingResponse for T { headers: Resource, ) -> wasmtime::Result> { let fields = get_fields_mut(self.table(), &headers)?.clone(); - self.table().delete_resource(headers)?; let id = self.table().push_resource(HostOutgoingResponse { status, diff --git a/crates/wasi-http/wit/deps/http/incoming-handler.wit b/crates/wasi-http/wit/deps/http/incoming-handler.wit index ad8a43f8ccf0..70a6a043a0c7 100644 --- a/crates/wasi-http/wit/deps/http/incoming-handler.wit +++ b/crates/wasi-http/wit/deps/http/incoming-handler.wit @@ -18,7 +18,7 @@ interface incoming-handler { // critical path, since there is no return value, there is no way to report // its success or failure. handle: func( - request: /* own */ incoming-request, - response-out: /* own */ response-outparam + request: incoming-request, + response-out: response-outparam ) } diff --git a/crates/wasi-http/wit/deps/http/outgoing-handler.wit b/crates/wasi-http/wit/deps/http/outgoing-handler.wit index 43bd0035fc06..9b6a73c0cb9b 100644 --- a/crates/wasi-http/wit/deps/http/outgoing-handler.wit +++ b/crates/wasi-http/wit/deps/http/outgoing-handler.wit @@ -14,7 +14,7 @@ interface outgoing-handler { // Consumes the outgoing-request. Gives an error if the outgoing-request // is invalid or cannot be satisfied by this handler. handle: func( - request: /* own */ outgoing-request, + request: outgoing-request, options: option ) -> result } diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index aa486f3c7c7c..edfa2fb8978a 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -87,7 +87,7 @@ interface types { // Will return the input-stream child at most once. If called more than // once, subsequent calls will return error. - consume: func() -> result< /* own */ incoming-body> + consume: func() -> result } resource outgoing-request { @@ -96,7 +96,7 @@ interface types { path-with-query: option, scheme: option, authority: option, - headers: /* borrow */ headers + headers: borrow ) // Will return the outgoing-body child at most once. If called more than @@ -127,7 +127,7 @@ interface types { // (the `wasi:http/handler` interface used for both incoming and outgoing can // simply return a `stream`). resource response-outparam { - set: static func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) + set: static func(param: response-outparam, response: result) } // This type corresponds to the HTTP standard Status Code. @@ -148,7 +148,7 @@ interface types { // May be called at most once. returns error if called additional times. // TODO: make incoming-request-consume work the same way, giving a child // incoming-body. - consume: func() -> result + consume: func() -> result } resource incoming-body { @@ -160,7 +160,7 @@ interface types { // takes ownership of incoming-body. this will trap if the // incoming-body-stream child is still alive! - finish: static func(this: /* own */ incoming-body) -> + finish: static func(this: incoming-body) -> /* transitive child of the incoming-response of incoming-body */ future-trailers } @@ -174,11 +174,11 @@ interface types { } resource outgoing-response { - constructor(status-code: status-code, headers: /* borrow */ headers) + constructor(status-code: status-code, headers: borrow) /// Will give the child outgoing-response at most once. subsequent calls will /// return an error. - write: func() -> result + write: func() -> result } resource outgoing-body { @@ -190,7 +190,7 @@ interface types { /// called to signal that the response is complete. If the `outgoing-body` is /// dropped without calling `outgoing-body-finalize`, the implementation /// should treat the body as corrupted. - finish: static func(this: /* own */ outgoing-body, trailers: /* own */ option) + finish: static func(this: outgoing-body, trailers: option) } /// The following block defines a special resource type used by the diff --git a/crates/wasi/wit/deps/http/incoming-handler.wit b/crates/wasi/wit/deps/http/incoming-handler.wit index ad8a43f8ccf0..70a6a043a0c7 100644 --- a/crates/wasi/wit/deps/http/incoming-handler.wit +++ b/crates/wasi/wit/deps/http/incoming-handler.wit @@ -18,7 +18,7 @@ interface incoming-handler { // critical path, since there is no return value, there is no way to report // its success or failure. handle: func( - request: /* own */ incoming-request, - response-out: /* own */ response-outparam + request: incoming-request, + response-out: response-outparam ) } diff --git a/crates/wasi/wit/deps/http/outgoing-handler.wit b/crates/wasi/wit/deps/http/outgoing-handler.wit index 43bd0035fc06..9b6a73c0cb9b 100644 --- a/crates/wasi/wit/deps/http/outgoing-handler.wit +++ b/crates/wasi/wit/deps/http/outgoing-handler.wit @@ -14,7 +14,7 @@ interface outgoing-handler { // Consumes the outgoing-request. Gives an error if the outgoing-request // is invalid or cannot be satisfied by this handler. handle: func( - request: /* own */ outgoing-request, + request: outgoing-request, options: option ) -> result } diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index aa486f3c7c7c..edfa2fb8978a 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -87,7 +87,7 @@ interface types { // Will return the input-stream child at most once. If called more than // once, subsequent calls will return error. - consume: func() -> result< /* own */ incoming-body> + consume: func() -> result } resource outgoing-request { @@ -96,7 +96,7 @@ interface types { path-with-query: option, scheme: option, authority: option, - headers: /* borrow */ headers + headers: borrow ) // Will return the outgoing-body child at most once. If called more than @@ -127,7 +127,7 @@ interface types { // (the `wasi:http/handler` interface used for both incoming and outgoing can // simply return a `stream`). resource response-outparam { - set: static func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) + set: static func(param: response-outparam, response: result) } // This type corresponds to the HTTP standard Status Code. @@ -148,7 +148,7 @@ interface types { // May be called at most once. returns error if called additional times. // TODO: make incoming-request-consume work the same way, giving a child // incoming-body. - consume: func() -> result + consume: func() -> result } resource incoming-body { @@ -160,7 +160,7 @@ interface types { // takes ownership of incoming-body. this will trap if the // incoming-body-stream child is still alive! - finish: static func(this: /* own */ incoming-body) -> + finish: static func(this: incoming-body) -> /* transitive child of the incoming-response of incoming-body */ future-trailers } @@ -174,11 +174,11 @@ interface types { } resource outgoing-response { - constructor(status-code: status-code, headers: /* borrow */ headers) + constructor(status-code: status-code, headers: borrow) /// Will give the child outgoing-response at most once. subsequent calls will /// return an error. - write: func() -> result + write: func() -> result } resource outgoing-body { @@ -190,7 +190,7 @@ interface types { /// called to signal that the response is complete. If the `outgoing-body` is /// dropped without calling `outgoing-body-finalize`, the implementation /// should treat the body as corrupted. - finish: static func(this: /* own */ outgoing-body, trailers: /* own */ option) + finish: static func(this: outgoing-body, trailers: option) } /// The following block defines a special resource type used by the From 722310a2d9ae352192e1c5052866aca74f3f1f94 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 5 Oct 2023 18:04:31 -0500 Subject: [PATCH 067/199] Add an error resource to WASI streams (#7152) * Change `bindgen!`'s trappable error to take an input type This commit removes the generated type for `trappable_error_type` from the `bindgen!` macro to allow users to provide a type instead. This works similarly as before with new conversion functions generated in traits which are used to convert the custom error into the ABI representation of what a WIT world expects. There are a few motivations for this commit: * This enables reducing the number of errors in play between async/sync bindings by using the same error type there. * This avoids an auto-generated type which is more difficult to inspect than one that's written down already and the source can more easily be inspected. * This enables richer conversions using `self` (e.g. `self.table_mut()`) between error types rather than purely relying on `Into`. This is important for #7017 where an error is going to be inserted into the table as it gets converted. * Fix tests * Update WASI to use new trappable errors This commit deals with the fallout of the previous commit for the WASI preview2 implementation. The main changes here are: * Bindgen-generated `Error` types no longer exist. These are replaced with `TrappableError` where type aliases are used such as ```rust type FsError = TrappableError; ``` * Type synonyms such as `FsResult` are now added for more conveniently writing down fallible signatures. * Some various error conversions are updated from converting to the old `Error` type to now instead directly into corresponding `ErrorCode` types. * A number of cases where unknown errors were turned into traps now return bland error codes and log the error instead since these aren't fatal events. * The `StreamError` type does not use `TrappableError` since it has other variants that it's concerned with such as a `LastOperationFailed` variant which has an `anyhow::Error` payload. * Some minor preview1 issues were fixed such as trapping errors being turned into normal I/O errors by accident. * Add an `error` resource to WASI streams This commit adds a new `error` resource to the `wasi:io/streams` interface. This `error` resource is returned as part of `last-operation-failed` and serves as a means to discover through other interfaces more granular type information than a generic string. This error type has a new function in the `filesystem` interface, for example, which enables getting filesystem-related error codes from I/O performed on filesystem-originating streams. This is plumbed through to the adapter as well to return more than `ERRNO_IO` from failed read/write operations now too. This is not super fancy just yet but is intended to be a vector through which future additions can be done. The main thing this enables is to avoid dropping errors on the floor in the host and enabling the guest to discover further information about I/O errors on streams. Closes #7017 * Update crates/wasi-http/wit/deps/io/streams.wit Co-authored-by: Trevor Elliott * Update wasi-http wit too * Remove unnecessary clone --------- Co-authored-by: Trevor Elliott --- crates/component-macro/src/bindgen.rs | 5 +- crates/component-macro/tests/codegen.rs | 42 +++ .../wasi-http/wit/deps/filesystem/types.wit | 14 +- crates/wasi-http/wit/deps/io/streams.wit | 26 +- .../src/lib.rs | 28 +- crates/wasi/src/preview2/command.rs | 8 - crates/wasi/src/preview2/error.rs | 74 +++++ crates/wasi/src/preview2/filesystem.rs | 29 +- crates/wasi/src/preview2/host/filesystem.rs | 272 +++++++++--------- .../wasi/src/preview2/host/filesystem/sync.rs | 115 ++++---- crates/wasi/src/preview2/host/io.rs | 180 +++++------- crates/wasi/src/preview2/host/network.rs | 40 +-- crates/wasi/src/preview2/host/tcp.rs | 82 ++---- .../src/preview2/host/tcp_create_socket.rs | 9 +- crates/wasi/src/preview2/ip_name_lookup.rs | 10 +- crates/wasi/src/preview2/mod.rs | 25 +- crates/wasi/src/preview2/network.rs | 24 ++ crates/wasi/src/preview2/preview1.rs | 102 ++----- crates/wasi/src/preview2/stream.rs | 38 ++- crates/wasi/wit/deps/filesystem/types.wit | 14 +- crates/wasi/wit/deps/io/streams.wit | 26 +- crates/wit-bindgen/src/lib.rs | 168 +++++------ tests/all/component_model/bindgen/results.rs | 133 ++++++--- 23 files changed, 825 insertions(+), 639 deletions(-) diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs index a87616e55d82..c5a9d2920ce8 100644 --- a/crates/component-macro/src/bindgen.rs +++ b/crates/component-macro/src/bindgen.rs @@ -1,10 +1,11 @@ use proc_macro2::{Span, TokenStream}; +use quote::ToTokens; use std::collections::HashMap; use std::collections::HashSet; use std::path::{Path, PathBuf}; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::punctuated::Punctuated; -use syn::{braced, token, Ident, Token}; +use syn::{braced, token, Token}; use wasmtime_wit_bindgen::{AsyncConfig, Opts, Ownership, TrappableError}; use wit_parser::{PackageId, Resolve, UnresolvedPackage, WorldId}; @@ -335,7 +336,7 @@ fn trappable_error_field_parse(input: ParseStream<'_>) -> Result input.parse::()?; let wit_type_name = ident_or_str(input)?; input.parse::()?; - let rust_type_name = input.parse::()?.to_string(); + let rust_type_name = input.parse::()?.to_token_stream().to_string(); Ok(TrappableError { wit_package_path, wit_type_name, diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs index 90d58bd890f2..49bb8c6d5726 100644 --- a/crates/component-macro/tests/codegen.rs +++ b/crates/component-macro/tests/codegen.rs @@ -106,3 +106,45 @@ mod with_key_and_resources { } } } + +mod trappable_errors { + wasmtime::component::bindgen!({ + inline: " + package demo:pkg; + + interface a { + type b = u64; + + z1: func() -> result<_, b>; + z2: func() -> result<_, b>; + } + + interface b { + use a.{b}; + z: func() -> result<_, b>; + } + + interface c { + type b = u64; + } + + interface d { + use c.{b}; + z: func() -> result<_, b>; + } + + world foo { + import a; + import b; + import d; + } + ", + trappable_error_type: { + "demo:pkg/a"::"b": MyX, + "demo:pkg/c"::"b": MyX, + }, + }); + + #[allow(dead_code)] + type MyX = u32; +} diff --git a/crates/wasi-http/wit/deps/filesystem/types.wit b/crates/wasi-http/wit/deps/filesystem/types.wit index 3f69bf997a29..aecdd0ef354b 100644 --- a/crates/wasi-http/wit/deps/filesystem/types.wit +++ b/crates/wasi-http/wit/deps/filesystem/types.wit @@ -23,7 +23,7 @@ /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md interface types { - use wasi:io/streams.{input-stream, output-stream} + use wasi:io/streams.{input-stream, output-stream, error} use wasi:clocks/wall-clock.{datetime} /// File size or length of a region within a file. @@ -795,4 +795,16 @@ interface types { /// Read a single directory entry from a `directory-entry-stream`. read-directory-entry: func() -> result, error-code> } + + /// Attempts to extract a filesystem-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// filesystem-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are filesystem-related errors. + filesystem-error-code: func(err: borrow) -> option } diff --git a/crates/wasi-http/wit/deps/io/streams.wit b/crates/wasi-http/wit/deps/io/streams.wit index 8240507976f7..55562d1cbfce 100644 --- a/crates/wasi-http/wit/deps/io/streams.wit +++ b/crates/wasi-http/wit/deps/io/streams.wit @@ -9,15 +9,37 @@ interface streams { use poll.{pollable} /// An error for input-stream and output-stream operations. - enum stream-error { + variant stream-error { /// The last operation (a write or flush) failed before completion. - last-operation-failed, + /// + /// More information is available in the `error` payload. + last-operation-failed(error), /// The stream is closed: no more input will be accepted by the /// stream. A closed output-stream will return this error on all /// future operations. closed } + /// Contextual error information about the last failure that happened on + /// a read, write, or flush from an `input-stream` or `output-stream`. + /// + /// This type is returned through the `stream-error` type whenever an + /// operation on a stream directly fails or an error is discovered + /// after-the-fact, for example when a write's failure shows up through a + /// later `flush` or `check-write`. + /// + /// Interfaces such as `wasi:filesystem/types` provide functionality to + /// further "downcast" this error into interface-specific error information. + resource error { + /// Returns a string that's suitable to assist humans in debugging this + /// error. + /// + /// The returned string will change across platforms and hosts which + /// means that parsing it, for example, would be a + /// platform-compatibility hazard. + to-debug-string: func() -> string + } + /// An input bytestream. /// /// `input-stream`s are *non-blocking* to the extent practical on underlying diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs index acbf82eea54d..56ae3294f4d0 100644 --- a/crates/wasi-preview1-component-adapter/src/lib.rs +++ b/crates/wasi-preview1-component-adapter/src/lib.rs @@ -900,7 +900,9 @@ pub unsafe extern "C" fn fd_read( *nread = 0; return Ok(()); } - Err(_) => Err(ERRNO_IO)?, + Err(streams::StreamError::LastOperationFailed(e)) => { + Err(stream_error_to_errno(e))? + } }; assert_eq!(data.as_ptr(), ptr); @@ -925,6 +927,13 @@ pub unsafe extern "C" fn fd_read( }) } +fn stream_error_to_errno(err: streams::Error) -> Errno { + match filesystem::filesystem_error_code(&err) { + Some(code) => code.into(), + None => ERRNO_IO, + } +} + /// Read directory entries from a directory. /// When successful, the contents of the output buffer consist of a sequence of /// directory entries. Each directory entry consists of a `dirent` object, @@ -2160,7 +2169,10 @@ impl BlockingMode { bytes = rest; match output_stream.blocking_write_and_flush(chunk) { Ok(()) => {} - Err(_) => return Err(ERRNO_IO), + Err(streams::StreamError::Closed) => return Err(ERRNO_IO), + Err(streams::StreamError::LastOperationFailed(e)) => { + return Err(stream_error_to_errno(e)) + } } } Ok(total) @@ -2170,7 +2182,9 @@ impl BlockingMode { let permit = match output_stream.check_write() { Ok(n) => n, Err(streams::StreamError::Closed) => 0, - Err(streams::StreamError::LastOperationFailed) => return Err(ERRNO_IO), + Err(streams::StreamError::LastOperationFailed(e)) => { + return Err(stream_error_to_errno(e)) + } }; let len = bytes.len().min(permit as usize); @@ -2181,13 +2195,17 @@ impl BlockingMode { match output_stream.write(&bytes[..len]) { Ok(_) => {} Err(streams::StreamError::Closed) => return Ok(0), - Err(streams::StreamError::LastOperationFailed) => return Err(ERRNO_IO), + Err(streams::StreamError::LastOperationFailed(e)) => { + return Err(stream_error_to_errno(e)) + } } match output_stream.blocking_flush() { Ok(_) => {} Err(streams::StreamError::Closed) => return Ok(0), - Err(streams::StreamError::LastOperationFailed) => return Err(ERRNO_IO), + Err(streams::StreamError::LastOperationFailed(e)) => { + return Err(stream_error_to_errno(e)) + } } Ok(len) diff --git a/crates/wasi/src/preview2/command.rs b/crates/wasi/src/preview2/command.rs index 7702e2a706da..811e3cf18e2c 100644 --- a/crates/wasi/src/preview2/command.rs +++ b/crates/wasi/src/preview2/command.rs @@ -4,10 +4,6 @@ wasmtime::component::bindgen!({ world: "wasi:cli/command", tracing: true, async: true, - trappable_error_type: { - "wasi:filesystem/types"::"error-code": Error, - "wasi:sockets/tcp"::"error-code": Error, - }, with: { "wasi:filesystem/types": crate::preview2::bindings::filesystem::types, "wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens, @@ -65,10 +61,6 @@ pub mod sync { world: "wasi:cli/command", tracing: true, async: false, - trappable_error_type: { - "wasi:filesystem/types"::"error-code": Error, - "wasi:sockets/tcp"::"error-code": Error, - }, with: { "wasi:filesystem/types": crate::preview2::bindings::sync_io::filesystem::types, "wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens, diff --git a/crates/wasi/src/preview2/error.rs b/crates/wasi/src/preview2/error.rs index c3493b238bee..ccae912d4668 100644 --- a/crates/wasi/src/preview2/error.rs +++ b/crates/wasi/src/preview2/error.rs @@ -1,4 +1,6 @@ +use std::error::Error; use std::fmt; +use std::marker; /// An error returned from the `proc_exit` host syscall. /// @@ -14,3 +16,75 @@ impl fmt::Display for I32Exit { } impl std::error::Error for I32Exit {} + +/// A helper error type used by many other modules through type aliases. +/// +/// This type is an `Error` itself and is intended to be a representation of +/// either: +/// +/// * A custom error type `T` +/// * A trap, represented as `anyhow::Error` +/// +/// This error is created through either the `::trap` constructor representing a +/// full-fledged trap or the `From` constructor which is intended to be used +/// with `?`. The goal is to make normal errors `T` "automatic" but enable error +/// paths to return a `::trap` error optionally still as necessary without extra +/// boilerplate everywhere else. +/// +/// Note that this type isn't used directly but instead is intended to be used +/// as: +/// +/// ```rust,ignore +/// type MyError = TrappableError; +/// ``` +/// +/// where `MyError` is what you'll use throughout bindings code and +/// `bindgen::TheError` is the type that this represents as generated by the +/// `bindgen!` macro. +#[repr(transparent)] +pub struct TrappableError { + err: anyhow::Error, + _marker: marker::PhantomData, +} + +impl TrappableError { + pub fn trap(err: impl Into) -> TrappableError { + TrappableError { + err: err.into(), + _marker: marker::PhantomData, + } + } + + pub fn downcast(self) -> anyhow::Result + where + T: Error + Send + Sync + 'static, + { + self.err.downcast() + } +} + +impl From for TrappableError +where + T: Error + Send + Sync + 'static, +{ + fn from(error: T) -> Self { + Self { + err: error.into(), + _marker: marker::PhantomData, + } + } +} + +impl fmt::Debug for TrappableError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.err.fmt(f) + } +} + +impl fmt::Display for TrappableError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.err.fmt(f) + } +} + +impl Error for TrappableError {} diff --git a/crates/wasi/src/preview2/filesystem.rs b/crates/wasi/src/preview2/filesystem.rs index 9b8467a86ed3..ac788414995b 100644 --- a/crates/wasi/src/preview2/filesystem.rs +++ b/crates/wasi/src/preview2/filesystem.rs @@ -1,6 +1,7 @@ use crate::preview2::bindings::filesystem::types; use crate::preview2::{ - spawn_blocking, AbortOnDropJoinHandle, HostOutputStream, StreamError, Subscribe, + spawn_blocking, AbortOnDropJoinHandle, HostOutputStream, StreamError, Subscribe, TableError, + TrappableError, }; use anyhow::anyhow; use bytes::{Bytes, BytesMut}; @@ -8,6 +9,22 @@ use std::io; use std::mem; use std::sync::Arc; +pub type FsResult = Result; + +pub type FsError = TrappableError; + +impl From for FsError { + fn from(error: TableError) -> Self { + Self::trap(error) + } +} + +impl From for FsError { + fn from(error: io::Error) -> Self { + types::ErrorCode::from(error).into() + } +} + pub enum Descriptor { File(File), Dir(Dir), @@ -276,24 +293,22 @@ impl Subscribe for FileOutputStream { } pub struct ReaddirIterator( - std::sync::Mutex< - Box> + Send + 'static>, - >, + std::sync::Mutex> + Send + 'static>>, ); impl ReaddirIterator { pub(crate) fn new( - i: impl Iterator> + Send + 'static, + i: impl Iterator> + Send + 'static, ) -> Self { ReaddirIterator(std::sync::Mutex::new(Box::new(i))) } - pub(crate) fn next(&self) -> Result, types::Error> { + pub(crate) fn next(&self) -> FsResult> { self.0.lock().unwrap().next().transpose() } } impl IntoIterator for ReaddirIterator { - type Item = Result; + type Item = FsResult; type IntoIter = Box + Send>; fn into_iter(self) -> Self::IntoIter { diff --git a/crates/wasi/src/preview2/host/filesystem.rs b/crates/wasi/src/preview2/host/filesystem.rs index a9a57e46c489..b0c77db3860b 100644 --- a/crates/wasi/src/preview2/host/filesystem.rs +++ b/crates/wasi/src/preview2/host/filesystem.rs @@ -1,23 +1,17 @@ use crate::preview2::bindings::clocks::wall_clock; -use crate::preview2::bindings::filesystem::types::{HostDescriptor, HostDirectoryEntryStream}; -use crate::preview2::bindings::filesystem::{preopens, types}; +use crate::preview2::bindings::filesystem::preopens; +use crate::preview2::bindings::filesystem::types::{ + self, ErrorCode, HostDescriptor, HostDirectoryEntryStream, +}; use crate::preview2::bindings::io::streams::{InputStream, OutputStream}; use crate::preview2::filesystem::{Descriptor, Dir, File, ReaddirIterator}; use crate::preview2::filesystem::{FileInputStream, FileOutputStream}; -use crate::preview2::{DirPerms, FilePerms, Table, TableError, WasiView}; +use crate::preview2::{DirPerms, FilePerms, FsError, FsResult, Table, WasiView}; use anyhow::Context; use wasmtime::component::Resource; -use types::ErrorCode; - mod sync; -impl From for types::Error { - fn from(error: TableError) -> Self { - Self::trap(error.into()) - } -} - impl preopens::Host for T { fn get_directories( &mut self, @@ -35,7 +29,26 @@ impl preopens::Host for T { } #[async_trait::async_trait] -impl types::Host for T {} +impl types::Host for T { + fn convert_error_code(&mut self, err: FsError) -> anyhow::Result { + err.downcast() + } + + fn filesystem_error_code( + &mut self, + err: Resource, + ) -> anyhow::Result> { + let err = self.table_mut().get_resource(&err)?; + + // Currently `err` always comes from the stream implementation which + // uses standard reads/writes so only check for `std::io::Error` here. + if let Some(err) = err.downcast_ref::() { + return Ok(Some(ErrorCode::from(err))); + } + + Ok(None) + } +} #[async_trait::async_trait] impl HostDescriptor for T { @@ -45,7 +58,7 @@ impl HostDescriptor for T { offset: types::Filesize, len: types::Filesize, advice: types::Advice, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { use system_interface::fs::{Advice as A, FileIoExt}; use types::Advice; @@ -64,7 +77,7 @@ impl HostDescriptor for T { Ok(()) } - async fn sync_data(&mut self, fd: Resource) -> Result<(), types::Error> { + async fn sync_data(&mut self, fd: Resource) -> FsResult<()> { let table = self.table(); match table.get_resource(&fd)? { @@ -94,7 +107,7 @@ impl HostDescriptor for T { async fn get_flags( &mut self, fd: Resource, - ) -> Result { + ) -> FsResult { use system_interface::fs::{FdFlags, GetSetFdFlags}; use types::DescriptorFlags; @@ -142,7 +155,7 @@ impl HostDescriptor for T { async fn get_type( &mut self, fd: Resource, - ) -> Result { + ) -> FsResult { let table = self.table(); match table.get_resource(&fd)? { @@ -158,7 +171,7 @@ impl HostDescriptor for T { &mut self, fd: Resource, size: types::Filesize, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { let f = self.table().get_resource(&fd)?.file()?; if !f.perms.contains(FilePerms::WRITE) { Err(ErrorCode::NotPermitted)?; @@ -172,7 +185,7 @@ impl HostDescriptor for T { fd: Resource, atim: types::NewTimestamp, mtim: types::NewTimestamp, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { use fs_set_times::SetTimes; let table = self.table(); @@ -203,7 +216,7 @@ impl HostDescriptor for T { fd: Resource, len: types::Filesize, offset: types::Filesize, - ) -> Result<(Vec, bool), types::Error> { + ) -> FsResult<(Vec, bool)> { use std::io::IoSliceMut; use system_interface::fs::FileIoExt; @@ -241,7 +254,7 @@ impl HostDescriptor for T { fd: Resource, buf: Vec, offset: types::Filesize, - ) -> Result { + ) -> FsResult { use std::io::IoSlice; use system_interface::fs::FileIoExt; @@ -261,7 +274,7 @@ impl HostDescriptor for T { async fn read_directory( &mut self, fd: Resource, - ) -> Result, types::Error> { + ) -> FsResult> { let table = self.table_mut(); let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::READ) { @@ -317,13 +330,13 @@ impl HostDescriptor for T { }); let entries = entries.map(|r| match r { Ok(r) => Ok(r), - Err(ReaddirError::Io(e)) => Err(types::Error::from(e)), + Err(ReaddirError::Io(e)) => Err(e.into()), Err(ReaddirError::IllegalSequence) => Err(ErrorCode::IllegalByteSequence.into()), }); Ok(table.push_resource(ReaddirIterator::new(entries))?) } - async fn sync(&mut self, fd: Resource) -> Result<(), types::Error> { + async fn sync(&mut self, fd: Resource) -> FsResult<()> { let table = self.table(); match table.get_resource(&fd)? { @@ -354,7 +367,7 @@ impl HostDescriptor for T { &mut self, fd: Resource, path: String, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { let table = self.table(); let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::MUTATE) { @@ -364,10 +377,7 @@ impl HostDescriptor for T { Ok(()) } - async fn stat( - &mut self, - fd: Resource, - ) -> Result { + async fn stat(&mut self, fd: Resource) -> FsResult { let table = self.table(); match table.get_resource(&fd)? { Descriptor::File(f) => { @@ -388,7 +398,7 @@ impl HostDescriptor for T { fd: Resource, path_flags: types::PathFlags, path: String, - ) -> Result { + ) -> FsResult { let table = self.table(); let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::READ) { @@ -410,7 +420,7 @@ impl HostDescriptor for T { path: String, atim: types::NewTimestamp, mtim: types::NewTimestamp, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { use cap_fs_ext::DirExt; let table = self.table(); @@ -450,7 +460,7 @@ impl HostDescriptor for T { old_path: String, new_descriptor: Resource, new_path: String, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { let table = self.table(); let old_dir = table.get_resource(&fd)?.dir()?; if !old_dir.perms.contains(DirPerms::MUTATE) { @@ -480,7 +490,7 @@ impl HostDescriptor for T { // TODO: These are the permissions to use when creating a new file. // Not implemented yet. _mode: types::Modes, - ) -> Result, types::Error> { + ) -> FsResult> { use cap_fs_ext::{FollowSymlinks, OpenOptionsFollowExt, OpenOptionsMaybeDirExt}; use system_interface::fs::{FdFlags, GetSetFdFlags}; use types::{DescriptorFlags, OpenFlags}; @@ -605,7 +615,7 @@ impl HostDescriptor for T { &mut self, fd: Resource, path: String, - ) -> Result { + ) -> FsResult { let table = self.table(); let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::READ) { @@ -622,7 +632,7 @@ impl HostDescriptor for T { &mut self, fd: Resource, path: String, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { let table = self.table(); let d = table.get_resource(&fd)?.dir()?; if !d.perms.contains(DirPerms::MUTATE) { @@ -637,7 +647,7 @@ impl HostDescriptor for T { old_path: String, new_fd: Resource, new_path: String, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { let table = self.table(); let old_dir = table.get_resource(&fd)?.dir()?; if !old_dir.perms.contains(DirPerms::MUTATE) { @@ -658,7 +668,7 @@ impl HostDescriptor for T { fd: Resource, src_path: String, dest_path: String, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { // On windows, Dir.symlink is provided by DirExt #[cfg(windows)] use cap_fs_ext::DirExt; @@ -676,7 +686,7 @@ impl HostDescriptor for T { &mut self, fd: Resource, path: String, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { use cap_fs_ext::DirExt; let table = self.table(); @@ -694,7 +704,7 @@ impl HostDescriptor for T { _path_flags: types::PathFlags, _path: String, _access: types::AccessType, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { todo!("filesystem access_at is not implemented") } @@ -704,7 +714,7 @@ impl HostDescriptor for T { _path_flags: types::PathFlags, _path: String, _mode: types::Modes, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { todo!("filesystem change_file_permissions_at is not implemented") } @@ -714,36 +724,27 @@ impl HostDescriptor for T { _path_flags: types::PathFlags, _path: String, _mode: types::Modes, - ) -> Result<(), types::Error> { + ) -> FsResult<()> { todo!("filesystem change_directory_permissions_at is not implemented") } - async fn lock_shared(&mut self, _fd: Resource) -> Result<(), types::Error> { + async fn lock_shared(&mut self, _fd: Resource) -> FsResult<()> { todo!("filesystem lock_shared is not implemented") } - async fn lock_exclusive( - &mut self, - _fd: Resource, - ) -> Result<(), types::Error> { + async fn lock_exclusive(&mut self, _fd: Resource) -> FsResult<()> { todo!("filesystem lock_exclusive is not implemented") } - async fn try_lock_shared( - &mut self, - _fd: Resource, - ) -> Result<(), types::Error> { + async fn try_lock_shared(&mut self, _fd: Resource) -> FsResult<()> { todo!("filesystem try_lock_shared is not implemented") } - async fn try_lock_exclusive( - &mut self, - _fd: Resource, - ) -> Result<(), types::Error> { + async fn try_lock_exclusive(&mut self, _fd: Resource) -> FsResult<()> { todo!("filesystem try_lock_exclusive is not implemented") } - async fn unlock(&mut self, _fd: Resource) -> Result<(), types::Error> { + async fn unlock(&mut self, _fd: Resource) -> FsResult<()> { todo!("filesystem unlock is not implemented") } @@ -751,7 +752,7 @@ impl HostDescriptor for T { &mut self, fd: Resource, offset: types::Filesize, - ) -> Result, types::Error> { + ) -> FsResult> { // Trap if fd lookup fails: let f = self.table().get_resource(&fd)?.file()?; @@ -774,7 +775,7 @@ impl HostDescriptor for T { &mut self, fd: Resource, offset: types::Filesize, - ) -> Result, types::Error> { + ) -> FsResult> { // Trap if fd lookup fails: let f = self.table().get_resource(&fd)?.file()?; @@ -798,7 +799,7 @@ impl HostDescriptor for T { fn append_via_stream( &mut self, fd: Resource, - ) -> Result, types::Error> { + ) -> FsResult> { // Trap if fd lookup fails: let f = self.table().get_resource(&fd)?.file()?; @@ -847,7 +848,7 @@ impl HostDescriptor for T { async fn metadata_hash( &mut self, fd: Resource, - ) -> Result { + ) -> FsResult { let table = self.table(); let meta = get_descriptor_metadata(table, fd).await?; Ok(calculate_metadata_hash(&meta)) @@ -857,7 +858,7 @@ impl HostDescriptor for T { fd: Resource, path_flags: types::PathFlags, path: String, - ) -> Result { + ) -> FsResult { let table = self.table(); let d = table.get_resource(&fd)?.dir()?; // No permissions check on metadata: if dir opened, allowed to stat it @@ -879,7 +880,7 @@ impl HostDirectoryEntryStream for T { async fn read_directory_entry( &mut self, stream: Resource, - ) -> Result, types::Error> { + ) -> FsResult> { let table = self.table(); let readdir = table.get_resource(&stream)?; readdir.next() @@ -894,7 +895,7 @@ impl HostDirectoryEntryStream for T { async fn get_descriptor_metadata( table: &Table, fd: Resource, -) -> Result { +) -> FsResult { match table.get_resource(&fd)? { Descriptor::File(f) => { // No permissions check on metadata: if opened, allowed to stat it @@ -932,100 +933,109 @@ fn calculate_metadata_hash(meta: &cap_std::fs::Metadata) -> types::MetadataHashV } #[cfg(unix)] -fn from_raw_os_error(err: Option) -> Option { +fn from_raw_os_error(err: Option) -> Option { use rustix::io::Errno as RustixErrno; if err.is_none() { return None; } Some(match RustixErrno::from_raw_os_error(err.unwrap()) { - RustixErrno::PIPE => ErrorCode::Pipe.into(), - RustixErrno::PERM => ErrorCode::NotPermitted.into(), - RustixErrno::NOENT => ErrorCode::NoEntry.into(), - RustixErrno::NOMEM => ErrorCode::InsufficientMemory.into(), - RustixErrno::IO => ErrorCode::Io.into(), - RustixErrno::BADF => ErrorCode::BadDescriptor.into(), - RustixErrno::BUSY => ErrorCode::Busy.into(), - RustixErrno::ACCESS => ErrorCode::Access.into(), - RustixErrno::NOTDIR => ErrorCode::NotDirectory.into(), - RustixErrno::ISDIR => ErrorCode::IsDirectory.into(), - RustixErrno::INVAL => ErrorCode::Invalid.into(), - RustixErrno::EXIST => ErrorCode::Exist.into(), - RustixErrno::FBIG => ErrorCode::FileTooLarge.into(), - RustixErrno::NOSPC => ErrorCode::InsufficientSpace.into(), - RustixErrno::SPIPE => ErrorCode::InvalidSeek.into(), - RustixErrno::MLINK => ErrorCode::TooManyLinks.into(), - RustixErrno::NAMETOOLONG => ErrorCode::NameTooLong.into(), - RustixErrno::NOTEMPTY => ErrorCode::NotEmpty.into(), - RustixErrno::LOOP => ErrorCode::Loop.into(), - RustixErrno::OVERFLOW => ErrorCode::Overflow.into(), - RustixErrno::ILSEQ => ErrorCode::IllegalByteSequence.into(), - RustixErrno::NOTSUP => ErrorCode::Unsupported.into(), - RustixErrno::ALREADY => ErrorCode::Already.into(), - RustixErrno::INPROGRESS => ErrorCode::InProgress.into(), - RustixErrno::INTR => ErrorCode::Interrupted.into(), - - // On some platforms.into(), these have the same value as other errno values. + RustixErrno::PIPE => ErrorCode::Pipe, + RustixErrno::PERM => ErrorCode::NotPermitted, + RustixErrno::NOENT => ErrorCode::NoEntry, + RustixErrno::NOMEM => ErrorCode::InsufficientMemory, + RustixErrno::IO => ErrorCode::Io, + RustixErrno::BADF => ErrorCode::BadDescriptor, + RustixErrno::BUSY => ErrorCode::Busy, + RustixErrno::ACCESS => ErrorCode::Access, + RustixErrno::NOTDIR => ErrorCode::NotDirectory, + RustixErrno::ISDIR => ErrorCode::IsDirectory, + RustixErrno::INVAL => ErrorCode::Invalid, + RustixErrno::EXIST => ErrorCode::Exist, + RustixErrno::FBIG => ErrorCode::FileTooLarge, + RustixErrno::NOSPC => ErrorCode::InsufficientSpace, + RustixErrno::SPIPE => ErrorCode::InvalidSeek, + RustixErrno::MLINK => ErrorCode::TooManyLinks, + RustixErrno::NAMETOOLONG => ErrorCode::NameTooLong, + RustixErrno::NOTEMPTY => ErrorCode::NotEmpty, + RustixErrno::LOOP => ErrorCode::Loop, + RustixErrno::OVERFLOW => ErrorCode::Overflow, + RustixErrno::ILSEQ => ErrorCode::IllegalByteSequence, + RustixErrno::NOTSUP => ErrorCode::Unsupported, + RustixErrno::ALREADY => ErrorCode::Already, + RustixErrno::INPROGRESS => ErrorCode::InProgress, + RustixErrno::INTR => ErrorCode::Interrupted, + + // On some platforms, these have the same value as other errno values. #[allow(unreachable_patterns)] - RustixErrno::OPNOTSUPP => ErrorCode::Unsupported.into(), + RustixErrno::OPNOTSUPP => ErrorCode::Unsupported, _ => return None, }) } #[cfg(windows)] -fn from_raw_os_error(raw_os_error: Option) -> Option { +fn from_raw_os_error(raw_os_error: Option) -> Option { use windows_sys::Win32::Foundation; Some(match raw_os_error.map(|code| code as u32) { - Some(Foundation::ERROR_FILE_NOT_FOUND) => ErrorCode::NoEntry.into(), - Some(Foundation::ERROR_PATH_NOT_FOUND) => ErrorCode::NoEntry.into(), - Some(Foundation::ERROR_ACCESS_DENIED) => ErrorCode::Access.into(), - Some(Foundation::ERROR_SHARING_VIOLATION) => ErrorCode::Access.into(), - Some(Foundation::ERROR_PRIVILEGE_NOT_HELD) => ErrorCode::NotPermitted.into(), - Some(Foundation::ERROR_INVALID_HANDLE) => ErrorCode::BadDescriptor.into(), - Some(Foundation::ERROR_INVALID_NAME) => ErrorCode::NoEntry.into(), - Some(Foundation::ERROR_NOT_ENOUGH_MEMORY) => ErrorCode::InsufficientMemory.into(), - Some(Foundation::ERROR_OUTOFMEMORY) => ErrorCode::InsufficientMemory.into(), - Some(Foundation::ERROR_DIR_NOT_EMPTY) => ErrorCode::NotEmpty.into(), - Some(Foundation::ERROR_NOT_READY) => ErrorCode::Busy.into(), - Some(Foundation::ERROR_BUSY) => ErrorCode::Busy.into(), - Some(Foundation::ERROR_NOT_SUPPORTED) => ErrorCode::Unsupported.into(), - Some(Foundation::ERROR_FILE_EXISTS) => ErrorCode::Exist.into(), - Some(Foundation::ERROR_BROKEN_PIPE) => ErrorCode::Pipe.into(), - Some(Foundation::ERROR_BUFFER_OVERFLOW) => ErrorCode::NameTooLong.into(), - Some(Foundation::ERROR_NOT_A_REPARSE_POINT) => ErrorCode::Invalid.into(), - Some(Foundation::ERROR_NEGATIVE_SEEK) => ErrorCode::Invalid.into(), - Some(Foundation::ERROR_DIRECTORY) => ErrorCode::NotDirectory.into(), - Some(Foundation::ERROR_ALREADY_EXISTS) => ErrorCode::Exist.into(), - Some(Foundation::ERROR_STOPPED_ON_SYMLINK) => ErrorCode::Loop.into(), - Some(Foundation::ERROR_DIRECTORY_NOT_SUPPORTED) => ErrorCode::IsDirectory.into(), + Some(Foundation::ERROR_FILE_NOT_FOUND) => ErrorCode::NoEntry, + Some(Foundation::ERROR_PATH_NOT_FOUND) => ErrorCode::NoEntry, + Some(Foundation::ERROR_ACCESS_DENIED) => ErrorCode::Access, + Some(Foundation::ERROR_SHARING_VIOLATION) => ErrorCode::Access, + Some(Foundation::ERROR_PRIVILEGE_NOT_HELD) => ErrorCode::NotPermitted, + Some(Foundation::ERROR_INVALID_HANDLE) => ErrorCode::BadDescriptor, + Some(Foundation::ERROR_INVALID_NAME) => ErrorCode::NoEntry, + Some(Foundation::ERROR_NOT_ENOUGH_MEMORY) => ErrorCode::InsufficientMemory, + Some(Foundation::ERROR_OUTOFMEMORY) => ErrorCode::InsufficientMemory, + Some(Foundation::ERROR_DIR_NOT_EMPTY) => ErrorCode::NotEmpty, + Some(Foundation::ERROR_NOT_READY) => ErrorCode::Busy, + Some(Foundation::ERROR_BUSY) => ErrorCode::Busy, + Some(Foundation::ERROR_NOT_SUPPORTED) => ErrorCode::Unsupported, + Some(Foundation::ERROR_FILE_EXISTS) => ErrorCode::Exist, + Some(Foundation::ERROR_BROKEN_PIPE) => ErrorCode::Pipe, + Some(Foundation::ERROR_BUFFER_OVERFLOW) => ErrorCode::NameTooLong, + Some(Foundation::ERROR_NOT_A_REPARSE_POINT) => ErrorCode::Invalid, + Some(Foundation::ERROR_NEGATIVE_SEEK) => ErrorCode::Invalid, + Some(Foundation::ERROR_DIRECTORY) => ErrorCode::NotDirectory, + Some(Foundation::ERROR_ALREADY_EXISTS) => ErrorCode::Exist, + Some(Foundation::ERROR_STOPPED_ON_SYMLINK) => ErrorCode::Loop, + Some(Foundation::ERROR_DIRECTORY_NOT_SUPPORTED) => ErrorCode::IsDirectory, _ => return None, }) } -impl From for types::Error { - fn from(err: std::io::Error) -> types::Error { +impl From for ErrorCode { + fn from(err: std::io::Error) -> ErrorCode { + ErrorCode::from(&err) + } +} + +impl<'a> From<&'a std::io::Error> for ErrorCode { + fn from(err: &'a std::io::Error) -> ErrorCode { match from_raw_os_error(err.raw_os_error()) { Some(errno) => errno, - None => match err.kind() { - std::io::ErrorKind::NotFound => ErrorCode::NoEntry.into(), - std::io::ErrorKind::PermissionDenied => ErrorCode::NotPermitted.into(), - std::io::ErrorKind::AlreadyExists => ErrorCode::Exist.into(), - std::io::ErrorKind::InvalidInput => ErrorCode::Invalid.into(), - _ => types::Error::trap(anyhow::anyhow!(err).context("Unknown OS error")), - }, + None => { + log::debug!("unknown raw os error: {err}"); + match err.kind() { + std::io::ErrorKind::NotFound => ErrorCode::NoEntry, + std::io::ErrorKind::PermissionDenied => ErrorCode::NotPermitted, + std::io::ErrorKind::AlreadyExists => ErrorCode::Exist, + std::io::ErrorKind::InvalidInput => ErrorCode::Invalid, + _ => ErrorCode::Io, + } + } } } } -impl From for types::Error { - fn from(err: cap_rand::Error) -> types::Error { +impl From for ErrorCode { + fn from(err: cap_rand::Error) -> ErrorCode { // I picked Error::Io as a 'reasonable default', FIXME dan is this ok? - from_raw_os_error(err.raw_os_error()).unwrap_or_else(|| types::Error::from(ErrorCode::Io)) + from_raw_os_error(err.raw_os_error()).unwrap_or(ErrorCode::Io) } } -impl From for types::Error { - fn from(_err: std::num::TryFromIntError) -> types::Error { - ErrorCode::Overflow.into() +impl From for ErrorCode { + fn from(_err: std::num::TryFromIntError) -> ErrorCode { + ErrorCode::Overflow } } @@ -1047,9 +1057,7 @@ fn descriptortype_from(ft: cap_std::fs::FileType) -> types::DescriptorType { } } -fn systemtimespec_from( - t: types::NewTimestamp, -) -> Result, types::Error> { +fn systemtimespec_from(t: types::NewTimestamp) -> FsResult> { use fs_set_times::SystemTimeSpec; use types::NewTimestamp; match t { @@ -1059,7 +1067,7 @@ fn systemtimespec_from( } } -fn systemtime_from(t: wall_clock::Datetime) -> Result { +fn systemtime_from(t: wall_clock::Datetime) -> FsResult { use std::time::{Duration, SystemTime}; SystemTime::UNIX_EPOCH .checked_add(Duration::new(t.seconds, t.nanoseconds)) diff --git a/crates/wasi/src/preview2/host/filesystem/sync.rs b/crates/wasi/src/preview2/host/filesystem/sync.rs index 36bb244b5719..6f6132a7b2af 100644 --- a/crates/wasi/src/preview2/host/filesystem/sync.rs +++ b/crates/wasi/src/preview2/host/filesystem/sync.rs @@ -1,10 +1,21 @@ use crate::preview2::bindings::filesystem::types as async_filesystem; use crate::preview2::bindings::sync_io::filesystem::types as sync_filesystem; use crate::preview2::bindings::sync_io::io::streams; -use crate::preview2::in_tokio; +use crate::preview2::{in_tokio, FsError, FsResult}; use wasmtime::component::Resource; -impl sync_filesystem::Host for T {} +impl sync_filesystem::Host for T { + fn convert_error_code(&mut self, err: FsError) -> anyhow::Result { + Ok(async_filesystem::Host::convert_error_code(self, err)?.into()) + } + + fn filesystem_error_code( + &mut self, + err: Resource, + ) -> anyhow::Result> { + Ok(async_filesystem::Host::filesystem_error_code(self, err)?.map(|e| e.into())) + } +} impl sync_filesystem::HostDescriptor for T { fn advise( @@ -13,16 +24,13 @@ impl sync_filesystem::HostDescriptor for T offset: sync_filesystem::Filesize, len: sync_filesystem::Filesize, advice: sync_filesystem::Advice, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::advise(self, fd, offset, len, advice.into()).await })?) } - fn sync_data( - &mut self, - fd: Resource, - ) -> Result<(), sync_filesystem::Error> { + fn sync_data(&mut self, fd: Resource) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::sync_data(self, fd).await })?) @@ -31,14 +39,14 @@ impl sync_filesystem::HostDescriptor for T fn get_flags( &mut self, fd: Resource, - ) -> Result { + ) -> FsResult { Ok(in_tokio(async { async_filesystem::HostDescriptor::get_flags(self, fd).await })?.into()) } fn get_type( &mut self, fd: Resource, - ) -> Result { + ) -> FsResult { Ok(in_tokio(async { async_filesystem::HostDescriptor::get_type(self, fd).await })?.into()) } @@ -46,7 +54,7 @@ impl sync_filesystem::HostDescriptor for T &mut self, fd: Resource, size: sync_filesystem::Filesize, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::set_size(self, fd, size).await })?) @@ -57,7 +65,7 @@ impl sync_filesystem::HostDescriptor for T fd: Resource, atim: sync_filesystem::NewTimestamp, mtim: sync_filesystem::NewTimestamp, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::set_times(self, fd, atim.into(), mtim.into()).await })?) @@ -68,7 +76,7 @@ impl sync_filesystem::HostDescriptor for T fd: Resource, len: sync_filesystem::Filesize, offset: sync_filesystem::Filesize, - ) -> Result<(Vec, bool), sync_filesystem::Error> { + ) -> FsResult<(Vec, bool)> { Ok(in_tokio(async { async_filesystem::HostDescriptor::read(self, fd, len, offset).await })?) @@ -79,7 +87,7 @@ impl sync_filesystem::HostDescriptor for T fd: Resource, buf: Vec, offset: sync_filesystem::Filesize, - ) -> Result { + ) -> FsResult { Ok(in_tokio(async { async_filesystem::HostDescriptor::write(self, fd, buf, offset).await })?) @@ -88,16 +96,13 @@ impl sync_filesystem::HostDescriptor for T fn read_directory( &mut self, fd: Resource, - ) -> Result, sync_filesystem::Error> { + ) -> FsResult> { Ok(in_tokio(async { async_filesystem::HostDescriptor::read_directory(self, fd).await })?) } - fn sync( - &mut self, - fd: Resource, - ) -> Result<(), sync_filesystem::Error> { + fn sync(&mut self, fd: Resource) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::sync(self, fd).await })?) @@ -107,7 +112,7 @@ impl sync_filesystem::HostDescriptor for T &mut self, fd: Resource, path: String, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::create_directory_at(self, fd, path).await })?) @@ -116,7 +121,7 @@ impl sync_filesystem::HostDescriptor for T fn stat( &mut self, fd: Resource, - ) -> Result { + ) -> FsResult { Ok(in_tokio(async { async_filesystem::HostDescriptor::stat(self, fd).await })?.into()) } @@ -125,7 +130,7 @@ impl sync_filesystem::HostDescriptor for T fd: Resource, path_flags: sync_filesystem::PathFlags, path: String, - ) -> Result { + ) -> FsResult { Ok(in_tokio(async { async_filesystem::HostDescriptor::stat_at(self, fd, path_flags.into(), path).await })? @@ -139,7 +144,7 @@ impl sync_filesystem::HostDescriptor for T path: String, atim: sync_filesystem::NewTimestamp, mtim: sync_filesystem::NewTimestamp, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::set_times_at( self, @@ -161,7 +166,7 @@ impl sync_filesystem::HostDescriptor for T old_path: String, new_descriptor: Resource, new_path: String, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::link_at( self, @@ -183,7 +188,7 @@ impl sync_filesystem::HostDescriptor for T oflags: sync_filesystem::OpenFlags, flags: sync_filesystem::DescriptorFlags, mode: sync_filesystem::Modes, - ) -> Result, sync_filesystem::Error> { + ) -> FsResult> { Ok(in_tokio(async { async_filesystem::HostDescriptor::open_at( self, @@ -206,7 +211,7 @@ impl sync_filesystem::HostDescriptor for T &mut self, fd: Resource, path: String, - ) -> Result { + ) -> FsResult { Ok(in_tokio(async { async_filesystem::HostDescriptor::readlink_at(self, fd, path).await })?) @@ -216,7 +221,7 @@ impl sync_filesystem::HostDescriptor for T &mut self, fd: Resource, path: String, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::remove_directory_at(self, fd, path).await })?) @@ -228,7 +233,7 @@ impl sync_filesystem::HostDescriptor for T old_path: String, new_fd: Resource, new_path: String, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::rename_at(self, fd, old_path, new_fd, new_path).await })?) @@ -239,7 +244,7 @@ impl sync_filesystem::HostDescriptor for T fd: Resource, src_path: String, dest_path: String, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::symlink_at(self, fd, src_path, dest_path).await })?) @@ -249,7 +254,7 @@ impl sync_filesystem::HostDescriptor for T &mut self, fd: Resource, path: String, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::unlink_file_at(self, fd, path).await })?) @@ -261,7 +266,7 @@ impl sync_filesystem::HostDescriptor for T path_flags: sync_filesystem::PathFlags, path: String, access: sync_filesystem::AccessType, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::access_at( self, @@ -280,7 +285,7 @@ impl sync_filesystem::HostDescriptor for T path_flags: sync_filesystem::PathFlags, path: String, mode: sync_filesystem::Modes, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::change_file_permissions_at( self, @@ -299,7 +304,7 @@ impl sync_filesystem::HostDescriptor for T path_flags: sync_filesystem::PathFlags, path: String, mode: sync_filesystem::Modes, - ) -> Result<(), sync_filesystem::Error> { + ) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::change_directory_permissions_at( self, @@ -312,46 +317,31 @@ impl sync_filesystem::HostDescriptor for T })?) } - fn lock_shared( - &mut self, - fd: Resource, - ) -> Result<(), sync_filesystem::Error> { + fn lock_shared(&mut self, fd: Resource) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::lock_shared(self, fd).await })?) } - fn lock_exclusive( - &mut self, - fd: Resource, - ) -> Result<(), sync_filesystem::Error> { + fn lock_exclusive(&mut self, fd: Resource) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::lock_exclusive(self, fd).await })?) } - fn try_lock_shared( - &mut self, - fd: Resource, - ) -> Result<(), sync_filesystem::Error> { + fn try_lock_shared(&mut self, fd: Resource) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::try_lock_shared(self, fd).await })?) } - fn try_lock_exclusive( - &mut self, - fd: Resource, - ) -> Result<(), sync_filesystem::Error> { + fn try_lock_exclusive(&mut self, fd: Resource) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::try_lock_exclusive(self, fd).await })?) } - fn unlock( - &mut self, - fd: Resource, - ) -> Result<(), sync_filesystem::Error> { + fn unlock(&mut self, fd: Resource) -> FsResult<()> { Ok(in_tokio(async { async_filesystem::HostDescriptor::unlock(self, fd).await })?) @@ -361,7 +351,7 @@ impl sync_filesystem::HostDescriptor for T &mut self, fd: Resource, offset: sync_filesystem::Filesize, - ) -> Result, sync_filesystem::Error> { + ) -> FsResult> { Ok(async_filesystem::HostDescriptor::read_via_stream( self, fd, offset, )?) @@ -371,7 +361,7 @@ impl sync_filesystem::HostDescriptor for T &mut self, fd: Resource, offset: sync_filesystem::Filesize, - ) -> Result, sync_filesystem::Error> { + ) -> FsResult> { Ok(async_filesystem::HostDescriptor::write_via_stream( self, fd, offset, )?) @@ -380,7 +370,7 @@ impl sync_filesystem::HostDescriptor for T fn append_via_stream( &mut self, fd: Resource, - ) -> Result, sync_filesystem::Error> { + ) -> FsResult> { Ok(async_filesystem::HostDescriptor::append_via_stream( self, fd, )?) @@ -398,7 +388,7 @@ impl sync_filesystem::HostDescriptor for T fn metadata_hash( &mut self, fd: Resource, - ) -> Result { + ) -> FsResult { Ok( in_tokio(async { async_filesystem::HostDescriptor::metadata_hash(self, fd).await })? .into(), @@ -409,7 +399,7 @@ impl sync_filesystem::HostDescriptor for T fd: Resource, path_flags: sync_filesystem::PathFlags, path: String, - ) -> Result { + ) -> FsResult { Ok(in_tokio(async { async_filesystem::HostDescriptor::metadata_hash_at(self, fd, path_flags.into(), path) .await @@ -424,7 +414,7 @@ impl sync_filesystem::HostDirecto fn read_directory_entry( &mut self, stream: Resource, - ) -> Result, sync_filesystem::Error> { + ) -> FsResult> { Ok(in_tokio(async { async_filesystem::HostDirectoryEntryStream::read_directory_entry(self, stream).await })? @@ -484,15 +474,6 @@ impl From for sync_filesystem::ErrorCode { } } -impl From for sync_filesystem::Error { - fn from(other: async_filesystem::Error) -> Self { - match other.downcast() { - Ok(errorcode) => Self::from(sync_filesystem::ErrorCode::from(errorcode)), - Err(other) => Self::trap(other), - } - } -} - impl From for async_filesystem::Advice { fn from(other: sync_filesystem::Advice) -> Self { use sync_filesystem::Advice; diff --git a/crates/wasi/src/preview2/host/io.rs b/crates/wasi/src/preview2/host/io.rs index 92cfa0cdb0de..72796de5a8df 100644 --- a/crates/wasi/src/preview2/host/io.rs +++ b/crates/wasi/src/preview2/host/io.rs @@ -1,31 +1,32 @@ use crate::preview2::{ bindings::io::streams::{self, InputStream, OutputStream}, poll::subscribe, - stream::StreamError, - Pollable, TableError, WasiView, + Pollable, StreamError, StreamResult, WasiView, }; use wasmtime::component::Resource; -impl From for streams::Error { - fn from(e: TableError) -> streams::Error { - streams::Error::trap(e.into()) - } -} -impl From for streams::Error { - fn from(e: StreamError) -> streams::Error { - match e { - StreamError::Closed => streams::StreamError::Closed.into(), - StreamError::LastOperationFailed(e) => { - tracing::debug!("streams::StreamError::LastOperationFailed: {e:?}"); - streams::StreamError::LastOperationFailed.into() - } - StreamError::Trap(e) => streams::Error::trap(e), +impl streams::Host for T { + fn convert_stream_error(&mut self, err: StreamError) -> anyhow::Result { + match err { + StreamError::Closed => Ok(streams::StreamError::Closed), + StreamError::LastOperationFailed(e) => Ok(streams::StreamError::LastOperationFailed( + self.table_mut().push_resource(e)?, + )), + StreamError::Trap(e) => Err(e), } } } -#[async_trait::async_trait] -impl streams::Host for T {} +impl streams::HostError for T { + fn drop(&mut self, err: Resource) -> anyhow::Result<()> { + self.table_mut().delete_resource(err)?; + Ok(()) + } + + fn to_debug_string(&mut self, err: Resource) -> anyhow::Result { + Ok(format!("{:?}", self.table_mut().get_resource(&err)?)) + } +} #[async_trait::async_trait] impl streams::HostOutputStream for T { @@ -34,16 +35,12 @@ impl streams::HostOutputStream for T { Ok(()) } - fn check_write(&mut self, stream: Resource) -> Result { + fn check_write(&mut self, stream: Resource) -> StreamResult { let bytes = self.table_mut().get_resource_mut(&stream)?.check_write()?; Ok(bytes as u64) } - fn write( - &mut self, - stream: Resource, - bytes: Vec, - ) -> Result<(), streams::Error> { + fn write(&mut self, stream: Resource, bytes: Vec) -> StreamResult<()> { self.table_mut() .get_resource_mut(&stream)? .write(bytes.into())?; @@ -58,13 +55,13 @@ impl streams::HostOutputStream for T { &mut self, stream: Resource, bytes: Vec, - ) -> Result<(), streams::Error> { + ) -> StreamResult<()> { let s = self.table_mut().get_resource_mut(&stream)?; if bytes.len() > 4096 { - return Err(streams::Error::trap(anyhow::anyhow!( - "Buffer too large for blocking-write-and-flush (expected at most 4096)" - ))); + return Err(StreamError::trap( + "Buffer too large for blocking-write-and-flush (expected at most 4096)", + )); } let mut bytes = bytes::Bytes::from(bytes); @@ -85,13 +82,13 @@ impl streams::HostOutputStream for T { &mut self, stream: Resource, len: u64, - ) -> Result<(), streams::Error> { + ) -> StreamResult<()> { let s = self.table_mut().get_resource_mut(&stream)?; if len > 4096 { - return Err(streams::Error::trap(anyhow::anyhow!( - "Buffer too large for blocking-write-zeroes-and-flush (expected at most 4096)" - ))); + return Err(StreamError::trap( + "Buffer too large for blocking-write-zeroes-and-flush (expected at most 4096)", + )); } let mut len = len; @@ -108,26 +105,19 @@ impl streams::HostOutputStream for T { Ok(()) } - fn write_zeroes( - &mut self, - stream: Resource, - len: u64, - ) -> Result<(), streams::Error> { + fn write_zeroes(&mut self, stream: Resource, len: u64) -> StreamResult<()> { self.table_mut() .get_resource_mut(&stream)? .write_zeroes(len as usize)?; Ok(()) } - fn flush(&mut self, stream: Resource) -> Result<(), streams::Error> { + fn flush(&mut self, stream: Resource) -> StreamResult<()> { self.table_mut().get_resource_mut(&stream)?.flush()?; Ok(()) } - async fn blocking_flush( - &mut self, - stream: Resource, - ) -> Result<(), streams::Error> { + async fn blocking_flush(&mut self, stream: Resource) -> StreamResult<()> { let s = self.table_mut().get_resource_mut(&stream)?; s.flush()?; s.write_ready().await?; @@ -139,7 +129,7 @@ impl streams::HostOutputStream for T { _dst: Resource, _src: Resource, _len: u64, - ) -> Result { + ) -> StreamResult { // TODO: We can't get two streams at the same time because they both // carry the exclusive lifetime of `ctx`. When [`get_many_mut`] is // stabilized, that could allow us to add a `get_many_stream_mut` or @@ -168,7 +158,7 @@ impl streams::HostOutputStream for T { _dst: Resource, _src: Resource, _len: u64, - ) -> Result { + ) -> StreamResult { // TODO: once splice is implemented, figure out what the blocking semantics are for waiting // on src and dest here. todo!("stream splice is not implemented") @@ -178,7 +168,7 @@ impl streams::HostOutputStream for T { &mut self, _dst: Resource, _src: Resource, - ) -> Result { + ) -> StreamResult { // TODO: We can't get two streams at the same time because they both // carry the exclusive lifetime of `ctx`. When [`get_many_mut`] is // stabilized, that could allow us to add a `get_many_stream_mut` or @@ -204,11 +194,6 @@ impl streams::HostOutputStream for T { } } -impl From for streams::Error { - fn from(e: std::num::TryFromIntError) -> Self { - streams::Error::trap(anyhow::anyhow!("length overflow: {e:?}")) - } -} #[async_trait::async_trait] impl streams::HostInputStream for T { fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { @@ -216,12 +201,8 @@ impl streams::HostInputStream for T { Ok(()) } - async fn read( - &mut self, - stream: Resource, - len: u64, - ) -> Result, streams::Error> { - let len = len.try_into()?; + async fn read(&mut self, stream: Resource, len: u64) -> StreamResult> { + let len = len.try_into().unwrap_or(usize::MAX); let bytes = match self.table_mut().get_resource_mut(&stream)? { InputStream::Host(s) => s.read(len)?, InputStream::File(s) => s.read(len).await?, @@ -234,19 +215,15 @@ impl streams::HostInputStream for T { &mut self, stream: Resource, len: u64, - ) -> Result, streams::Error> { + ) -> StreamResult> { if let InputStream::Host(s) = self.table_mut().get_resource_mut(&stream)? { s.ready().await; } self.read(stream, len).await } - async fn skip( - &mut self, - stream: Resource, - len: u64, - ) -> Result { - let len = len.try_into()?; + async fn skip(&mut self, stream: Resource, len: u64) -> StreamResult { + let len = len.try_into().unwrap_or(usize::MAX); let written = match self.table_mut().get_resource_mut(&stream)? { InputStream::Host(s) => s.skip(len)?, InputStream::File(s) => s.skip(len).await?, @@ -258,7 +235,7 @@ impl streams::HostInputStream for T { &mut self, stream: Resource, len: u64, - ) -> Result { + ) -> StreamResult { if let InputStream::Host(s) = self.table_mut().get_resource_mut(&stream)? { s.ready().await; } @@ -273,48 +250,53 @@ impl streams::HostInputStream for T { pub mod sync { use crate::preview2::{ bindings::io::streams::{ - self as async_streams, HostInputStream as AsyncHostInputStream, - HostOutputStream as AsyncHostOutputStream, + self as async_streams, Host as AsyncHost, HostError as AsyncHostError, + HostInputStream as AsyncHostInputStream, HostOutputStream as AsyncHostOutputStream, }, bindings::sync_io::io::poll::Pollable, bindings::sync_io::io::streams::{self, InputStream, OutputStream}, - in_tokio, WasiView, + in_tokio, StreamError, StreamResult, WasiView, }; use wasmtime::component::Resource; impl From for streams::StreamError { fn from(other: async_streams::StreamError) -> Self { match other { - async_streams::StreamError::LastOperationFailed => Self::LastOperationFailed, + async_streams::StreamError::LastOperationFailed(e) => Self::LastOperationFailed(e), async_streams::StreamError::Closed => Self::Closed, } } } - impl From for streams::Error { - fn from(other: async_streams::Error) -> Self { - match other.downcast() { - Ok(write_error) => streams::Error::from(streams::StreamError::from(write_error)), - Err(e) => streams::Error::trap(e), - } + + impl streams::Host for T { + fn convert_stream_error( + &mut self, + err: StreamError, + ) -> anyhow::Result { + Ok(AsyncHost::convert_stream_error(self, err)?.into()) } } - impl streams::Host for T {} + impl streams::HostError for T { + fn drop(&mut self, err: Resource) -> anyhow::Result<()> { + AsyncHostError::drop(self, err) + } + + fn to_debug_string(&mut self, err: Resource) -> anyhow::Result { + AsyncHostError::to_debug_string(self, err) + } + } impl streams::HostOutputStream for T { fn drop(&mut self, stream: Resource) -> anyhow::Result<()> { AsyncHostOutputStream::drop(self, stream) } - fn check_write(&mut self, stream: Resource) -> Result { + fn check_write(&mut self, stream: Resource) -> StreamResult { Ok(AsyncHostOutputStream::check_write(self, stream)?) } - fn write( - &mut self, - stream: Resource, - bytes: Vec, - ) -> Result<(), streams::Error> { + fn write(&mut self, stream: Resource, bytes: Vec) -> StreamResult<()> { Ok(AsyncHostOutputStream::write(self, stream, bytes)?) } @@ -322,7 +304,7 @@ pub mod sync { &mut self, stream: Resource, bytes: Vec, - ) -> Result<(), streams::Error> { + ) -> StreamResult<()> { Ok(in_tokio(async { AsyncHostOutputStream::blocking_write_and_flush(self, stream, bytes).await })?) @@ -332,7 +314,7 @@ pub mod sync { &mut self, stream: Resource, len: u64, - ) -> Result<(), streams::Error> { + ) -> StreamResult<()> { Ok(in_tokio(async { AsyncHostOutputStream::blocking_write_zeroes_and_flush(self, stream, len).await })?) @@ -345,22 +327,18 @@ pub mod sync { Ok(AsyncHostOutputStream::subscribe(self, stream)?) } - fn write_zeroes( - &mut self, - stream: Resource, - len: u64, - ) -> Result<(), streams::Error> { + fn write_zeroes(&mut self, stream: Resource, len: u64) -> StreamResult<()> { Ok(AsyncHostOutputStream::write_zeroes(self, stream, len)?) } - fn flush(&mut self, stream: Resource) -> Result<(), streams::Error> { + fn flush(&mut self, stream: Resource) -> StreamResult<()> { Ok(AsyncHostOutputStream::flush( self, Resource::new_borrow(stream.rep()), )?) } - fn blocking_flush(&mut self, stream: Resource) -> Result<(), streams::Error> { + fn blocking_flush(&mut self, stream: Resource) -> StreamResult<()> { Ok(in_tokio(async { AsyncHostOutputStream::blocking_flush(self, Resource::new_borrow(stream.rep())) .await @@ -372,7 +350,7 @@ pub mod sync { dst: Resource, src: Resource, len: u64, - ) -> Result { + ) -> StreamResult { Ok(in_tokio(async { AsyncHostOutputStream::splice(self, dst, src, len).await })?) @@ -383,7 +361,7 @@ pub mod sync { dst: Resource, src: Resource, len: u64, - ) -> Result { + ) -> StreamResult { Ok(in_tokio(async { AsyncHostOutputStream::blocking_splice(self, dst, src, len).await })?) @@ -393,7 +371,7 @@ pub mod sync { &mut self, dst: Resource, src: Resource, - ) -> Result { + ) -> StreamResult { Ok(in_tokio(async { AsyncHostOutputStream::forward(self, dst, src).await })?) @@ -405,11 +383,7 @@ pub mod sync { AsyncHostInputStream::drop(self, stream) } - fn read( - &mut self, - stream: Resource, - len: u64, - ) -> Result, streams::Error> { + fn read(&mut self, stream: Resource, len: u64) -> StreamResult> { Ok(in_tokio(async { AsyncHostInputStream::read(self, stream, len).await })?) @@ -419,23 +393,19 @@ pub mod sync { &mut self, stream: Resource, len: u64, - ) -> Result, streams::Error> { + ) -> StreamResult> { Ok(in_tokio(async { AsyncHostInputStream::blocking_read(self, stream, len).await })?) } - fn skip(&mut self, stream: Resource, len: u64) -> Result { + fn skip(&mut self, stream: Resource, len: u64) -> StreamResult { Ok(in_tokio(async { AsyncHostInputStream::skip(self, stream, len).await })?) } - fn blocking_skip( - &mut self, - stream: Resource, - len: u64, - ) -> Result { + fn blocking_skip(&mut self, stream: Resource, len: u64) -> StreamResult { Ok(in_tokio(async { AsyncHostInputStream::blocking_skip(self, stream, len).await })?) diff --git a/crates/wasi/src/preview2/host/network.rs b/crates/wasi/src/preview2/host/network.rs index 811213050e8e..b2a8ff59a700 100644 --- a/crates/wasi/src/preview2/host/network.rs +++ b/crates/wasi/src/preview2/host/network.rs @@ -2,11 +2,15 @@ use crate::preview2::bindings::sockets::network::{ self, ErrorCode, IpAddressFamily, IpSocketAddress, Ipv4Address, Ipv4SocketAddress, Ipv6Address, Ipv6SocketAddress, }; -use crate::preview2::{TableError, WasiView}; +use crate::preview2::{SocketError, WasiView}; use std::io; use wasmtime::component::Resource; -impl network::Host for T {} +impl network::Host for T { + fn convert_error_code(&mut self, error: SocketError) -> anyhow::Result { + error.downcast() + } +} impl crate::preview2::bindings::sockets::network::HostNetwork for T { fn drop(&mut self, this: Resource) -> Result<(), anyhow::Error> { @@ -18,13 +22,7 @@ impl crate::preview2::bindings::sockets::network::HostNetwork for T } } -impl From for network::Error { - fn from(error: TableError) -> Self { - Self::trap(error.into()) - } -} - -impl From for network::Error { +impl From for ErrorCode { fn from(error: io::Error) -> Self { match error.kind() { // Errors that we can directly map. @@ -39,24 +37,8 @@ impl From for network::Error { io::ErrorKind::Unsupported => ErrorCode::NotSupported, io::ErrorKind::OutOfMemory => ErrorCode::OutOfMemory, - // Errors we don't expect to see here. - io::ErrorKind::Interrupted | io::ErrorKind::ConnectionAborted => { - // Transient errors should be skipped. - return Self::trap(error.into()); - } - - // Errors not expected from network APIs. - io::ErrorKind::WriteZero - | io::ErrorKind::InvalidInput - | io::ErrorKind::InvalidData - | io::ErrorKind::BrokenPipe - | io::ErrorKind::NotFound - | io::ErrorKind::UnexpectedEof - | io::ErrorKind::AlreadyExists => return Self::trap(error.into()), - // Errors that don't correspond to a Rust `io::ErrorKind`. io::ErrorKind::Other => match error.raw_os_error() { - None => return Self::trap(error.into()), Some(libc::ENOBUFS) | Some(libc::ENOMEM) => ErrorCode::OutOfMemory, Some(libc::EOPNOTSUPP) => ErrorCode::NotSupported, Some(libc::ENETUNREACH) | Some(libc::EHOSTUNREACH) | Some(libc::ENETDOWN) => { @@ -65,7 +47,10 @@ impl From for network::Error { Some(libc::ECONNRESET) => ErrorCode::ConnectionReset, Some(libc::ECONNREFUSED) => ErrorCode::ConnectionRefused, Some(libc::EADDRINUSE) => ErrorCode::AddressInUse, - Some(_) => return Self::trap(error.into()), + Some(_) | None => { + log::debug!("unknown I/O error: {error}"); + ErrorCode::Unknown + } }, _ => { @@ -73,11 +58,10 @@ impl From for network::Error { ErrorCode::Unknown } } - .into() } } -impl From for network::Error { +impl From for ErrorCode { fn from(error: rustix::io::Errno) -> Self { std::io::Error::from(error).into() } diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index 6565a589413a..0cf88b9100a4 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -1,10 +1,10 @@ use crate::preview2::bindings::{ io::streams::{InputStream, OutputStream}, - sockets::network::{self, ErrorCode, IpAddressFamily, IpSocketAddress, Network}, + sockets::network::{ErrorCode, IpAddressFamily, IpSocketAddress, Network}, sockets::tcp::{self, ShutdownType}, }; use crate::preview2::tcp::{TcpSocket, TcpState}; -use crate::preview2::{Pollable, WasiView}; +use crate::preview2::{Pollable, SocketResult, WasiView}; use cap_net_ext::{Blocking, PoolExt, TcpListenerExt}; use cap_std::net::TcpListener; use io_lifetimes::AsSocketlike; @@ -21,7 +21,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, network: Resource, local_address: IpSocketAddress, - ) -> Result<(), network::Error> { + ) -> SocketResult<()> { let table = self.table_mut(); let socket = table.get_resource(&this)?; @@ -44,7 +44,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { Ok(()) } - fn finish_bind(&mut self, this: Resource) -> Result<(), network::Error> { + fn finish_bind(&mut self, this: Resource) -> SocketResult<()> { let table = self.table_mut(); let socket = table.get_resource_mut(&this)?; @@ -63,7 +63,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, network: Resource, remote_address: IpSocketAddress, - ) -> Result<(), network::Error> { + ) -> SocketResult<()> { let table = self.table_mut(); let r = { let socket = table.get_resource(&this)?; @@ -107,7 +107,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { fn finish_connect( &mut self, this: Resource, - ) -> Result<(Resource, Resource), network::Error> { + ) -> SocketResult<(Resource, Resource)> { let table = self.table_mut(); let socket = table.get_resource_mut(&this)?; @@ -145,7 +145,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { Ok((input_stream, output_stream)) } - fn start_listen(&mut self, this: Resource) -> Result<(), network::Error> { + fn start_listen(&mut self, this: Resource) -> SocketResult<()> { let table = self.table_mut(); let socket = table.get_resource_mut(&this)?; @@ -166,7 +166,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { Ok(()) } - fn finish_listen(&mut self, this: Resource) -> Result<(), network::Error> { + fn finish_listen(&mut self, this: Resource) -> SocketResult<()> { let table = self.table_mut(); let socket = table.get_resource_mut(&this)?; @@ -183,14 +183,11 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { fn accept( &mut self, this: Resource, - ) -> Result< - ( - Resource, - Resource, - Resource, - ), - network::Error, - > { + ) -> SocketResult<( + Resource, + Resource, + Resource, + )> { let table = self.table(); let socket = table.get_resource(&this)?; @@ -222,10 +219,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { Ok((tcp_socket, input_stream, output_stream)) } - fn local_address( - &mut self, - this: Resource, - ) -> Result { + fn local_address(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; let addr = socket @@ -235,10 +229,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { Ok(addr.into()) } - fn remote_address( - &mut self, - this: Resource, - ) -> Result { + fn remote_address(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; let addr = socket @@ -296,17 +287,13 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { } } - fn ipv6_only(&mut self, this: Resource) -> Result { + fn ipv6_only(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; Ok(sockopt::get_ipv6_v6only(socket.tcp_socket())?) } - fn set_ipv6_only( - &mut self, - this: Resource, - value: bool, - ) -> Result<(), network::Error> { + fn set_ipv6_only(&mut self, this: Resource, value: bool) -> SocketResult<()> { let table = self.table(); let socket = table.get_resource(&this)?; Ok(sockopt::set_ipv6_v6only(socket.tcp_socket(), value)?) @@ -316,7 +303,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { &mut self, this: Resource, value: u64, - ) -> Result<(), network::Error> { + ) -> SocketResult<()> { const MIN_BACKLOG: i32 = 1; const MAX_BACKLOG: i32 = i32::MAX; // OS'es will most likely limit it down even further. @@ -354,39 +341,31 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { } } - fn keep_alive(&mut self, this: Resource) -> Result { + fn keep_alive(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; Ok(sockopt::get_socket_keepalive(socket.tcp_socket())?) } - fn set_keep_alive( - &mut self, - this: Resource, - value: bool, - ) -> Result<(), network::Error> { + fn set_keep_alive(&mut self, this: Resource, value: bool) -> SocketResult<()> { let table = self.table(); let socket = table.get_resource(&this)?; Ok(sockopt::set_socket_keepalive(socket.tcp_socket(), value)?) } - fn no_delay(&mut self, this: Resource) -> Result { + fn no_delay(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; Ok(sockopt::get_tcp_nodelay(socket.tcp_socket())?) } - fn set_no_delay( - &mut self, - this: Resource, - value: bool, - ) -> Result<(), network::Error> { + fn set_no_delay(&mut self, this: Resource, value: bool) -> SocketResult<()> { let table = self.table(); let socket = table.get_resource(&this)?; Ok(sockopt::set_tcp_nodelay(socket.tcp_socket(), value)?) } - fn unicast_hop_limit(&mut self, this: Resource) -> Result { + fn unicast_hop_limit(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; @@ -407,7 +386,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { &mut self, this: Resource, value: u8, - ) -> Result<(), network::Error> { + ) -> SocketResult<()> { let table = self.table(); let socket = table.get_resource(&this)?; @@ -420,10 +399,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { } } - fn receive_buffer_size( - &mut self, - this: Resource, - ) -> Result { + fn receive_buffer_size(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; Ok(sockopt::get_socket_recv_buffer_size(socket.tcp_socket())? as u64) @@ -433,7 +409,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { &mut self, this: Resource, value: u64, - ) -> Result<(), network::Error> { + ) -> SocketResult<()> { let table = self.table(); let socket = table.get_resource(&this)?; let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; @@ -443,7 +419,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { )?) } - fn send_buffer_size(&mut self, this: Resource) -> Result { + fn send_buffer_size(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; Ok(sockopt::get_socket_send_buffer_size(socket.tcp_socket())? as u64) @@ -453,7 +429,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { &mut self, this: Resource, value: u64, - ) -> Result<(), network::Error> { + ) -> SocketResult<()> { let table = self.table(); let socket = table.get_resource(&this)?; let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; @@ -471,7 +447,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { &mut self, this: Resource, shutdown_type: ShutdownType, - ) -> Result<(), network::Error> { + ) -> SocketResult<()> { let table = self.table(); let socket = table.get_resource(&this)?; diff --git a/crates/wasi/src/preview2/host/tcp_create_socket.rs b/crates/wasi/src/preview2/host/tcp_create_socket.rs index 60fc34482399..417c03081b05 100644 --- a/crates/wasi/src/preview2/host/tcp_create_socket.rs +++ b/crates/wasi/src/preview2/host/tcp_create_socket.rs @@ -1,16 +1,13 @@ -use crate::preview2::bindings::{ - sockets::network::{self, IpAddressFamily}, - sockets::tcp_create_socket, -}; +use crate::preview2::bindings::{sockets::network::IpAddressFamily, sockets::tcp_create_socket}; use crate::preview2::tcp::TcpSocket; -use crate::preview2::WasiView; +use crate::preview2::{SocketResult, WasiView}; use wasmtime::component::Resource; impl tcp_create_socket::Host for T { fn create_tcp_socket( &mut self, address_family: IpAddressFamily, - ) -> Result, network::Error> { + ) -> SocketResult> { let socket = TcpSocket::new(address_family.into())?; let socket = self.table_mut().push_resource(socket)?; Ok(socket) diff --git a/crates/wasi/src/preview2/ip_name_lookup.rs b/crates/wasi/src/preview2/ip_name_lookup.rs index b4a02427b475..ce7cde93b9b9 100644 --- a/crates/wasi/src/preview2/ip_name_lookup.rs +++ b/crates/wasi/src/preview2/ip_name_lookup.rs @@ -1,9 +1,7 @@ use crate::preview2::bindings::sockets::ip_name_lookup::{Host, HostResolveAddressStream}; -use crate::preview2::bindings::sockets::network::{ - Error, ErrorCode, IpAddress, IpAddressFamily, Network, -}; +use crate::preview2::bindings::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, Network}; use crate::preview2::poll::{subscribe, Pollable, Subscribe}; -use crate::preview2::{spawn_blocking, AbortOnDropJoinHandle, WasiView}; +use crate::preview2::{spawn_blocking, AbortOnDropJoinHandle, SocketError, WasiView}; use anyhow::Result; use std::io; use std::mem; @@ -25,7 +23,7 @@ impl Host for T { name: String, family: Option, include_unavailable: bool, - ) -> Result, Error> { + ) -> Result, SocketError> { let network = self.table().get_resource(&network)?; // `Host::parse` serves us two functions: @@ -87,7 +85,7 @@ impl HostResolveAddressStream for T { fn resolve_next_address( &mut self, resource: Resource, - ) -> Result, Error> { + ) -> Result, SocketError> { let stream = self.table_mut().get_resource_mut(&resource)?; loop { match stream { diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index 9b4a273b8af9..aecc414ea4b2 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -40,14 +40,15 @@ mod write_stream; pub use self::clocks::{HostMonotonicClock, HostWallClock}; pub use self::ctx::{WasiCtx, WasiCtxBuilder, WasiView}; -pub use self::error::I32Exit; -pub use self::filesystem::{DirPerms, FilePerms}; +pub use self::error::{I32Exit, TrappableError}; +pub use self::filesystem::{DirPerms, FilePerms, FsError, FsResult}; +pub use self::network::{Network, SocketError, SocketResult}; pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe}; pub use self::random::{thread_rng, Deterministic}; -pub use self::stdio::{ - stderr, stdin, stdout, IsATTY, Stderr, Stdin, StdinStream, Stdout, StdoutStream, +pub use self::stdio::{stderr, stdin, stdout, IsATTY, Stderr, Stdin, Stdout}; +pub use self::stream::{ + HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult, }; -pub use self::stream::{HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError}; pub use self::table::{Table, TableError}; pub use cap_fs_ext::SystemTimeSpec; pub use cap_rand::RngCore; @@ -59,6 +60,8 @@ pub mod bindings { // have some functions in `only_imports` below for being async. pub mod sync_io { pub(crate) mod _internal { + use crate::preview2::{FsError, StreamError}; + wasmtime::component::bindgen!({ path: "wit", interfaces: " @@ -68,8 +71,8 @@ pub mod bindings { ", tracing: true, trappable_error_type: { - "wasi:io/streams"::"stream-error": Error, - "wasi:filesystem/types"::"error-code": Error, + "wasi:io/streams"::"stream-error": StreamError, + "wasi:filesystem/types"::"error-code": FsError, }, with: { "wasi:clocks/wall-clock": crate::preview2::bindings::clocks::wall_clock, @@ -78,6 +81,7 @@ pub mod bindings { "wasi:io/poll/pollable": super::super::io::poll::Pollable, "wasi:io/streams/input-stream": super::super::io::streams::InputStream, "wasi:io/streams/output-stream": super::super::io::streams::OutputStream, + "wasi:io/streams/error": super::super::io::streams::Error, } }); } @@ -149,9 +153,9 @@ pub mod bindings { "wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream, }, trappable_error_type: { - "wasi:io/streams"::"stream-error": Error, - "wasi:filesystem/types"::"error-code": Error, - "wasi:sockets/network"::"error-code": Error, + "wasi:io/streams"::"stream-error": crate::preview2::StreamError, + "wasi:filesystem/types"::"error-code": crate::preview2::FsError, + "wasi:sockets/network"::"error-code": crate::preview2::SocketError, }, with: { "wasi:sockets/network/network": super::network::Network, @@ -160,6 +164,7 @@ pub mod bindings { "wasi:filesystem/types/descriptor": super::filesystem::Descriptor, "wasi:io/streams/input-stream": super::stream::InputStream, "wasi:io/streams/output-stream": super::stream::OutputStream, + "wasi:io/streams/error": super::stream::Error, "wasi:io/poll/pollable": super::poll::Pollable, "wasi:cli/terminal-input/terminal-input": super::stdio::TerminalInput, "wasi:cli/terminal-output/terminal-output": super::stdio::TerminalOutput, diff --git a/crates/wasi/src/preview2/network.rs b/crates/wasi/src/preview2/network.rs index 614130392d29..1f49e06f349f 100644 --- a/crates/wasi/src/preview2/network.rs +++ b/crates/wasi/src/preview2/network.rs @@ -1,6 +1,30 @@ +use crate::preview2::bindings::wasi::sockets::network::ErrorCode; +use crate::preview2::{TableError, TrappableError}; use cap_std::net::Pool; pub struct Network { pub pool: Pool, pub allow_ip_name_lookup: bool, } + +pub type SocketResult = Result; + +pub type SocketError = TrappableError; + +impl From for SocketError { + fn from(error: TableError) -> Self { + Self::trap(error) + } +} + +impl From for SocketError { + fn from(error: std::io::Error) -> Self { + ErrorCode::from(error).into() + } +} + +impl From for SocketError { + fn from(error: rustix::io::Errno) -> Self { + ErrorCode::from(error).into() + } +} diff --git a/crates/wasi/src/preview2/preview1.rs b/crates/wasi/src/preview2/preview1.rs index a4bdada9b7f6..1a94ad7d0213 100644 --- a/crates/wasi/src/preview2/preview1.rs +++ b/crates/wasi/src/preview2/preview1.rs @@ -8,7 +8,7 @@ use crate::preview2::bindings::{ filesystem::{preopens, types as filesystem}, io::{poll, streams}, }; -use crate::preview2::{IsATTY, StreamError, TableError, WasiView}; +use crate::preview2::{FsError, IsATTY, StreamError, StreamResult, TableError, WasiView}; use anyhow::{anyhow, bail, Context}; use std::borrow::Borrow; use std::collections::{BTreeMap, HashSet}; @@ -63,26 +63,16 @@ impl BlockingMode { match streams::HostInputStream::blocking_read(host, input_stream, max_size).await { Ok(r) if r.is_empty() => Err(types::Errno::Intr.into()), Ok(r) => Ok(r), - Err(e) if matches!(e.downcast_ref(), Some(streams::StreamError::Closed)) => { - Ok(Vec::new()) - } - Err(e) => { - tracing::trace!("throwing away read error to report as Errno::Io: {e:?}"); - Err(types::Errno::Io.into()) - } + Err(StreamError::Closed) => Ok(Vec::new()), + Err(e) => Err(e.into()), } } BlockingMode::NonBlocking => { match streams::HostInputStream::read(host, input_stream, max_size).await { Ok(r) => Ok(r), - Err(e) if matches!(e.downcast_ref(), Some(streams::StreamError::Closed)) => { - Ok(Vec::new()) - } - Err(e) => { - tracing::trace!("throwing away read error to report as Errno::Io: {e:?}"); - Err(types::Errno::Io.into()) - } + Err(StreamError::Closed) => Ok(Vec::new()), + Err(e) => Err(e.into()), } } } @@ -92,7 +82,7 @@ impl BlockingMode { host: &mut (impl streams::Host + poll::Host), output_stream: Resource, mut bytes: &[u8], - ) -> Result { + ) -> StreamResult { use streams::HostOutputStream as Streams; match self { @@ -117,7 +107,7 @@ impl BlockingMode { BlockingMode::NonBlocking => { let n = match Streams::check_write(host, output_stream.borrowed()) { Ok(n) => n, - Err(e) if matches!(e.downcast_ref(), Some(streams::StreamError::Closed)) => 0, + Err(StreamError::Closed) => 0, Err(e) => Err(e)?, }; @@ -128,17 +118,13 @@ impl BlockingMode { match Streams::write(host, output_stream.borrowed(), bytes[..len].to_vec()) { Ok(()) => {} - Err(e) if matches!(e.downcast_ref(), Some(streams::StreamError::Closed)) => { - return Ok(0) - } + Err(StreamError::Closed) => return Ok(0), Err(e) => Err(e)?, } match Streams::blocking_flush(host, output_stream.borrowed()).await { Ok(()) => {} - Err(e) if matches!(e.downcast_ref(), Some(streams::StreamError::Closed)) => { - return Ok(0) - } + Err(StreamError::Closed) => return Ok(0), Err(e) => Err(e)?, }; @@ -576,25 +562,25 @@ impl wiggle::GuestErrorType for types::Errno { impl From for types::Error { fn from(err: StreamError) -> Self { - types::Error::from(streams::Error::from(err)) - } -} - -impl From for types::Error { - fn from(err: streams::Error) -> Self { - match err.downcast() { - Ok(se) => se.into(), - Err(t) => types::Error::trap(t), + match err { + StreamError::Closed => types::Errno::Io.into(), + StreamError::LastOperationFailed(e) => match e.downcast::() { + Ok(err) => filesystem::ErrorCode::from(err).into(), + Err(e) => { + log::debug!("dropping error {e:?}"); + types::Errno::Io.into() + } + }, + StreamError::Trap(e) => types::Error::trap(e), } } } -impl From for types::Error { - fn from(err: streams::StreamError) -> Self { - match err { - streams::StreamError::Closed | streams::StreamError::LastOperationFailed => { - types::Errno::Io.into() - } +impl From for types::Error { + fn from(err: FsError) -> Self { + match err.downcast() { + Ok(code) => code.into(), + Err(e) => types::Error::trap(e), } } } @@ -788,28 +774,6 @@ impl From for types::Error { } } -impl TryFrom for types::Errno { - type Error = anyhow::Error; - - fn try_from(err: filesystem::Error) -> Result { - match err.downcast() { - Ok(code) => Ok(code.into()), - Err(e) => Err(e), - } - } -} - -impl TryFrom for types::Error { - type Error = anyhow::Error; - - fn try_from(err: filesystem::Error) -> Result { - match err.downcast() { - Ok(code) => Ok(code.into()), - Err(e) => Err(e), - } - } -} - impl From for types::Error { fn from(err: TableError) -> Self { types::Error::trap(err.into()) @@ -2231,12 +2195,8 @@ impl< let fd = fd.borrowed(); let position = position.clone(); drop(t); - match self - .stat(fd) - .await - .map_err(|e| e.try_into().context("failed to call `stat`")) - { - Ok(filesystem::DescriptorStat { size, .. }) => { + match self.stat(fd).await? { + filesystem::DescriptorStat { size, .. } => { let pos = position.load(Ordering::Relaxed); let nbytes = size.saturating_sub(pos); types::Event { @@ -2253,16 +2213,6 @@ impl< }, } } - Err(Ok(error)) => types::Event { - userdata: sub.userdata, - error, - type_: types::Eventtype::FdRead, - fd_readwrite: types::EventFdReadwrite { - flags: types::Eventrwflags::empty(), - nbytes: 1, - }, - }, - Err(Err(error)) => return Err(types::Error::trap(error)), } } // TODO: Support sockets diff --git a/crates/wasi/src/preview2/stream.rs b/crates/wasi/src/preview2/stream.rs index 4143b9ad86c2..6200e1226d07 100644 --- a/crates/wasi/src/preview2/stream.rs +++ b/crates/wasi/src/preview2/stream.rs @@ -1,5 +1,6 @@ use crate::preview2::filesystem::FileInputStream; use crate::preview2::poll::Subscribe; +use crate::preview2::TableError; use anyhow::Result; use bytes::Bytes; @@ -19,24 +20,40 @@ pub trait HostInputStream: Subscribe { /// /// The [`StreamError`] return value communicates when this stream is /// closed, when a read fails, or when a trap should be generated. - fn read(&mut self, size: usize) -> Result; + fn read(&mut self, size: usize) -> StreamResult; /// Same as the `read` method except that bytes are skipped. /// /// Note that this method is non-blocking like `read` and returns the same /// errors. - fn skip(&mut self, nelem: usize) -> Result { + fn skip(&mut self, nelem: usize) -> StreamResult { let bs = self.read(nelem)?; Ok(bs.len()) } } +/// Representation of the `error` resource type in the `wasi:io/streams` +/// interface. +/// +/// This is currently `anyhow::Error` to retain full type information for +/// errors. +pub type Error = anyhow::Error; + +pub type StreamResult = Result; + #[derive(Debug)] pub enum StreamError { Closed, LastOperationFailed(anyhow::Error), Trap(anyhow::Error), } + +impl StreamError { + pub fn trap(msg: &str) -> StreamError { + StreamError::Trap(anyhow::anyhow!("{msg}")) + } +} + impl std::fmt::Display for StreamError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -46,6 +63,7 @@ impl std::fmt::Display for StreamError { } } } + impl std::error::Error for StreamError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { @@ -55,6 +73,12 @@ impl std::error::Error for StreamError { } } +impl From for StreamError { + fn from(error: TableError) -> Self { + Self::Trap(error.into()) + } +} + /// Host trait for implementing the `wasi:io/streams.output-stream` resource: /// A bytestream which can be written to. #[async_trait::async_trait] @@ -75,7 +99,7 @@ pub trait HostOutputStream: Subscribe { /// - stream is closed /// - prior operation ([`write`](Self::write) or [`flush`](Self::flush)) failed /// - caller performed an illegal operation (e.g. wrote more bytes than were permitted) - fn write(&mut self, bytes: Bytes) -> Result<(), StreamError>; + fn write(&mut self, bytes: Bytes) -> StreamResult<()>; /// Trigger a flush of any bytes buffered in this stream implementation. /// @@ -96,7 +120,7 @@ pub trait HostOutputStream: Subscribe { /// - stream is closed /// - prior operation ([`write`](Self::write) or [`flush`](Self::flush)) failed /// - caller performed an illegal operation (e.g. wrote more bytes than were permitted) - fn flush(&mut self) -> Result<(), StreamError>; + fn flush(&mut self) -> StreamResult<()>; /// Returns the number of bytes that are ready to be written to this stream. /// @@ -110,13 +134,13 @@ pub trait HostOutputStream: Subscribe { /// Returns an [`StreamError`] if: /// - stream is closed /// - prior operation ([`write`](Self::write) or [`flush`](Self::flush)) failed - fn check_write(&mut self) -> Result; + fn check_write(&mut self) -> StreamResult; /// Repeatedly write a byte to a stream. /// Important: this write must be non-blocking! /// Returning an Err which downcasts to a [`StreamError`] will be /// reported to Wasm as the empty error result. Otherwise, errors will trap. - fn write_zeroes(&mut self, nelem: usize) -> Result<(), StreamError> { + fn write_zeroes(&mut self, nelem: usize) -> StreamResult<()> { // TODO: We could optimize this to not allocate one big zeroed buffer, and instead write // repeatedly from a 'static buffer of zeros. let bs = Bytes::from_iter(core::iter::repeat(0 as u8).take(nelem)); @@ -126,7 +150,7 @@ pub trait HostOutputStream: Subscribe { /// Simultaneously waits for this stream to be writable and then returns how /// much may be written or the last error that happened. - async fn write_ready(&mut self) -> Result { + async fn write_ready(&mut self) -> StreamResult { self.ready().await; self.check_write() } diff --git a/crates/wasi/wit/deps/filesystem/types.wit b/crates/wasi/wit/deps/filesystem/types.wit index 3f69bf997a29..aecdd0ef354b 100644 --- a/crates/wasi/wit/deps/filesystem/types.wit +++ b/crates/wasi/wit/deps/filesystem/types.wit @@ -23,7 +23,7 @@ /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md interface types { - use wasi:io/streams.{input-stream, output-stream} + use wasi:io/streams.{input-stream, output-stream, error} use wasi:clocks/wall-clock.{datetime} /// File size or length of a region within a file. @@ -795,4 +795,16 @@ interface types { /// Read a single directory entry from a `directory-entry-stream`. read-directory-entry: func() -> result, error-code> } + + /// Attempts to extract a filesystem-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// filesystem-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are filesystem-related errors. + filesystem-error-code: func(err: borrow) -> option } diff --git a/crates/wasi/wit/deps/io/streams.wit b/crates/wasi/wit/deps/io/streams.wit index 8240507976f7..55562d1cbfce 100644 --- a/crates/wasi/wit/deps/io/streams.wit +++ b/crates/wasi/wit/deps/io/streams.wit @@ -9,15 +9,37 @@ interface streams { use poll.{pollable} /// An error for input-stream and output-stream operations. - enum stream-error { + variant stream-error { /// The last operation (a write or flush) failed before completion. - last-operation-failed, + /// + /// More information is available in the `error` payload. + last-operation-failed(error), /// The stream is closed: no more input will be accepted by the /// stream. A closed output-stream will return this error on all /// future operations. closed } + /// Contextual error information about the last failure that happened on + /// a read, write, or flush from an `input-stream` or `output-stream`. + /// + /// This type is returned through the `stream-error` type whenever an + /// operation on a stream directly fails or an error is discovered + /// after-the-fact, for example when a write's failure shows up through a + /// later `flush` or `check-write`. + /// + /// Interfaces such as `wasi:filesystem/types` provide functionality to + /// further "downcast" this error into interface-specific error information. + resource error { + /// Returns a string that's suitable to assist humans in debugging this + /// error. + /// + /// The returned string will change across platforms and hosts which + /// means that parsing it, for example, would be a + /// platform-compatibility hazard. + to-debug-string: func() -> string + } + /// An input bytestream. /// /// `input-stream`s are *non-blocking* to the extent practical on underlying diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index 45fe6aedf3b1..7183e0bf232c 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -2,7 +2,7 @@ use crate::rust::{to_rust_ident, to_rust_upper_camel_case, RustGenerator, TypeMo use crate::types::{TypeInfo, Types}; use anyhow::{anyhow, bail, Context}; use heck::*; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use std::collections::{BTreeMap, HashMap, HashSet}; use std::fmt::Write as _; use std::io::{Read, Write}; @@ -48,6 +48,7 @@ struct Wasmtime { interface_names: HashMap, with_name_counter: usize, interface_last_seen_as_import: HashMap, + trappable_errors: IndexMap, } struct ImportInterface { @@ -218,6 +219,16 @@ impl Wasmtime { fn generate(&mut self, resolve: &Resolve, id: WorldId) -> String { self.types.analyze(resolve, id); + for (i, te) in self.opts.trappable_error_type.iter().enumerate() { + let id = resolve_type_in_package(resolve, &te.wit_package_path, &te.wit_type_name) + .context(format!("resolving {:?}", te)) + .unwrap(); + let name = format!("_TrappableError{i}"); + uwriteln!(self.src, "type {name} = {};", te.rust_type_name); + let prev = self.trappable_errors.insert(id, name); + assert!(prev.is_none()); + } + let world = &resolve.worlds[id]; for (name, import) in world.imports.iter() { if !self.opts.only_interfaces || matches!(import, WorldItem::Interface(_)) { @@ -837,32 +848,15 @@ struct InterfaceGenerator<'a> { gen: &'a mut Wasmtime, resolve: &'a Resolve, current_interface: Option<(InterfaceId, &'a WorldKey, bool)>, - - /// A mapping of wit types to their rust type name equivalent. This is the pre-processed - /// version of `gen.opts.trappable_error_types`, where the types have been eagerly resolved. - trappable_errors: IndexMap, } impl<'a> InterfaceGenerator<'a> { fn new(gen: &'a mut Wasmtime, resolve: &'a Resolve) -> InterfaceGenerator<'a> { - let trappable_errors = gen - .opts - .trappable_error_type - .iter() - .map(|te| { - let id = resolve_type_in_package(resolve, &te.wit_package_path, &te.wit_type_name) - .context(format!("resolving {:?}", te))?; - Ok((id, te.rust_type_name.clone())) - }) - .collect::>>() - .unwrap(); - InterfaceGenerator { src: Source::default(), gen, resolve, current_interface: None, - trappable_errors, } } @@ -876,10 +870,6 @@ impl<'a> InterfaceGenerator<'a> { fn types(&mut self, id: InterfaceId) { for (name, id) in self.resolve.interfaces[id].types.iter() { self.define_type(name, *id); - - if let Some(rust_name) = self.trappable_errors.get(id) { - self.define_trappable_error_type(*id, rust_name.clone()) - } } } @@ -1467,9 +1457,11 @@ impl<'a> InterfaceGenerator<'a> { _ => return None, }; - let rust_type = self.trappable_errors.get(&error_typeid)?; + let name = self.gen.trappable_errors.get(&error_typeid)?; - Some((result, error_typeid, rust_type.clone())) + let mut path = self.path_to_root(); + uwrite!(path, "{name}"); + Some((result, error_typeid, path)) } fn generate_add_to_linker(&mut self, id: InterfaceId, name: &str) { @@ -1499,14 +1491,59 @@ impl<'a> InterfaceGenerator<'a> { } self.generate_function_trait_sig(func); } + + // Generate `convert_*` functions to convert custom trappable errors + // into the representation required by Wasmtime's component API. + let mut required_conversion_traits = IndexSet::new(); + let mut errors_converted = IndexSet::new(); + let my_error_types = iface + .types + .iter() + .filter(|(_, id)| self.gen.trappable_errors.contains_key(*id)) + .map(|(_, id)| *id); + let used_error_types = iface + .functions + .iter() + .filter_map(|(_, func)| self.special_case_trappable_error(&func.results)) + .map(|(_, id, _)| id); + for err in my_error_types.chain(used_error_types).collect::>() { + let custom_name = &self.gen.trappable_errors[&err]; + let err = &self.resolve.types[resolve_type_definition_id(self.resolve, err)]; + let err_name = err.name.as_ref().unwrap(); + let err_snake = err_name.to_snake_case(); + let err_camel = err_name.to_upper_camel_case(); + let owner = match err.owner { + TypeOwner::Interface(i) => i, + _ => unimplemented!(), + }; + match self.path_to_interface(owner) { + Some(path) => { + required_conversion_traits.insert(format!("{path}::Host")); + } + None => { + if errors_converted.insert(err_name) { + let root = self.path_to_root(); + uwriteln!( + self.src, + "fn convert_{err_snake}(&mut self, err: {root}{custom_name}) -> wasmtime::Result<{err_camel}>;" + ); + } + } + } + } uwriteln!(self.src, "}}"); - let where_clause = if self.gen.opts.async_.maybe_async() { + let mut where_clause = if self.gen.opts.async_.maybe_async() { "T: Send, U: Host + Send".to_string() } else { "U: Host".to_string() }; + for t in required_conversion_traits { + where_clause.push_str(" + "); + where_clause.push_str(&t); + } + uwriteln!( self.src, " @@ -1655,16 +1692,24 @@ impl<'a> InterfaceGenerator<'a> { ); } - if self.special_case_trappable_error(&func.results).is_some() { + if let Some((_, err, _)) = self.special_case_trappable_error(&func.results) { + let err = &self.resolve.types[resolve_type_definition_id(self.resolve, err)]; + let err_name = err.name.as_ref().unwrap(); + let owner = match err.owner { + TypeOwner::Interface(i) => i, + _ => unimplemented!(), + }; + let convert_trait = match self.path_to_interface(owner) { + Some(path) => format!("{path}::Host"), + None => format!("Host"), + }; + let convert = format!("{}::convert_{}", convert_trait, err_name.to_snake_case()); uwrite!( self.src, - "match r {{ - Ok(a) => Ok((Ok(a),)), - Err(e) => match e.downcast() {{ - Ok(api_error) => Ok((Err(api_error),)), - Err(anyhow_error) => Err(anyhow_error), - }} - }}" + "Ok((match r {{ + Ok(a) => Ok(a), + Err(e) => Err({convert}(host, e)?), + }},))" ); } else if func.results.iter_types().len() == 1 { uwrite!(self.src, "Ok((r?,))\n"); @@ -1699,9 +1744,7 @@ impl<'a> InterfaceGenerator<'a> { self.push_str(")"); self.push_str(" -> "); - if let Some((r, error_id, error_typename)) = - self.special_case_trappable_error(&func.results) - { + if let Some((r, _id, error_typename)) = self.special_case_trappable_error(&func.results) { // Functions which have a single result `result` get special // cased to use the host_wasmtime_rust::Error, making it possible // for them to trap or use `?` to propogate their errors @@ -1712,12 +1755,6 @@ impl<'a> InterfaceGenerator<'a> { self.push_str("()"); } self.push_str(","); - if let TypeOwner::Interface(id) = self.resolve.types[error_id].owner { - if let Some(path) = self.path_to_interface(id) { - self.push_str(&path); - self.push_str("::"); - } - } self.push_str(&error_typename); self.push_str(">"); } else { @@ -1867,53 +1904,6 @@ impl<'a> InterfaceGenerator<'a> { self.src.push_str("}\n"); } - fn define_trappable_error_type(&mut self, id: TypeId, rust_name: String) { - let info = self.info(id); - if self.lifetime_for(&info, TypeMode::Owned).is_some() { - panic!("wit error for {rust_name} is not 'static") - } - let abi_type = self.param_name(id); - - uwriteln!( - self.src, - " - #[derive(Debug)] - pub struct {rust_name} {{ - inner: anyhow::Error, - }} - impl std::fmt::Display for {rust_name} {{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{ - write!(f, \"{{}}\", self.inner) - }} - }} - impl std::error::Error for {rust_name} {{ - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {{ - self.inner.source() - }} - }} - impl {rust_name} {{ - pub fn trap(inner: anyhow::Error) -> Self {{ - Self {{ inner }} - }} - pub fn downcast(self) -> Result<{abi_type}, anyhow::Error> {{ - self.inner.downcast() - }} - pub fn downcast_ref(&self) -> Option<&{abi_type}> {{ - self.inner.downcast_ref() - }} - pub fn context(self, s: impl Into) -> Self {{ - Self {{ inner: self.inner.context(s.into()) }} - }} - }} - impl From<{abi_type}> for {rust_name} {{ - fn from(abi: {abi_type}) -> {rust_name} {{ - {rust_name} {{ inner: anyhow::Error::from(abi) }} - }} - }} - " - ); - } - fn rustdoc(&mut self, docs: &Docs) { let docs = match &docs.contents { Some(docs) => docs, diff --git a/tests/all/component_model/bindgen/results.rs b/tests/all/component_model/bindgen/results.rs index 6c0b3f8b6472..32a645682629 100644 --- a/tests/all/component_model/bindgen/results.rs +++ b/tests/all/component_model/bindgen/results.rs @@ -241,6 +241,39 @@ mod enum_error { trappable_error_type: { "inline:inline/imports"::e1: TrappableE1 } }); + // You can create concrete trap types which make it all the way out to the + // host caller, via downcast_ref below. + #[derive(Debug)] + pub struct MyTrap; + + impl std::fmt::Display for MyTrap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } + } + impl std::error::Error for MyTrap {} + + pub enum TrappableE1 { + Normal(imports::E1), + MyTrap(MyTrap), + } + + // It is possible to define From impls that target these generated trappable + // types. This allows you to integrate libraries with other error types, or + // use your own more descriptive error types, and use ? to convert them at + // their throw site. + impl From for TrappableE1 { + fn from(t: MyTrap) -> TrappableE1 { + TrappableE1::MyTrap(t) + } + } + + impl From for TrappableE1 { + fn from(t: imports::E1) -> TrappableE1 { + TrappableE1::Normal(t) + } + } + #[test] fn run() -> Result<(), Error> { let engine = engine(); @@ -305,33 +338,18 @@ mod enum_error { ), )?; - // You can create concrete trap types which make it all the way out to the - // host caller, via downcast_ref below. - #[derive(Debug)] - struct MyTrap; - - impl std::fmt::Display for MyTrap { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } - } - impl std::error::Error for MyTrap {} - - // It is possible to define From impls that target these generated trappable - // types. This allows you to integrate libraries with other error types, or - // use your own more descriptive error types, and use ? to convert them at - // their throw site. - impl From for imports::TrappableE1 { - fn from(t: MyTrap) -> imports::TrappableE1 { - imports::TrappableE1::trap(anyhow!(t)) - } - } - #[derive(Default)] struct MyImports {} impl imports::Host for MyImports { - fn enum_error(&mut self, a: f64) -> Result { + fn convert_e1(&mut self, err: TrappableE1) -> anyhow::Result { + match err { + TrappableE1::Normal(e) => Ok(e), + TrappableE1::MyTrap(e) => Err(e.into()), + } + } + + fn enum_error(&mut self, a: f64) -> Result { if a == 0.0 { Ok(a) } else if a == 1.0 { @@ -405,6 +423,17 @@ mod record_error { trappable_error_type: { "inline:inline/imports"::"e2": TrappableE2 } }); + pub enum TrappableE2 { + Normal(imports::E2), + Trap(anyhow::Error), + } + + impl From for TrappableE2 { + fn from(t: imports::E2) -> TrappableE2 { + TrappableE2::Normal(t) + } + } + #[test] fn run() -> Result<(), Error> { let engine = engine(); @@ -476,7 +505,13 @@ mod record_error { struct MyImports {} impl imports::Host for MyImports { - fn record_error(&mut self, a: f64) -> Result { + fn convert_e2(&mut self, err: TrappableE2) -> anyhow::Result { + match err { + TrappableE2::Normal(e) => Ok(e), + TrappableE2::Trap(e) => Err(e.into()), + } + } + fn record_error(&mut self, a: f64) -> Result { if a == 0.0 { Ok(a) } else if a == 1.0 { @@ -485,7 +520,7 @@ mod record_error { col: 1312, })? } else { - Err(imports::TrappableE2::trap(anyhow!("record_error: trap"))) + Err(TrappableE2::Trap(anyhow!("record_error: trap"))) } } } @@ -559,6 +594,17 @@ mod variant_error { trappable_error_type: { "inline:inline/imports"::e3: TrappableE3 } }); + pub enum TrappableE3 { + Normal(imports::E3), + Trap(anyhow::Error), + } + + impl From for TrappableE3 { + fn from(t: imports::E3) -> TrappableE3 { + TrappableE3::Normal(t) + } + } + #[test] fn run() -> Result<(), Error> { let engine = engine(); @@ -653,7 +699,13 @@ mod variant_error { struct MyImports {} impl imports::Host for MyImports { - fn variant_error(&mut self, a: f64) -> Result { + fn convert_e3(&mut self, err: TrappableE3) -> anyhow::Result { + match err { + TrappableE3::Normal(e) => Ok(e), + TrappableE3::Trap(e) => Err(e.into()), + } + } + fn variant_error(&mut self, a: f64) -> Result { if a == 0.0 { Ok(a) } else if a == 1.0 { @@ -662,7 +714,7 @@ mod variant_error { col: 1312, }))? } else { - Err(imports::TrappableE3::trap(anyhow!("variant_error: trap"))) + Err(TrappableE3::Trap(anyhow!("variant_error: trap"))) } } } @@ -737,6 +789,17 @@ mod multiple_interfaces_error { trappable_error_type: { "inline:inline/types"::e1: TrappableE1 } }); + pub enum TrappableE1 { + Normal(types::E1), + Trap(anyhow::Error), + } + + impl From for TrappableE1 { + fn from(t: imports::E1) -> TrappableE1 { + TrappableE1::Normal(t) + } + } + #[test] fn run() -> Result<(), Error> { let engine = engine(); @@ -819,9 +882,9 @@ mod multiple_interfaces_error { // types. This allows you to integrate libraries with other error types, or // use your own more descriptive error types, and use ? to convert them at // their throw site. - impl From for types::TrappableE1 { - fn from(t: MyTrap) -> types::TrappableE1 { - types::TrappableE1::trap(anyhow!(t)) + impl From for TrappableE1 { + fn from(t: MyTrap) -> TrappableE1 { + TrappableE1::Trap(anyhow!(t)) } } @@ -829,7 +892,13 @@ mod multiple_interfaces_error { struct MyImports {} impl types::Host for MyImports { - fn enum_error(&mut self, a: f64) -> Result { + fn convert_e1(&mut self, err: TrappableE1) -> anyhow::Result { + match err { + TrappableE1::Normal(e) => Ok(e), + TrappableE1::Trap(e) => Err(e.into()), + } + } + fn enum_error(&mut self, a: f64) -> Result { if a == 0.0 { Ok(a) } else if a == 1.0 { @@ -841,7 +910,7 @@ mod multiple_interfaces_error { } impl imports::Host for MyImports { - fn enum_error(&mut self, a: f64) -> Result { + fn enum_error(&mut self, a: f64) -> Result { if a == 0.0 { Ok(a) } else if a == 1.0 { From 542c1809e78c8812cfc11b91bb53beca6c8c2c67 Mon Sep 17 00:00:00 2001 From: Anatol Liu Date: Thu, 5 Oct 2023 23:09:05 -0400 Subject: [PATCH 068/199] add i128 support for iabs on x86_64 (#7166) --- cranelift/codegen/src/isa/x64/lower.isle | 21 ++++++++++ .../filetests/filetests/isa/x64/i128.clif | 38 +++++++++++++++++++ .../filetests/runtests/i128-iabs.clif | 4 ++ 3 files changed, 63 insertions(+) diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle index 9691cec469ef..af11e659de8d 100644 --- a/cranelift/codegen/src/isa/x64/lower.isle +++ b/cranelift/codegen/src/isa/x64/lower.isle @@ -1265,6 +1265,27 @@ (cmove ConsumesFlags (cmove ty (CC.S) src neg_result))) (with_flags_reg (produces_flags_ignore neg) cmove))) +;; `i128`. Negate the low bits, `adc` to the higher bits, then negate high bits. +(rule (lower (has_type $I128 (iabs x))) + ;; Get the high/low registers for `x`. + (let ((x_regs ValueRegs x) + (x_lo Gpr (value_regs_get_gpr x_regs 0)) + (x_hi Gpr (value_regs_get_gpr x_regs 1)) + ; negate low bits, then add 0 with carry to high bits. + (neg_lo ProducesFlags (x64_neg_paired $I64 x_lo)) + (adc_hi ConsumesFlags (x64_adc_paired $I64 x_hi (imm $I64 0))) + (neg_adc_vals ValueRegs (with_flags neg_lo adc_hi)) + ; negate high bits. + (neg_hi ProducesFlags (x64_neg_paired $I64 (value_regs_get neg_adc_vals 1))) + (neg_hi_flag_only ProducesFlags (produces_flags_ignore neg_hi)) + ; cmove based on sign flag from hi negation. + (cmove_lo ConsumesFlags (cmove $I64 (CC.S) x_lo + (value_regs_get neg_adc_vals 0))) + (cmove_hi ConsumesFlags (cmove $I64 (CC.S) x_hi + (produces_flags_get_reg neg_hi))) + (cmoves ConsumesFlags (consumes_flags_concat cmove_lo cmove_hi))) + (with_flags neg_hi_flag_only cmoves))) + ;;;; Rules for `fabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type $F32 (fabs x))) diff --git a/cranelift/filetests/filetests/isa/x64/i128.clif b/cranelift/filetests/filetests/isa/x64/i128.clif index abc78be6005d..0654089deadf 100644 --- a/cranelift/filetests/filetests/isa/x64/i128.clif +++ b/cranelift/filetests/filetests/isa/x64/i128.clif @@ -1973,3 +1973,41 @@ block0(v0: i128, v1: i128): ; popq %rbp ; retq +function %iabs_i128(i128) -> i128 { +block0(v0: i128): + v1 = iabs.i128 v0 + return v1 +} + +; VCode: +; pushq %rbp +; movq %rsp, %rbp +; block0: +; xorq %r8, %r8, %r8 +; movq %rdi, %rax +; negq %rax, %rax +; movq %rsi, %rdx +; adcq %rdx, %r8, %rdx +; negq %rdx, %rdx +; cmovsq %rdi, %rax, %rax +; cmovsq %rsi, %rdx, %rdx +; movq %rbp, %rsp +; popq %rbp +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; pushq %rbp +; movq %rsp, %rbp +; block1: ; offset 0x4 +; xorq %r8, %r8 +; movq %rdi, %rax +; negq %rax +; movq %rsi, %rdx +; adcq %r8, %rdx +; negq %rdx +; cmovsq %rdi, %rax +; cmovsq %rsi, %rdx +; movq %rbp, %rsp +; popq %rbp +; retq diff --git a/cranelift/filetests/filetests/runtests/i128-iabs.clif b/cranelift/filetests/filetests/runtests/i128-iabs.clif index c84f2c03e09c..578596c8b634 100644 --- a/cranelift/filetests/filetests/runtests/i128-iabs.clif +++ b/cranelift/filetests/filetests/runtests/i128-iabs.clif @@ -1,6 +1,8 @@ test interpret test run +set enable_llvm_abi_extensions=true target s390x +target x86_64 function %iabs_i128(i128) -> i128 { block0(v0: i128): @@ -10,4 +12,6 @@ block0(v0: i128): ; run: %iabs_i128(0) == 0 ; run: %iabs_i128(-1) == 1 ; run: %iabs_i128(1) == 1 +; run: %iabs_i128(0x40000000_00000000_80000000_00000000) == 0x40000000_00000000_80000000_00000000 +; run: %iabs_i128(0x80000000_00000000_80000000_00000000) == 0x7fffffff_ffffffff_80000000_00000000 ; run: %iabs_i128(0x80000000_00000000_00000000_00000000) == 0x80000000_00000000_00000000_00000000 From 786ebef8467506a752c8de03af389e62b14087be Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Fri, 6 Oct 2023 14:52:45 +0100 Subject: [PATCH 069/199] fuzzgen: Enable iabs.i128 on fuzzgen (#7168) --- cranelift/fuzzgen/src/function_generator.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs index a264155cad68..62fbad2206ca 100644 --- a/cranelift/fuzzgen/src/function_generator.rs +++ b/cranelift/fuzzgen/src/function_generator.rs @@ -513,8 +513,6 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) - (Opcode::Udiv | Opcode::Sdiv, &[I128, I128]), // https://github.com/bytecodealliance/wasmtime/issues/5474 (Opcode::Urem | Opcode::Srem, &[I128, I128]), - // https://github.com/bytecodealliance/wasmtime/issues/5466 - (Opcode::Iabs, &[I128]), // https://github.com/bytecodealliance/wasmtime/issues/3370 ( Opcode::Smin | Opcode::Umin | Opcode::Smax | Opcode::Umax, From fd3a827e28720559e62c680363c815975b7b8425 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 6 Oct 2023 09:11:43 -0500 Subject: [PATCH 070/199] Add a whole lot of semicolons to WIT files (#7159) To prepare this commit I've run `WIT_REQUIRE_SEMICOLONS=1 ./ci/run-tests.sh` locally which configures `wit-parser` to generate an error for missing semicolons in WIT files. This led me to add quite a few semicolons in quite a few places in what is going to be the first of a few batches of semicolons. CI checks for this cannot be added just yet because the wasi-nn spec is a submodule which needs to be updated with semicolons before this repository can require semicolons. Nevertheless that doesn't stop us from using semicolons in the meantime (yay gradual rollout of changes!) so I figure this would be good to get in sooner rather than later. --- crates/component-macro/src/bindgen.rs | 2 +- crates/component-macro/tests/codegen.rs | 16 +-- crates/component-macro/tests/codegen/char.wit | 10 +- .../tests/codegen/conventions.wit | 30 ++-- .../tests/codegen/direct-import.wit | 4 +- .../component-macro/tests/codegen/empty.wit | 2 +- .../component-macro/tests/codegen/flags.wit | 20 +-- .../component-macro/tests/codegen/floats.wit | 14 +- .../tests/codegen/function-new.wit | 4 +- .../tests/codegen/integers.wit | 42 +++--- .../component-macro/tests/codegen/lists.wit | 66 ++++----- .../tests/codegen/many-arguments.wit | 10 +- .../tests/codegen/multi-return.wit | 16 +-- .../component-macro/tests/codegen/records.wit | 34 ++--- .../component-macro/tests/codegen/rename.wit | 12 +- .../tests/codegen/resources-export.wit | 32 ++--- .../tests/codegen/resources-import.wit | 80 +++++------ .../tests/codegen/share-types.wit | 10 +- .../tests/codegen/simple-functions.wit | 18 +-- .../tests/codegen/simple-lists.wit | 14 +- .../tests/codegen/simple-wasi.wit | 10 +- .../tests/codegen/small-anonymous.wit | 8 +- .../tests/codegen/smoke-default.wit | 4 +- .../tests/codegen/smoke-export.wit | 4 +- .../component-macro/tests/codegen/smoke.wit | 4 +- .../component-macro/tests/codegen/strings.wit | 12 +- .../tests/codegen/use-paths.wit | 20 +-- .../tests/codegen/variants.wit | 50 +++---- .../tests/codegen/worlds-with-types.wit | 10 +- crates/wasi-http/src/lib.rs | 8 +- crates/wasi-http/wit/command-extended.wit | 58 ++++---- crates/wasi-http/wit/deps/cli/command.wit | 6 +- crates/wasi-http/wit/deps/cli/environment.wit | 6 +- crates/wasi-http/wit/deps/cli/exit.wit | 2 +- crates/wasi-http/wit/deps/cli/reactor.wit | 57 ++++---- crates/wasi-http/wit/deps/cli/run.wit | 2 +- crates/wasi-http/wit/deps/cli/stdio.wit | 12 +- crates/wasi-http/wit/deps/cli/terminal.wit | 16 +-- .../wit/deps/clocks/monotonic-clock.wit | 10 +- crates/wasi-http/wit/deps/clocks/timezone.wit | 6 +- .../wasi-http/wit/deps/clocks/wall-clock.wit | 4 +- crates/wasi-http/wit/deps/clocks/world.wit | 8 +- .../wit/deps/filesystem/preopens.wit | 4 +- .../wasi-http/wit/deps/filesystem/types.wit | 80 +++++------ .../wasi-http/wit/deps/filesystem/world.wit | 6 +- .../wit/deps/http/incoming-handler.wit | 4 +- .../wit/deps/http/outgoing-handler.wit | 4 +- crates/wasi-http/wit/deps/http/proxy.wit | 20 +-- crates/wasi-http/wit/deps/http/types.wit | 68 ++++----- crates/wasi-http/wit/deps/io/poll.wit | 8 +- crates/wasi-http/wit/deps/io/streams.wit | 36 ++--- crates/wasi-http/wit/deps/io/world.wit | 6 +- crates/wasi-http/wit/deps/logging/logging.wit | 4 +- crates/wasi-http/wit/deps/logging/world.wit | 4 +- .../wit/deps/random/insecure-seed.wit | 2 +- crates/wasi-http/wit/deps/random/insecure.wit | 4 +- crates/wasi-http/wit/deps/random/random.wit | 4 +- crates/wasi-http/wit/deps/random/world.wit | 8 +- .../wit/deps/sockets/instance-network.wit | 4 +- .../wit/deps/sockets/ip-name-lookup.wit | 30 ++-- crates/wasi-http/wit/deps/sockets/network.wit | 32 ++--- .../wit/deps/sockets/tcp-create-socket.wit | 16 +-- crates/wasi-http/wit/deps/sockets/tcp.wit | 134 +++++++++--------- .../wit/deps/sockets/udp-create-socket.wit | 16 +-- crates/wasi-http/wit/deps/sockets/udp.wit | 108 +++++++------- crates/wasi-http/wit/deps/sockets/world.wit | 16 +-- crates/wasi-http/wit/main.wit | 56 ++++---- crates/wasi-http/wit/test.wit | 60 ++++---- crates/wasi/src/preview2/mod.rs | 8 +- crates/wasi/wit/command-extended.wit | 58 ++++---- crates/wasi/wit/deps/cli/command.wit | 6 +- crates/wasi/wit/deps/cli/environment.wit | 6 +- crates/wasi/wit/deps/cli/exit.wit | 2 +- crates/wasi/wit/deps/cli/reactor.wit | 57 ++++---- crates/wasi/wit/deps/cli/run.wit | 2 +- crates/wasi/wit/deps/cli/stdio.wit | 12 +- crates/wasi/wit/deps/cli/terminal.wit | 16 +-- .../wasi/wit/deps/clocks/monotonic-clock.wit | 10 +- crates/wasi/wit/deps/clocks/timezone.wit | 6 +- crates/wasi/wit/deps/clocks/wall-clock.wit | 4 +- crates/wasi/wit/deps/clocks/world.wit | 8 +- crates/wasi/wit/deps/filesystem/preopens.wit | 4 +- crates/wasi/wit/deps/filesystem/types.wit | 80 +++++------ crates/wasi/wit/deps/filesystem/world.wit | 6 +- .../wasi/wit/deps/http/incoming-handler.wit | 4 +- .../wasi/wit/deps/http/outgoing-handler.wit | 4 +- crates/wasi/wit/deps/http/proxy.wit | 20 +-- crates/wasi/wit/deps/http/types.wit | 68 ++++----- crates/wasi/wit/deps/io/poll.wit | 8 +- crates/wasi/wit/deps/io/streams.wit | 36 ++--- crates/wasi/wit/deps/io/world.wit | 6 +- crates/wasi/wit/deps/logging/logging.wit | 4 +- crates/wasi/wit/deps/logging/world.wit | 4 +- crates/wasi/wit/deps/random/insecure-seed.wit | 2 +- crates/wasi/wit/deps/random/insecure.wit | 4 +- crates/wasi/wit/deps/random/random.wit | 4 +- crates/wasi/wit/deps/random/world.wit | 8 +- .../wit/deps/sockets/instance-network.wit | 4 +- .../wasi/wit/deps/sockets/ip-name-lookup.wit | 30 ++-- crates/wasi/wit/deps/sockets/network.wit | 32 ++--- .../wit/deps/sockets/tcp-create-socket.wit | 16 +-- crates/wasi/wit/deps/sockets/tcp.wit | 134 +++++++++--------- .../wit/deps/sockets/udp-create-socket.wit | 16 +-- crates/wasi/wit/deps/sockets/udp.wit | 108 +++++++------- crates/wasi/wit/deps/sockets/world.wit | 16 +-- crates/wasi/wit/main.wit | 56 ++++---- crates/wasi/wit/test.wit | 60 ++++---- crates/wasmtime/src/component/mod.rs | 20 +-- tests/all/component_model/bindgen.rs | 66 ++++----- .../all/component_model/bindgen/ownership.rs | 24 ++-- tests/all/component_model/bindgen/results.rs | 56 ++++---- 111 files changed, 1290 insertions(+), 1292 deletions(-) diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs index c5a9d2920ce8..e3aca760c1cc 100644 --- a/crates/component-macro/src/bindgen.rs +++ b/crates/component-macro/src/bindgen.rs @@ -89,7 +89,7 @@ impl Parse for Config { } inline = Some(format!( " - package wasmtime:component-macro-synthesized + package wasmtime:component-macro-synthesized; world interfaces {{ {} diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs index 49bb8c6d5726..5b6292989d82 100644 --- a/crates/component-macro/tests/codegen.rs +++ b/crates/component-macro/tests/codegen.rs @@ -31,23 +31,23 @@ mod with_key_and_resources { wasmtime::component::bindgen!({ inline: " - package demo:pkg + package demo:pkg; interface bar { - resource a - resource b + resource a; + resource b; } world foo { - resource a - resource b + resource a; + resource b; import foo: interface { - resource a - resource b + resource a; + resource b; } - import bar + import bar; } ", with: { diff --git a/crates/component-macro/tests/codegen/char.wit b/crates/component-macro/tests/codegen/char.wit index b49947b38abb..165642279af0 100644 --- a/crates/component-macro/tests/codegen/char.wit +++ b/crates/component-macro/tests/codegen/char.wit @@ -1,13 +1,13 @@ -package foo:foo +package foo:foo; interface chars { /// A function that accepts a character - take-char: func(x: char) + take-char: func(x: char); /// A function that returns a character - return-char: func() -> char + return-char: func() -> char; } world the-world { - import chars - export chars + import chars; + export chars; } diff --git a/crates/component-macro/tests/codegen/conventions.wit b/crates/component-macro/tests/codegen/conventions.wit index a96b5afd3444..507ef40b95b4 100644 --- a/crates/component-macro/tests/codegen/conventions.wit +++ b/crates/component-macro/tests/codegen/conventions.wit @@ -1,22 +1,22 @@ // hello 🐱 world -package foo:foo +package foo:foo; interface conventions { - kebab-case: func() + kebab-case: func(); record ludicrous-speed { how-fast-are-you-going: u32, i-am-going-extremely-slow: u64, } - foo: func(x: ludicrous-speed) - %function-with-dashes: func() - %function-with-no-weird-characters: func() + foo: func(x: ludicrous-speed); + %function-with-dashes: func(); + %function-with-no-weird-characters: func(); - apple: func() - apple-pear: func() - apple-pear-grape: func() - a0: func() + apple: func(); + apple-pear: func(); + apple-pear-grape: func(); + a0: func(); // Comment out identifiers that collide when mapped to snake_case, for now; see // https://github.com/WebAssembly/component-model/issues/118 @@ -24,16 +24,16 @@ interface conventions { //APPLE-pear-GRAPE: func() //apple-PEAR-grape: func() - is-XML: func() + is-XML: func(); - %explicit: func() - %explicit-kebab: func() + %explicit: func(); + %explicit-kebab: func(); // Identifiers with the same name as keywords are quoted. - %bool: func() + %bool: func(); } world the-world { - import conventions - export conventions + import conventions; + export conventions; } diff --git a/crates/component-macro/tests/codegen/direct-import.wit b/crates/component-macro/tests/codegen/direct-import.wit index 67d252a4a898..20d8fe753517 100644 --- a/crates/component-macro/tests/codegen/direct-import.wit +++ b/crates/component-macro/tests/codegen/direct-import.wit @@ -1,5 +1,5 @@ -package foo:foo +package foo:foo; world foo { - import foo: func() + import foo: func(); } diff --git a/crates/component-macro/tests/codegen/empty.wit b/crates/component-macro/tests/codegen/empty.wit index b720c27d628e..ea2e3c8d405e 100644 --- a/crates/component-macro/tests/codegen/empty.wit +++ b/crates/component-macro/tests/codegen/empty.wit @@ -1,2 +1,2 @@ -package foo:foo +package foo:foo; world empty {} diff --git a/crates/component-macro/tests/codegen/flags.wit b/crates/component-macro/tests/codegen/flags.wit index 8c9c1437169d..296c47e65c62 100644 --- a/crates/component-macro/tests/codegen/flags.wit +++ b/crates/component-macro/tests/codegen/flags.wit @@ -1,4 +1,4 @@ -package foo:foo +package foo:foo; interface flegs { flags flag1 { @@ -40,16 +40,16 @@ interface flegs { b56, b57, b58, b59, b60, b61, b62, b63, } - roundtrip-flag1: func(x: flag1) -> flag1 - roundtrip-flag2: func(x: flag2) -> flag2 - roundtrip-flag4: func(x: flag4) -> flag4 - roundtrip-flag8: func(x: flag8) -> flag8 - roundtrip-flag16: func(x: flag16) -> flag16 - roundtrip-flag32: func(x: flag32) -> flag32 - roundtrip-flag64: func(x: flag64) -> flag64 + roundtrip-flag1: func(x: flag1) -> flag1; + roundtrip-flag2: func(x: flag2) -> flag2; + roundtrip-flag4: func(x: flag4) -> flag4; + roundtrip-flag8: func(x: flag8) -> flag8; + roundtrip-flag16: func(x: flag16) -> flag16; + roundtrip-flag32: func(x: flag32) -> flag32; + roundtrip-flag64: func(x: flag64) -> flag64; } world the-flags { - import flegs - export flegs + import flegs; + export flegs; } diff --git a/crates/component-macro/tests/codegen/floats.wit b/crates/component-macro/tests/codegen/floats.wit index 6cd720a8dc39..690e920aa1b9 100644 --- a/crates/component-macro/tests/codegen/floats.wit +++ b/crates/component-macro/tests/codegen/floats.wit @@ -1,13 +1,13 @@ -package foo:foo +package foo:foo; interface floats { - float32-param: func(x: float32) - float64-param: func(x: float64) - float32-result: func() -> float32 - float64-result: func() -> float64 + float32-param: func(x: float32); + float64-param: func(x: float64); + float32-result: func() -> float32; + float64-result: func() -> float64; } world the-world { - import floats - export floats + import floats; + export floats; } diff --git a/crates/component-macro/tests/codegen/function-new.wit b/crates/component-macro/tests/codegen/function-new.wit index 2f4e407566cd..cba7fe65d1d6 100644 --- a/crates/component-macro/tests/codegen/function-new.wit +++ b/crates/component-macro/tests/codegen/function-new.wit @@ -1,4 +1,4 @@ -package foo:foo +package foo:foo; world foo { - export new: func() + export new: func(); } diff --git a/crates/component-macro/tests/codegen/integers.wit b/crates/component-macro/tests/codegen/integers.wit index 2f5866bb2ae6..ad96a59d2c38 100644 --- a/crates/component-macro/tests/codegen/integers.wit +++ b/crates/component-macro/tests/codegen/integers.wit @@ -1,14 +1,14 @@ -package foo:foo +package foo:foo; interface integers { - a1: func(x: u8) - a2: func(x: s8) - a3: func(x: u16) - a4: func(x: s16) - a5: func(x: u32) - a6: func(x: s32) - a7: func(x: u64) - a8: func(x: s64) + a1: func(x: u8); + a2: func(x: s8); + a3: func(x: u16); + a4: func(x: s16); + a5: func(x: u32); + a6: func(x: s32); + a7: func(x: u64); + a8: func(x: s64); a9: func( p1: u8, @@ -19,22 +19,22 @@ interface integers { p6: s32, p7: u64, p8: s64, - ) + ); - r1: func() -> u8 - r2: func() -> s8 - r3: func() -> u16 - r4: func() -> s16 - r5: func() -> u32 - r6: func() -> s32 - r7: func() -> u64 - r8: func() -> s64 + r1: func() -> u8; + r2: func() -> s8; + r3: func() -> u16; + r4: func() -> s16; + r5: func() -> u32; + r6: func() -> s32; + r7: func() -> u64; + r8: func() -> s64; - pair-ret: func() -> tuple + pair-ret: func() -> tuple; } world the-world { - import integers - export integers + import integers; + export integers; } diff --git a/crates/component-macro/tests/codegen/lists.wit b/crates/component-macro/tests/codegen/lists.wit index 256a1cdf8fcf..a6d323fcb5c7 100644 --- a/crates/component-macro/tests/codegen/lists.wit +++ b/crates/component-macro/tests/codegen/lists.wit @@ -1,33 +1,33 @@ -package foo:foo +package foo:foo; interface lists { - list-u8-param: func(x: list) - list-u16-param: func(x: list) - list-u32-param: func(x: list) - list-u64-param: func(x: list) - list-s8-param: func(x: list) - list-s16-param: func(x: list) - list-s32-param: func(x: list) - list-s64-param: func(x: list) - list-float32-param: func(x: list) - list-float64-param: func(x: list) + list-u8-param: func(x: list); + list-u16-param: func(x: list); + list-u32-param: func(x: list); + list-u64-param: func(x: list); + list-s8-param: func(x: list); + list-s16-param: func(x: list); + list-s32-param: func(x: list); + list-s64-param: func(x: list); + list-float32-param: func(x: list); + list-float64-param: func(x: list); - list-u8-ret: func() -> list - list-u16-ret: func() -> list - list-u32-ret: func() -> list - list-u64-ret: func() -> list - list-s8-ret: func() -> list - list-s16-ret: func() -> list - list-s32-ret: func() -> list - list-s64-ret: func() -> list - list-float32-ret: func() -> list - list-float64-ret: func() -> list + list-u8-ret: func() -> list; + list-u16-ret: func() -> list; + list-u32-ret: func() -> list; + list-u64-ret: func() -> list; + list-s8-ret: func() -> list; + list-s16-ret: func() -> list; + list-s32-ret: func() -> list; + list-s64-ret: func() -> list; + list-float32-ret: func() -> list; + list-float64-ret: func() -> list; - tuple-list: func(x: list>) -> list> - string-list-arg: func(a: list) - string-list-ret: func() -> list - tuple-string-list: func(x: list>) -> list> - string-list: func(x: list) -> list + tuple-list: func(x: list>) -> list>; + string-list-arg: func(a: list); + string-list-ret: func() -> list; + tuple-string-list: func(x: list>) -> list>; + string-list: func(x: list) -> list; record some-record { x: string, @@ -46,8 +46,8 @@ interface lists { b: string, c: list, } - record-list: func(x: list) -> list - record-list-reverse: func(x: list) -> list + record-list: func(x: list) -> list; + record-list-reverse: func(x: list) -> list; variant some-variant { a(string), @@ -60,7 +60,7 @@ interface lists { b(u32), c(string), } - variant-list: func(x: list) -> list + variant-list: func(x: list) -> list; type load-store-all-sizes = list> - load-store-everything: func(a: load-store-all-sizes) -> load-store-all-sizes + >>; + load-store-everything: func(a: load-store-all-sizes) -> load-store-all-sizes; } world the-lists { - import lists - export lists + import lists; + export lists; } diff --git a/crates/component-macro/tests/codegen/many-arguments.wit b/crates/component-macro/tests/codegen/many-arguments.wit index cbfc123144fa..234b74b2022e 100644 --- a/crates/component-macro/tests/codegen/many-arguments.wit +++ b/crates/component-macro/tests/codegen/many-arguments.wit @@ -1,4 +1,4 @@ -package foo:foo +package foo:foo; interface manyarg { many-args: func( @@ -18,7 +18,7 @@ interface manyarg { a14: u64, a15: u64, a16: u64, - ) + ); record big-struct { a1: string, @@ -43,10 +43,10 @@ interface manyarg { a20: string, } - big-argument: func(x: big-struct) + big-argument: func(x: big-struct); } world the-world { - import manyarg - export manyarg + import manyarg; + export manyarg; } diff --git a/crates/component-macro/tests/codegen/multi-return.wit b/crates/component-macro/tests/codegen/multi-return.wit index 69951b2c3393..3076ccf758c9 100644 --- a/crates/component-macro/tests/codegen/multi-return.wit +++ b/crates/component-macro/tests/codegen/multi-return.wit @@ -1,14 +1,14 @@ -package foo:foo +package foo:foo; interface multi-return { - mra: func() - mrb: func() -> () - mrc: func() -> u32 - mrd: func() -> (a: u32) - mre: func() -> (a: u32, b: float32) + mra: func(); + mrb: func() -> (); + mrc: func() -> u32; + mrd: func() -> (a: u32); + mre: func() -> (a: u32, b: float32); } world the-world { - import multi-return - export multi-return + import multi-return; + export multi-return; } diff --git a/crates/component-macro/tests/codegen/records.wit b/crates/component-macro/tests/codegen/records.wit index f51bb8f28f75..e47af0cbf61e 100644 --- a/crates/component-macro/tests/codegen/records.wit +++ b/crates/component-macro/tests/codegen/records.wit @@ -1,13 +1,13 @@ -package foo:foo +package foo:foo; interface records { - tuple-arg: func(x: tuple) - tuple-result: func() -> tuple + tuple-arg: func(x: tuple); + tuple-result: func() -> tuple; record empty {} - empty-arg: func(x: empty) - empty-result: func() -> empty + empty-arg: func(x: empty); + empty-result: func() -> empty; /// A record containing two scalar fields /// that both have the same type @@ -18,8 +18,8 @@ interface records { b: u32, } - scalar-arg: func(x: scalars) - scalar-result: func() -> scalars + scalar-arg: func(x: scalars); + scalar-result: func() -> scalars; /// A record that is really just flags /// All of the fields are bool @@ -35,8 +35,8 @@ interface records { i: bool, } - flags-arg: func(x: really-flags) - flags-result: func() -> really-flags + flags-arg: func(x: really-flags); + flags-result: func() -> really-flags; record aggregates { a: scalars, @@ -46,16 +46,16 @@ interface records { e: really-flags, } - aggregate-arg: func(x: aggregates) - aggregate-result: func() -> aggregates + aggregate-arg: func(x: aggregates); + aggregate-result: func() -> aggregates; - type tuple-typedef = tuple - type int-typedef = s32 - type tuple-typedef2 = tuple - typedef-inout: func(e: tuple-typedef2) -> s32 + type tuple-typedef = tuple; + type int-typedef = s32; + type tuple-typedef2 = tuple; + typedef-inout: func(e: tuple-typedef2) -> s32; } world the-world { - import records - export records + import records; + export records; } diff --git a/crates/component-macro/tests/codegen/rename.wit b/crates/component-macro/tests/codegen/rename.wit index 8bfc7f36862b..0b6dfee5a7ca 100644 --- a/crates/component-macro/tests/codegen/rename.wit +++ b/crates/component-macro/tests/codegen/rename.wit @@ -1,16 +1,16 @@ -package foo:foo +package foo:foo; interface red { - use green.{thing} + use green.{thing}; - foo: func() -> thing + foo: func() -> thing; } interface green { - type thing = s32 + type thing = s32; } world neptune { - import red - import green + import red; + import green; } diff --git a/crates/component-macro/tests/codegen/resources-export.wit b/crates/component-macro/tests/codegen/resources-export.wit index f0e943671c16..da0027991c79 100644 --- a/crates/component-macro/tests/codegen/resources-export.wit +++ b/crates/component-macro/tests/codegen/resources-export.wit @@ -1,43 +1,43 @@ -package foo:foo +package foo:foo; world w { - export simple-export - export export-using-import + export simple-export; + export export-using-import; - export export-using-export1 - export export-using-export2 + export export-using-export1; + export export-using-export2; } interface simple-export { resource a { - constructor() - static-a: static func() -> u32 - method-a: func() -> u32 + constructor(); + static-a: static func() -> u32; + method-a: func() -> u32; } } interface export-using-import { - use transitive-import.{y} + use transitive-import.{y}; resource a { - constructor(y: y) - static-a: static func() -> y - method-a: func(y: y) -> y + constructor(y: y); + static-a: static func() -> y; + method-a: func(y: y) -> y; } } interface transitive-import { - resource y + resource y; } interface export-using-export1 { resource a { - constructor() + constructor(); } } interface export-using-export2 { - use export-using-export1.{a} + use export-using-export1.{a}; resource b { - constructor(a: a) + constructor(a: a); } } diff --git a/crates/component-macro/tests/codegen/resources-import.wit b/crates/component-macro/tests/codegen/resources-import.wit index bfe0c40deed3..d45916436b4f 100644 --- a/crates/component-macro/tests/codegen/resources-import.wit +++ b/crates/component-macro/tests/codegen/resources-import.wit @@ -1,31 +1,31 @@ -package foo:foo +package foo:foo; interface resources { resource bar { - constructor() - static-a: static func() -> u32 - method-a: func() -> u32 + constructor(); + static-a: static func() -> u32; + method-a: func() -> u32; } - bar-own-arg: func(x: own) - bar-borrow-arg: func(x: borrow) - bar-result: func() -> own + bar-own-arg: func(x: own); + bar-borrow-arg: func(x: borrow); + bar-result: func() -> own; - tuple-own-arg: func(x: tuple, u32>) - tuple-borrow-arg: func(x: tuple, u32>) - tuple-result: func() -> tuple, u32> + tuple-own-arg: func(x: tuple, u32>); + tuple-borrow-arg: func(x: tuple, u32>); + tuple-result: func() -> tuple, u32>; - option-own-arg: func(x: option>) - option-borrow-arg: func(x: option>) - option-result: func() -> option> + option-own-arg: func(x: option>); + option-borrow-arg: func(x: option>); + option-result: func() -> option>; - result-own-arg: func(x: result>) - result-borrow-arg: func(x: result>) - result-result: func() -> result> + result-own-arg: func(x: result>); + result-borrow-arg: func(x: result>); + result-result: func() -> result>; - list-own-arg: func(x: list>) - list-borrow-arg: func(x: list>) - list-result: func() -> list> + list-own-arg: func(x: list>); + list-borrow-arg: func(x: list>); + list-result: func() -> list>; record nested-own { nested-bar: own @@ -35,53 +35,53 @@ interface resources { nested-bar: borrow } - record-own-arg: func(x: nested-own) - record-borrow-arg: func(x: nested-borrow) - record-result: func() -> nested-own + record-own-arg: func(x: nested-own); + record-borrow-arg: func(x: nested-borrow); + record-result: func() -> nested-own; - type some-handle = borrow - func-with-handle-typedef: func(x: some-handle) + type some-handle = borrow; + func-with-handle-typedef: func(x: some-handle); } world the-world { - import resources + import resources; resource world-resource { - constructor() + constructor(); - foo: func() - static-foo: static func() + foo: func(); + static-foo: static func(); } - import some-world-func: func() -> world-resource - export some-world-func2: func() -> world-resource + import some-world-func: func() -> world-resource; + export some-world-func2: func() -> world-resource; - export uses-resource-transitively + export uses-resource-transitively; - import long-use-chain4 + import long-use-chain4; } interface transitive-interface-with-resource { - resource foo + resource foo; } interface uses-resource-transitively { - use transitive-interface-with-resource.{foo} + use transitive-interface-with-resource.{foo}; - handle: func(x: foo) + handle: func(x: foo); } interface long-use-chain4 { - use long-use-chain3.{a} + use long-use-chain3.{a}; - foo: func() -> a + foo: func() -> a; } interface long-use-chain3 { - use long-use-chain2.{a} + use long-use-chain2.{a}; } interface long-use-chain2 { - use long-use-chain1.{a} + use long-use-chain1.{a}; } interface long-use-chain1 { - resource a + resource a; } diff --git a/crates/component-macro/tests/codegen/share-types.wit b/crates/component-macro/tests/codegen/share-types.wit index 8d36d1b0aa88..85d2c03712f2 100644 --- a/crates/component-macro/tests/codegen/share-types.wit +++ b/crates/component-macro/tests/codegen/share-types.wit @@ -1,4 +1,4 @@ -package foo:foo +package foo:foo; interface http-types{ record request { @@ -11,11 +11,11 @@ interface http-types{ world http-interface { export http-handler: interface { - use http-types.{request,response} - handle-request: func(request: request) -> response + use http-types.{request,response}; + handle-request: func(request: request) -> response; } import http-fetch: interface { - use http-types.{request,response} - fetch-request: func(request: request) -> response + use http-types.{request,response}; + fetch-request: func(request: request) -> response; } } diff --git a/crates/component-macro/tests/codegen/simple-functions.wit b/crates/component-macro/tests/codegen/simple-functions.wit index 2593d199e565..199fc522fb2d 100644 --- a/crates/component-macro/tests/codegen/simple-functions.wit +++ b/crates/component-macro/tests/codegen/simple-functions.wit @@ -1,17 +1,17 @@ -package foo:foo +package foo:foo; interface simple { - f1: func() - f2: func(a: u32) - f3: func(a: u32, b: u32) + f1: func(); + f2: func(a: u32); + f3: func(a: u32, b: u32); - f4: func() -> u32 - f5: func() -> tuple + f4: func() -> u32; + f5: func() -> tuple; - f6: func(a: u32, b: u32, c: u32) -> tuple + f6: func(a: u32, b: u32, c: u32) -> tuple; } world the-world { - import simple - export simple + import simple; + export simple; } diff --git a/crates/component-macro/tests/codegen/simple-lists.wit b/crates/component-macro/tests/codegen/simple-lists.wit index 8e082b6e34e7..f814cee85ad2 100644 --- a/crates/component-macro/tests/codegen/simple-lists.wit +++ b/crates/component-macro/tests/codegen/simple-lists.wit @@ -1,13 +1,13 @@ -package foo:foo +package foo:foo; interface simple-lists { - simple-list1: func(l: list) - simple-list2: func() -> list - simple-list3: func(a: list, b: list) -> tuple, list> - simple-list4: func(l: list>) -> list> + simple-list1: func(l: list); + simple-list2: func() -> list; + simple-list3: func(a: list, b: list) -> tuple, list>; + simple-list4: func(l: list>) -> list>; } world my-world { - import simple-lists - export simple-lists + import simple-lists; + export simple-lists; } diff --git a/crates/component-macro/tests/codegen/simple-wasi.wit b/crates/component-macro/tests/codegen/simple-wasi.wit index 60192473d99e..2ab07d32d837 100644 --- a/crates/component-macro/tests/codegen/simple-wasi.wit +++ b/crates/component-macro/tests/codegen/simple-wasi.wit @@ -1,4 +1,4 @@ -package foo:foo +package foo:foo; interface wasi-filesystem { record descriptor-stat { @@ -7,9 +7,9 @@ interface wasi-filesystem { enum errno { e } - create-directory-at: func() -> result<_, errno> + create-directory-at: func() -> result<_, errno>; - stat: func() -> result + stat: func() -> result; } interface wall-clock { @@ -18,6 +18,6 @@ interface wall-clock { } world wasi { - import wasi-filesystem - import wall-clock + import wasi-filesystem; + import wall-clock; } diff --git a/crates/component-macro/tests/codegen/small-anonymous.wit b/crates/component-macro/tests/codegen/small-anonymous.wit index ad9c67287d4e..3a314fbfe4e9 100644 --- a/crates/component-macro/tests/codegen/small-anonymous.wit +++ b/crates/component-macro/tests/codegen/small-anonymous.wit @@ -1,4 +1,4 @@ -package foo:foo +package foo:foo; interface anon { enum error { @@ -6,10 +6,10 @@ interface anon { failure, } - option-test: func() -> result, error> + option-test: func() -> result, error>; } world the-world { - import anon - export anon + import anon; + export anon; } diff --git a/crates/component-macro/tests/codegen/smoke-default.wit b/crates/component-macro/tests/codegen/smoke-default.wit index 90be0b1e3cdc..6206a7c8e131 100644 --- a/crates/component-macro/tests/codegen/smoke-default.wit +++ b/crates/component-macro/tests/codegen/smoke-default.wit @@ -1,5 +1,5 @@ -package foo:foo +package foo:foo; world the-world { - export y: func() + export y: func(); } diff --git a/crates/component-macro/tests/codegen/smoke-export.wit b/crates/component-macro/tests/codegen/smoke-export.wit index 983f1c35066e..a8b7b15e70e6 100644 --- a/crates/component-macro/tests/codegen/smoke-export.wit +++ b/crates/component-macro/tests/codegen/smoke-export.wit @@ -1,7 +1,7 @@ -package foo:foo +package foo:foo; world the-world { export the-name: interface { - y: func() + y: func(); } } diff --git a/crates/component-macro/tests/codegen/smoke.wit b/crates/component-macro/tests/codegen/smoke.wit index 0476bd988ec1..14502341f7ca 100644 --- a/crates/component-macro/tests/codegen/smoke.wit +++ b/crates/component-macro/tests/codegen/smoke.wit @@ -1,7 +1,7 @@ -package foo:foo +package foo:foo; world the-world { import imports: interface { - y: func() + y: func(); } } diff --git a/crates/component-macro/tests/codegen/strings.wit b/crates/component-macro/tests/codegen/strings.wit index 650300acfb9d..6d45a9845bce 100644 --- a/crates/component-macro/tests/codegen/strings.wit +++ b/crates/component-macro/tests/codegen/strings.wit @@ -1,12 +1,12 @@ -package foo:foo +package foo:foo; interface strings { - a: func(x: string) - b: func() -> string - c: func(a: string, b: string) -> string + a: func(x: string); + b: func() -> string; + c: func(a: string, b: string) -> string; } world the-world { - import strings - export strings + import strings; + export strings; } diff --git a/crates/component-macro/tests/codegen/use-paths.wit b/crates/component-macro/tests/codegen/use-paths.wit index dab6646afac8..3a16b375d34c 100644 --- a/crates/component-macro/tests/codegen/use-paths.wit +++ b/crates/component-macro/tests/codegen/use-paths.wit @@ -1,29 +1,29 @@ -package foo:foo +package foo:foo; interface a { record foo {} - a: func() -> foo + a: func() -> foo; } interface b { - use a.{foo} + use a.{foo}; - a: func() -> foo + a: func() -> foo; } interface c { - use b.{foo} + use b.{foo}; - a: func() -> foo + a: func() -> foo; } world d { - import a - import b + import a; + import b; import d: interface { - use c.{foo} + use c.{foo}; - b: func() -> foo + b: func() -> foo; } } diff --git a/crates/component-macro/tests/codegen/variants.wit b/crates/component-macro/tests/codegen/variants.wit index a53938cb02ef..8ba28d40ebdf 100644 --- a/crates/component-macro/tests/codegen/variants.wit +++ b/crates/component-macro/tests/codegen/variants.wit @@ -1,12 +1,12 @@ -package foo:foo +package foo:foo; interface variants { enum e1 { a, } - e1-arg: func(x: e1) - e1-result: func() -> e1 + e1-arg: func(x: e1); + e1-result: func() -> e1; record empty {} @@ -19,11 +19,11 @@ interface variants { g(u32), } - v1-arg: func(x: v1) - v1-result: func() -> v1 + v1-arg: func(x: v1); + v1-result: func() -> v1; - bool-arg: func(x: bool) - bool-result: func() -> bool + bool-arg: func(x: bool); + bool-result: func() -> bool; option-arg: func( a: option, @@ -32,7 +32,7 @@ interface variants { d: option, e: option, g: option>, - ) + ); option-result: func() -> tuple< option, option>, @@ -40,7 +40,7 @@ interface variants { option, option, option>, - > + >; variant casts1 { a(s32), @@ -86,7 +86,7 @@ interface variants { casts4, casts5, casts6, - > + >; result-arg: func( a: result, @@ -95,7 +95,7 @@ interface variants { d: result, tuple<>>, e: result, f: result>, - ) + ); result-result: func() -> tuple< result, result<_, e1>, @@ -103,34 +103,34 @@ interface variants { result, tuple<>>, result, result>, - > + >; enum my-errno { bad1, bad2, } - return-result-sugar: func() -> result - return-result-sugar2: func() -> result<_, my-errno> - return-result-sugar3: func() -> result - return-result-sugar4: func() -> result, my-errno> - return-option-sugar: func() -> option - return-option-sugar2: func() -> option + return-result-sugar: func() -> result; + return-result-sugar2: func() -> result<_, my-errno>; + return-result-sugar3: func() -> result; + return-result-sugar4: func() -> result, my-errno>; + return-option-sugar: func() -> option; + return-option-sugar2: func() -> option; - result-simple: func() -> result + result-simple: func() -> result; record is-clone { v1: v1, } - is-clone-arg: func(a: is-clone) - is-clone-return: func() -> is-clone + is-clone-arg: func(a: is-clone); + is-clone-return: func() -> is-clone; - return-named-option: func() -> (a: option) - return-named-result: func() -> (a: result) + return-named-option: func() -> (a: option); + return-named-result: func() -> (a: result); } world my-world { - import variants - export variants + import variants; + export variants; } diff --git a/crates/component-macro/tests/codegen/worlds-with-types.wit b/crates/component-macro/tests/codegen/worlds-with-types.wit index dc4cbddcbabe..149e392dfc7a 100644 --- a/crates/component-macro/tests/codegen/worlds-with-types.wit +++ b/crates/component-macro/tests/codegen/worlds-with-types.wit @@ -1,16 +1,16 @@ -package foo:foo +package foo:foo; interface i { - type t = u16 + type t = u16; } world foo { - use i.{t as u} + use i.{t as u}; - type t = u32 + type t = u32; record r { } - export f: func() -> tuple + export f: func() -> tuple; } diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs index 2fac15be2ca2..a45b45477c64 100644 --- a/crates/wasi-http/src/lib.rs +++ b/crates/wasi-http/src/lib.rs @@ -10,10 +10,10 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", interfaces: " - import wasi:http/incoming-handler - import wasi:http/outgoing-handler - import wasi:http/types - ", + import wasi:http/incoming-handler; + import wasi:http/outgoing-handler; + import wasi:http/types; + ", tracing: true, async: false, with: { diff --git a/crates/wasi-http/wit/command-extended.wit b/crates/wasi-http/wit/command-extended.wit index 3c56808e4abe..b4d08aed3f3b 100644 --- a/crates/wasi-http/wit/command-extended.wit +++ b/crates/wasi-http/wit/command-extended.wit @@ -1,37 +1,37 @@ // All of the same imports and exports available in the wasi:cli/command world // with addition of HTTP proxy related imports: world command-extended { - import wasi:clocks/wall-clock - import wasi:clocks/monotonic-clock - import wasi:clocks/timezone - import wasi:filesystem/types - import wasi:filesystem/preopens - import wasi:sockets/instance-network - import wasi:sockets/ip-name-lookup - import wasi:sockets/network - import wasi:sockets/tcp-create-socket - import wasi:sockets/tcp - import wasi:sockets/udp-create-socket - import wasi:sockets/udp - import wasi:random/random - import wasi:random/insecure - import wasi:random/insecure-seed - import wasi:io/poll - import wasi:io/streams - import wasi:cli/environment - import wasi:cli/exit - import wasi:cli/stdin - import wasi:cli/stdout - import wasi:cli/stderr - import wasi:cli/terminal-input - import wasi:cli/terminal-output - import wasi:cli/terminal-stdin - import wasi:cli/terminal-stdout - import wasi:cli/terminal-stderr + import wasi:clocks/wall-clock; + import wasi:clocks/monotonic-clock; + import wasi:clocks/timezone; + import wasi:filesystem/types; + import wasi:filesystem/preopens; + import wasi:sockets/instance-network; + import wasi:sockets/ip-name-lookup; + import wasi:sockets/network; + import wasi:sockets/tcp-create-socket; + import wasi:sockets/tcp; + import wasi:sockets/udp-create-socket; + import wasi:sockets/udp; + import wasi:random/random; + import wasi:random/insecure; + import wasi:random/insecure-seed; + import wasi:io/poll; + import wasi:io/streams; + import wasi:cli/environment; + import wasi:cli/exit; + import wasi:cli/stdin; + import wasi:cli/stdout; + import wasi:cli/stderr; + import wasi:cli/terminal-input; + import wasi:cli/terminal-output; + import wasi:cli/terminal-stdin; + import wasi:cli/terminal-stdout; + import wasi:cli/terminal-stderr; // We should replace all others with `include self.command` // as soon as the unioning of worlds is available: // https://github.com/WebAssembly/component-model/issues/169 - import wasi:logging/logging - import wasi:http/outgoing-handler + import wasi:logging/logging; + import wasi:http/outgoing-handler; } diff --git a/crates/wasi-http/wit/deps/cli/command.wit b/crates/wasi-http/wit/deps/cli/command.wit index d28f5f6282a4..86c9c73b7634 100644 --- a/crates/wasi-http/wit/deps/cli/command.wit +++ b/crates/wasi-http/wit/deps/cli/command.wit @@ -1,7 +1,7 @@ -package wasi:cli +package wasi:cli; world command { - include reactor + include reactor; - export run + export run; } diff --git a/crates/wasi-http/wit/deps/cli/environment.wit b/crates/wasi-http/wit/deps/cli/environment.wit index 36790fe714d2..70065233e81b 100644 --- a/crates/wasi-http/wit/deps/cli/environment.wit +++ b/crates/wasi-http/wit/deps/cli/environment.wit @@ -7,12 +7,12 @@ interface environment { /// Morally, these are a value import, but until value imports are available /// in the component model, this import function should return the same /// values each time it is called. - get-environment: func() -> list> + get-environment: func() -> list>; /// Get the POSIX-style arguments to the program. - get-arguments: func() -> list + get-arguments: func() -> list; /// Return a path that programs should use as their initial current working /// directory, interpreting `.` as shorthand for this. - initial-cwd: func() -> option + initial-cwd: func() -> option; } diff --git a/crates/wasi-http/wit/deps/cli/exit.wit b/crates/wasi-http/wit/deps/cli/exit.wit index 4831d50789af..d0c2b82ae2c7 100644 --- a/crates/wasi-http/wit/deps/cli/exit.wit +++ b/crates/wasi-http/wit/deps/cli/exit.wit @@ -1,4 +1,4 @@ interface exit { /// Exit the current instance and any linked instances. - exit: func(status: result) + exit: func(status: result); } diff --git a/crates/wasi-http/wit/deps/cli/reactor.wit b/crates/wasi-http/wit/deps/cli/reactor.wit index 274d0644dc41..6bd780e76649 100644 --- a/crates/wasi-http/wit/deps/cli/reactor.wit +++ b/crates/wasi-http/wit/deps/cli/reactor.wit @@ -1,33 +1,32 @@ -package wasi:cli +package wasi:cli; world reactor { - import wasi:clocks/wall-clock - import wasi:clocks/monotonic-clock - import wasi:clocks/timezone - import wasi:filesystem/types - import wasi:filesystem/preopens - import wasi:sockets/instance-network - import wasi:sockets/ip-name-lookup - import wasi:sockets/network - import wasi:sockets/tcp-create-socket - import wasi:sockets/tcp - import wasi:sockets/udp-create-socket - import wasi:sockets/udp - import wasi:random/random - import wasi:random/insecure - import wasi:random/insecure-seed - import wasi:io/poll - import wasi:io/streams + import wasi:clocks/wall-clock; + import wasi:clocks/monotonic-clock; + import wasi:clocks/timezone; + import wasi:filesystem/types; + import wasi:filesystem/preopens; + import wasi:sockets/instance-network; + import wasi:sockets/ip-name-lookup; + import wasi:sockets/network; + import wasi:sockets/tcp-create-socket; + import wasi:sockets/tcp; + import wasi:sockets/udp-create-socket; + import wasi:sockets/udp; + import wasi:random/random; + import wasi:random/insecure; + import wasi:random/insecure-seed; + import wasi:io/poll; + import wasi:io/streams; - import environment - import exit - import stdin - import stdout - import stderr - import terminal-input - import terminal-output - import terminal-stdin - import terminal-stdout - import terminal-stderr + import environment; + import exit; + import stdin; + import stdout; + import stderr; + import terminal-input; + import terminal-output; + import terminal-stdin; + import terminal-stdout; + import terminal-stderr; } - diff --git a/crates/wasi-http/wit/deps/cli/run.wit b/crates/wasi-http/wit/deps/cli/run.wit index 45a1ca533f0e..a70ee8c038f2 100644 --- a/crates/wasi-http/wit/deps/cli/run.wit +++ b/crates/wasi-http/wit/deps/cli/run.wit @@ -1,4 +1,4 @@ interface run { /// Run the program. - run: func() -> result + run: func() -> result; } diff --git a/crates/wasi-http/wit/deps/cli/stdio.wit b/crates/wasi-http/wit/deps/cli/stdio.wit index 6c9d4a41a6f9..1bb6c55837c2 100644 --- a/crates/wasi-http/wit/deps/cli/stdio.wit +++ b/crates/wasi-http/wit/deps/cli/stdio.wit @@ -1,17 +1,17 @@ interface stdin { - use wasi:io/streams.{input-stream} + use wasi:io/streams.{input-stream}; - get-stdin: func() -> input-stream + get-stdin: func() -> input-stream; } interface stdout { - use wasi:io/streams.{output-stream} + use wasi:io/streams.{output-stream}; - get-stdout: func() -> output-stream + get-stdout: func() -> output-stream; } interface stderr { - use wasi:io/streams.{output-stream} + use wasi:io/streams.{output-stream}; - get-stderr: func() -> output-stream + get-stderr: func() -> output-stream; } diff --git a/crates/wasi-http/wit/deps/cli/terminal.wit b/crates/wasi-http/wit/deps/cli/terminal.wit index b0a5bec2a2c7..47495769b31f 100644 --- a/crates/wasi-http/wit/deps/cli/terminal.wit +++ b/crates/wasi-http/wit/deps/cli/terminal.wit @@ -1,6 +1,6 @@ interface terminal-input { /// The input side of a terminal. - resource terminal-input + resource terminal-input; // In the future, this may include functions for disabling echoing, // disabling input buffering so that keyboard events are sent through @@ -9,7 +9,7 @@ interface terminal-input { interface terminal-output { /// The output side of a terminal. - resource terminal-output + resource terminal-output; // In the future, this may include functions for querying the terminal // size, being notified of terminal size changes, querying supported @@ -19,29 +19,29 @@ interface terminal-output { /// An interface providing an optional `terminal-input` for stdin as a /// link-time authority. interface terminal-stdin { - use terminal-input.{terminal-input} + use terminal-input.{terminal-input}; /// If stdin is connected to a terminal, return a `terminal-input` handle /// allowing further interaction with it. - get-terminal-stdin: func() -> option + get-terminal-stdin: func() -> option; } /// An interface providing an optional `terminal-output` for stdout as a /// link-time authority. interface terminal-stdout { - use terminal-output.{terminal-output} + use terminal-output.{terminal-output}; /// If stdout is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - get-terminal-stdout: func() -> option + get-terminal-stdout: func() -> option; } /// An interface providing an optional `terminal-output` for stderr as a /// link-time authority. interface terminal-stderr { - use terminal-output.{terminal-output} + use terminal-output.{terminal-output}; /// If stderr is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - get-terminal-stderr: func() -> option + get-terminal-stderr: func() -> option; } diff --git a/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit b/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit index 703a5fb7a503..d9ac7cb3fb6b 100644 --- a/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit +++ b/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit @@ -9,24 +9,24 @@ /// /// It is intended for measuring elapsed time. interface monotonic-clock { - use wasi:io/poll.{pollable} + use wasi:io/poll.{pollable}; /// A timestamp in nanoseconds. - type instant = u64 + type instant = u64; /// Read the current value of the clock. /// /// The clock is monotonic, therefore calling this function repeatedly will /// produce a sequence of non-decreasing values. - now: func() -> instant + now: func() -> instant; /// Query the resolution of the clock. - resolution: func() -> instant + resolution: func() -> instant; /// Create a `pollable` which will resolve once the specified time has been /// reached. subscribe: func( when: instant, absolute: bool - ) -> pollable + ) -> pollable; } diff --git a/crates/wasi-http/wit/deps/clocks/timezone.wit b/crates/wasi-http/wit/deps/clocks/timezone.wit index a872bffc7414..e717e7b8dda9 100644 --- a/crates/wasi-http/wit/deps/clocks/timezone.wit +++ b/crates/wasi-http/wit/deps/clocks/timezone.wit @@ -1,5 +1,5 @@ interface timezone { - use wall-clock.{datetime} + use wall-clock.{datetime}; /// Return information needed to display the given `datetime`. This includes /// the UTC offset, the time zone name, and a flag indicating whether @@ -8,10 +8,10 @@ interface timezone { /// If the timezone cannot be determined for the given `datetime`, return a /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight /// saving time. - display: func(when: datetime) -> timezone-display + display: func(when: datetime) -> timezone-display; /// The same as `display`, but only return the UTC offset. - utc-offset: func(when: datetime) -> s32 + utc-offset: func(when: datetime) -> s32; /// Information useful for displaying the timezone of a specific `datetime`. /// diff --git a/crates/wasi-http/wit/deps/clocks/wall-clock.wit b/crates/wasi-http/wit/deps/clocks/wall-clock.wit index dae44a7308cd..c39564967a7e 100644 --- a/crates/wasi-http/wit/deps/clocks/wall-clock.wit +++ b/crates/wasi-http/wit/deps/clocks/wall-clock.wit @@ -32,10 +32,10 @@ interface wall-clock { /// /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time - now: func() -> datetime + now: func() -> datetime; /// Query the resolution of the clock. /// /// The nanoseconds field of the output is always less than 1000000000. - resolution: func() -> datetime + resolution: func() -> datetime; } diff --git a/crates/wasi-http/wit/deps/clocks/world.wit b/crates/wasi-http/wit/deps/clocks/world.wit index 5c2dd411d2d0..3295ba8d92ab 100644 --- a/crates/wasi-http/wit/deps/clocks/world.wit +++ b/crates/wasi-http/wit/deps/clocks/world.wit @@ -1,7 +1,7 @@ -package wasi:clocks +package wasi:clocks; world imports { - import monotonic-clock - import wall-clock - import timezone + import monotonic-clock; + import wall-clock; + import timezone; } diff --git a/crates/wasi-http/wit/deps/filesystem/preopens.wit b/crates/wasi-http/wit/deps/filesystem/preopens.wit index f45661b8a849..3f787ac3fead 100644 --- a/crates/wasi-http/wit/deps/filesystem/preopens.wit +++ b/crates/wasi-http/wit/deps/filesystem/preopens.wit @@ -1,6 +1,6 @@ interface preopens { - use types.{descriptor} + use types.{descriptor}; /// Return the set of preopened directories, and their path. - get-directories: func() -> list> + get-directories: func() -> list>; } diff --git a/crates/wasi-http/wit/deps/filesystem/types.wit b/crates/wasi-http/wit/deps/filesystem/types.wit index aecdd0ef354b..7efb7e5b3208 100644 --- a/crates/wasi-http/wit/deps/filesystem/types.wit +++ b/crates/wasi-http/wit/deps/filesystem/types.wit @@ -23,11 +23,11 @@ /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md interface types { - use wasi:io/streams.{input-stream, output-stream, error} - use wasi:clocks/wall-clock.{datetime} + use wasi:io/streams.{input-stream, output-stream, error}; + use wasi:clocks/wall-clock.{datetime}; /// File size or length of a region within a file. - type filesize = u64 + type filesize = u64; /// The type of a filesystem object referenced by a descriptor. /// @@ -166,7 +166,7 @@ interface types { } /// Number of hard links to an inode. - type link-count = u64 + type link-count = u64; /// When setting a timestamp, this gives the value to set it to. variant new-timestamp { @@ -315,7 +315,7 @@ interface types { read-via-stream: func( /// The offset within the file at which to start reading. offset: filesize, - ) -> result + ) -> result; /// Return a stream for writing to a file, if available. /// @@ -326,7 +326,7 @@ interface types { write-via-stream: func( /// The offset within the file at which to start writing. offset: filesize, - ) -> result + ) -> result; /// Return a stream for appending to a file, if available. /// @@ -334,7 +334,7 @@ interface types { /// /// Note: This allows using `write-stream`, which is similar to `write` with /// `O_APPEND` in in POSIX. - append-via-stream: func() -> result + append-via-stream: func() -> result; /// Provide file advisory information on a descriptor. /// @@ -346,7 +346,7 @@ interface types { length: filesize, /// The advice. advice: advice - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Synchronize the data of a file to disk. /// @@ -354,7 +354,7 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fdatasync` in POSIX. - sync-data: func() -> result<_, error-code> + sync-data: func() -> result<_, error-code>; /// Get flags associated with a descriptor. /// @@ -362,7 +362,7 @@ interface types { /// /// Note: This returns the value that was the `fs_flags` value returned /// from `fdstat_get` in earlier versions of WASI. - get-flags: func() -> result + get-flags: func() -> result; /// Get the dynamic type of a descriptor. /// @@ -374,13 +374,13 @@ interface types { /// /// Note: This returns the value that was the `fs_filetype` value returned /// from `fdstat_get` in earlier versions of WASI. - get-type: func() -> result + get-type: func() -> result; /// Adjust the size of an open file. If this increases the file's size, the /// extra bytes are filled with zeros. /// /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. - set-size: func(size: filesize) -> result<_, error-code> + set-size: func(size: filesize) -> result<_, error-code>; /// Adjust the timestamps of an open file or directory. /// @@ -392,7 +392,7 @@ interface types { data-access-timestamp: new-timestamp, /// The desired values of the data modification timestamp. data-modification-timestamp: new-timestamp, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Read from a descriptor, without using and updating the descriptor's offset. /// @@ -410,7 +410,7 @@ interface types { length: filesize, /// The offset within the file at which to read. offset: filesize, - ) -> result, bool>, error-code> + ) -> result, bool>, error-code>; /// Write to a descriptor, without using and updating the descriptor's offset. /// @@ -426,7 +426,7 @@ interface types { buffer: list, /// The offset within the file at which to write. offset: filesize, - ) -> result + ) -> result; /// Read directory entries from a directory. /// @@ -437,7 +437,7 @@ interface types { /// This always returns a new stream which starts at the beginning of the /// directory. Multiple streams may be active on the same directory, and they /// do not interfere with each other. - read-directory: func() -> result + read-directory: func() -> result; /// Synchronize the data and metadata of a file to disk. /// @@ -445,7 +445,7 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fsync` in POSIX. - sync: func() -> result<_, error-code> + sync: func() -> result<_, error-code>; /// Create a directory. /// @@ -453,7 +453,7 @@ interface types { create-directory-at: func( /// The relative path at which to create the directory. path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Return the attributes of an open file or directory. /// @@ -464,7 +464,7 @@ interface types { /// modified, use `metadata-hash`. /// /// Note: This was called `fd_filestat_get` in earlier versions of WASI. - stat: func() -> result + stat: func() -> result; /// Return the attributes of a file or directory. /// @@ -478,7 +478,7 @@ interface types { path-flags: path-flags, /// The relative path of the file or directory to inspect. path: string, - ) -> result + ) -> result; /// Adjust the timestamps of a file or directory. /// @@ -495,7 +495,7 @@ interface types { data-access-timestamp: new-timestamp, /// The desired values of the data modification timestamp. data-modification-timestamp: new-timestamp, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Create a hard link. /// @@ -509,7 +509,7 @@ interface types { new-descriptor: borrow, /// The relative destination path at which to create the hard link. new-path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Open a file or directory. /// @@ -540,7 +540,7 @@ interface types { %flags: descriptor-flags, /// Permissions to use when creating a new file. modes: modes - ) -> result + ) -> result; /// Read the contents of a symbolic link. /// @@ -551,7 +551,7 @@ interface types { readlink-at: func( /// The relative path of the symbolic link from which to read. path: string, - ) -> result + ) -> result; /// Remove a directory. /// @@ -561,7 +561,7 @@ interface types { remove-directory-at: func( /// The relative path to a directory to remove. path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Rename a filesystem object. /// @@ -573,7 +573,7 @@ interface types { new-descriptor: borrow, /// The relative destination path to which to rename the file or directory. new-path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Create a symbolic link (also known as a "symlink"). /// @@ -586,7 +586,7 @@ interface types { old-path: string, /// The relative destination path at which to create the symbolic link. new-path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Check accessibility of a filesystem path. /// @@ -605,7 +605,7 @@ interface types { path: string, /// The type of check to perform. %type: access-type - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Unlink a filesystem object that is not a directory. /// @@ -614,7 +614,7 @@ interface types { unlink-file-at: func( /// The relative path to a file to unlink. path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Change the permissions of a filesystem object that is not a directory. /// @@ -629,7 +629,7 @@ interface types { path: string, /// The new permissions for the filesystem object. modes: modes, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Change the permissions of a directory. /// @@ -648,7 +648,7 @@ interface types { path: string, /// The new permissions for the directory. modes: modes, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Request a shared advisory lock for an open file. /// @@ -670,7 +670,7 @@ interface types { /// locking, this function returns `error-code::unsupported`. /// /// Note: This is similar to `flock(fd, LOCK_SH)` in Unix. - lock-shared: func() -> result<_, error-code> + lock-shared: func() -> result<_, error-code>; /// Request an exclusive advisory lock for an open file. /// @@ -694,7 +694,7 @@ interface types { /// locking, this function returns `error-code::unsupported`. /// /// Note: This is similar to `flock(fd, LOCK_EX)` in Unix. - lock-exclusive: func() -> result<_, error-code> + lock-exclusive: func() -> result<_, error-code>; /// Request a shared advisory lock for an open file. /// @@ -717,7 +717,7 @@ interface types { /// locking, this function returns `error-code::unsupported`. /// /// Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. - try-lock-shared: func() -> result<_, error-code> + try-lock-shared: func() -> result<_, error-code>; /// Request an exclusive advisory lock for an open file. /// @@ -742,12 +742,12 @@ interface types { /// locking, this function returns `error-code::unsupported`. /// /// Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. - try-lock-exclusive: func() -> result<_, error-code> + try-lock-exclusive: func() -> result<_, error-code>; /// Release a shared or exclusive lock on an open file. /// /// Note: This is similar to `flock(fd, LOCK_UN)` in Unix. - unlock: func() -> result<_, error-code> + unlock: func() -> result<_, error-code>; /// Test whether two descriptors refer to the same filesystem object. /// @@ -755,7 +755,7 @@ interface types { /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. /// wasi-filesystem does not expose device and inode numbers, so this function /// may be used instead. - is-same-object: func(other: borrow) -> bool + is-same-object: func(other: borrow) -> bool; /// Return a hash of the metadata associated with a filesystem object referred /// to by a descriptor. @@ -776,7 +776,7 @@ interface types { /// computed hash. /// /// However, none of these is required. - metadata-hash: func() -> result + metadata-hash: func() -> result; /// Return a hash of the metadata associated with a filesystem object referred /// to by a directory descriptor and a relative path. @@ -787,13 +787,13 @@ interface types { path-flags: path-flags, /// The relative path of the file or directory to inspect. path: string, - ) -> result + ) -> result; } /// A stream of directory entries. resource directory-entry-stream { /// Read a single directory entry from a `directory-entry-stream`. - read-directory-entry: func() -> result, error-code> + read-directory-entry: func() -> result, error-code>; } /// Attempts to extract a filesystem-related `error-code` from the stream diff --git a/crates/wasi-http/wit/deps/filesystem/world.wit b/crates/wasi-http/wit/deps/filesystem/world.wit index 5fa7eafdb850..bd472942da1a 100644 --- a/crates/wasi-http/wit/deps/filesystem/world.wit +++ b/crates/wasi-http/wit/deps/filesystem/world.wit @@ -1,6 +1,6 @@ -package wasi:filesystem +package wasi:filesystem; world imports { - import types - import preopens + import types; + import preopens; } diff --git a/crates/wasi-http/wit/deps/http/incoming-handler.wit b/crates/wasi-http/wit/deps/http/incoming-handler.wit index 70a6a043a0c7..6968d6331f8e 100644 --- a/crates/wasi-http/wit/deps/http/incoming-handler.wit +++ b/crates/wasi-http/wit/deps/http/incoming-handler.wit @@ -7,7 +7,7 @@ // that takes a `request` parameter and returns a `response` result. // interface incoming-handler { - use types.{incoming-request, response-outparam} + use types.{incoming-request, response-outparam}; // The `handle` function takes an outparam instead of returning its response // so that the component may stream its response while streaming any other @@ -20,5 +20,5 @@ interface incoming-handler { handle: func( request: incoming-request, response-out: response-outparam - ) + ); } diff --git a/crates/wasi-http/wit/deps/http/outgoing-handler.wit b/crates/wasi-http/wit/deps/http/outgoing-handler.wit index 9b6a73c0cb9b..286e2833bc8a 100644 --- a/crates/wasi-http/wit/deps/http/outgoing-handler.wit +++ b/crates/wasi-http/wit/deps/http/outgoing-handler.wit @@ -6,7 +6,7 @@ // that takes a `request` parameter and returns a `response` result. // interface outgoing-handler { - use types.{outgoing-request, request-options, future-incoming-response, error} + use types.{outgoing-request, request-options, future-incoming-response, error}; // The parameter and result types of the `handle` function allow the caller // to concurrently stream the bodies of the outgoing request and the incoming @@ -16,5 +16,5 @@ interface outgoing-handler { handle: func( request: outgoing-request, options: option - ) -> result + ) -> result; } diff --git a/crates/wasi-http/wit/deps/http/proxy.wit b/crates/wasi-http/wit/deps/http/proxy.wit index 162ab32b2348..8ee589207999 100644 --- a/crates/wasi-http/wit/deps/http/proxy.wit +++ b/crates/wasi-http/wit/deps/http/proxy.wit @@ -1,4 +1,4 @@ -package wasi:http +package wasi:http; // The `wasi:http/proxy` world captures a widely-implementable intersection of // hosts that includes HTTP forward and reverse proxies. Components targeting @@ -6,29 +6,29 @@ package wasi:http // outgoing HTTP requests. world proxy { // HTTP proxies have access to time and randomness. - import wasi:clocks/wall-clock - import wasi:clocks/monotonic-clock - import wasi:clocks/timezone - import wasi:random/random + import wasi:clocks/wall-clock; + import wasi:clocks/monotonic-clock; + import wasi:clocks/timezone; + import wasi:random/random; // Proxies have standard output and error streams which are expected to // terminate in a developer-facing console provided by the host. - import wasi:cli/stdout - import wasi:cli/stderr + import wasi:cli/stdout; + import wasi:cli/stderr; // TODO: this is a temporary workaround until component tooling is able to // gracefully handle the absence of stdin. Hosts must return an eof stream // for this import, which is what wasi-libc + tooling will do automatically // when this import is properly removed. - import wasi:cli/stdin + import wasi:cli/stdin; // This is the default handler to use when user code simply wants to make an // HTTP request (e.g., via `fetch()`). - import outgoing-handler + import outgoing-handler; // The host delivers incoming HTTP requests to a component by calling the // `handle` function of this exported interface. A host may arbitrarily reuse // or not reuse component instance when delivering incoming HTTP requests and // thus a component must be able to handle 0..N calls to `handle`. - export incoming-handler + export incoming-handler; } diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index edfa2fb8978a..f42d195af634 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -2,8 +2,8 @@ // define the HTTP resource types and operations used by the component's // imported and exported interfaces. interface types { - use wasi:io/streams.{input-stream, output-stream} - use wasi:io/poll.{pollable} + use wasi:io/streams.{input-stream, output-stream}; + use wasi:io/poll.{pollable}; // This type corresponds to HTTP standard Methods. variant method { @@ -43,28 +43,28 @@ interface types { resource fields { // Multiple values for a header are multiple entries in the list with the // same key. - constructor(entries: list>>) + constructor(entries: list>>); // Values off wire are not necessarily well formed, so they are given by // list instead of string. - get: func(name: string) -> list> + get: func(name: string) -> list>; // Values off wire are not necessarily well formed, so they are given by // list instead of string. - set: func(name: string, value: list>) - delete: func(name: string) - append: func(name: string, value: list) + set: func(name: string, value: list>); + delete: func(name: string); + append: func(name: string, value: list); // Values off wire are not necessarily well formed, so they are given by // list instead of string. - entries: func() -> list>> + entries: func() -> list>>; // Deep copy of all contents in a fields. - clone: func() -> fields + clone: func() -> fields; } - type headers = fields - type trailers = fields + type headers = fields; + type trailers = fields; // The following block defines the `incoming-request` and `outgoing-request` // resource types that correspond to HTTP standard Requests. Soon, when @@ -75,19 +75,19 @@ interface types { // above). The `consume` and `write` methods may only be called once (and // return failure thereafter). resource incoming-request { - method: func() -> method + method: func() -> method; - path-with-query: func() -> option + path-with-query: func() -> option; - scheme: func() -> option + scheme: func() -> option; - authority: func() -> option + authority: func() -> option; - headers: func() -> /* child */ headers + headers: func() -> /* child */ headers; // Will return the input-stream child at most once. If called more than // once, subsequent calls will return error. - consume: func() -> result + consume: func() -> result; } resource outgoing-request { @@ -97,11 +97,11 @@ interface types { scheme: option, authority: option, headers: borrow - ) + ); // Will return the outgoing-body child at most once. If called more than // once, subsequent calls will return error. - write: func() -> result< /* child */ outgoing-body> + write: func() -> result< /* child */ outgoing-body>; } // Additional optional parameters that can be set when making a request. @@ -127,11 +127,11 @@ interface types { // (the `wasi:http/handler` interface used for both incoming and outgoing can // simply return a `stream`). resource response-outparam { - set: static func(param: response-outparam, response: result) + set: static func(param: response-outparam, response: result); } // This type corresponds to the HTTP standard Status Code. - type status-code = u16 + type status-code = u16; // The following block defines the `incoming-response` and `outgoing-response` // resource types that correspond to HTTP standard Responses. Soon, when @@ -141,14 +141,14 @@ interface types { // type (that uses the single `stream` type mentioned above). The `consume` and // `write` methods may only be called once (and return failure thereafter). resource incoming-response { - status: func() -> status-code + status: func() -> status-code; - headers: func() -> /* child */ headers + headers: func() -> /* child */ headers; // May be called at most once. returns error if called additional times. // TODO: make incoming-request-consume work the same way, giving a child // incoming-body. - consume: func() -> result + consume: func() -> result; } resource incoming-body { @@ -156,41 +156,41 @@ interface types { // incoming-body is dropped (or consumed by call to // incoming-body-finish) before the input-stream is dropped. // May be called at most once. returns error if called additional times. - %stream: func() -> result + %stream: func() -> result; // takes ownership of incoming-body. this will trap if the // incoming-body-stream child is still alive! finish: static func(this: incoming-body) -> - /* transitive child of the incoming-response of incoming-body */ future-trailers + /* transitive child of the incoming-response of incoming-body */ future-trailers; } resource future-trailers { /// Pollable that resolves when the body has been fully read, and the trailers /// are ready to be consumed. - subscribe: func() -> /* child */ pollable + subscribe: func() -> /* child */ pollable; /// Retrieve reference to trailers, if they are ready. - get: func() -> option> + get: func() -> option>; } resource outgoing-response { - constructor(status-code: status-code, headers: borrow) + constructor(status-code: status-code, headers: borrow); /// Will give the child outgoing-response at most once. subsequent calls will /// return an error. - write: func() -> result + write: func() -> result; } resource outgoing-body { /// Will give the child output-stream at most once. subsequent calls will /// return an error. - write: func() -> result + write: func() -> result; /// Finalize an outgoing body, optionally providing trailers. This must be /// called to signal that the response is complete. If the `outgoing-body` is /// dropped without calling `outgoing-body-finalize`, the implementation /// should treat the body as corrupted. - finish: static func(this: outgoing-body, trailers: option) + finish: static func(this: outgoing-body, trailers: option); } /// The following block defines a special resource type used by the @@ -207,8 +207,8 @@ interface types { /// will return an error here. /// inner result indicates whether the incoming-response was available, or an /// error occured. - get: func() -> option>> + get: func() -> option>>; - subscribe: func() -> /* child */ pollable + subscribe: func() -> /* child */ pollable; } } diff --git a/crates/wasi-http/wit/deps/io/poll.wit b/crates/wasi-http/wit/deps/io/poll.wit index e95762b915db..254f5341871b 100644 --- a/crates/wasi-http/wit/deps/io/poll.wit +++ b/crates/wasi-http/wit/deps/io/poll.wit @@ -1,10 +1,10 @@ -package wasi:io +package wasi:io; /// A poll API intended to let users wait for I/O events on multiple handles /// at once. interface poll { /// A "pollable" handle. - resource pollable + resource pollable; /// Poll for completion on a set of pollables. /// @@ -24,11 +24,11 @@ interface poll { /// do any I/O so it doesn't fail. If any of the I/O sources identified by /// the pollables has an error, it is indicated by marking the source as /// being reaedy for I/O. - poll-list: func(in: list>) -> list + poll-list: func(in: list>) -> list; /// Poll for completion on a single pollable. /// /// This function is similar to `poll-list`, but operates on only a single /// pollable. When it returns, the handle is ready for I/O. - poll-one: func(in: borrow) + poll-one: func(in: borrow); } diff --git a/crates/wasi-http/wit/deps/io/streams.wit b/crates/wasi-http/wit/deps/io/streams.wit index 55562d1cbfce..81832b2da958 100644 --- a/crates/wasi-http/wit/deps/io/streams.wit +++ b/crates/wasi-http/wit/deps/io/streams.wit @@ -1,4 +1,4 @@ -package wasi:io +package wasi:io; /// WASI I/O is an I/O abstraction API which is currently focused on providing /// stream types. @@ -6,7 +6,7 @@ package wasi:io /// In the future, the component model is expected to add built-in stream types; /// when it does, they are expected to subsume this API. interface streams { - use poll.{pollable} + use poll.{pollable}; /// An error for input-stream and output-stream operations. variant stream-error { @@ -74,14 +74,14 @@ interface streams { read: func( /// The maximum number of bytes to read len: u64 - ) -> result, stream-error> + ) -> result, stream-error>; /// Read bytes from a stream, after blocking until at least one byte can /// be read. Except for blocking, identical to `read`. blocking-read: func( /// The maximum number of bytes to read len: u64 - ) -> result, stream-error> + ) -> result, stream-error>; /// Skip bytes from a stream. /// @@ -98,14 +98,14 @@ interface streams { skip: func( /// The maximum number of bytes to skip. len: u64, - ) -> result + ) -> result; /// Skip bytes from a stream, after blocking until at least one byte /// can be skipped. Except for blocking behavior, identical to `skip`. blocking-skip: func( /// The maximum number of bytes to skip. len: u64, - ) -> result + ) -> result; /// Create a `pollable` which will resolve once either the specified stream /// has bytes available to read or the other end of the stream has been @@ -113,7 +113,7 @@ interface streams { /// The created `pollable` is a child resource of the `input-stream`. /// Implementations may trap if the `input-stream` is dropped before /// all derived `pollable`s created with this function are dropped. - subscribe: func() -> pollable + subscribe: func() -> pollable; } @@ -135,7 +135,7 @@ interface streams { /// When this function returns 0 bytes, the `subscribe` pollable will /// become ready when this function will report at least 1 byte, or an /// error. - check-write: func() -> result + check-write: func() -> result; /// Perform a write. This function never blocks. /// @@ -146,7 +146,7 @@ interface streams { /// the last call to check-write provided a permit. write: func( contents: list - ) -> result<_, stream-error> + ) -> result<_, stream-error>; /// Perform a write of up to 4096 bytes, and then flush the stream. Block /// until all of these operations are complete, or an error occurs. @@ -174,7 +174,7 @@ interface streams { /// ``` blocking-write-and-flush: func( contents: list - ) -> result<_, stream-error> + ) -> result<_, stream-error>; /// Request to flush buffered output. This function never blocks. /// @@ -186,11 +186,11 @@ interface streams { /// writes (`check-write` will return `ok(0)`) until the flush has /// completed. The `subscribe` pollable will become ready when the /// flush has completed and the stream can accept more writes. - flush: func() -> result<_, stream-error> + flush: func() -> result<_, stream-error>; /// Request to flush buffered output, and block until flush completes /// and stream is ready for writing again. - blocking-flush: func() -> result<_, stream-error> + blocking-flush: func() -> result<_, stream-error>; /// Create a `pollable` which will resolve once the output-stream /// is ready for more writing, or an error has occured. When this @@ -202,7 +202,7 @@ interface streams { /// The created `pollable` is a child resource of the `output-stream`. /// Implementations may trap if the `output-stream` is dropped before /// all derived `pollable`s created with this function are dropped. - subscribe: func() -> pollable + subscribe: func() -> pollable; /// Write zeroes to a stream. /// @@ -213,7 +213,7 @@ interface streams { write-zeroes: func( /// The number of zero-bytes to write len: u64 - ) -> result<_, stream-error> + ) -> result<_, stream-error>; /// Perform a write of up to 4096 zeroes, and then flush the stream. /// Block until all of these operations are complete, or an error @@ -242,7 +242,7 @@ interface streams { blocking-write-zeroes-and-flush: func( /// The number of zero-bytes to write len: u64 - ) -> result<_, stream-error> + ) -> result<_, stream-error>; /// Read from one stream and write to another. /// @@ -256,7 +256,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result + ) -> result; /// Read from one stream and write to another, with blocking. /// @@ -267,7 +267,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result + ) -> result; /// Forward the entire contents of an input stream to an output stream. /// @@ -284,6 +284,6 @@ interface streams { forward: func( /// The stream to read from src: input-stream - ) -> result + ) -> result; } } diff --git a/crates/wasi-http/wit/deps/io/world.wit b/crates/wasi-http/wit/deps/io/world.wit index 8738dba756d9..05244a965fb2 100644 --- a/crates/wasi-http/wit/deps/io/world.wit +++ b/crates/wasi-http/wit/deps/io/world.wit @@ -1,6 +1,6 @@ -package wasi:io +package wasi:io; world imports { - import streams - import poll + import streams; + import poll; } diff --git a/crates/wasi-http/wit/deps/logging/logging.wit b/crates/wasi-http/wit/deps/logging/logging.wit index b0cc4514dc8f..da537e479926 100644 --- a/crates/wasi-http/wit/deps/logging/logging.wit +++ b/crates/wasi-http/wit/deps/logging/logging.wit @@ -1,4 +1,4 @@ -package wasi:logging +package wasi:logging; /// WASI Logging is a logging API intended to let users emit log messages with /// simple priority levels and context values. @@ -33,5 +33,5 @@ interface logging { /// sent, a context, which is an uninterpreted string meant to help /// consumers group similar messages, and a string containing the message /// text. - log: func(level: level, context: string, message: string) + log: func(level: level, context: string, message: string); } diff --git a/crates/wasi-http/wit/deps/logging/world.wit b/crates/wasi-http/wit/deps/logging/world.wit index 7d49acfaddaa..ede6286430b6 100644 --- a/crates/wasi-http/wit/deps/logging/world.wit +++ b/crates/wasi-http/wit/deps/logging/world.wit @@ -1,5 +1,5 @@ -package wasi:logging +package wasi:logging; world imports { - import logging + import logging; } diff --git a/crates/wasi-http/wit/deps/random/insecure-seed.wit b/crates/wasi-http/wit/deps/random/insecure-seed.wit index ff2ff65d0754..139aed15927c 100644 --- a/crates/wasi-http/wit/deps/random/insecure-seed.wit +++ b/crates/wasi-http/wit/deps/random/insecure-seed.wit @@ -20,5 +20,5 @@ interface insecure-seed { /// This will likely be changed to a value import, to prevent it from being /// called multiple times and potentially used for purposes other than DoS /// protection. - insecure-seed: func() -> tuple + insecure-seed: func() -> tuple; } diff --git a/crates/wasi-http/wit/deps/random/insecure.wit b/crates/wasi-http/wit/deps/random/insecure.wit index ff0826822d5f..2ffd223cb344 100644 --- a/crates/wasi-http/wit/deps/random/insecure.wit +++ b/crates/wasi-http/wit/deps/random/insecure.wit @@ -11,11 +11,11 @@ interface insecure { /// There are no requirements on the values of the returned bytes, however /// implementations are encouraged to return evenly distributed values with /// a long period. - get-insecure-random-bytes: func(len: u64) -> list + get-insecure-random-bytes: func(len: u64) -> list; /// Return an insecure pseudo-random `u64` value. /// /// This function returns the same type of pseudo-random data as /// `get-insecure-random-bytes`, represented as a `u64`. - get-insecure-random-u64: func() -> u64 + get-insecure-random-u64: func() -> u64; } diff --git a/crates/wasi-http/wit/deps/random/random.wit b/crates/wasi-http/wit/deps/random/random.wit index 2a282dab7eb9..2c3c6a859c49 100644 --- a/crates/wasi-http/wit/deps/random/random.wit +++ b/crates/wasi-http/wit/deps/random/random.wit @@ -15,11 +15,11 @@ interface random { /// This function must always return fresh data. Deterministic environments /// must omit this function, rather than implementing it with deterministic /// data. - get-random-bytes: func(len: u64) -> list + get-random-bytes: func(len: u64) -> list; /// Return a cryptographically-secure random or pseudo-random `u64` value. /// /// This function returns the same type of data as `get-random-bytes`, /// represented as a `u64`. - get-random-u64: func() -> u64 + get-random-u64: func() -> u64; } diff --git a/crates/wasi-http/wit/deps/random/world.wit b/crates/wasi-http/wit/deps/random/world.wit index 41dc9ed10353..bb1dd7b592e7 100644 --- a/crates/wasi-http/wit/deps/random/world.wit +++ b/crates/wasi-http/wit/deps/random/world.wit @@ -1,7 +1,7 @@ -package wasi:random +package wasi:random; world imports { - import random - import insecure - import insecure-seed + import random; + import insecure; + import insecure-seed; } diff --git a/crates/wasi-http/wit/deps/sockets/instance-network.wit b/crates/wasi-http/wit/deps/sockets/instance-network.wit index d911a29cc8dd..14e4479e6c0c 100644 --- a/crates/wasi-http/wit/deps/sockets/instance-network.wit +++ b/crates/wasi-http/wit/deps/sockets/instance-network.wit @@ -1,9 +1,9 @@ /// This interface provides a value-export of the default network handle.. interface instance-network { - use network.{network} + use network.{network}; /// Get a handle to the default network. - instance-network: func() -> network + instance-network: func() -> network; } diff --git a/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit index f998aae140ab..da9b435d9ef9 100644 --- a/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit +++ b/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit @@ -1,13 +1,13 @@ interface ip-name-lookup { - use wasi:io/poll.{pollable} - use network.{network, error-code, ip-address, ip-address-family} + use wasi:io/poll.{pollable}; + use network.{network, error-code, ip-address, ip-address-family}; /// Resolve an internet host name to a list of IP addresses. - /// + /// /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. - /// + /// /// # Parameters /// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted /// to ASCII using IDNA encoding. @@ -17,45 +17,45 @@ interface ip-name-lookup { /// systems without an active IPv6 interface. Notes: /// - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address. /// - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged. - /// + /// /// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream` /// that can be used to (asynchronously) fetch the results. - /// + /// /// At the moment, the stream never completes successfully with 0 items. Ie. the first call /// to `resolve-next-address` never returns `ok(none)`. This may change in the future. - /// + /// /// # Typical errors /// - `invalid-name`: `name` is a syntactically invalid domain name. /// - `invalid-name`: `name` is an IP address. /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) - /// + /// /// # References: /// - /// - /// - /// - - resolve-addresses: func(network: borrow, name: string, address-family: option, include-unavailable: bool) -> result + resolve-addresses: func(network: borrow, name: string, address-family: option, include-unavailable: bool) -> result; resource resolve-address-stream { /// Returns the next address from the resolver. - /// + /// /// This function should be called multiple times. On each call, it will /// return the next address in connection order preference. If all /// addresses have been exhausted, this function returns `none`. - /// + /// /// This function never returns IPv4-mapped IPv6 addresses. - /// + /// /// # Typical errors /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) - resolve-next-address: func() -> result, error-code> + resolve-next-address: func() -> result, error-code>; /// Create a `pollable` which will resolve once the stream is ready for I/O. - /// + /// /// Note: this function is here for WASI Preview2 only. /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func() -> pollable + subscribe: func() -> pollable; } } diff --git a/crates/wasi-http/wit/deps/sockets/network.wit b/crates/wasi-http/wit/deps/sockets/network.wit index 8214eaaf7211..03755253b294 100644 --- a/crates/wasi-http/wit/deps/sockets/network.wit +++ b/crates/wasi-http/wit/deps/sockets/network.wit @@ -3,10 +3,10 @@ interface network { /// An opaque resource that represents access to (a subset of) the network. /// This enables context-based security for networking. /// There is no need for this to map 1:1 to a physical network interface. - resource network + resource network; /// Error codes. - /// + /// /// In theory, every API can return any error code. /// In practice, API's typically only return the errors documented per API /// combined with a couple of errors that are always possible: @@ -14,7 +14,7 @@ interface network { /// - `access-denied` /// - `not-supported` /// - `out-of-memory` - /// + /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. enum error-code { // ### GENERAL ERRORS ### @@ -23,17 +23,17 @@ interface network { unknown, /// Access denied. - /// + /// /// POSIX equivalent: EACCES, EPERM access-denied, /// The operation is not supported. - /// + /// /// POSIX equivalent: EOPNOTSUPP not-supported, /// Not enough memory to complete the operation. - /// + /// /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY out-of-memory, @@ -46,12 +46,12 @@ interface network { /// Trying to finish an asynchronous operation that: /// - has not been started yet, or: /// - was already finished by a previous `finish-*` call. - /// + /// /// Note: this is scheduled to be removed when `future`s are natively supported. not-in-progress, /// The operation has been aborted because it could not be completed immediately. - /// + /// /// Note: this is scheduled to be removed when `future`s are natively supported. would-block, @@ -79,7 +79,7 @@ interface network { /// A new socket resource could not be created because of a system limit. new-socket-limit, - + /// The socket is already attached to another network. already-attached, @@ -106,10 +106,10 @@ interface network { /// The remote address is not reachable remote-unreachable, - + // ### TCP SOCKET ERRORS ### - + /// The socket is already in the Listener state. already-listening, @@ -121,14 +121,14 @@ interface network { /// The connection was reset. connection-reset, - + // ### UDP SOCKET ERRORS ### datagram-too-large, // ### NAME LOOKUP ERRORS ### - + /// The provided name is a syntactically invalid domain name. invalid-name, @@ -144,14 +144,14 @@ interface network { enum ip-address-family { /// Similar to `AF_INET` in POSIX. - ipv4, + ipv4, /// Similar to `AF_INET6` in POSIX. ipv6, } - type ipv4-address = tuple - type ipv6-address = tuple + type ipv4-address = tuple; + type ipv6-address = tuple; variant ip-address { ipv4(ipv4-address), diff --git a/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit b/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit index f43bc8979047..b64cabba7993 100644 --- a/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit +++ b/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit @@ -1,27 +1,27 @@ interface tcp-create-socket { - use network.{network, error-code, ip-address-family} - use tcp.{tcp-socket} + use network.{network, error-code, ip-address-family}; + use tcp.{tcp-socket}; /// Create a new TCP socket. - /// + /// /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. - /// + /// /// This function does not require a network capability handle. This is considered to be safe because /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. - /// + /// /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. - /// + /// /// # Typical errors /// - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP) /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) - /// + /// /// # References /// - /// - /// - /// - - create-tcp-socket: func(address-family: ip-address-family) -> result + create-tcp-socket: func(address-family: ip-address-family) -> result; } diff --git a/crates/wasi-http/wit/deps/sockets/tcp.wit b/crates/wasi-http/wit/deps/sockets/tcp.wit index 175626cc7620..0ae7c05e8fcb 100644 --- a/crates/wasi-http/wit/deps/sockets/tcp.wit +++ b/crates/wasi-http/wit/deps/sockets/tcp.wit @@ -1,8 +1,8 @@ interface tcp { - use wasi:io/streams.{input-stream, output-stream} - use wasi:io/poll.{pollable} - use network.{network, error-code, ip-socket-address, ip-address-family} + use wasi:io/streams.{input-stream, output-stream}; + use wasi:io/poll.{pollable}; + use network.{network, error-code, ip-socket-address, ip-address-family}; enum shutdown-type { /// Similar to `SHUT_RD` in POSIX. @@ -23,38 +23,38 @@ interface tcp { /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which /// network interface(s) to bind to. /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// + /// /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will /// implicitly bind the socket. - /// + /// /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// + /// /// # Typical `start` errors /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) /// - `already-bound`: The socket is already bound. (EINVAL) /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - /// + /// /// # Typical `finish` errors /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) /// - `address-in-use`: Address is already in use. (EADDRINUSE) /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) /// - `not-in-progress`: A `bind` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - /// - /// - - start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code> - finish-bind: func() -> result<_, error-code> + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; /// Connect to a remote endpoint. - /// + /// /// On success: /// - the socket is transitioned into the Connection state /// - a pair of streams is returned that can be used to read & write to the connection - /// + /// /// # Typical `start` errors /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) @@ -63,7 +63,7 @@ interface tcp { /// - `already-connected`: The socket is already in the Connection state. (EISCONN) /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - /// + /// /// # Typical `finish` errors /// - `timeout`: Connection timed out. (ETIMEDOUT) /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) @@ -72,23 +72,23 @@ interface tcp { /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) /// - `not-in-progress`: A `connect` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - /// - /// - - start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code> - finish-connect: func() -> result, error-code> + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + finish-connect: func() -> result, error-code>; /// Start listening for new connections. - /// + /// /// Transitions the socket into the Listener state. - /// + /// /// Unlike POSIX: /// - this function is async. This enables interactive WASI hosts to inject permission prompts. /// - the socket must already be explicitly bound. - /// + /// /// # Typical `start` errors /// - `not-bound`: The socket is not bound to any local address. (EDESTADDRREQ) /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) @@ -105,145 +105,145 @@ interface tcp { /// - /// - /// - - start-listen: func() -> result<_, error-code> - finish-listen: func() -> result<_, error-code> + start-listen: func() -> result<_, error-code>; + finish-listen: func() -> result<_, error-code>; /// Accept a new client socket. - /// + /// /// The returned socket is bound and in the Connection state. - /// + /// /// On success, this function returns the newly accepted client socket along with /// a pair of streams that can be used to read & write to the connection. - /// + /// /// # Typical errors /// - `not-listening`: Socket is not in the Listener state. (EINVAL) /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) - /// + /// /// Host implementations must skip over transient errors returned by the native accept syscall. - /// + /// /// # References /// - /// - /// - /// - - accept: func() -> result, error-code> + accept: func() -> result, error-code>; /// Get the bound local address. - /// + /// /// # Typical errors /// - `not-bound`: The socket is not bound to any local address. - /// + /// /// # References /// - /// - /// - /// - - local-address: func() -> result + local-address: func() -> result; /// Get the bound remote address. - /// + /// /// # Typical errors /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// + /// /// # References /// - /// - /// - /// - - remote-address: func() -> result + remote-address: func() -> result; /// Whether this is a IPv4 or IPv6 socket. - /// + /// /// Equivalent to the SO_DOMAIN socket option. - address-family: func() -> ip-address-family - + address-family: func() -> ip-address-family; + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// + /// /// Equivalent to the IPV6_V6ONLY socket option. - /// + /// /// # Typical errors /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. /// - `already-bound`: (set) The socket is already bound. /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - ipv6-only: func() -> result - set-ipv6-only: func(value: bool) -> result<_, error-code> + ipv6-only: func() -> result; + set-ipv6-only: func(value: bool) -> result<_, error-code>; /// Hints the desired listen queue size. Implementations are free to ignore this. - /// + /// /// # Typical errors /// - `already-connected`: (set) The socket is already in the Connection state. /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - set-listen-backlog-size: func(value: u64) -> result<_, error-code> + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; /// Equivalent to the SO_KEEPALIVE socket option. - /// + /// /// # Typical errors /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - keep-alive: func() -> result - set-keep-alive: func(value: bool) -> result<_, error-code> + keep-alive: func() -> result; + set-keep-alive: func(value: bool) -> result<_, error-code>; /// Equivalent to the TCP_NODELAY socket option. - /// + /// /// # Typical errors /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - no-delay: func() -> result - set-no-delay: func(value: bool) -> result<_, error-code> - + no-delay: func() -> result; + set-no-delay: func(value: bool) -> result<_, error-code>; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// + /// /// # Typical errors /// - `already-connected`: (set) The socket is already in the Connection state. /// - `already-listening`: (set) The socket is already in the Listener state. /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - unicast-hop-limit: func() -> result - set-unicast-hop-limit: func(value: u8) -> result<_, error-code> + unicast-hop-limit: func() -> result; + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; /// The kernel buffer space reserved for sends/receives on this socket. - /// + /// /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. /// In other words, after setting a value, reading the same setting back may return a different value. - /// + /// /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of /// actual data to be sent/received by the application, because the kernel might also use the buffer space /// for internal metadata structures. - /// + /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// + /// /// # Typical errors /// - `already-connected`: (set) The socket is already in the Connection state. /// - `already-listening`: (set) The socket is already in the Listener state. /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - receive-buffer-size: func() -> result - set-receive-buffer-size: func(value: u64) -> result<_, error-code> - send-buffer-size: func() -> result - set-send-buffer-size: func(value: u64) -> result<_, error-code> + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// + /// /// Note: this function is here for WASI Preview2 only. /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func() -> pollable + subscribe: func() -> pollable; /// Initiate a graceful shutdown. - /// + /// /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. /// Any data still in the receive queue at time of calling `shutdown` will be discarded. /// - send: the socket is not expecting to send any more data to the peer. All subsequent write /// operations on the `output-stream` associated with this socket will return an error. /// - both: same effect as receive & send combined. - /// + /// /// The shutdown function does not close (drop) the socket. - /// + /// /// # Typical errors /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) - /// + /// /// # References /// - /// - /// - /// - - shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code> + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; } } diff --git a/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit b/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit index cd4c08fb1000..64d899456ca8 100644 --- a/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit +++ b/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit @@ -1,27 +1,27 @@ interface udp-create-socket { - use network.{network, error-code, ip-address-family} - use udp.{udp-socket} + use network.{network, error-code, ip-address-family}; + use udp.{udp-socket}; /// Create a new UDP socket. - /// + /// /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. - /// + /// /// This function does not require a network capability handle. This is considered to be safe because /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. - /// + /// /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. - /// + /// /// # Typical errors /// - `not-supported`: The host does not support UDP sockets. (EOPNOTSUPP) /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) - /// + /// /// # References: /// - /// - /// - /// - - create-udp-socket: func(address-family: ip-address-family) -> result + create-udp-socket: func(address-family: ip-address-family) -> result; } diff --git a/crates/wasi-http/wit/deps/sockets/udp.wit b/crates/wasi-http/wit/deps/sockets/udp.wit index 01e5b95b97b7..a29250caa9af 100644 --- a/crates/wasi-http/wit/deps/sockets/udp.wit +++ b/crates/wasi-http/wit/deps/sockets/udp.wit @@ -1,7 +1,7 @@ interface udp { - use wasi:io/poll.{pollable} - use network.{network, error-code, ip-socket-address, ip-address-family} + use wasi:io/poll.{pollable}; + use network.{network, error-code, ip-socket-address, ip-address-family}; record datagram { @@ -25,74 +25,74 @@ interface udp { /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which /// network interface(s) to bind to. /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// + /// /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. - /// + /// /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// + /// /// # Typical `start` errors /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) /// - `already-bound`: The socket is already bound. (EINVAL) /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) - /// + /// /// # Typical `finish` errors /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) /// - `address-in-use`: Address is already in use. (EADDRINUSE) /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) /// - `not-in-progress`: A `bind` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - /// - /// - - start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code> - finish-bind: func() -> result<_, error-code> + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; /// Set the destination address. - /// + /// /// The local-address is updated based on the best network path to `remote-address`. - /// + /// /// When a destination address is set: /// - all receive operations will only return datagrams sent from the provided `remote-address`. /// - the `send` function can only be used to send to this destination. - /// + /// /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". - /// + /// /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// + /// /// # Typical `start` errors /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) - /// + /// /// # Typical `finish` errors /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) /// - `not-in-progress`: A `connect` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - /// - /// - - start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code> - finish-connect: func() -> result<_, error-code> + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + finish-connect: func() -> result<_, error-code>; /// Receive messages on the socket. - /// + /// /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. /// The returned list may contain fewer elements than requested, but never more. /// If `max-results` is 0, this function returns successfully with an empty list. - /// + /// /// # Typical errors /// - `not-bound`: The socket is not bound to any local address. (EINVAL) /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - @@ -102,22 +102,22 @@ interface udp { /// - /// - /// - - receive: func(max-results: u64) -> result, error-code> + receive: func(max-results: u64) -> result, error-code>; /// Send messages on the socket. - /// + /// /// This function attempts to send all provided `datagrams` on the socket without blocking and /// returns how many messages were actually sent (or queued for sending). - /// + /// /// This function semantically behaves the same as iterating the `datagrams` list and sequentially /// sending each individual datagram until either the end of the list has been reached or the first error occurred. /// If at least one datagram has been sent successfully, this function never returns an error. - /// + /// /// If the input list is empty, the function returns `ok(0)`. - /// + /// /// The remote address option is required. To send a message to the "connected" peer, /// call `remote-address` to get their address. - /// + /// /// # Typical errors /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) @@ -127,7 +127,7 @@ interface udp { /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - @@ -137,78 +137,78 @@ interface udp { /// - /// - /// - - send: func(datagrams: list) -> result + send: func(datagrams: list) -> result; /// Get the current bound address. - /// + /// /// # Typical errors /// - `not-bound`: The socket is not bound to any local address. - /// + /// /// # References /// - /// - /// - /// - - local-address: func() -> result + local-address: func() -> result; /// Get the address set with `connect`. - /// + /// /// # Typical errors /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// + /// /// # References /// - /// - /// - /// - - remote-address: func() -> result + remote-address: func() -> result; /// Whether this is a IPv4 or IPv6 socket. - /// + /// /// Equivalent to the SO_DOMAIN socket option. - address-family: func() -> ip-address-family + address-family: func() -> ip-address-family; /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// + /// /// Equivalent to the IPV6_V6ONLY socket option. - /// + /// /// # Typical errors /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. /// - `already-bound`: (set) The socket is already bound. /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - ipv6-only: func() -> result - set-ipv6-only: func(value: bool) -> result<_, error-code> + ipv6-only: func() -> result; + set-ipv6-only: func(value: bool) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// + /// /// # Typical errors /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - unicast-hop-limit: func() -> result - set-unicast-hop-limit: func(value: u8) -> result<_, error-code> + unicast-hop-limit: func() -> result; + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; /// The kernel buffer space reserved for sends/receives on this socket. - /// + /// /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. /// In other words, after setting a value, reading the same setting back may return a different value. - /// + /// /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of /// actual data to be sent/received by the application, because the kernel might also use the buffer space /// for internal metadata structures. - /// + /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// + /// /// # Typical errors /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - receive-buffer-size: func() -> result - set-receive-buffer-size: func(value: u64) -> result<_, error-code> - send-buffer-size: func() -> result - set-send-buffer-size: func(value: u64) -> result<_, error-code> + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// + /// /// Note: this function is here for WASI Preview2 only. /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func() -> pollable + subscribe: func() -> pollable; } } diff --git a/crates/wasi-http/wit/deps/sockets/world.wit b/crates/wasi-http/wit/deps/sockets/world.wit index 12f3c2868177..432b0dc99d00 100644 --- a/crates/wasi-http/wit/deps/sockets/world.wit +++ b/crates/wasi-http/wit/deps/sockets/world.wit @@ -1,11 +1,11 @@ -package wasi:sockets +package wasi:sockets; world imports { - import instance-network - import network - import udp - import udp-create-socket - import tcp - import tcp-create-socket - import ip-name-lookup + import instance-network; + import network; + import udp; + import udp-create-socket; + import tcp; + import tcp-create-socket; + import ip-name-lookup; } diff --git a/crates/wasi-http/wit/main.wit b/crates/wasi-http/wit/main.wit index 739e1bd4ac48..e843cefceaf2 100644 --- a/crates/wasi-http/wit/main.wit +++ b/crates/wasi-http/wit/main.wit @@ -1,33 +1,33 @@ -package wasmtime:wasi +package wasmtime:wasi; // All of the same imports available in the wasi:cli/command world, but no // export required: world preview1-adapter-reactor { - import wasi:clocks/wall-clock - import wasi:clocks/monotonic-clock - import wasi:clocks/timezone - import wasi:filesystem/types - import wasi:filesystem/preopens - import wasi:sockets/instance-network - import wasi:sockets/ip-name-lookup - import wasi:sockets/network - import wasi:sockets/tcp-create-socket - import wasi:sockets/tcp - import wasi:sockets/udp-create-socket - import wasi:sockets/udp - import wasi:random/random - import wasi:random/insecure - import wasi:random/insecure-seed - import wasi:io/poll - import wasi:io/streams - import wasi:cli/environment - import wasi:cli/exit - import wasi:cli/stdin - import wasi:cli/stdout - import wasi:cli/stderr - import wasi:cli/terminal-input - import wasi:cli/terminal-output - import wasi:cli/terminal-stdin - import wasi:cli/terminal-stdout - import wasi:cli/terminal-stderr + import wasi:clocks/wall-clock; + import wasi:clocks/monotonic-clock; + import wasi:clocks/timezone; + import wasi:filesystem/types; + import wasi:filesystem/preopens; + import wasi:sockets/instance-network; + import wasi:sockets/ip-name-lookup; + import wasi:sockets/network; + import wasi:sockets/tcp-create-socket; + import wasi:sockets/tcp; + import wasi:sockets/udp-create-socket; + import wasi:sockets/udp; + import wasi:random/random; + import wasi:random/insecure; + import wasi:random/insecure-seed; + import wasi:io/poll; + import wasi:io/streams; + import wasi:cli/environment; + import wasi:cli/exit; + import wasi:cli/stdin; + import wasi:cli/stdout; + import wasi:cli/stderr; + import wasi:cli/terminal-input; + import wasi:cli/terminal-output; + import wasi:cli/terminal-stdin; + import wasi:cli/terminal-stdout; + import wasi:cli/terminal-stderr; } diff --git a/crates/wasi-http/wit/test.wit b/crates/wasi-http/wit/test.wit index 03073513f8e6..fc9c357522bf 100644 --- a/crates/wasi-http/wit/test.wit +++ b/crates/wasi-http/wit/test.wit @@ -1,44 +1,44 @@ // only used as part of `test-programs` world test-reactor { - import wasi:cli/environment - import wasi:io/poll - import wasi:io/streams - import wasi:filesystem/types - import wasi:filesystem/preopens - import wasi:cli/exit + import wasi:cli/environment; + import wasi:io/poll; + import wasi:io/streams; + import wasi:filesystem/types; + import wasi:filesystem/preopens; + import wasi:cli/exit; - export add-strings: func(s: list) -> u32 - export get-strings: func() -> list + export add-strings: func(s: list) -> u32; + export get-strings: func() -> list; - use wasi:io/streams.{output-stream} + use wasi:io/streams.{output-stream}; - export write-strings-to: func(o: output-stream) -> result + export write-strings-to: func(o: output-stream) -> result; - use wasi:filesystem/types.{descriptor-stat} - export pass-an-imported-record: func(d: descriptor-stat) -> string + use wasi:filesystem/types.{descriptor-stat}; + export pass-an-imported-record: func(d: descriptor-stat) -> string; } world test-command { - import wasi:io/poll - import wasi:io/streams - import wasi:cli/environment - import wasi:cli/stdin - import wasi:cli/stdout - import wasi:cli/stderr + import wasi:io/poll; + import wasi:io/streams; + import wasi:cli/environment; + import wasi:cli/stdin; + import wasi:cli/stdout; + import wasi:cli/stderr; } world test-command-with-sockets { - import wasi:io/poll - import wasi:io/streams - import wasi:cli/environment - import wasi:cli/stdin - import wasi:cli/stdout - import wasi:cli/stderr - import wasi:sockets/tcp - import wasi:sockets/tcp-create-socket - import wasi:sockets/network - import wasi:sockets/instance-network - import wasi:sockets/ip-name-lookup - import wasi:clocks/monotonic-clock + import wasi:io/poll; + import wasi:io/streams; + import wasi:cli/environment; + import wasi:cli/stdin; + import wasi:cli/stdout; + import wasi:cli/stderr; + import wasi:sockets/tcp; + import wasi:sockets/tcp-create-socket; + import wasi:sockets/network; + import wasi:sockets/instance-network; + import wasi:sockets/ip-name-lookup; + import wasi:clocks/monotonic-clock; } diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index aecc414ea4b2..de6f8687a5b8 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -65,9 +65,9 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", interfaces: " - import wasi:io/poll - import wasi:io/streams - import wasi:filesystem/types + import wasi:io/poll; + import wasi:io/streams; + import wasi:filesystem/types; ", tracing: true, trappable_error_type: { @@ -90,7 +90,7 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", - interfaces: "include wasi:cli/reactor", + interfaces: "include wasi:cli/reactor;", tracing: true, async: { // Only these functions are `async` and everything else is sync diff --git a/crates/wasi/wit/command-extended.wit b/crates/wasi/wit/command-extended.wit index 3c56808e4abe..b4d08aed3f3b 100644 --- a/crates/wasi/wit/command-extended.wit +++ b/crates/wasi/wit/command-extended.wit @@ -1,37 +1,37 @@ // All of the same imports and exports available in the wasi:cli/command world // with addition of HTTP proxy related imports: world command-extended { - import wasi:clocks/wall-clock - import wasi:clocks/monotonic-clock - import wasi:clocks/timezone - import wasi:filesystem/types - import wasi:filesystem/preopens - import wasi:sockets/instance-network - import wasi:sockets/ip-name-lookup - import wasi:sockets/network - import wasi:sockets/tcp-create-socket - import wasi:sockets/tcp - import wasi:sockets/udp-create-socket - import wasi:sockets/udp - import wasi:random/random - import wasi:random/insecure - import wasi:random/insecure-seed - import wasi:io/poll - import wasi:io/streams - import wasi:cli/environment - import wasi:cli/exit - import wasi:cli/stdin - import wasi:cli/stdout - import wasi:cli/stderr - import wasi:cli/terminal-input - import wasi:cli/terminal-output - import wasi:cli/terminal-stdin - import wasi:cli/terminal-stdout - import wasi:cli/terminal-stderr + import wasi:clocks/wall-clock; + import wasi:clocks/monotonic-clock; + import wasi:clocks/timezone; + import wasi:filesystem/types; + import wasi:filesystem/preopens; + import wasi:sockets/instance-network; + import wasi:sockets/ip-name-lookup; + import wasi:sockets/network; + import wasi:sockets/tcp-create-socket; + import wasi:sockets/tcp; + import wasi:sockets/udp-create-socket; + import wasi:sockets/udp; + import wasi:random/random; + import wasi:random/insecure; + import wasi:random/insecure-seed; + import wasi:io/poll; + import wasi:io/streams; + import wasi:cli/environment; + import wasi:cli/exit; + import wasi:cli/stdin; + import wasi:cli/stdout; + import wasi:cli/stderr; + import wasi:cli/terminal-input; + import wasi:cli/terminal-output; + import wasi:cli/terminal-stdin; + import wasi:cli/terminal-stdout; + import wasi:cli/terminal-stderr; // We should replace all others with `include self.command` // as soon as the unioning of worlds is available: // https://github.com/WebAssembly/component-model/issues/169 - import wasi:logging/logging - import wasi:http/outgoing-handler + import wasi:logging/logging; + import wasi:http/outgoing-handler; } diff --git a/crates/wasi/wit/deps/cli/command.wit b/crates/wasi/wit/deps/cli/command.wit index d28f5f6282a4..86c9c73b7634 100644 --- a/crates/wasi/wit/deps/cli/command.wit +++ b/crates/wasi/wit/deps/cli/command.wit @@ -1,7 +1,7 @@ -package wasi:cli +package wasi:cli; world command { - include reactor + include reactor; - export run + export run; } diff --git a/crates/wasi/wit/deps/cli/environment.wit b/crates/wasi/wit/deps/cli/environment.wit index 36790fe714d2..70065233e81b 100644 --- a/crates/wasi/wit/deps/cli/environment.wit +++ b/crates/wasi/wit/deps/cli/environment.wit @@ -7,12 +7,12 @@ interface environment { /// Morally, these are a value import, but until value imports are available /// in the component model, this import function should return the same /// values each time it is called. - get-environment: func() -> list> + get-environment: func() -> list>; /// Get the POSIX-style arguments to the program. - get-arguments: func() -> list + get-arguments: func() -> list; /// Return a path that programs should use as their initial current working /// directory, interpreting `.` as shorthand for this. - initial-cwd: func() -> option + initial-cwd: func() -> option; } diff --git a/crates/wasi/wit/deps/cli/exit.wit b/crates/wasi/wit/deps/cli/exit.wit index 4831d50789af..d0c2b82ae2c7 100644 --- a/crates/wasi/wit/deps/cli/exit.wit +++ b/crates/wasi/wit/deps/cli/exit.wit @@ -1,4 +1,4 @@ interface exit { /// Exit the current instance and any linked instances. - exit: func(status: result) + exit: func(status: result); } diff --git a/crates/wasi/wit/deps/cli/reactor.wit b/crates/wasi/wit/deps/cli/reactor.wit index 274d0644dc41..6bd780e76649 100644 --- a/crates/wasi/wit/deps/cli/reactor.wit +++ b/crates/wasi/wit/deps/cli/reactor.wit @@ -1,33 +1,32 @@ -package wasi:cli +package wasi:cli; world reactor { - import wasi:clocks/wall-clock - import wasi:clocks/monotonic-clock - import wasi:clocks/timezone - import wasi:filesystem/types - import wasi:filesystem/preopens - import wasi:sockets/instance-network - import wasi:sockets/ip-name-lookup - import wasi:sockets/network - import wasi:sockets/tcp-create-socket - import wasi:sockets/tcp - import wasi:sockets/udp-create-socket - import wasi:sockets/udp - import wasi:random/random - import wasi:random/insecure - import wasi:random/insecure-seed - import wasi:io/poll - import wasi:io/streams + import wasi:clocks/wall-clock; + import wasi:clocks/monotonic-clock; + import wasi:clocks/timezone; + import wasi:filesystem/types; + import wasi:filesystem/preopens; + import wasi:sockets/instance-network; + import wasi:sockets/ip-name-lookup; + import wasi:sockets/network; + import wasi:sockets/tcp-create-socket; + import wasi:sockets/tcp; + import wasi:sockets/udp-create-socket; + import wasi:sockets/udp; + import wasi:random/random; + import wasi:random/insecure; + import wasi:random/insecure-seed; + import wasi:io/poll; + import wasi:io/streams; - import environment - import exit - import stdin - import stdout - import stderr - import terminal-input - import terminal-output - import terminal-stdin - import terminal-stdout - import terminal-stderr + import environment; + import exit; + import stdin; + import stdout; + import stderr; + import terminal-input; + import terminal-output; + import terminal-stdin; + import terminal-stdout; + import terminal-stderr; } - diff --git a/crates/wasi/wit/deps/cli/run.wit b/crates/wasi/wit/deps/cli/run.wit index 45a1ca533f0e..a70ee8c038f2 100644 --- a/crates/wasi/wit/deps/cli/run.wit +++ b/crates/wasi/wit/deps/cli/run.wit @@ -1,4 +1,4 @@ interface run { /// Run the program. - run: func() -> result + run: func() -> result; } diff --git a/crates/wasi/wit/deps/cli/stdio.wit b/crates/wasi/wit/deps/cli/stdio.wit index 6c9d4a41a6f9..1bb6c55837c2 100644 --- a/crates/wasi/wit/deps/cli/stdio.wit +++ b/crates/wasi/wit/deps/cli/stdio.wit @@ -1,17 +1,17 @@ interface stdin { - use wasi:io/streams.{input-stream} + use wasi:io/streams.{input-stream}; - get-stdin: func() -> input-stream + get-stdin: func() -> input-stream; } interface stdout { - use wasi:io/streams.{output-stream} + use wasi:io/streams.{output-stream}; - get-stdout: func() -> output-stream + get-stdout: func() -> output-stream; } interface stderr { - use wasi:io/streams.{output-stream} + use wasi:io/streams.{output-stream}; - get-stderr: func() -> output-stream + get-stderr: func() -> output-stream; } diff --git a/crates/wasi/wit/deps/cli/terminal.wit b/crates/wasi/wit/deps/cli/terminal.wit index b0a5bec2a2c7..47495769b31f 100644 --- a/crates/wasi/wit/deps/cli/terminal.wit +++ b/crates/wasi/wit/deps/cli/terminal.wit @@ -1,6 +1,6 @@ interface terminal-input { /// The input side of a terminal. - resource terminal-input + resource terminal-input; // In the future, this may include functions for disabling echoing, // disabling input buffering so that keyboard events are sent through @@ -9,7 +9,7 @@ interface terminal-input { interface terminal-output { /// The output side of a terminal. - resource terminal-output + resource terminal-output; // In the future, this may include functions for querying the terminal // size, being notified of terminal size changes, querying supported @@ -19,29 +19,29 @@ interface terminal-output { /// An interface providing an optional `terminal-input` for stdin as a /// link-time authority. interface terminal-stdin { - use terminal-input.{terminal-input} + use terminal-input.{terminal-input}; /// If stdin is connected to a terminal, return a `terminal-input` handle /// allowing further interaction with it. - get-terminal-stdin: func() -> option + get-terminal-stdin: func() -> option; } /// An interface providing an optional `terminal-output` for stdout as a /// link-time authority. interface terminal-stdout { - use terminal-output.{terminal-output} + use terminal-output.{terminal-output}; /// If stdout is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - get-terminal-stdout: func() -> option + get-terminal-stdout: func() -> option; } /// An interface providing an optional `terminal-output` for stderr as a /// link-time authority. interface terminal-stderr { - use terminal-output.{terminal-output} + use terminal-output.{terminal-output}; /// If stderr is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - get-terminal-stderr: func() -> option + get-terminal-stderr: func() -> option; } diff --git a/crates/wasi/wit/deps/clocks/monotonic-clock.wit b/crates/wasi/wit/deps/clocks/monotonic-clock.wit index 703a5fb7a503..d9ac7cb3fb6b 100644 --- a/crates/wasi/wit/deps/clocks/monotonic-clock.wit +++ b/crates/wasi/wit/deps/clocks/monotonic-clock.wit @@ -9,24 +9,24 @@ /// /// It is intended for measuring elapsed time. interface monotonic-clock { - use wasi:io/poll.{pollable} + use wasi:io/poll.{pollable}; /// A timestamp in nanoseconds. - type instant = u64 + type instant = u64; /// Read the current value of the clock. /// /// The clock is monotonic, therefore calling this function repeatedly will /// produce a sequence of non-decreasing values. - now: func() -> instant + now: func() -> instant; /// Query the resolution of the clock. - resolution: func() -> instant + resolution: func() -> instant; /// Create a `pollable` which will resolve once the specified time has been /// reached. subscribe: func( when: instant, absolute: bool - ) -> pollable + ) -> pollable; } diff --git a/crates/wasi/wit/deps/clocks/timezone.wit b/crates/wasi/wit/deps/clocks/timezone.wit index a872bffc7414..e717e7b8dda9 100644 --- a/crates/wasi/wit/deps/clocks/timezone.wit +++ b/crates/wasi/wit/deps/clocks/timezone.wit @@ -1,5 +1,5 @@ interface timezone { - use wall-clock.{datetime} + use wall-clock.{datetime}; /// Return information needed to display the given `datetime`. This includes /// the UTC offset, the time zone name, and a flag indicating whether @@ -8,10 +8,10 @@ interface timezone { /// If the timezone cannot be determined for the given `datetime`, return a /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight /// saving time. - display: func(when: datetime) -> timezone-display + display: func(when: datetime) -> timezone-display; /// The same as `display`, but only return the UTC offset. - utc-offset: func(when: datetime) -> s32 + utc-offset: func(when: datetime) -> s32; /// Information useful for displaying the timezone of a specific `datetime`. /// diff --git a/crates/wasi/wit/deps/clocks/wall-clock.wit b/crates/wasi/wit/deps/clocks/wall-clock.wit index dae44a7308cd..c39564967a7e 100644 --- a/crates/wasi/wit/deps/clocks/wall-clock.wit +++ b/crates/wasi/wit/deps/clocks/wall-clock.wit @@ -32,10 +32,10 @@ interface wall-clock { /// /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time - now: func() -> datetime + now: func() -> datetime; /// Query the resolution of the clock. /// /// The nanoseconds field of the output is always less than 1000000000. - resolution: func() -> datetime + resolution: func() -> datetime; } diff --git a/crates/wasi/wit/deps/clocks/world.wit b/crates/wasi/wit/deps/clocks/world.wit index 5c2dd411d2d0..3295ba8d92ab 100644 --- a/crates/wasi/wit/deps/clocks/world.wit +++ b/crates/wasi/wit/deps/clocks/world.wit @@ -1,7 +1,7 @@ -package wasi:clocks +package wasi:clocks; world imports { - import monotonic-clock - import wall-clock - import timezone + import monotonic-clock; + import wall-clock; + import timezone; } diff --git a/crates/wasi/wit/deps/filesystem/preopens.wit b/crates/wasi/wit/deps/filesystem/preopens.wit index f45661b8a849..3f787ac3fead 100644 --- a/crates/wasi/wit/deps/filesystem/preopens.wit +++ b/crates/wasi/wit/deps/filesystem/preopens.wit @@ -1,6 +1,6 @@ interface preopens { - use types.{descriptor} + use types.{descriptor}; /// Return the set of preopened directories, and their path. - get-directories: func() -> list> + get-directories: func() -> list>; } diff --git a/crates/wasi/wit/deps/filesystem/types.wit b/crates/wasi/wit/deps/filesystem/types.wit index aecdd0ef354b..7efb7e5b3208 100644 --- a/crates/wasi/wit/deps/filesystem/types.wit +++ b/crates/wasi/wit/deps/filesystem/types.wit @@ -23,11 +23,11 @@ /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md interface types { - use wasi:io/streams.{input-stream, output-stream, error} - use wasi:clocks/wall-clock.{datetime} + use wasi:io/streams.{input-stream, output-stream, error}; + use wasi:clocks/wall-clock.{datetime}; /// File size or length of a region within a file. - type filesize = u64 + type filesize = u64; /// The type of a filesystem object referenced by a descriptor. /// @@ -166,7 +166,7 @@ interface types { } /// Number of hard links to an inode. - type link-count = u64 + type link-count = u64; /// When setting a timestamp, this gives the value to set it to. variant new-timestamp { @@ -315,7 +315,7 @@ interface types { read-via-stream: func( /// The offset within the file at which to start reading. offset: filesize, - ) -> result + ) -> result; /// Return a stream for writing to a file, if available. /// @@ -326,7 +326,7 @@ interface types { write-via-stream: func( /// The offset within the file at which to start writing. offset: filesize, - ) -> result + ) -> result; /// Return a stream for appending to a file, if available. /// @@ -334,7 +334,7 @@ interface types { /// /// Note: This allows using `write-stream`, which is similar to `write` with /// `O_APPEND` in in POSIX. - append-via-stream: func() -> result + append-via-stream: func() -> result; /// Provide file advisory information on a descriptor. /// @@ -346,7 +346,7 @@ interface types { length: filesize, /// The advice. advice: advice - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Synchronize the data of a file to disk. /// @@ -354,7 +354,7 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fdatasync` in POSIX. - sync-data: func() -> result<_, error-code> + sync-data: func() -> result<_, error-code>; /// Get flags associated with a descriptor. /// @@ -362,7 +362,7 @@ interface types { /// /// Note: This returns the value that was the `fs_flags` value returned /// from `fdstat_get` in earlier versions of WASI. - get-flags: func() -> result + get-flags: func() -> result; /// Get the dynamic type of a descriptor. /// @@ -374,13 +374,13 @@ interface types { /// /// Note: This returns the value that was the `fs_filetype` value returned /// from `fdstat_get` in earlier versions of WASI. - get-type: func() -> result + get-type: func() -> result; /// Adjust the size of an open file. If this increases the file's size, the /// extra bytes are filled with zeros. /// /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. - set-size: func(size: filesize) -> result<_, error-code> + set-size: func(size: filesize) -> result<_, error-code>; /// Adjust the timestamps of an open file or directory. /// @@ -392,7 +392,7 @@ interface types { data-access-timestamp: new-timestamp, /// The desired values of the data modification timestamp. data-modification-timestamp: new-timestamp, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Read from a descriptor, without using and updating the descriptor's offset. /// @@ -410,7 +410,7 @@ interface types { length: filesize, /// The offset within the file at which to read. offset: filesize, - ) -> result, bool>, error-code> + ) -> result, bool>, error-code>; /// Write to a descriptor, without using and updating the descriptor's offset. /// @@ -426,7 +426,7 @@ interface types { buffer: list, /// The offset within the file at which to write. offset: filesize, - ) -> result + ) -> result; /// Read directory entries from a directory. /// @@ -437,7 +437,7 @@ interface types { /// This always returns a new stream which starts at the beginning of the /// directory. Multiple streams may be active on the same directory, and they /// do not interfere with each other. - read-directory: func() -> result + read-directory: func() -> result; /// Synchronize the data and metadata of a file to disk. /// @@ -445,7 +445,7 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fsync` in POSIX. - sync: func() -> result<_, error-code> + sync: func() -> result<_, error-code>; /// Create a directory. /// @@ -453,7 +453,7 @@ interface types { create-directory-at: func( /// The relative path at which to create the directory. path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Return the attributes of an open file or directory. /// @@ -464,7 +464,7 @@ interface types { /// modified, use `metadata-hash`. /// /// Note: This was called `fd_filestat_get` in earlier versions of WASI. - stat: func() -> result + stat: func() -> result; /// Return the attributes of a file or directory. /// @@ -478,7 +478,7 @@ interface types { path-flags: path-flags, /// The relative path of the file or directory to inspect. path: string, - ) -> result + ) -> result; /// Adjust the timestamps of a file or directory. /// @@ -495,7 +495,7 @@ interface types { data-access-timestamp: new-timestamp, /// The desired values of the data modification timestamp. data-modification-timestamp: new-timestamp, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Create a hard link. /// @@ -509,7 +509,7 @@ interface types { new-descriptor: borrow, /// The relative destination path at which to create the hard link. new-path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Open a file or directory. /// @@ -540,7 +540,7 @@ interface types { %flags: descriptor-flags, /// Permissions to use when creating a new file. modes: modes - ) -> result + ) -> result; /// Read the contents of a symbolic link. /// @@ -551,7 +551,7 @@ interface types { readlink-at: func( /// The relative path of the symbolic link from which to read. path: string, - ) -> result + ) -> result; /// Remove a directory. /// @@ -561,7 +561,7 @@ interface types { remove-directory-at: func( /// The relative path to a directory to remove. path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Rename a filesystem object. /// @@ -573,7 +573,7 @@ interface types { new-descriptor: borrow, /// The relative destination path to which to rename the file or directory. new-path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Create a symbolic link (also known as a "symlink"). /// @@ -586,7 +586,7 @@ interface types { old-path: string, /// The relative destination path at which to create the symbolic link. new-path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Check accessibility of a filesystem path. /// @@ -605,7 +605,7 @@ interface types { path: string, /// The type of check to perform. %type: access-type - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Unlink a filesystem object that is not a directory. /// @@ -614,7 +614,7 @@ interface types { unlink-file-at: func( /// The relative path to a file to unlink. path: string, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Change the permissions of a filesystem object that is not a directory. /// @@ -629,7 +629,7 @@ interface types { path: string, /// The new permissions for the filesystem object. modes: modes, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Change the permissions of a directory. /// @@ -648,7 +648,7 @@ interface types { path: string, /// The new permissions for the directory. modes: modes, - ) -> result<_, error-code> + ) -> result<_, error-code>; /// Request a shared advisory lock for an open file. /// @@ -670,7 +670,7 @@ interface types { /// locking, this function returns `error-code::unsupported`. /// /// Note: This is similar to `flock(fd, LOCK_SH)` in Unix. - lock-shared: func() -> result<_, error-code> + lock-shared: func() -> result<_, error-code>; /// Request an exclusive advisory lock for an open file. /// @@ -694,7 +694,7 @@ interface types { /// locking, this function returns `error-code::unsupported`. /// /// Note: This is similar to `flock(fd, LOCK_EX)` in Unix. - lock-exclusive: func() -> result<_, error-code> + lock-exclusive: func() -> result<_, error-code>; /// Request a shared advisory lock for an open file. /// @@ -717,7 +717,7 @@ interface types { /// locking, this function returns `error-code::unsupported`. /// /// Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. - try-lock-shared: func() -> result<_, error-code> + try-lock-shared: func() -> result<_, error-code>; /// Request an exclusive advisory lock for an open file. /// @@ -742,12 +742,12 @@ interface types { /// locking, this function returns `error-code::unsupported`. /// /// Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. - try-lock-exclusive: func() -> result<_, error-code> + try-lock-exclusive: func() -> result<_, error-code>; /// Release a shared or exclusive lock on an open file. /// /// Note: This is similar to `flock(fd, LOCK_UN)` in Unix. - unlock: func() -> result<_, error-code> + unlock: func() -> result<_, error-code>; /// Test whether two descriptors refer to the same filesystem object. /// @@ -755,7 +755,7 @@ interface types { /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. /// wasi-filesystem does not expose device and inode numbers, so this function /// may be used instead. - is-same-object: func(other: borrow) -> bool + is-same-object: func(other: borrow) -> bool; /// Return a hash of the metadata associated with a filesystem object referred /// to by a descriptor. @@ -776,7 +776,7 @@ interface types { /// computed hash. /// /// However, none of these is required. - metadata-hash: func() -> result + metadata-hash: func() -> result; /// Return a hash of the metadata associated with a filesystem object referred /// to by a directory descriptor and a relative path. @@ -787,13 +787,13 @@ interface types { path-flags: path-flags, /// The relative path of the file or directory to inspect. path: string, - ) -> result + ) -> result; } /// A stream of directory entries. resource directory-entry-stream { /// Read a single directory entry from a `directory-entry-stream`. - read-directory-entry: func() -> result, error-code> + read-directory-entry: func() -> result, error-code>; } /// Attempts to extract a filesystem-related `error-code` from the stream diff --git a/crates/wasi/wit/deps/filesystem/world.wit b/crates/wasi/wit/deps/filesystem/world.wit index 5fa7eafdb850..bd472942da1a 100644 --- a/crates/wasi/wit/deps/filesystem/world.wit +++ b/crates/wasi/wit/deps/filesystem/world.wit @@ -1,6 +1,6 @@ -package wasi:filesystem +package wasi:filesystem; world imports { - import types - import preopens + import types; + import preopens; } diff --git a/crates/wasi/wit/deps/http/incoming-handler.wit b/crates/wasi/wit/deps/http/incoming-handler.wit index 70a6a043a0c7..6968d6331f8e 100644 --- a/crates/wasi/wit/deps/http/incoming-handler.wit +++ b/crates/wasi/wit/deps/http/incoming-handler.wit @@ -7,7 +7,7 @@ // that takes a `request` parameter and returns a `response` result. // interface incoming-handler { - use types.{incoming-request, response-outparam} + use types.{incoming-request, response-outparam}; // The `handle` function takes an outparam instead of returning its response // so that the component may stream its response while streaming any other @@ -20,5 +20,5 @@ interface incoming-handler { handle: func( request: incoming-request, response-out: response-outparam - ) + ); } diff --git a/crates/wasi/wit/deps/http/outgoing-handler.wit b/crates/wasi/wit/deps/http/outgoing-handler.wit index 9b6a73c0cb9b..286e2833bc8a 100644 --- a/crates/wasi/wit/deps/http/outgoing-handler.wit +++ b/crates/wasi/wit/deps/http/outgoing-handler.wit @@ -6,7 +6,7 @@ // that takes a `request` parameter and returns a `response` result. // interface outgoing-handler { - use types.{outgoing-request, request-options, future-incoming-response, error} + use types.{outgoing-request, request-options, future-incoming-response, error}; // The parameter and result types of the `handle` function allow the caller // to concurrently stream the bodies of the outgoing request and the incoming @@ -16,5 +16,5 @@ interface outgoing-handler { handle: func( request: outgoing-request, options: option - ) -> result + ) -> result; } diff --git a/crates/wasi/wit/deps/http/proxy.wit b/crates/wasi/wit/deps/http/proxy.wit index 162ab32b2348..8ee589207999 100644 --- a/crates/wasi/wit/deps/http/proxy.wit +++ b/crates/wasi/wit/deps/http/proxy.wit @@ -1,4 +1,4 @@ -package wasi:http +package wasi:http; // The `wasi:http/proxy` world captures a widely-implementable intersection of // hosts that includes HTTP forward and reverse proxies. Components targeting @@ -6,29 +6,29 @@ package wasi:http // outgoing HTTP requests. world proxy { // HTTP proxies have access to time and randomness. - import wasi:clocks/wall-clock - import wasi:clocks/monotonic-clock - import wasi:clocks/timezone - import wasi:random/random + import wasi:clocks/wall-clock; + import wasi:clocks/monotonic-clock; + import wasi:clocks/timezone; + import wasi:random/random; // Proxies have standard output and error streams which are expected to // terminate in a developer-facing console provided by the host. - import wasi:cli/stdout - import wasi:cli/stderr + import wasi:cli/stdout; + import wasi:cli/stderr; // TODO: this is a temporary workaround until component tooling is able to // gracefully handle the absence of stdin. Hosts must return an eof stream // for this import, which is what wasi-libc + tooling will do automatically // when this import is properly removed. - import wasi:cli/stdin + import wasi:cli/stdin; // This is the default handler to use when user code simply wants to make an // HTTP request (e.g., via `fetch()`). - import outgoing-handler + import outgoing-handler; // The host delivers incoming HTTP requests to a component by calling the // `handle` function of this exported interface. A host may arbitrarily reuse // or not reuse component instance when delivering incoming HTTP requests and // thus a component must be able to handle 0..N calls to `handle`. - export incoming-handler + export incoming-handler; } diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index edfa2fb8978a..f42d195af634 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -2,8 +2,8 @@ // define the HTTP resource types and operations used by the component's // imported and exported interfaces. interface types { - use wasi:io/streams.{input-stream, output-stream} - use wasi:io/poll.{pollable} + use wasi:io/streams.{input-stream, output-stream}; + use wasi:io/poll.{pollable}; // This type corresponds to HTTP standard Methods. variant method { @@ -43,28 +43,28 @@ interface types { resource fields { // Multiple values for a header are multiple entries in the list with the // same key. - constructor(entries: list>>) + constructor(entries: list>>); // Values off wire are not necessarily well formed, so they are given by // list instead of string. - get: func(name: string) -> list> + get: func(name: string) -> list>; // Values off wire are not necessarily well formed, so they are given by // list instead of string. - set: func(name: string, value: list>) - delete: func(name: string) - append: func(name: string, value: list) + set: func(name: string, value: list>); + delete: func(name: string); + append: func(name: string, value: list); // Values off wire are not necessarily well formed, so they are given by // list instead of string. - entries: func() -> list>> + entries: func() -> list>>; // Deep copy of all contents in a fields. - clone: func() -> fields + clone: func() -> fields; } - type headers = fields - type trailers = fields + type headers = fields; + type trailers = fields; // The following block defines the `incoming-request` and `outgoing-request` // resource types that correspond to HTTP standard Requests. Soon, when @@ -75,19 +75,19 @@ interface types { // above). The `consume` and `write` methods may only be called once (and // return failure thereafter). resource incoming-request { - method: func() -> method + method: func() -> method; - path-with-query: func() -> option + path-with-query: func() -> option; - scheme: func() -> option + scheme: func() -> option; - authority: func() -> option + authority: func() -> option; - headers: func() -> /* child */ headers + headers: func() -> /* child */ headers; // Will return the input-stream child at most once. If called more than // once, subsequent calls will return error. - consume: func() -> result + consume: func() -> result; } resource outgoing-request { @@ -97,11 +97,11 @@ interface types { scheme: option, authority: option, headers: borrow - ) + ); // Will return the outgoing-body child at most once. If called more than // once, subsequent calls will return error. - write: func() -> result< /* child */ outgoing-body> + write: func() -> result< /* child */ outgoing-body>; } // Additional optional parameters that can be set when making a request. @@ -127,11 +127,11 @@ interface types { // (the `wasi:http/handler` interface used for both incoming and outgoing can // simply return a `stream`). resource response-outparam { - set: static func(param: response-outparam, response: result) + set: static func(param: response-outparam, response: result); } // This type corresponds to the HTTP standard Status Code. - type status-code = u16 + type status-code = u16; // The following block defines the `incoming-response` and `outgoing-response` // resource types that correspond to HTTP standard Responses. Soon, when @@ -141,14 +141,14 @@ interface types { // type (that uses the single `stream` type mentioned above). The `consume` and // `write` methods may only be called once (and return failure thereafter). resource incoming-response { - status: func() -> status-code + status: func() -> status-code; - headers: func() -> /* child */ headers + headers: func() -> /* child */ headers; // May be called at most once. returns error if called additional times. // TODO: make incoming-request-consume work the same way, giving a child // incoming-body. - consume: func() -> result + consume: func() -> result; } resource incoming-body { @@ -156,41 +156,41 @@ interface types { // incoming-body is dropped (or consumed by call to // incoming-body-finish) before the input-stream is dropped. // May be called at most once. returns error if called additional times. - %stream: func() -> result + %stream: func() -> result; // takes ownership of incoming-body. this will trap if the // incoming-body-stream child is still alive! finish: static func(this: incoming-body) -> - /* transitive child of the incoming-response of incoming-body */ future-trailers + /* transitive child of the incoming-response of incoming-body */ future-trailers; } resource future-trailers { /// Pollable that resolves when the body has been fully read, and the trailers /// are ready to be consumed. - subscribe: func() -> /* child */ pollable + subscribe: func() -> /* child */ pollable; /// Retrieve reference to trailers, if they are ready. - get: func() -> option> + get: func() -> option>; } resource outgoing-response { - constructor(status-code: status-code, headers: borrow) + constructor(status-code: status-code, headers: borrow); /// Will give the child outgoing-response at most once. subsequent calls will /// return an error. - write: func() -> result + write: func() -> result; } resource outgoing-body { /// Will give the child output-stream at most once. subsequent calls will /// return an error. - write: func() -> result + write: func() -> result; /// Finalize an outgoing body, optionally providing trailers. This must be /// called to signal that the response is complete. If the `outgoing-body` is /// dropped without calling `outgoing-body-finalize`, the implementation /// should treat the body as corrupted. - finish: static func(this: outgoing-body, trailers: option) + finish: static func(this: outgoing-body, trailers: option); } /// The following block defines a special resource type used by the @@ -207,8 +207,8 @@ interface types { /// will return an error here. /// inner result indicates whether the incoming-response was available, or an /// error occured. - get: func() -> option>> + get: func() -> option>>; - subscribe: func() -> /* child */ pollable + subscribe: func() -> /* child */ pollable; } } diff --git a/crates/wasi/wit/deps/io/poll.wit b/crates/wasi/wit/deps/io/poll.wit index e95762b915db..254f5341871b 100644 --- a/crates/wasi/wit/deps/io/poll.wit +++ b/crates/wasi/wit/deps/io/poll.wit @@ -1,10 +1,10 @@ -package wasi:io +package wasi:io; /// A poll API intended to let users wait for I/O events on multiple handles /// at once. interface poll { /// A "pollable" handle. - resource pollable + resource pollable; /// Poll for completion on a set of pollables. /// @@ -24,11 +24,11 @@ interface poll { /// do any I/O so it doesn't fail. If any of the I/O sources identified by /// the pollables has an error, it is indicated by marking the source as /// being reaedy for I/O. - poll-list: func(in: list>) -> list + poll-list: func(in: list>) -> list; /// Poll for completion on a single pollable. /// /// This function is similar to `poll-list`, but operates on only a single /// pollable. When it returns, the handle is ready for I/O. - poll-one: func(in: borrow) + poll-one: func(in: borrow); } diff --git a/crates/wasi/wit/deps/io/streams.wit b/crates/wasi/wit/deps/io/streams.wit index 55562d1cbfce..81832b2da958 100644 --- a/crates/wasi/wit/deps/io/streams.wit +++ b/crates/wasi/wit/deps/io/streams.wit @@ -1,4 +1,4 @@ -package wasi:io +package wasi:io; /// WASI I/O is an I/O abstraction API which is currently focused on providing /// stream types. @@ -6,7 +6,7 @@ package wasi:io /// In the future, the component model is expected to add built-in stream types; /// when it does, they are expected to subsume this API. interface streams { - use poll.{pollable} + use poll.{pollable}; /// An error for input-stream and output-stream operations. variant stream-error { @@ -74,14 +74,14 @@ interface streams { read: func( /// The maximum number of bytes to read len: u64 - ) -> result, stream-error> + ) -> result, stream-error>; /// Read bytes from a stream, after blocking until at least one byte can /// be read. Except for blocking, identical to `read`. blocking-read: func( /// The maximum number of bytes to read len: u64 - ) -> result, stream-error> + ) -> result, stream-error>; /// Skip bytes from a stream. /// @@ -98,14 +98,14 @@ interface streams { skip: func( /// The maximum number of bytes to skip. len: u64, - ) -> result + ) -> result; /// Skip bytes from a stream, after blocking until at least one byte /// can be skipped. Except for blocking behavior, identical to `skip`. blocking-skip: func( /// The maximum number of bytes to skip. len: u64, - ) -> result + ) -> result; /// Create a `pollable` which will resolve once either the specified stream /// has bytes available to read or the other end of the stream has been @@ -113,7 +113,7 @@ interface streams { /// The created `pollable` is a child resource of the `input-stream`. /// Implementations may trap if the `input-stream` is dropped before /// all derived `pollable`s created with this function are dropped. - subscribe: func() -> pollable + subscribe: func() -> pollable; } @@ -135,7 +135,7 @@ interface streams { /// When this function returns 0 bytes, the `subscribe` pollable will /// become ready when this function will report at least 1 byte, or an /// error. - check-write: func() -> result + check-write: func() -> result; /// Perform a write. This function never blocks. /// @@ -146,7 +146,7 @@ interface streams { /// the last call to check-write provided a permit. write: func( contents: list - ) -> result<_, stream-error> + ) -> result<_, stream-error>; /// Perform a write of up to 4096 bytes, and then flush the stream. Block /// until all of these operations are complete, or an error occurs. @@ -174,7 +174,7 @@ interface streams { /// ``` blocking-write-and-flush: func( contents: list - ) -> result<_, stream-error> + ) -> result<_, stream-error>; /// Request to flush buffered output. This function never blocks. /// @@ -186,11 +186,11 @@ interface streams { /// writes (`check-write` will return `ok(0)`) until the flush has /// completed. The `subscribe` pollable will become ready when the /// flush has completed and the stream can accept more writes. - flush: func() -> result<_, stream-error> + flush: func() -> result<_, stream-error>; /// Request to flush buffered output, and block until flush completes /// and stream is ready for writing again. - blocking-flush: func() -> result<_, stream-error> + blocking-flush: func() -> result<_, stream-error>; /// Create a `pollable` which will resolve once the output-stream /// is ready for more writing, or an error has occured. When this @@ -202,7 +202,7 @@ interface streams { /// The created `pollable` is a child resource of the `output-stream`. /// Implementations may trap if the `output-stream` is dropped before /// all derived `pollable`s created with this function are dropped. - subscribe: func() -> pollable + subscribe: func() -> pollable; /// Write zeroes to a stream. /// @@ -213,7 +213,7 @@ interface streams { write-zeroes: func( /// The number of zero-bytes to write len: u64 - ) -> result<_, stream-error> + ) -> result<_, stream-error>; /// Perform a write of up to 4096 zeroes, and then flush the stream. /// Block until all of these operations are complete, or an error @@ -242,7 +242,7 @@ interface streams { blocking-write-zeroes-and-flush: func( /// The number of zero-bytes to write len: u64 - ) -> result<_, stream-error> + ) -> result<_, stream-error>; /// Read from one stream and write to another. /// @@ -256,7 +256,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result + ) -> result; /// Read from one stream and write to another, with blocking. /// @@ -267,7 +267,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result + ) -> result; /// Forward the entire contents of an input stream to an output stream. /// @@ -284,6 +284,6 @@ interface streams { forward: func( /// The stream to read from src: input-stream - ) -> result + ) -> result; } } diff --git a/crates/wasi/wit/deps/io/world.wit b/crates/wasi/wit/deps/io/world.wit index 8738dba756d9..05244a965fb2 100644 --- a/crates/wasi/wit/deps/io/world.wit +++ b/crates/wasi/wit/deps/io/world.wit @@ -1,6 +1,6 @@ -package wasi:io +package wasi:io; world imports { - import streams - import poll + import streams; + import poll; } diff --git a/crates/wasi/wit/deps/logging/logging.wit b/crates/wasi/wit/deps/logging/logging.wit index b0cc4514dc8f..da537e479926 100644 --- a/crates/wasi/wit/deps/logging/logging.wit +++ b/crates/wasi/wit/deps/logging/logging.wit @@ -1,4 +1,4 @@ -package wasi:logging +package wasi:logging; /// WASI Logging is a logging API intended to let users emit log messages with /// simple priority levels and context values. @@ -33,5 +33,5 @@ interface logging { /// sent, a context, which is an uninterpreted string meant to help /// consumers group similar messages, and a string containing the message /// text. - log: func(level: level, context: string, message: string) + log: func(level: level, context: string, message: string); } diff --git a/crates/wasi/wit/deps/logging/world.wit b/crates/wasi/wit/deps/logging/world.wit index 7d49acfaddaa..ede6286430b6 100644 --- a/crates/wasi/wit/deps/logging/world.wit +++ b/crates/wasi/wit/deps/logging/world.wit @@ -1,5 +1,5 @@ -package wasi:logging +package wasi:logging; world imports { - import logging + import logging; } diff --git a/crates/wasi/wit/deps/random/insecure-seed.wit b/crates/wasi/wit/deps/random/insecure-seed.wit index ff2ff65d0754..139aed15927c 100644 --- a/crates/wasi/wit/deps/random/insecure-seed.wit +++ b/crates/wasi/wit/deps/random/insecure-seed.wit @@ -20,5 +20,5 @@ interface insecure-seed { /// This will likely be changed to a value import, to prevent it from being /// called multiple times and potentially used for purposes other than DoS /// protection. - insecure-seed: func() -> tuple + insecure-seed: func() -> tuple; } diff --git a/crates/wasi/wit/deps/random/insecure.wit b/crates/wasi/wit/deps/random/insecure.wit index ff0826822d5f..2ffd223cb344 100644 --- a/crates/wasi/wit/deps/random/insecure.wit +++ b/crates/wasi/wit/deps/random/insecure.wit @@ -11,11 +11,11 @@ interface insecure { /// There are no requirements on the values of the returned bytes, however /// implementations are encouraged to return evenly distributed values with /// a long period. - get-insecure-random-bytes: func(len: u64) -> list + get-insecure-random-bytes: func(len: u64) -> list; /// Return an insecure pseudo-random `u64` value. /// /// This function returns the same type of pseudo-random data as /// `get-insecure-random-bytes`, represented as a `u64`. - get-insecure-random-u64: func() -> u64 + get-insecure-random-u64: func() -> u64; } diff --git a/crates/wasi/wit/deps/random/random.wit b/crates/wasi/wit/deps/random/random.wit index 2a282dab7eb9..2c3c6a859c49 100644 --- a/crates/wasi/wit/deps/random/random.wit +++ b/crates/wasi/wit/deps/random/random.wit @@ -15,11 +15,11 @@ interface random { /// This function must always return fresh data. Deterministic environments /// must omit this function, rather than implementing it with deterministic /// data. - get-random-bytes: func(len: u64) -> list + get-random-bytes: func(len: u64) -> list; /// Return a cryptographically-secure random or pseudo-random `u64` value. /// /// This function returns the same type of data as `get-random-bytes`, /// represented as a `u64`. - get-random-u64: func() -> u64 + get-random-u64: func() -> u64; } diff --git a/crates/wasi/wit/deps/random/world.wit b/crates/wasi/wit/deps/random/world.wit index 41dc9ed10353..bb1dd7b592e7 100644 --- a/crates/wasi/wit/deps/random/world.wit +++ b/crates/wasi/wit/deps/random/world.wit @@ -1,7 +1,7 @@ -package wasi:random +package wasi:random; world imports { - import random - import insecure - import insecure-seed + import random; + import insecure; + import insecure-seed; } diff --git a/crates/wasi/wit/deps/sockets/instance-network.wit b/crates/wasi/wit/deps/sockets/instance-network.wit index d911a29cc8dd..14e4479e6c0c 100644 --- a/crates/wasi/wit/deps/sockets/instance-network.wit +++ b/crates/wasi/wit/deps/sockets/instance-network.wit @@ -1,9 +1,9 @@ /// This interface provides a value-export of the default network handle.. interface instance-network { - use network.{network} + use network.{network}; /// Get a handle to the default network. - instance-network: func() -> network + instance-network: func() -> network; } diff --git a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit index f998aae140ab..da9b435d9ef9 100644 --- a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit +++ b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit @@ -1,13 +1,13 @@ interface ip-name-lookup { - use wasi:io/poll.{pollable} - use network.{network, error-code, ip-address, ip-address-family} + use wasi:io/poll.{pollable}; + use network.{network, error-code, ip-address, ip-address-family}; /// Resolve an internet host name to a list of IP addresses. - /// + /// /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. - /// + /// /// # Parameters /// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted /// to ASCII using IDNA encoding. @@ -17,45 +17,45 @@ interface ip-name-lookup { /// systems without an active IPv6 interface. Notes: /// - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address. /// - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged. - /// + /// /// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream` /// that can be used to (asynchronously) fetch the results. - /// + /// /// At the moment, the stream never completes successfully with 0 items. Ie. the first call /// to `resolve-next-address` never returns `ok(none)`. This may change in the future. - /// + /// /// # Typical errors /// - `invalid-name`: `name` is a syntactically invalid domain name. /// - `invalid-name`: `name` is an IP address. /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) - /// + /// /// # References: /// - /// - /// - /// - - resolve-addresses: func(network: borrow, name: string, address-family: option, include-unavailable: bool) -> result + resolve-addresses: func(network: borrow, name: string, address-family: option, include-unavailable: bool) -> result; resource resolve-address-stream { /// Returns the next address from the resolver. - /// + /// /// This function should be called multiple times. On each call, it will /// return the next address in connection order preference. If all /// addresses have been exhausted, this function returns `none`. - /// + /// /// This function never returns IPv4-mapped IPv6 addresses. - /// + /// /// # Typical errors /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) - resolve-next-address: func() -> result, error-code> + resolve-next-address: func() -> result, error-code>; /// Create a `pollable` which will resolve once the stream is ready for I/O. - /// + /// /// Note: this function is here for WASI Preview2 only. /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func() -> pollable + subscribe: func() -> pollable; } } diff --git a/crates/wasi/wit/deps/sockets/network.wit b/crates/wasi/wit/deps/sockets/network.wit index 8214eaaf7211..03755253b294 100644 --- a/crates/wasi/wit/deps/sockets/network.wit +++ b/crates/wasi/wit/deps/sockets/network.wit @@ -3,10 +3,10 @@ interface network { /// An opaque resource that represents access to (a subset of) the network. /// This enables context-based security for networking. /// There is no need for this to map 1:1 to a physical network interface. - resource network + resource network; /// Error codes. - /// + /// /// In theory, every API can return any error code. /// In practice, API's typically only return the errors documented per API /// combined with a couple of errors that are always possible: @@ -14,7 +14,7 @@ interface network { /// - `access-denied` /// - `not-supported` /// - `out-of-memory` - /// + /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. enum error-code { // ### GENERAL ERRORS ### @@ -23,17 +23,17 @@ interface network { unknown, /// Access denied. - /// + /// /// POSIX equivalent: EACCES, EPERM access-denied, /// The operation is not supported. - /// + /// /// POSIX equivalent: EOPNOTSUPP not-supported, /// Not enough memory to complete the operation. - /// + /// /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY out-of-memory, @@ -46,12 +46,12 @@ interface network { /// Trying to finish an asynchronous operation that: /// - has not been started yet, or: /// - was already finished by a previous `finish-*` call. - /// + /// /// Note: this is scheduled to be removed when `future`s are natively supported. not-in-progress, /// The operation has been aborted because it could not be completed immediately. - /// + /// /// Note: this is scheduled to be removed when `future`s are natively supported. would-block, @@ -79,7 +79,7 @@ interface network { /// A new socket resource could not be created because of a system limit. new-socket-limit, - + /// The socket is already attached to another network. already-attached, @@ -106,10 +106,10 @@ interface network { /// The remote address is not reachable remote-unreachable, - + // ### TCP SOCKET ERRORS ### - + /// The socket is already in the Listener state. already-listening, @@ -121,14 +121,14 @@ interface network { /// The connection was reset. connection-reset, - + // ### UDP SOCKET ERRORS ### datagram-too-large, // ### NAME LOOKUP ERRORS ### - + /// The provided name is a syntactically invalid domain name. invalid-name, @@ -144,14 +144,14 @@ interface network { enum ip-address-family { /// Similar to `AF_INET` in POSIX. - ipv4, + ipv4, /// Similar to `AF_INET6` in POSIX. ipv6, } - type ipv4-address = tuple - type ipv6-address = tuple + type ipv4-address = tuple; + type ipv6-address = tuple; variant ip-address { ipv4(ipv4-address), diff --git a/crates/wasi/wit/deps/sockets/tcp-create-socket.wit b/crates/wasi/wit/deps/sockets/tcp-create-socket.wit index f43bc8979047..b64cabba7993 100644 --- a/crates/wasi/wit/deps/sockets/tcp-create-socket.wit +++ b/crates/wasi/wit/deps/sockets/tcp-create-socket.wit @@ -1,27 +1,27 @@ interface tcp-create-socket { - use network.{network, error-code, ip-address-family} - use tcp.{tcp-socket} + use network.{network, error-code, ip-address-family}; + use tcp.{tcp-socket}; /// Create a new TCP socket. - /// + /// /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. - /// + /// /// This function does not require a network capability handle. This is considered to be safe because /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. - /// + /// /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. - /// + /// /// # Typical errors /// - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP) /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) - /// + /// /// # References /// - /// - /// - /// - - create-tcp-socket: func(address-family: ip-address-family) -> result + create-tcp-socket: func(address-family: ip-address-family) -> result; } diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit index 175626cc7620..0ae7c05e8fcb 100644 --- a/crates/wasi/wit/deps/sockets/tcp.wit +++ b/crates/wasi/wit/deps/sockets/tcp.wit @@ -1,8 +1,8 @@ interface tcp { - use wasi:io/streams.{input-stream, output-stream} - use wasi:io/poll.{pollable} - use network.{network, error-code, ip-socket-address, ip-address-family} + use wasi:io/streams.{input-stream, output-stream}; + use wasi:io/poll.{pollable}; + use network.{network, error-code, ip-socket-address, ip-address-family}; enum shutdown-type { /// Similar to `SHUT_RD` in POSIX. @@ -23,38 +23,38 @@ interface tcp { /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which /// network interface(s) to bind to. /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// + /// /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will /// implicitly bind the socket. - /// + /// /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// + /// /// # Typical `start` errors /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) /// - `already-bound`: The socket is already bound. (EINVAL) /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - /// + /// /// # Typical `finish` errors /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) /// - `address-in-use`: Address is already in use. (EADDRINUSE) /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) /// - `not-in-progress`: A `bind` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - /// - /// - - start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code> - finish-bind: func() -> result<_, error-code> + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; /// Connect to a remote endpoint. - /// + /// /// On success: /// - the socket is transitioned into the Connection state /// - a pair of streams is returned that can be used to read & write to the connection - /// + /// /// # Typical `start` errors /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) @@ -63,7 +63,7 @@ interface tcp { /// - `already-connected`: The socket is already in the Connection state. (EISCONN) /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - /// + /// /// # Typical `finish` errors /// - `timeout`: Connection timed out. (ETIMEDOUT) /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) @@ -72,23 +72,23 @@ interface tcp { /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) /// - `not-in-progress`: A `connect` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - /// - /// - - start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code> - finish-connect: func() -> result, error-code> + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + finish-connect: func() -> result, error-code>; /// Start listening for new connections. - /// + /// /// Transitions the socket into the Listener state. - /// + /// /// Unlike POSIX: /// - this function is async. This enables interactive WASI hosts to inject permission prompts. /// - the socket must already be explicitly bound. - /// + /// /// # Typical `start` errors /// - `not-bound`: The socket is not bound to any local address. (EDESTADDRREQ) /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) @@ -105,145 +105,145 @@ interface tcp { /// - /// - /// - - start-listen: func() -> result<_, error-code> - finish-listen: func() -> result<_, error-code> + start-listen: func() -> result<_, error-code>; + finish-listen: func() -> result<_, error-code>; /// Accept a new client socket. - /// + /// /// The returned socket is bound and in the Connection state. - /// + /// /// On success, this function returns the newly accepted client socket along with /// a pair of streams that can be used to read & write to the connection. - /// + /// /// # Typical errors /// - `not-listening`: Socket is not in the Listener state. (EINVAL) /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) - /// + /// /// Host implementations must skip over transient errors returned by the native accept syscall. - /// + /// /// # References /// - /// - /// - /// - - accept: func() -> result, error-code> + accept: func() -> result, error-code>; /// Get the bound local address. - /// + /// /// # Typical errors /// - `not-bound`: The socket is not bound to any local address. - /// + /// /// # References /// - /// - /// - /// - - local-address: func() -> result + local-address: func() -> result; /// Get the bound remote address. - /// + /// /// # Typical errors /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// + /// /// # References /// - /// - /// - /// - - remote-address: func() -> result + remote-address: func() -> result; /// Whether this is a IPv4 or IPv6 socket. - /// + /// /// Equivalent to the SO_DOMAIN socket option. - address-family: func() -> ip-address-family - + address-family: func() -> ip-address-family; + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// + /// /// Equivalent to the IPV6_V6ONLY socket option. - /// + /// /// # Typical errors /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. /// - `already-bound`: (set) The socket is already bound. /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - ipv6-only: func() -> result - set-ipv6-only: func(value: bool) -> result<_, error-code> + ipv6-only: func() -> result; + set-ipv6-only: func(value: bool) -> result<_, error-code>; /// Hints the desired listen queue size. Implementations are free to ignore this. - /// + /// /// # Typical errors /// - `already-connected`: (set) The socket is already in the Connection state. /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - set-listen-backlog-size: func(value: u64) -> result<_, error-code> + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; /// Equivalent to the SO_KEEPALIVE socket option. - /// + /// /// # Typical errors /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - keep-alive: func() -> result - set-keep-alive: func(value: bool) -> result<_, error-code> + keep-alive: func() -> result; + set-keep-alive: func(value: bool) -> result<_, error-code>; /// Equivalent to the TCP_NODELAY socket option. - /// + /// /// # Typical errors /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - no-delay: func() -> result - set-no-delay: func(value: bool) -> result<_, error-code> - + no-delay: func() -> result; + set-no-delay: func(value: bool) -> result<_, error-code>; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// + /// /// # Typical errors /// - `already-connected`: (set) The socket is already in the Connection state. /// - `already-listening`: (set) The socket is already in the Listener state. /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - unicast-hop-limit: func() -> result - set-unicast-hop-limit: func(value: u8) -> result<_, error-code> + unicast-hop-limit: func() -> result; + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; /// The kernel buffer space reserved for sends/receives on this socket. - /// + /// /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. /// In other words, after setting a value, reading the same setting back may return a different value. - /// + /// /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of /// actual data to be sent/received by the application, because the kernel might also use the buffer space /// for internal metadata structures. - /// + /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// + /// /// # Typical errors /// - `already-connected`: (set) The socket is already in the Connection state. /// - `already-listening`: (set) The socket is already in the Listener state. /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - receive-buffer-size: func() -> result - set-receive-buffer-size: func(value: u64) -> result<_, error-code> - send-buffer-size: func() -> result - set-send-buffer-size: func(value: u64) -> result<_, error-code> + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// + /// /// Note: this function is here for WASI Preview2 only. /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func() -> pollable + subscribe: func() -> pollable; /// Initiate a graceful shutdown. - /// + /// /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. /// Any data still in the receive queue at time of calling `shutdown` will be discarded. /// - send: the socket is not expecting to send any more data to the peer. All subsequent write /// operations on the `output-stream` associated with this socket will return an error. /// - both: same effect as receive & send combined. - /// + /// /// The shutdown function does not close (drop) the socket. - /// + /// /// # Typical errors /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) - /// + /// /// # References /// - /// - /// - /// - - shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code> + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; } } diff --git a/crates/wasi/wit/deps/sockets/udp-create-socket.wit b/crates/wasi/wit/deps/sockets/udp-create-socket.wit index cd4c08fb1000..64d899456ca8 100644 --- a/crates/wasi/wit/deps/sockets/udp-create-socket.wit +++ b/crates/wasi/wit/deps/sockets/udp-create-socket.wit @@ -1,27 +1,27 @@ interface udp-create-socket { - use network.{network, error-code, ip-address-family} - use udp.{udp-socket} + use network.{network, error-code, ip-address-family}; + use udp.{udp-socket}; /// Create a new UDP socket. - /// + /// /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. - /// + /// /// This function does not require a network capability handle. This is considered to be safe because /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. - /// + /// /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. - /// + /// /// # Typical errors /// - `not-supported`: The host does not support UDP sockets. (EOPNOTSUPP) /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) - /// + /// /// # References: /// - /// - /// - /// - - create-udp-socket: func(address-family: ip-address-family) -> result + create-udp-socket: func(address-family: ip-address-family) -> result; } diff --git a/crates/wasi/wit/deps/sockets/udp.wit b/crates/wasi/wit/deps/sockets/udp.wit index 01e5b95b97b7..a29250caa9af 100644 --- a/crates/wasi/wit/deps/sockets/udp.wit +++ b/crates/wasi/wit/deps/sockets/udp.wit @@ -1,7 +1,7 @@ interface udp { - use wasi:io/poll.{pollable} - use network.{network, error-code, ip-socket-address, ip-address-family} + use wasi:io/poll.{pollable}; + use network.{network, error-code, ip-socket-address, ip-address-family}; record datagram { @@ -25,74 +25,74 @@ interface udp { /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which /// network interface(s) to bind to. /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// + /// /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. - /// + /// /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// + /// /// # Typical `start` errors /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) /// - `already-bound`: The socket is already bound. (EINVAL) /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) - /// + /// /// # Typical `finish` errors /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) /// - `address-in-use`: Address is already in use. (EADDRINUSE) /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) /// - `not-in-progress`: A `bind` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - /// - /// - - start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code> - finish-bind: func() -> result<_, error-code> + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; /// Set the destination address. - /// + /// /// The local-address is updated based on the best network path to `remote-address`. - /// + /// /// When a destination address is set: /// - all receive operations will only return datagrams sent from the provided `remote-address`. /// - the `send` function can only be used to send to this destination. - /// + /// /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". - /// + /// /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// + /// /// # Typical `start` errors /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) - /// + /// /// # Typical `finish` errors /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) /// - `not-in-progress`: A `connect` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - /// - /// - - start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code> - finish-connect: func() -> result<_, error-code> + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + finish-connect: func() -> result<_, error-code>; /// Receive messages on the socket. - /// + /// /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. /// The returned list may contain fewer elements than requested, but never more. /// If `max-results` is 0, this function returns successfully with an empty list. - /// + /// /// # Typical errors /// - `not-bound`: The socket is not bound to any local address. (EINVAL) /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - @@ -102,22 +102,22 @@ interface udp { /// - /// - /// - - receive: func(max-results: u64) -> result, error-code> + receive: func(max-results: u64) -> result, error-code>; /// Send messages on the socket. - /// + /// /// This function attempts to send all provided `datagrams` on the socket without blocking and /// returns how many messages were actually sent (or queued for sending). - /// + /// /// This function semantically behaves the same as iterating the `datagrams` list and sequentially /// sending each individual datagram until either the end of the list has been reached or the first error occurred. /// If at least one datagram has been sent successfully, this function never returns an error. - /// + /// /// If the input list is empty, the function returns `ok(0)`. - /// + /// /// The remote address option is required. To send a message to the "connected" peer, /// call `remote-address` to get their address. - /// + /// /// # Typical errors /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) @@ -127,7 +127,7 @@ interface udp { /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) - /// + /// /// # References /// - /// - @@ -137,78 +137,78 @@ interface udp { /// - /// - /// - - send: func(datagrams: list) -> result + send: func(datagrams: list) -> result; /// Get the current bound address. - /// + /// /// # Typical errors /// - `not-bound`: The socket is not bound to any local address. - /// + /// /// # References /// - /// - /// - /// - - local-address: func() -> result + local-address: func() -> result; /// Get the address set with `connect`. - /// + /// /// # Typical errors /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// + /// /// # References /// - /// - /// - /// - - remote-address: func() -> result + remote-address: func() -> result; /// Whether this is a IPv4 or IPv6 socket. - /// + /// /// Equivalent to the SO_DOMAIN socket option. - address-family: func() -> ip-address-family + address-family: func() -> ip-address-family; /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// + /// /// Equivalent to the IPV6_V6ONLY socket option. - /// + /// /// # Typical errors /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. /// - `already-bound`: (set) The socket is already bound. /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - ipv6-only: func() -> result - set-ipv6-only: func(value: bool) -> result<_, error-code> + ipv6-only: func() -> result; + set-ipv6-only: func(value: bool) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// + /// /// # Typical errors /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - unicast-hop-limit: func() -> result - set-unicast-hop-limit: func(value: u8) -> result<_, error-code> + unicast-hop-limit: func() -> result; + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; /// The kernel buffer space reserved for sends/receives on this socket. - /// + /// /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. /// In other words, after setting a value, reading the same setting back may return a different value. - /// + /// /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of /// actual data to be sent/received by the application, because the kernel might also use the buffer space /// for internal metadata structures. - /// + /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// + /// /// # Typical errors /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - receive-buffer-size: func() -> result - set-receive-buffer-size: func(value: u64) -> result<_, error-code> - send-buffer-size: func() -> result - set-send-buffer-size: func(value: u64) -> result<_, error-code> + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// + /// /// Note: this function is here for WASI Preview2 only. /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func() -> pollable + subscribe: func() -> pollable; } } diff --git a/crates/wasi/wit/deps/sockets/world.wit b/crates/wasi/wit/deps/sockets/world.wit index 12f3c2868177..432b0dc99d00 100644 --- a/crates/wasi/wit/deps/sockets/world.wit +++ b/crates/wasi/wit/deps/sockets/world.wit @@ -1,11 +1,11 @@ -package wasi:sockets +package wasi:sockets; world imports { - import instance-network - import network - import udp - import udp-create-socket - import tcp - import tcp-create-socket - import ip-name-lookup + import instance-network; + import network; + import udp; + import udp-create-socket; + import tcp; + import tcp-create-socket; + import ip-name-lookup; } diff --git a/crates/wasi/wit/main.wit b/crates/wasi/wit/main.wit index 739e1bd4ac48..e843cefceaf2 100644 --- a/crates/wasi/wit/main.wit +++ b/crates/wasi/wit/main.wit @@ -1,33 +1,33 @@ -package wasmtime:wasi +package wasmtime:wasi; // All of the same imports available in the wasi:cli/command world, but no // export required: world preview1-adapter-reactor { - import wasi:clocks/wall-clock - import wasi:clocks/monotonic-clock - import wasi:clocks/timezone - import wasi:filesystem/types - import wasi:filesystem/preopens - import wasi:sockets/instance-network - import wasi:sockets/ip-name-lookup - import wasi:sockets/network - import wasi:sockets/tcp-create-socket - import wasi:sockets/tcp - import wasi:sockets/udp-create-socket - import wasi:sockets/udp - import wasi:random/random - import wasi:random/insecure - import wasi:random/insecure-seed - import wasi:io/poll - import wasi:io/streams - import wasi:cli/environment - import wasi:cli/exit - import wasi:cli/stdin - import wasi:cli/stdout - import wasi:cli/stderr - import wasi:cli/terminal-input - import wasi:cli/terminal-output - import wasi:cli/terminal-stdin - import wasi:cli/terminal-stdout - import wasi:cli/terminal-stderr + import wasi:clocks/wall-clock; + import wasi:clocks/monotonic-clock; + import wasi:clocks/timezone; + import wasi:filesystem/types; + import wasi:filesystem/preopens; + import wasi:sockets/instance-network; + import wasi:sockets/ip-name-lookup; + import wasi:sockets/network; + import wasi:sockets/tcp-create-socket; + import wasi:sockets/tcp; + import wasi:sockets/udp-create-socket; + import wasi:sockets/udp; + import wasi:random/random; + import wasi:random/insecure; + import wasi:random/insecure-seed; + import wasi:io/poll; + import wasi:io/streams; + import wasi:cli/environment; + import wasi:cli/exit; + import wasi:cli/stdin; + import wasi:cli/stdout; + import wasi:cli/stderr; + import wasi:cli/terminal-input; + import wasi:cli/terminal-output; + import wasi:cli/terminal-stdin; + import wasi:cli/terminal-stdout; + import wasi:cli/terminal-stderr; } diff --git a/crates/wasi/wit/test.wit b/crates/wasi/wit/test.wit index 03073513f8e6..fc9c357522bf 100644 --- a/crates/wasi/wit/test.wit +++ b/crates/wasi/wit/test.wit @@ -1,44 +1,44 @@ // only used as part of `test-programs` world test-reactor { - import wasi:cli/environment - import wasi:io/poll - import wasi:io/streams - import wasi:filesystem/types - import wasi:filesystem/preopens - import wasi:cli/exit + import wasi:cli/environment; + import wasi:io/poll; + import wasi:io/streams; + import wasi:filesystem/types; + import wasi:filesystem/preopens; + import wasi:cli/exit; - export add-strings: func(s: list) -> u32 - export get-strings: func() -> list + export add-strings: func(s: list) -> u32; + export get-strings: func() -> list; - use wasi:io/streams.{output-stream} + use wasi:io/streams.{output-stream}; - export write-strings-to: func(o: output-stream) -> result + export write-strings-to: func(o: output-stream) -> result; - use wasi:filesystem/types.{descriptor-stat} - export pass-an-imported-record: func(d: descriptor-stat) -> string + use wasi:filesystem/types.{descriptor-stat}; + export pass-an-imported-record: func(d: descriptor-stat) -> string; } world test-command { - import wasi:io/poll - import wasi:io/streams - import wasi:cli/environment - import wasi:cli/stdin - import wasi:cli/stdout - import wasi:cli/stderr + import wasi:io/poll; + import wasi:io/streams; + import wasi:cli/environment; + import wasi:cli/stdin; + import wasi:cli/stdout; + import wasi:cli/stderr; } world test-command-with-sockets { - import wasi:io/poll - import wasi:io/streams - import wasi:cli/environment - import wasi:cli/stdin - import wasi:cli/stdout - import wasi:cli/stderr - import wasi:sockets/tcp - import wasi:sockets/tcp-create-socket - import wasi:sockets/network - import wasi:sockets/instance-network - import wasi:sockets/ip-name-lookup - import wasi:clocks/monotonic-clock + import wasi:io/poll; + import wasi:io/streams; + import wasi:cli/environment; + import wasi:cli/stdin; + import wasi:cli/stdout; + import wasi:cli/stderr; + import wasi:sockets/tcp; + import wasi:sockets/tcp-create-socket; + import wasi:sockets/network; + import wasi:sockets/instance-network; + import wasi:sockets/ip-name-lookup; + import wasi:clocks/monotonic-clock; } diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs index 73aaad8e1cfc..b44d4f5ca2f7 100644 --- a/crates/wasmtime/src/component/mod.rs +++ b/crates/wasmtime/src/component/mod.rs @@ -74,11 +74,11 @@ pub(crate) use self::store::ComponentStoreData; /// ```text,ignore /// // wit/my-component.wit /// -/// package my:project +/// package my:project; /// /// world hello-world { -/// import name: func() -> string -/// export greet: func() +/// import name: func() -> string; +/// export greet: func(); /// } /// ``` /// @@ -153,18 +153,18 @@ pub(crate) use self::store::ComponentStoreData; /// ```text,ignore /// // wit/my-component.wit /// -/// package my:project +/// package my:project; /// /// interface host { -/// gen-random-integer: func() -> u32 -/// sha256: func(bytes: list) -> string +/// gen-random-integer: func() -> u32; +/// sha256: func(bytes: list) -> string; /// } /// /// default world hello-world { -/// import host +/// import host; /// /// export demo: interface { -/// run: func() +/// run: func(); /// } /// } /// ``` @@ -258,7 +258,7 @@ pub(crate) use self::store::ComponentStoreData; /// // Instead of `path` the WIT document can be provided inline if /// // desired. /// inline: " -/// package my:inline +/// package my:inline; /// /// world foo { /// // ... @@ -326,7 +326,7 @@ pub(crate) use self::store::ComponentStoreData; /// // Restrict the code generated to what's needed for the interface /// // imports in the inlined WIT document fragment. /// interfaces: " -/// import wasi:cli/command +/// import wasi:cli/command; /// ", /// /// // Remap imported interfaces or resources to types defined in Rust diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs index 583e1b32bf59..1c0b713a1c6e 100644 --- a/tests/all/component_model/bindgen.rs +++ b/tests/all/component_model/bindgen.rs @@ -15,14 +15,14 @@ mod no_imports { wasmtime::component::bindgen!({ inline: " - package foo:foo + package foo:foo; world no-imports { export foo: interface { - foo: func() + foo: func(); } - export bar: func() + export bar: func(); } ", }); @@ -62,14 +62,14 @@ mod one_import { wasmtime::component::bindgen!({ inline: " - package foo:foo + package foo:foo; world one-import { import foo: interface { - foo: func() + foo: func(); } - export bar: func() + export bar: func(); } ", }); @@ -127,14 +127,14 @@ mod resources_at_world_level { wasmtime::component::bindgen!({ inline: " - package foo:foo + package foo:foo; world resources { resource x { - constructor() + constructor(); } - export y: func(x: x) + export y: func(x: x); } ", }); @@ -216,22 +216,22 @@ mod resources_at_interface_level { wasmtime::component::bindgen!({ inline: " - package foo:foo + package foo:foo; interface def { resource x { - constructor() + constructor(); } } interface user { - use def.{x} + use def.{x}; - y: func(x: x) + y: func(x: x); } world resources { - export user + export user; } ", }); @@ -322,12 +322,12 @@ mod async_config { wasmtime::component::bindgen!({ inline: " - package foo:foo + package foo:foo; world t1 { - import x: func() - import y: func() - export z: func() + import x: func(); + import y: func(); + export z: func(); } ", async: true, @@ -352,12 +352,12 @@ mod async_config { wasmtime::component::bindgen!({ inline: " - package foo:foo + package foo:foo; world t2 { - import x: func() - import y: func() - export z: func() + import x: func(); + import y: func(); + export z: func(); } ", async: { @@ -382,12 +382,12 @@ mod async_config { wasmtime::component::bindgen!({ inline: " - package foo:foo + package foo:foo; world t3 { - import x: func() - import y: func() - export z: func() + import x: func(); + import y: func(); + export z: func(); } ", async: { @@ -418,27 +418,27 @@ mod exported_resources { wasmtime::component::bindgen!({ inline: " - package foo:foo + package foo:foo; interface a { resource x { - constructor() + constructor(); } } world resources { export b: interface { - use a.{x as y} + use a.{x as y}; resource x { - constructor(y: y) - foo: func() -> u32 + constructor(y: y); + foo: func() -> u32; } } - resource x + resource x; - export f: func(x1: x, x2: x) -> x + export f: func(x1: x, x2: x) -> x; } ", }); diff --git a/tests/all/component_model/bindgen/ownership.rs b/tests/all/component_model/bindgen/ownership.rs index fd0dd89da269..68ebab8573bf 100644 --- a/tests/all/component_model/bindgen/ownership.rs +++ b/tests/all/component_model/bindgen/ownership.rs @@ -118,10 +118,10 @@ fn component() -> String { fn owning() -> Result<()> { wasmtime::component::bindgen!({ inline: " - package inline:inline + package inline:inline; world test { export lists: interface { - foo: func(a: list>) -> list> + foo: func(a: list>) -> list>; } export thing-in: interface { @@ -130,7 +130,7 @@ fn owning() -> Result<()> { value: list } - bar: func(a: thing) + bar: func(a: thing); } export thing-in-and-out: interface { @@ -139,7 +139,7 @@ fn owning() -> Result<()> { value: list } - baz: func(a: thing) -> thing + baz: func(a: thing) -> thing; } }", ownership: Owning @@ -186,10 +186,10 @@ fn owning() -> Result<()> { fn borrowing_no_duplication() -> Result<()> { wasmtime::component::bindgen!({ inline: " - package inline:inline + package inline:inline; world test { export lists: interface { - foo: func(a: list>) -> list> + foo: func(a: list>) -> list>; } export thing-in: interface { @@ -198,7 +198,7 @@ fn borrowing_no_duplication() -> Result<()> { value: list } - bar: func(a: thing) + bar: func(a: thing); } export thing-in-and-out: interface { @@ -207,7 +207,7 @@ fn borrowing_no_duplication() -> Result<()> { value: list } - baz: func(a: thing) -> thing + baz: func(a: thing) -> thing; } }", ownership: Borrowing { @@ -256,10 +256,10 @@ fn borrowing_no_duplication() -> Result<()> { fn borrowing_with_duplication() -> Result<()> { wasmtime::component::bindgen!({ inline: " - package inline:inline + package inline:inline; world test { export lists: interface { - foo: func(a: list>) -> list> + foo: func(a: list>) -> list>; } export thing-in: interface { @@ -268,7 +268,7 @@ fn borrowing_with_duplication() -> Result<()> { value: list } - bar: func(a: thing) + bar: func(a: thing); } export thing-in-and-out: interface { @@ -277,7 +277,7 @@ fn borrowing_with_duplication() -> Result<()> { value: list } - baz: func(a: thing) -> thing + baz: func(a: thing) -> thing; } }", ownership: Borrowing { diff --git a/tests/all/component_model/bindgen/results.rs b/tests/all/component_model/bindgen/results.rs index 32a645682629..bc491ced250e 100644 --- a/tests/all/component_model/bindgen/results.rs +++ b/tests/all/component_model/bindgen/results.rs @@ -9,13 +9,13 @@ mod empty_error { use super::*; wasmtime::component::bindgen!({ inline: " - package inline:inline + package inline:inline; world result-playground { import imports: interface { - empty-error: func(a: float64) -> result + empty-error: func(a: float64) -> result; } - export empty-error: func(a: float64) -> result + export empty-error: func(a: float64) -> result; }", }); @@ -110,13 +110,13 @@ mod string_error { use super::*; wasmtime::component::bindgen!({ inline: " - package inline:inline + package inline:inline; world result-playground { import imports: interface { - string-error: func(a: float64) -> result + string-error: func(a: float64) -> result; } - export string-error: func(a: float64) -> result + export string-error: func(a: float64) -> result; }", }); @@ -226,16 +226,16 @@ mod enum_error { wasmtime::component::bindgen!({ inline: " - package inline:inline + package inline:inline; interface imports { enum e1 { a, b, c } - enum-error: func(a: float64) -> result + enum-error: func(a: float64) -> result; } world result-playground { - import imports + import imports; export foo: interface { enum e1 { a, b, c } - enum-error: func(a: float64) -> result + enum-error: func(a: float64) -> result; } }", trappable_error_type: { "inline:inline/imports"::e1: TrappableE1 } @@ -406,16 +406,16 @@ mod record_error { wasmtime::component::bindgen!({ inline: " - package inline:inline + package inline:inline; interface imports { record e2 { line: u32, col: u32 } - record-error: func(a: float64) -> result + record-error: func(a: float64) -> result; } world result-playground { - import imports + import imports; export foo: interface { record e2 { line: u32, col: u32 } - record-error: func(a: float64) -> result + record-error: func(a: float64) -> result; } }", // Literal strings can be used for the interface and typename fields instead of @@ -575,20 +575,20 @@ mod variant_error { wasmtime::component::bindgen!({ inline: " - package inline:inline + package inline:inline; interface imports { enum e1 { a, b, c } record e2 { line: u32, col: u32 } variant e3 { E1(e1), E2(e2) } - variant-error: func(a: float64) -> result + variant-error: func(a: float64) -> result; } world result-playground { - import imports + import imports; export foo: interface { enum e1 { a, b, c } record e2 { line: u32, col: u32 } variant e3 { E1(e1), E2(e2) } - variant-error: func(a: float64) -> result + variant-error: func(a: float64) -> result; } }", trappable_error_type: { "inline:inline/imports"::e3: TrappableE3 } @@ -770,20 +770,20 @@ mod multiple_interfaces_error { wasmtime::component::bindgen!({ inline: " - package inline:inline + package inline:inline; interface types { enum e1 { a, b, c } - enum-error: func(a: float64) -> result + enum-error: func(a: float64) -> result; } interface imports { - use types.{e1} - enum-error: func(a: float64) -> result + use types.{e1}; + enum-error: func(a: float64) -> result; } world result-playground { - import imports + import imports; export foo: interface { enum e1 { a, b, c } - enum-error: func(a: float64) -> result + enum-error: func(a: float64) -> result; } }", trappable_error_type: { "inline:inline/types"::e1: TrappableE1 } @@ -967,20 +967,20 @@ mod with_remapping { wasmtime::component::bindgen!({ interfaces: " import imports: interface { - empty-error: func(a: float64) -> result + empty-error: func(a: float64) -> result; }", }); } wasmtime::component::bindgen!({ inline: " - package inline:inline + package inline:inline; world result-playground { import imports: interface { - empty-error: func(a: float64) -> result + empty-error: func(a: float64) -> result; } - export empty-error: func(a: float64) -> result + export empty-error: func(a: float64) -> result; }", with: { "imports": interfaces::imports, From 2c53c42205a391f61e6ee6e186328556bc92ae8f Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 6 Oct 2023 07:25:41 -0700 Subject: [PATCH 071/199] wasi-nn: reenable CI task (#7164) The task was disabled due to a failing checksum. Since then, the `install-openvino-action` has improved and now _almost_ has cross-platform support. This change just restores the status quo in expectation that #6895 will significantly improve the testing story. prtest:full --- .github/workflows/main.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a1ab4c206332..adbad6b80985 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -483,8 +483,7 @@ jobs: # Build and test the wasi-nn module. test_wasi_nn: needs: determine - # FIXME(#7125) flaky at the moment - if: needs.determine.outputs.run-full && false + if: needs.determine.outputs.run-full name: Test wasi-nn module runs-on: ubuntu-latest steps: @@ -493,7 +492,7 @@ jobs: submodules: true - uses: ./.github/actions/install-rust - run: rustup target add wasm32-wasi - - uses: abrown/install-openvino-action@v6 + - uses: abrown/install-openvino-action@v7 with: version: 2022.3.0 apt: true From 6a3d9d05fd3f4f7b359b4e419a9913d8fecea074 Mon Sep 17 00:00:00 2001 From: Eduardo de Moura Rodrigues <16357187+eduardomourar@users.noreply.github.com> Date: Fri, 6 Oct 2023 18:39:47 +0200 Subject: [PATCH 072/199] fix(wasmtime-cli): wrong linker in wasi http (#7167) * chore: convert wasi http cli test to component * fix(wasmtime-cli): wrong linker in wasi http * fix(wasmtime-cli): fix multiple functions added to linker We will allow shadowing in linker when using WASI HTTP in order to prevent the following error: > import of `wasi:clocks/wall-clock` defined twice * chore: update to latest io streams wit definition * chore: rename main function of core module prtest:full * fix(wasmtime-cli): add only http functions to linker * chore: add test building http request and response * chore: disable temporarily wasi http test --- .../tests/wasi-http-components-sync.rs | 6 ++ .../tests/wasi-http-components.rs | 6 ++ .../bin/outbound_request_response_build.rs | 40 ++++++++ crates/wasi-http/src/proxy.rs | 11 +++ src/commands/run.rs | 2 +- tests/all/cli_tests.rs | 20 ++-- tests/all/cli_tests/wasi-http.wat | 93 ------------------- 7 files changed, 75 insertions(+), 103 deletions(-) create mode 100644 crates/test-programs/wasi-http-tests/src/bin/outbound_request_response_build.rs delete mode 100644 tests/all/cli_tests/wasi-http.wat diff --git a/crates/test-programs/tests/wasi-http-components-sync.rs b/crates/test-programs/tests/wasi-http-components-sync.rs index c54b98f7f421..a05614595018 100644 --- a/crates/test-programs/tests/wasi-http-components-sync.rs +++ b/crates/test-programs/tests/wasi-http-components-sync.rs @@ -160,3 +160,9 @@ fn outbound_request_invalid_dnsname() -> Result<()> { let server = Server::http1()?; run("outbound_request_invalid_dnsname", &server) } + +#[test_log::test] +fn outbound_request_response_build() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_response_build", &server) +} diff --git a/crates/test-programs/tests/wasi-http-components.rs b/crates/test-programs/tests/wasi-http-components.rs index 7d70cb365036..6bec2ab717f7 100644 --- a/crates/test-programs/tests/wasi-http-components.rs +++ b/crates/test-programs/tests/wasi-http-components.rs @@ -161,3 +161,9 @@ async fn outbound_request_invalid_dnsname() -> Result<()> { let server = Server::http1()?; run("outbound_request_invalid_dnsname", &server).await } + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn outbound_request_response_build() -> Result<()> { + let server = Server::http1()?; + run("outbound_request_response_build", &server).await +} diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_response_build.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_response_build.rs new file mode 100644 index 000000000000..322992380d49 --- /dev/null +++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request_response_build.rs @@ -0,0 +1,40 @@ +use wasi_http_tests::bindings::wasi::{cli::stdout::get_stdout, http::types as http_types}; + +fn print(msg: &[u8]) { + let _ = get_stdout().blocking_write_and_flush(&msg); +} + +fn main() { + print("Called _start\n".as_bytes()); + { + let headers = http_types::Headers::new(&[( + "Content-Type".to_string(), + "application/json".to_string().into_bytes(), + )]); + let request = http_types::OutgoingRequest::new( + &http_types::Method::Get, + None, + Some(&http_types::Scheme::Https), + Some("www.example.com"), + &headers, + ); + let outgoing_body = request.write().unwrap(); + let request_body = outgoing_body.write().unwrap(); + request_body + .blocking_write_and_flush("request-body".as_bytes()) + .unwrap(); + } + { + let headers = http_types::Headers::new(&[( + "Content-Type".to_string(), + "application/text".to_string().into_bytes(), + )]); + let response = http_types::OutgoingResponse::new(200, &headers); + let outgoing_body = response.write().unwrap(); + let response_body = outgoing_body.write().unwrap(); + response_body + .blocking_write_and_flush("response-body".as_bytes()) + .unwrap(); + } + print("Done\n".as_bytes()); +} diff --git a/crates/wasi-http/src/proxy.rs b/crates/wasi-http/src/proxy.rs index 1a7bca511c29..155a55fd11fd 100644 --- a/crates/wasi-http/src/proxy.rs +++ b/crates/wasi-http/src/proxy.rs @@ -67,6 +67,17 @@ pub mod sync { // dependencies. preview2::command::sync::add_to_linker(l)?; + add_only_http_to_linker(l)?; + + Ok(()) + } + + #[doc(hidden)] + // TODO: This is temporary solution until the preview2 command functions can be removed + pub fn add_only_http_to_linker(l: &mut wasmtime::component::Linker) -> anyhow::Result<()> + where + T: WasiHttpView + preview2::WasiView + bindings::http::types::Host, + { bindings::http::outgoing_handler::add_to_linker(l, |t| t)?; bindings::http::types::add_to_linker(l, |t| t)?; diff --git a/src/commands/run.rs b/src/commands/run.rs index b45f48a5ae6b..4bf6da4c4974 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -643,7 +643,7 @@ impl RunCommand { bail!("Cannot enable wasi-http for core wasm modules"); } CliLinker::Component(linker) => { - wasmtime_wasi_http::proxy::add_to_linker(linker)?; + wasmtime_wasi_http::proxy::sync::add_only_http_to_linker(linker)?; } } diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs index da2e7adedc97..f1b39c078b77 100644 --- a/tests/all/cli_tests.rs +++ b/tests/all/cli_tests.rs @@ -790,23 +790,25 @@ fn run_basic_component() -> Result<()> { Ok(()) } -#[cfg(feature = "wasi-http")] -#[ignore = "needs to be ported to components"] #[test] -fn run_wasi_http_module() -> Result<()> { +// #[cfg_attr(not(feature = "wasi-http"), ignore)] +#[ignore = "re-enable when we can reference tests from test-programs"] +fn run_wasi_http_component() -> Result<()> { let output = run_wasmtime_for_output( &[ - "-Shttp,preview2", "-Ccache=no", - "tests/all/cli_tests/wasi-http.wat", + "-Wcomponent-model", + "-Scommon,http,preview2", + "tests/all/cli_tests/wasi-http-component.wat", ], None, )?; println!("{}", String::from_utf8_lossy(&output.stderr)); - assert!(String::from_utf8(output.stdout) - .unwrap() - .starts_with("Called _start\n")); - assert!(!output.status.success()); + let stdout = String::from_utf8_lossy(&output.stdout); + println!("{}", stdout); + assert!(stdout.starts_with("Called _start\n")); + assert!(stdout.ends_with("Done\n")); + assert!(output.status.success()); Ok(()) } diff --git a/tests/all/cli_tests/wasi-http.wat b/tests/all/cli_tests/wasi-http.wat deleted file mode 100644 index 8893c3ca4584..000000000000 --- a/tests/all/cli_tests/wasi-http.wat +++ /dev/null @@ -1,93 +0,0 @@ -(module - (import "wasi_snapshot_preview1" "proc_exit" - (func $__wasi_proc_exit (param i32))) - (import "wasi:io/streams" "write" - (func $__wasi_io_streams_write (param i32 i32 i32 i32))) - (import "wasi:io/streams" "blocking-write-and-flush" - (func $__wasi_io_streams_blocking_write_and_flush (param i32 i32 i32 i32))) - (import "wasi:io/streams" "subscribe-to-output-stream" - (func $__wasi_io_streams_subscribe_to_output_stream (param i32) (result i32))) - (import "wasi:http/types" "new-fields" - (func $__wasi_http_types_new_fields (param i32 i32) (result i32))) - (import "wasi:http/types" "drop-fields" - (func $__wasi_http_types_drop_fields (param i32))) - (import "wasi:http/types" "new-outgoing-request" - (func $__wasi_http_types_new_outgoing_request (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32))) - (import "wasi:http/types" "outgoing-request-write" - (func $__wasi_http_types_outgoing_request_write (param i32 i32))) - (import "wasi:http/types" "drop-outgoing-request" - (func $__wasi_http_types_drop_outgoing_request (param i32))) - (func $_start - (local i32) - (local $headers_id i32) - (local $request_id i32) - (local $body_stream_id i32) - - ;; Print "Called _start". - (call $print (i32.const 32) (i32.const 14)) - - (local.set $headers_id (call $__wasi_http_types_new_fields - i32.const 0 ;; base pointer - i32.const 0 ;; length - )) - (local.set $request_id (call $__wasi_http_types_new_outgoing_request - i32.const 0 ;; method = Method::Get - i32.const 0 ;; method pointer - i32.const 0 ;; method length - i32.const 0 ;; path is some = None - i32.const 0 ;; path pointer - i32.const 0 ;; path length - i32.const 1 ;; scheme is some = Some - i32.const 1 ;; scheme = Scheme::Https - i32.const 0 ;; scheme pointer - i32.const 0 ;; scheme length - i32.const 1 ;; authority is some = Some - i32.const 96 ;; authority pointer = Constant value - i32.const 15 ;; authority length - local.get $headers_id ;; headers id - )) - (call $__wasi_http_types_outgoing_request_write (local.get $request_id) (local.get 0)) - local.get 0 - i32.const 4 - i32.add - i32.load - local.set $body_stream_id - (call $__wasi_io_streams_write - (local.get $body_stream_id) ;; body stream id (usually 8) - (i32.const 128) ;; body stream pointer - (i32.const 4) ;; body stream length - (i32.const 0) - ) - (drop (call $__wasi_io_streams_subscribe_to_output_stream (local.get $body_stream_id))) - (call $__wasi_http_types_drop_fields (local.get $headers_id)) - (call $__wasi_http_types_drop_outgoing_request (local.get $request_id)) - - (call $print (i32.const 64) (i32.const 5)) - (drop (call $__wasi_io_streams_subscribe_to_output_stream (i32.const 4))) - - (call $__wasi_proc_exit (i32.const 1)) - ) - - ;; A helper function for printing ptr-len strings. - (func $print (param $ptr i32) (param $len i32) - (call $__wasi_io_streams_blocking_write_and_flush - i32.const 4 ;; Value for stdout - local.get $ptr - local.get $len - i32.const 0 - ) - ) - - (func $cabi_realloc (param i32 i32 i32 i32) (result i32) - i32.const 0 - ) - - (memory 1) - (export "memory" (memory 0)) - (export "_start" (func $_start)) - (export "cabi_realloc" (func $cabi_realloc)) - (data (i32.const 32) "Called _start\0a") - (data (i32.const 64) "Done\0a") - (data (i32.const 96) "www.example.com") - (data (i32.const 128) "body") -) From c699fba278f529e1f22f87bdaf770185798d258e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Cabrera?= Date: Fri, 6 Oct 2023 14:00:30 -0400 Subject: [PATCH 073/199] docs(component): Fix typo in component translate (#7173) --- crates/environ/src/component/translate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs index b054214536a8..77e75ed456e9 100644 --- a/crates/environ/src/component/translate.rs +++ b/crates/environ/src/component/translate.rs @@ -281,7 +281,7 @@ impl<'a, 'data> Translator<'a, 'data> { /// `component` does not have to be valid and it will be validated during /// compilation. /// - /// THe result of this function is a tuple of the final component's + /// The result of this function is a tuple of the final component's /// description plus a list of core wasm modules found within the /// component. The component's description actually erases internal /// components, instances, etc, as much as it can. Instead `Component` From 55c38b2dd61350694974f642570461de0d6d5626 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 6 Oct 2023 14:31:45 -0500 Subject: [PATCH 074/199] Require semicolons in WIT files in CI (#7174) This commit updates CI to require semicolons in all WIT files and parsed WIT documents. Some minor updates were required in existing WIT files and the wasi-nn proposal was additionally updated to its latest version with semicolons. The wasi-nn update brought some minor changes to the WIT which required some minor changes here as well. --- .github/actions/install-rust/action.yml | 4 ++++ crates/wasi-http/wit/deps/filesystem/types.wit | 2 +- crates/wasi-http/wit/deps/io/streams.wit | 2 +- crates/wasi-nn/spec | 2 +- crates/wasi-nn/src/backend/openvino.rs | 2 ++ crates/wasi-nn/src/witx.rs | 2 ++ crates/wasi/wit/deps/filesystem/types.wit | 2 +- crates/wasi/wit/deps/io/streams.wit | 2 +- 8 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/actions/install-rust/action.yml b/.github/actions/install-rust/action.yml index 003c994f6dfc..a84a148f1aa5 100644 --- a/.github/actions/install-rust/action.yml +++ b/.github/actions/install-rust/action.yml @@ -61,6 +61,10 @@ runs: CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse EOF + - name: Require semicolons in WIT + shell: bash + run: echo WIT_REQUIRE_SEMICOLONS=1 >> "$GITHUB_ENV" + - name: Choose registry cache key shell: bash # Update the registry index cache at most once per day. actions/cache diff --git a/crates/wasi-http/wit/deps/filesystem/types.wit b/crates/wasi-http/wit/deps/filesystem/types.wit index 7efb7e5b3208..73b93c0b097a 100644 --- a/crates/wasi-http/wit/deps/filesystem/types.wit +++ b/crates/wasi-http/wit/deps/filesystem/types.wit @@ -806,5 +806,5 @@ interface types { /// /// Note that this function is fallible because not all stream-related /// errors are filesystem-related errors. - filesystem-error-code: func(err: borrow) -> option + filesystem-error-code: func(err: borrow) -> option; } diff --git a/crates/wasi-http/wit/deps/io/streams.wit b/crates/wasi-http/wit/deps/io/streams.wit index 81832b2da958..60ad1c33dfb0 100644 --- a/crates/wasi-http/wit/deps/io/streams.wit +++ b/crates/wasi-http/wit/deps/io/streams.wit @@ -37,7 +37,7 @@ interface streams { /// The returned string will change across platforms and hosts which /// means that parsing it, for example, would be a /// platform-compatibility hazard. - to-debug-string: func() -> string + to-debug-string: func() -> string; } /// An input bytestream. diff --git a/crates/wasi-nn/spec b/crates/wasi-nn/spec index c1f8b87e923a..e2310b860db2 160000 --- a/crates/wasi-nn/spec +++ b/crates/wasi-nn/spec @@ -1 +1 @@ -Subproject commit c1f8b87e923aedda02964c31b0e1d37e331ec402 +Subproject commit e2310b860db2ff1719c9d69816099b87e85fabdb diff --git a/crates/wasi-nn/src/backend/openvino.rs b/crates/wasi-nn/src/backend/openvino.rs index 428e0fcbd98d..be9c0250dbb7 100644 --- a/crates/wasi-nn/src/backend/openvino.rs +++ b/crates/wasi-nn/src/backend/openvino.rs @@ -163,8 +163,10 @@ fn map_tensor_type_to_precision(tensor_type: TensorType) -> openvino::Precision match tensor_type { TensorType::Fp16 => Precision::FP16, TensorType::Fp32 => Precision::FP32, + TensorType::Fp64 => Precision::FP64, TensorType::U8 => Precision::U8, TensorType::I32 => Precision::I32, + TensorType::I64 => Precision::I64, TensorType::Bf16 => todo!("not yet supported in `openvino` bindings"), } } diff --git a/crates/wasi-nn/src/witx.rs b/crates/wasi-nn/src/witx.rs index e13e2678daaa..d2f85d25f4a1 100644 --- a/crates/wasi-nn/src/witx.rs +++ b/crates/wasi-nn/src/witx.rs @@ -179,6 +179,8 @@ impl From for crate::wit::types::TensorType { gen::types::TensorType::F32 => crate::wit::types::TensorType::Fp32, gen::types::TensorType::U8 => crate::wit::types::TensorType::U8, gen::types::TensorType::I32 => crate::wit::types::TensorType::I32, + gen::types::TensorType::I64 => crate::wit::types::TensorType::I64, + gen::types::TensorType::F64 => crate::wit::types::TensorType::Fp64, } } } diff --git a/crates/wasi/wit/deps/filesystem/types.wit b/crates/wasi/wit/deps/filesystem/types.wit index 7efb7e5b3208..73b93c0b097a 100644 --- a/crates/wasi/wit/deps/filesystem/types.wit +++ b/crates/wasi/wit/deps/filesystem/types.wit @@ -806,5 +806,5 @@ interface types { /// /// Note that this function is fallible because not all stream-related /// errors are filesystem-related errors. - filesystem-error-code: func(err: borrow) -> option + filesystem-error-code: func(err: borrow) -> option; } diff --git a/crates/wasi/wit/deps/io/streams.wit b/crates/wasi/wit/deps/io/streams.wit index 81832b2da958..60ad1c33dfb0 100644 --- a/crates/wasi/wit/deps/io/streams.wit +++ b/crates/wasi/wit/deps/io/streams.wit @@ -37,7 +37,7 @@ interface streams { /// The returned string will change across platforms and hosts which /// means that parsing it, for example, would be a /// platform-compatibility hazard. - to-debug-string: func() -> string + to-debug-string: func() -> string; } /// An input bytestream. From a6b62d6cd017eb4bb14f943ae1686684e29d66c3 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Fri, 6 Oct 2023 20:35:43 +0100 Subject: [PATCH 075/199] cranelift: Consider functions with TLS values as non leaf (#7177) --- cranelift/codegen/src/ir/function.rs | 12 ++++- .../filetests/isa/riscv64/tls-elf.clif | 51 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/cranelift/codegen/src/ir/function.rs b/cranelift/codegen/src/ir/function.rs index f5b85c50af80..db91464d9e2f 100644 --- a/cranelift/codegen/src/ir/function.rs +++ b/cranelift/codegen/src/ir/function.rs @@ -314,7 +314,17 @@ impl FunctionStencil { pub fn is_leaf(&self) -> bool { // Conservative result: if there's at least one function signature referenced in this // function, assume it is not a leaf. - self.dfg.signatures.is_empty() + let has_signatures = !self.dfg.signatures.is_empty(); + + // Under some TLS models, retrieving the address of a TLS variable requires calling a + // function. Conservatively assume that any function that references a tls global value + // is not a leaf. + let has_tls = self.global_values.values().any(|gv| match gv { + GlobalValueData::Symbol { tls, .. } => *tls, + _ => false, + }); + + !has_signatures && !has_tls } /// Replace the `dst` instruction's data with the `src` instruction's data diff --git a/cranelift/filetests/filetests/isa/riscv64/tls-elf.clif b/cranelift/filetests/filetests/isa/riscv64/tls-elf.clif index 18e781b2fc0d..ab352c4f1b75 100644 --- a/cranelift/filetests/filetests/isa/riscv64/tls-elf.clif +++ b/cranelift/filetests/filetests/isa/riscv64/tls-elf.clif @@ -56,3 +56,54 @@ block0(v0: i32): ; addi sp, sp, 0x10 ; ret + + +function u0:1(i64) -> i64 system_v { +gv0 = symbol colocated tls u1:0 + +block0(v0: i64): + v1 = tls_value.i64 gv0 + v2 = iconst.i8 0 + store notrap v2, v1 + v3 = iconst.i64 0 + return v3 +} + +; VCode: +; add sp,-16 +; sd ra,8(sp) +; sd fp,0(sp) +; mv fp,sp +; block0: +; elf_tls_get_addr a0,userextname0 +; li a4,0 +; sb a4,0(a0) +; li a0,0 +; ld ra,8(sp) +; ld fp,0(sp) +; add sp,+16 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; addi sp, sp, -0x10 +; sd ra, 8(sp) +; sd s0, 0(sp) +; mv s0, sp +; block1: ; offset 0x10 +; auipc a0, 0 ; reloc_external RiscvTlsGdHi20 u1:0 0 +; mv a0, a0 ; reloc_external RiscvPCRelLo12I func+16 0 +; auipc t5, 0 +; ld t5, 0xc(t5) +; j 0xc +; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %ElfTlsGetAddr 0 +; .byte 0x00, 0x00, 0x00, 0x00 +; jalr t5 +; mv a4, zero +; sb a4, 0(a0) +; mv a0, zero +; ld ra, 8(sp) +; ld s0, 0(sp) +; addi sp, sp, 0x10 +; ret + From b77b407b25c3c158be209b8df6d9054ac6e43203 Mon Sep 17 00:00:00 2001 From: Tyler Rockwood Date: Fri, 6 Oct 2023 16:05:17 -0500 Subject: [PATCH 076/199] c-api: Fix type (#7178) This should be wasmtime_instance_pre_t Signed-off-by: Tyler Rockwood --- crates/c-api/include/wasmtime/linker.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h index 3215efe9ff6c..750c18d8558a 100644 --- a/crates/c-api/include/wasmtime/linker.h +++ b/crates/c-api/include/wasmtime/linker.h @@ -308,7 +308,7 @@ WASM_API_EXTERN bool wasmtime_linker_get( WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate_pre( const wasmtime_linker_t *linker, const wasmtime_module_t *module, - wasmtime_instance_t **instance_pre); + wasmtime_instance_pre_t **instance_pre); #ifdef __cplusplus } // extern "C" From a109d2abe50f979021dd46adff8a92da92816ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Cabrera?= Date: Fri, 6 Oct 2023 18:10:55 -0400 Subject: [PATCH 077/199] winch(x64): Add support for table instructions (#7155) * winch(x64): Add support for table instructions This change adds support for the following table insructions: `elem.drop`, `table.copy`, `table.set`, `table.get`, `table.fill`, `table.grow`, `table.size`, `table.init`. This change also introduces partial support for the `Ref` WebAssembly type, more conretely the `Func` heap type, which means that all the table instructions above, only work this WebAssembly type as of this change. Finally, this change is also a small follow up to the primitives introduced in https://github.com/bytecodealliance/wasmtime/pull/7100, more concretely: * `FnCall::with_lib`: tracks the presence of a libcall and ensures that any result registers are freed right when the call is emitted. * `MacroAssembler::table_elem_addr` returns an address rather than the value of the address, making it convenient for other use cases like `table.set`. -- prtest:full * chore: Make stack functions take impl IntoIterator<..> * Update winch/codegen/src/codegen/call.rs Co-authored-by: Trevor Elliott * Remove a dangling `dbg!` * Add comment on branching --------- Co-authored-by: Trevor Elliott --- build.rs | 29 +- fuzz/fuzz_targets/differential.rs | 10 +- tests/misc_testsuite/winch/table_fill.wast | 65 ++++ tests/misc_testsuite/winch/table_get.wast | 13 + tests/misc_testsuite/winch/table_grow.wast | 26 ++ tests/misc_testsuite/winch/table_set.wast | 27 ++ winch/codegen/src/abi/mod.rs | 12 +- winch/codegen/src/codegen/call.rs | 26 +- winch/codegen/src/codegen/context.rs | 8 +- winch/codegen/src/codegen/env.rs | 78 +++-- winch/codegen/src/codegen/mod.rs | 92 +++++- winch/codegen/src/isa/aarch64/masm.rs | 16 +- winch/codegen/src/isa/x64/abi.rs | 19 +- winch/codegen/src/isa/x64/masm.rs | 69 ++-- winch/codegen/src/masm.rs | 20 +- winch/codegen/src/stack.rs | 27 +- winch/codegen/src/visitor.rs | 296 +++++++++++++++--- .../x64/call_indirect/call_indirect.wat | 140 +++++---- winch/filetests/filetests/x64/table/fill.wat | 99 ++++++ winch/filetests/filetests/x64/table/get.wat | 54 ++++ winch/filetests/filetests/x64/table/grow.wat | 30 ++ .../filetests/x64/table/init_copy_drop.wat | 225 +++++++++++++ winch/filetests/filetests/x64/table/set.wat | 105 +++++++ winch/filetests/filetests/x64/table/size.wat | 15 + 24 files changed, 1301 insertions(+), 200 deletions(-) create mode 100644 tests/misc_testsuite/winch/table_fill.wast create mode 100644 tests/misc_testsuite/winch/table_get.wast create mode 100644 tests/misc_testsuite/winch/table_grow.wast create mode 100644 tests/misc_testsuite/winch/table_set.wast create mode 100644 winch/filetests/filetests/x64/table/fill.wat create mode 100644 winch/filetests/filetests/x64/table/get.wat create mode 100644 winch/filetests/filetests/x64/table/grow.wat create mode 100644 winch/filetests/filetests/x64/table/init_copy_drop.wat create mode 100644 winch/filetests/filetests/x64/table/set.wat create mode 100644 winch/filetests/filetests/x64/table/size.wat diff --git a/build.rs b/build.rs index 041373eba0c3..64584ca886c4 100644 --- a/build.rs +++ b/build.rs @@ -205,19 +205,34 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool { // We ignore tests that assert for traps on windows, given // that Winch doesn't encode unwind information for Windows, yet. if strategy == "Winch" { + let assert_trap = [ + "i32", + "i64", + "call_indirect", + "table_fill", + "table_init", + "table_copy", + "table_set", + "table_get", + ] + .contains(&testname); + + if assert_trap && env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "windows" { + return true; + } + if testsuite == "misc_testsuite" { // The misc/call_indirect is fully supported by Winch. - if testname == "call_indirect" { - return false; + if testname != "call_indirect" { + return true; } } - if testsuite != "winch" { - return true; + if testsuite == "spec_testsuite" { + // The official table init and table copy tests are now supported. + return !["table_init", "table_copy"].contains(&testname); } - let assert_trap = ["i32", "i64", "call_indirect"].contains(&testname); - - if assert_trap && env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "windows" { + if testsuite != "winch" { return true; } } diff --git a/fuzz/fuzz_targets/differential.rs b/fuzz/fuzz_targets/differential.rs index 34164666ebf0..d4bdd3bdd35d 100644 --- a/fuzz/fuzz_targets/differential.rs +++ b/fuzz/fuzz_targets/differential.rs @@ -375,7 +375,15 @@ fn winch_supports_module(module: &[u8]) -> bool { | F64Abs { .. } | F32Neg { .. } | F64Neg { .. } - | CallIndirect { .. } => {} + | CallIndirect { .. } + | ElemDrop { .. } + | TableCopy { .. } + | TableSet { .. } + | TableGet { .. } + | TableFill { .. } + | TableGrow { .. } + | TableSize { .. } + | TableInit { .. } => {} _ => { supported = false; break 'main; diff --git a/tests/misc_testsuite/winch/table_fill.wast b/tests/misc_testsuite/winch/table_fill.wast new file mode 100644 index 000000000000..56a98c164eb6 --- /dev/null +++ b/tests/misc_testsuite/winch/table_fill.wast @@ -0,0 +1,65 @@ +(module + (type $t0 (func)) + (func $f1 (type $t0)) + (func $f2 (type $t0)) + (func $f3 (type $t0)) + + ;; Define two tables of funcref + (table $t1 3 funcref) + (table $t2 10 funcref) + + ;; Initialize table $t1 with functions $f1, $f2, $f3 + (elem (i32.const 0) $f1 $f2 $f3) + + ;; Function to fill table $t1 using a function reference from table $t2 + (func (export "fill") (param $i i32) (param $r i32) (param $n i32) + (local $ref funcref) + (local.set $ref (table.get $t1 (local.get $r))) + (table.fill $t2 (local.get $i) (local.get $ref) (local.get $n)) + ) + + (func (export "get") (param $i i32) (result funcref) + (table.get $t2 (local.get $i)) + ) +) + +(assert_return (invoke "get" (i32.const 1)) (ref.null func)) +(assert_return (invoke "get" (i32.const 2)) (ref.null func)) +(assert_return (invoke "get" (i32.const 3)) (ref.null func)) +(assert_return (invoke "get" (i32.const 4)) (ref.null func)) +(assert_return (invoke "get" (i32.const 5)) (ref.null func)) + +(assert_return (invoke "fill" (i32.const 2) (i32.const 0) (i32.const 3))) +(assert_return (invoke "get" (i32.const 1)) (ref.null func)) +(assert_return (invoke "get" (i32.const 2)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 3)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 4)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 5)) (ref.null func)) + +(assert_return (invoke "fill" (i32.const 4) (i32.const 1) (i32.const 2))) +(assert_return (invoke "get" (i32.const 3)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 4)) (ref.func 1)) +(assert_return (invoke "get" (i32.const 5)) (ref.func 1)) +(assert_return (invoke "get" (i32.const 6)) (ref.null func)) + +(assert_return (invoke "fill" (i32.const 4) (i32.const 2) (i32.const 0))) +(assert_return (invoke "get" (i32.const 3)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 4)) (ref.func 1)) +(assert_return (invoke "get" (i32.const 5)) (ref.func 1)) + +(assert_return (invoke "fill" (i32.const 8) (i32.const 0) (i32.const 2))) +(assert_return (invoke "get" (i32.const 7)) (ref.null func)) +(assert_return (invoke "get" (i32.const 8)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 9)) (ref.func 0)) + +(assert_return (invoke "fill" (i32.const 9) (i32.const 2) (i32.const 1))) +(assert_return (invoke "get" (i32.const 8)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 9)) (ref.func 2)) + +(assert_return (invoke "fill" (i32.const 10) (i32.const 1) (i32.const 0))) +(assert_return (invoke "get" (i32.const 9)) (ref.func 2)) + +(assert_trap + (invoke "fill" (i32.const 8) (i32.const 0) (i32.const 3)) + "out of bounds table access" +) diff --git a/tests/misc_testsuite/winch/table_get.wast b/tests/misc_testsuite/winch/table_get.wast new file mode 100644 index 000000000000..09f57cace9f3 --- /dev/null +++ b/tests/misc_testsuite/winch/table_get.wast @@ -0,0 +1,13 @@ +(module + (table $t3 3 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) +) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) +(assert_trap (invoke "get-funcref" (i32.const 3)) "out of bounds table access") +(assert_trap (invoke "get-funcref" (i32.const -1)) "out of bounds table access") + diff --git a/tests/misc_testsuite/winch/table_grow.wast b/tests/misc_testsuite/winch/table_grow.wast new file mode 100644 index 000000000000..be293cb79469 --- /dev/null +++ b/tests/misc_testsuite/winch/table_grow.wast @@ -0,0 +1,26 @@ +(module + (table $t1 0 funcref) + + (func (export "grow-by-10") (param $r funcref) (result i32) + (table.grow $t1 (local.get $r) (i32.const 10)) + ) + (func (export "grow-over") (param $r funcref) (result i32) + (table.grow $t1 (local.get $r) (i32.const 0xffff_fff0)) + ) + + (func (export "size") (result i32) + (table.size $t1)) +) + +(assert_return (invoke "size") (i32.const 0)) +(assert_return (invoke "grow-by-10" (ref.null func)) (i32.const 0)) +(assert_return (invoke "size") (i32.const 10)) + +(module + (table $t 0x10 funcref) + (func $f (export "grow") (param $r funcref) (result i32) + (table.grow $t (local.get $r) (i32.const 0xffff_fff0)) + ) +) + +(assert_return (invoke "grow" (ref.null func)) (i32.const -1)) diff --git a/tests/misc_testsuite/winch/table_set.wast b/tests/misc_testsuite/winch/table_set.wast new file mode 100644 index 000000000000..fb1f783883f2 --- /dev/null +++ b/tests/misc_testsuite/winch/table_set.wast @@ -0,0 +1,27 @@ +(module + (table $t3 2 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) + + (func (export "set-funcref") (param $i i32) (param $r funcref) + (table.set $t3 (local.get $i) (local.get $r)) + ) + (func (export "set-funcref-from") (param $i i32) (param $j i32) + (table.set $t3 (local.get $i) (table.get $t3 (local.get $j))) + ) +) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) +(assert_return (invoke "set-funcref-from" (i32.const 0) (i32.const 1))) +(assert_return (invoke "set-funcref" (i32.const 0) (ref.null func))) +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) + +(assert_trap (invoke "set-funcref" (i32.const 3) (ref.null func)) "out of bounds table access") +(assert_trap (invoke "set-funcref" (i32.const -1) (ref.null func)) "out of bounds table access") + +(assert_trap (invoke "set-funcref-from" (i32.const 3) (i32.const 1)) "out of bounds table access") +(assert_trap (invoke "set-funcref-from" (i32.const -1) (i32.const 1)) "out of bounds table access") diff --git a/winch/codegen/src/abi/mod.rs b/winch/codegen/src/abi/mod.rs index 5362a81cfc78..a92bf6bebe47 100644 --- a/winch/codegen/src/abi/mod.rs +++ b/winch/codegen/src/abi/mod.rs @@ -47,7 +47,7 @@ use crate::isa::{reg::Reg, CallingConvention}; use crate::masm::OperandSize; use smallvec::SmallVec; use std::ops::{Add, BitAnd, Not, Sub}; -use wasmtime_environ::{WasmFuncType, WasmType}; +use wasmtime_environ::{WasmFuncType, WasmHeapType, WasmType}; pub(crate) mod local; pub(crate) use local::*; @@ -267,7 +267,15 @@ pub(crate) fn ty_size(ty: &WasmType) -> u32 { match *ty { WasmType::I32 | WasmType::F32 => 4, WasmType::I64 | WasmType::F64 => 8, - _ => panic!(), + WasmType::Ref(rt) => match rt.heap_type { + // TODO: Similar to the comment in visitor.rs at impl From for + // OperandSize, Once Wasmtime supports 32-bit architectures, this will + // need to be updated to derive operand size from the target's pointer + // size. + WasmHeapType::Func => 8, + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, + t => unimplemented!("Support for WasmType: {t}"), } } diff --git a/winch/codegen/src/codegen/call.rs b/winch/codegen/src/codegen/call.rs index 6b311e94848b..3f9bbba93c4d 100644 --- a/winch/codegen/src/codegen/call.rs +++ b/winch/codegen/src/codegen/call.rs @@ -1,7 +1,7 @@ //! Function call emission. For more details around the ABI and //! calling convention, see [ABI]. use crate::{ - abi::{ABIArg, ABISig, ABI}, + abi::{ABIArg, ABIResult, ABISig, ABI}, codegen::{BuiltinFunction, CodeGenContext}, masm::{CalleeKind, MacroAssembler, OperandSize}, reg::Reg, @@ -65,6 +65,8 @@ pub(crate) struct FnCall<'a> { arg_stack_space: u32, /// The ABI-specific signature of the callee. pub abi_sig: &'a ABISig, + /// Whether this a built-in function call. + lib: bool, } impl<'a> FnCall<'a> { @@ -74,6 +76,7 @@ impl<'a> FnCall<'a> { abi_sig: &callee_sig, arg_stack_space: callee_sig.stack_bytes, call_stack_space: None, + lib: false, } } @@ -238,6 +241,7 @@ impl<'a> FnCall<'a> { ) where F: FnMut(&mut CodeGenContext, &mut M, &mut Self, Reg), { + self.lib = true; // When dealing with libcalls, we don't have all the information // upfront (all necessary arguments in the stack) in order to optimize // saving the live registers, so we save all the values available in @@ -288,6 +292,26 @@ impl<'a> FnCall<'a> { regalloc.free(v.get_reg().into()); } }); + + // When emitting built-calls we ensure that none of the registers + // (params and results) used as part of the ABI signature are + // allocatable throughout the lifetime of the `with_lib` callback, since + // such registers will be used to assign arguments and hold results. + // After executing the callback, it's only safe to free the param + // registers, since depending on the signature, the caller + // will push any result registers to the stack, keeping those registers allocated. + // Here we ensure that any allocated result registers are correctly + // freed before finalizing the function call and pushing any results to + // the value stack. + if self.lib { + match self.abi_sig.result { + ABIResult::Reg { reg, .. } => { + assert!(!context.regalloc.reg_available(reg)); + context.free_reg(reg); + } + _ => {} + } + } context.push_abi_results(&self.abi_sig.result, masm); } diff --git a/winch/codegen/src/codegen/context.rs b/winch/codegen/src/codegen/context.rs index 1f7658611911..d2821aa3ec54 100644 --- a/winch/codegen/src/codegen/context.rs +++ b/winch/codegen/src/codegen/context.rs @@ -1,4 +1,4 @@ -use wasmtime_environ::WasmType; +use wasmtime_environ::{WasmHeapType, WasmType}; use super::ControlStackFrame; use crate::{ @@ -63,7 +63,11 @@ impl<'a> CodeGenContext<'a> { match ty { I32 | I64 => self.reg_for_class(RegClass::Int, masm), F32 | F64 => self.reg_for_class(RegClass::Float, masm), - t => panic!("unsupported type {:?}", t), + Ref(rt) => match rt.heap_type { + WasmHeapType::Func => self.reg_for_class(RegClass::Int, masm), + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, + t => unimplemented!("Support for WasmType: {t}"), } } diff --git a/winch/codegen/src/codegen/env.rs b/winch/codegen/src/codegen/env.rs index 8e2d36c05f3f..32f40cc791bb 100644 --- a/winch/codegen/src/codegen/env.rs +++ b/winch/codegen/src/codegen/env.rs @@ -1,12 +1,20 @@ -use crate::{codegen::BuiltinFunctions, CallingConvention}; +use crate::{ + codegen::{BuiltinFunctions, OperandSize}, + CallingConvention, +}; use smallvec::{smallvec, SmallVec}; +use std::collections::{ + hash_map::Entry::{Occupied, Vacant}, + HashMap, +}; use wasmparser::BlockType; use wasmtime_environ::{ - FuncIndex, GlobalIndex, ModuleTranslation, ModuleTypes, PtrSize, TableIndex, TypeConvert, - TypeIndex, VMOffsets, WasmFuncType, WasmType, + FuncIndex, GlobalIndex, ModuleTranslation, ModuleTypes, PtrSize, TableIndex, TablePlan, + TypeConvert, TypeIndex, VMOffsets, WasmFuncType, WasmType, }; /// Table metadata. +#[derive(Debug, Copy, Clone)] pub struct TableData { /// The offset to the base of the table. pub offset: u32, @@ -15,8 +23,10 @@ pub struct TableData { /// If the table is imported, return the base /// offset of the `from` field in `VMTableImport`. pub base: Option, - /// The size of the table elements, in bytes. - pub element_size: u8, + /// The size of the table elements. + pub(crate) element_size: OperandSize, + /// The size of the current elements field. + pub(crate) current_elements_size: OperandSize, } /// A function callee. @@ -53,6 +63,8 @@ pub struct FuncEnv<'a, P: PtrSize> { pub builtins: BuiltinFunctions, /// The module's function types. pub types: &'a ModuleTypes, + /// Track resolved table information. + resolved_tables: HashMap, } pub fn ptr_type_from_ptr_size(size: u8) -> WasmType { @@ -77,6 +89,7 @@ impl<'a, P: PtrSize> FuncEnv<'a, P> { translation, builtins: BuiltinFunctions::new(size, call_conv, builtins_base), types, + resolved_tables: HashMap::new(), } } @@ -141,27 +154,40 @@ impl<'a, P: PtrSize> FuncEnv<'a, P> { } /// Returns the table information for the given table index. - pub fn resolve_table_data(&self, index: TableIndex) -> TableData { - let (from_offset, base_offset, current_elems_offset) = - match self.translation.module.defined_table_index(index) { - Some(defined) => ( - None, - self.vmoffsets.vmctx_vmtable_definition_base(defined), - self.vmoffsets - .vmctx_vmtable_definition_current_elements(defined), - ), - None => ( - Some(self.vmoffsets.vmctx_vmtable_import_from(index)), - self.vmoffsets.vmtable_definition_base().into(), - self.vmoffsets.vmtable_definition_current_elements().into(), - ), - }; - - TableData { - base: from_offset, - offset: base_offset, - current_elems_offset, - element_size: self.vmoffsets.ptr.size(), + pub fn resolve_table_data(&mut self, index: TableIndex) -> TableData { + match self.resolved_tables.entry(index) { + Occupied(entry) => *entry.get(), + Vacant(entry) => { + let (from_offset, base_offset, current_elems_offset) = + match self.translation.module.defined_table_index(index) { + Some(defined) => ( + None, + self.vmoffsets.vmctx_vmtable_definition_base(defined), + self.vmoffsets + .vmctx_vmtable_definition_current_elements(defined), + ), + None => ( + Some(self.vmoffsets.vmctx_vmtable_import_from(index)), + self.vmoffsets.vmtable_definition_base().into(), + self.vmoffsets.vmtable_definition_current_elements().into(), + ), + }; + + *entry.insert(TableData { + base: from_offset, + offset: base_offset, + current_elems_offset, + element_size: OperandSize::from_bytes(self.vmoffsets.ptr.size()), + current_elements_size: OperandSize::from_bytes( + self.vmoffsets.size_of_vmtable_definition_current_elements(), + ), + }) + } } } + + /// Get a [`TablePlan`] from a [`TableIndex`]. + pub fn table_plan(&mut self, index: TableIndex) -> &TablePlan { + &self.translation.module.table_plans[index] + } } diff --git a/winch/codegen/src/codegen/mod.rs b/winch/codegen/src/codegen/mod.rs index 6771e15ea668..f591605d3fb2 100644 --- a/winch/codegen/src/codegen/mod.rs +++ b/winch/codegen/src/codegen/mod.rs @@ -1,6 +1,7 @@ use crate::{ abi::{ABISig, ABI}, isa::reg::Reg, + masm::RegImm, masm::{CmpKind, MacroAssembler, OperandSize, TrapCode}, stack::{TypedReg, Val}, CallingConvention, @@ -8,7 +9,9 @@ use crate::{ use anyhow::Result; use smallvec::SmallVec; use wasmparser::{BinaryReader, FuncValidator, Operator, ValidatorResources, VisitOperator}; -use wasmtime_environ::{PtrSize, TypeIndex, WasmFuncType, WasmType}; +use wasmtime_environ::{ + PtrSize, TableIndex, TypeIndex, WasmFuncType, WasmHeapType, WasmType, FUNCREF_MASK, +}; mod context; pub(crate) use context::*; @@ -254,6 +257,7 @@ where /// * A function import. /// * A funcref. pub fn emit_call(&mut self, callee: Callee) { + let ptr_type = self.env.ptr_type(); match callee { Callee::Import(callee) => { let mut params = Vec::with_capacity(callee.ty.params().len() + 2); @@ -268,10 +272,7 @@ where .vmoffsets .vmctx_vmfunction_import_vmctx(callee.index); let callee_vmctx_addr = self.masm.address_at_vmctx(callee_vmctx_offset); - // FIXME Remove harcoded operand size, this will be needed - // once 32-bit architectures are supported. - self.masm - .load(callee_vmctx_addr, callee_vmctx, OperandSize::S64); + self.masm.load_ptr(callee_vmctx_addr, callee_vmctx); let callee_body_offset = self .env @@ -284,8 +285,12 @@ where // and second arguments. let stack = &mut self.context.stack; let location = stack.len() - (sig.params().len() - 2); - stack.insert(location as usize, TypedReg::i64(caller_vmctx).into()); - stack.insert(location as usize, TypedReg::i64(callee_vmctx).into()); + let values = [ + TypedReg::new(ptr_type, callee_vmctx).into(), + TypedReg::new(ptr_type, caller_vmctx).into(), + ] + .into_iter(); + self.context.stack.insert_many(location, values); let abi_sig = ::sig(&sig, &CallingConvention::Default); FnCall::new(&abi_sig) @@ -302,7 +307,6 @@ where Callee::FuncRef(ty) => { // Get type for the caller and callee VMContext. - let ptr_type = self.env.ptr_type(); let abi_sig = ::sig(&ty, &CallingConvention::Default); // Pop the funcref pointer to a register and allocate a register to hold the // address of the funcref. Since the callee is not addressed from a global non @@ -404,10 +408,80 @@ where match &ty { I32 | I64 | F32 | F64 => self.masm.store(src.into(), addr, ty.into()), - _ => panic!("Unsupported type {:?}", ty), + Ref(rt) => match rt.heap_type { + WasmHeapType::Func => self.masm.store_ptr(src.into(), addr), + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, + _ => unimplemented!("Support for WasmType {ty}"), } }); } + + /// Emits a series of instructions to lazily initialize a function reference. + pub fn emit_lazy_init_funcref( + table_data: &TableData, + table_index: TableIndex, + ptr_type: WasmType, + context: &mut CodeGenContext, + masm: &mut M, + call: &mut FnCall, + callee: Reg, + ) { + let index = context.pop_to_reg(masm, None); + let elem_value: Reg = context.any_gpr(masm).into(); + let base = context.any_gpr(masm); + let elem_addr = masm.table_elem_address(index.into(), base, &table_data, context); + masm.load_ptr(elem_addr, elem_value); + + let defined = masm.get_label(); + let cont = masm.get_label(); + + // Preemptively move the table element address to the + // result register, to avoid conflicts at the control flow merge. + let result = call.abi_sig.result.result_reg().unwrap(); + masm.mov(elem_value.into(), result, ptr_type.into()); + + // Push the builtin function arguments to the stack. + context + .stack + .push(TypedReg::new(ptr_type, ::vmctx_reg()).into()); + context.stack.push(table_index.as_u32().try_into().unwrap()); + context.stack.push(index.into()); + + // `branch` in this case will perform a test of the given register, + // and jump to the defined branch if it's not zero. + masm.branch( + CmpKind::Ne, + elem_value.into(), + elem_value, + defined, + ptr_type.into(), + ); + + call.calculate_call_stack_space(context) + .reg(masm, context, callee); + // We know the signature of the libcall in this case, so we assert that there's + // one element in the stack and that it's the ABI signature's result register. + let top = context.stack.peek().unwrap(); + let top = top.get_reg(); + debug_assert!(top.reg == result); + masm.jmp(cont); + + // In the defined case, mask the funcref address in place, by peeking into the + // last element of the value stack, which was pushed by the `indirect` function + // call above. + masm.bind(defined); + let imm = RegImm::i64(FUNCREF_MASK as i64); + let dst = top.into(); + masm.and(dst, dst, imm, top.ty.into()); + + masm.bind(cont); + // The indirect call above, will take care of freeing the registers used as + // params. + // So we only free the params used to lazily initialize the func ref. + context.free_reg(base); + context.free_reg(elem_value); + } } /// Returns the index of the [`ControlStackFrame`] for the given diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs index 880357d60587..f6032a9461ac 100644 --- a/winch/codegen/src/isa/aarch64/masm.rs +++ b/winch/codegen/src/isa/aarch64/masm.rs @@ -100,10 +100,14 @@ impl Masm for MacroAssembler { fn table_elem_address( &mut self, _index: Reg, - _size: OperandSize, + _base: Reg, _table_data: &TableData, _context: &mut CodeGenContext, - ) -> Reg { + ) -> Self::Address { + todo!() + } + + fn table_size(&mut self, _table_data: &TableData, _context: &mut CodeGenContext) { todo!() } @@ -119,6 +123,10 @@ impl Masm for MacroAssembler { todo!() } + fn store_ptr(&mut self, _src: Reg, _dst: Self::Address) { + todo!() + } + fn store(&mut self, src: RegImm, dst: Address, size: OperandSize) { let src = match src { RegImm::Imm(v) => { @@ -350,6 +358,10 @@ impl Masm for MacroAssembler { todo!() } + fn trapz(&mut self, _src: Reg, _code: TrapCode) { + todo!() + } + fn trapif(&mut self, _cc: CmpKind, _code: TrapCode) { todo!() } diff --git a/winch/codegen/src/isa/x64/abi.rs b/winch/codegen/src/isa/x64/abi.rs index be52fe9fb455..6fed025684fa 100644 --- a/winch/codegen/src/isa/x64/abi.rs +++ b/winch/codegen/src/isa/x64/abi.rs @@ -5,7 +5,7 @@ use crate::{ masm::OperandSize, }; use smallvec::SmallVec; -use wasmtime_environ::{WasmFuncType, WasmType}; +use wasmtime_environ::{WasmFuncType, WasmHeapType, WasmType}; /// Helper environment to track argument-register /// assignment in x64. @@ -142,6 +142,10 @@ impl ABI for X64ABI { // NOTE This should be updated when supporting multi-value. WasmType::I32 | WasmType::I64 => regs::rax(), WasmType::F32 | WasmType::F64 => regs::xmm0(), + WasmType::Ref(rt) => { + assert!(rt.heap_type == WasmHeapType::Func); + regs::rax() + } t => panic!("Unsupported return type {:?}", t), }; ABIResult::reg(ty, reg) @@ -171,9 +175,13 @@ impl ABI for X64ABI { fn stack_arg_slot_size_for_type(ty: WasmType) -> u32 { match ty { + WasmType::Ref(rt) => match rt.heap_type { + WasmHeapType::Func => Self::word_bytes(), + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, WasmType::F64 | WasmType::I32 | WasmType::I64 => Self::word_bytes(), WasmType::F32 => Self::word_bytes() / 2, - _ => unreachable!(), + ty => unimplemented!("Support for WasmType: {ty}"), } } } @@ -186,6 +194,11 @@ impl X64ABI { fastcall: bool, ) -> ABIArg { let (reg, ty) = match wasm_arg { + ty @ WasmType::Ref(rt) => match rt.heap_type { + WasmHeapType::Func => (Self::int_reg_for(index_env.next_gpr(), fastcall), ty), + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, + ty @ (WasmType::I32 | WasmType::I64) => { (Self::int_reg_for(index_env.next_gpr(), fastcall), ty) } @@ -194,7 +207,7 @@ impl X64ABI { (Self::float_reg_for(index_env.next_fpr(), fastcall), ty) } - ty => unreachable!("Unsupported argument type {:?}", ty), + ty => unimplemented!("Support for argument of WasmType: {ty}"), }; let default = || { diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs index 8d89bd435c79..25264de83e5a 100644 --- a/winch/codegen/src/isa/x64/masm.rs +++ b/winch/codegen/src/isa/x64/masm.rs @@ -9,7 +9,7 @@ use crate::masm::{ CmpKind, DivKind, Imm as I, MacroAssembler as Masm, OperandSize, RegImm, RemKind, RoundingMode, ShiftKind, TrapCode, }; -use crate::{abi::ABI, masm::StackSlot}; +use crate::{abi::ABI, masm::StackSlot, stack::TypedReg}; use crate::{ abi::{self, align_to, calculate_frame_adjustment, LocalSlot}, codegen::{ptr_type_from_ptr_size, CodeGenContext, TableData}, @@ -112,14 +112,13 @@ impl Masm for MacroAssembler { fn table_elem_address( &mut self, index: Reg, - size: OperandSize, + ptr_base: Reg, table_data: &TableData, context: &mut CodeGenContext, - ) -> Reg { + ) -> Self::Address { let vmctx = ::vmctx_reg(); let scratch = regs::scratch(); let bound = context.any_gpr(self); - let ptr_base = context.any_gpr(self); let tmp = context.any_gpr(self); if let Some(offset) = table_data.base { @@ -127,47 +126,68 @@ impl Masm for MacroAssembler { // load the address into a register to further use it as // the table address. self.asm - .mov_mr(&Address::offset(vmctx, offset), ptr_base, OperandSize::S64); + .mov_mr(&self.address_at_vmctx(offset), ptr_base, self.ptr_size); } else { // Else, simply move the vmctx register into the addr register as // the base to calculate the table address. - self.asm.mov_rr(vmctx, ptr_base, OperandSize::S64); + self.asm.mov_rr(vmctx, ptr_base, self.ptr_size); }; // OOB check. - let bound_addr = Address::offset(ptr_base, table_data.current_elems_offset); - self.asm.mov_mr(&bound_addr, bound, OperandSize::S64); - self.asm.cmp_rr(bound, index, size); + let bound_addr = self.address_at_reg(ptr_base, table_data.current_elems_offset); + let bound_size = table_data.current_elements_size; + self.asm.mov_mr(&bound_addr, bound, bound_size); + self.asm.cmp_rr(bound, index, bound_size); self.asm.trapif(CmpKind::GeU, TrapCode::TableOutOfBounds); // Move the index into the scratch register to calcualte the table // element address. // Moving the value of the index register to the scratch register // also avoids overwriting the context of the index register. - self.asm.mov_rr(index, scratch, OperandSize::S32); - self.asm - .mul_ir(table_data.element_size as i32, scratch, OperandSize::S64); + self.asm.mov_rr(index, scratch, bound_size); + self.asm.mul_ir( + table_data.element_size.bytes() as i32, + scratch, + table_data.element_size, + ); self.asm.mov_mr( - &Address::offset(ptr_base, table_data.offset), + &self.address_at_reg(ptr_base, table_data.offset), ptr_base, - OperandSize::S64, + self.ptr_size, ); // Copy the value of the table base into a temporary register // so that we can use it later in case of a misspeculation. - self.asm.mov_rr(ptr_base, tmp, OperandSize::S64); + self.asm.mov_rr(ptr_base, tmp, self.ptr_size); // Calculate the address of the table element. - self.asm.add_rr(scratch, ptr_base, OperandSize::S64); + self.asm.add_rr(scratch, ptr_base, self.ptr_size); if self.shared_flags.enable_table_access_spectre_mitigation() { // Perform a bounds check and override the value of the // table element address in case the index is out of bounds. self.asm.cmp_rr(bound, index, OperandSize::S32); - self.asm.cmov(tmp, ptr_base, CmpKind::GeU, OperandSize::S64); + self.asm.cmov(tmp, ptr_base, CmpKind::GeU, self.ptr_size); } - self.asm - .mov_mr(&Address::offset(ptr_base, 0), ptr_base, OperandSize::S64); context.free_reg(bound); context.free_reg(tmp); - ptr_base + self.address_at_reg(ptr_base, 0) + } + + fn table_size(&mut self, table_data: &TableData, context: &mut CodeGenContext) { + let vmctx = ::vmctx_reg(); + let scratch = regs::scratch(); + let size = context.any_gpr(self); + + if let Some(offset) = table_data.base { + self.asm + .mov_mr(&self.address_at_vmctx(offset), scratch, self.ptr_size); + } else { + self.asm.mov_rr(vmctx, scratch, self.ptr_size); + }; + + let size_addr = Address::offset(scratch, table_data.current_elems_offset); + self.asm + .mov_mr(&size_addr, size, table_data.current_elements_size); + + context.stack.push(TypedReg::i32(size).into()); } fn address_from_sp(&self, offset: u32) -> Self::Address { @@ -182,6 +202,10 @@ impl Masm for MacroAssembler { Address::offset(::vmctx_reg(), offset) } + fn store_ptr(&mut self, src: Reg, dst: Self::Address) { + self.store(src.into(), dst, self.ptr_size); + } + fn store(&mut self, src: RegImm, dst: Address, size: OperandSize) { match src { RegImm::Imm(imm) => match imm { @@ -708,6 +732,11 @@ impl Masm for MacroAssembler { self.asm.trapif(cc, code); } + fn trapz(&mut self, src: Reg, code: TrapCode) { + self.asm.test_rr(src, src, self.ptr_size); + self.asm.trapif(CmpKind::Eq, code); + } + fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg) { // At least one default target. assert!(targets.len() >= 1); diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs index ec258b7b39af..ab032cfb7253 100644 --- a/winch/codegen/src/masm.rs +++ b/winch/codegen/src/masm.rs @@ -32,7 +32,7 @@ pub struct StackSlot { pub size: u32, } -/// Kinds of binary comparison in WebAssembly. The [`masm`] implementation for +/// Kinds of binary comparison in WebAssembly. The [`MacroAssembler`] implementation for /// each ISA is responsible for emitting the correct sequence of instructions /// when lowering to machine code. #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -305,15 +305,18 @@ pub(crate) trait MacroAssembler { /// Get the address of a local slot. fn local_address(&mut self, local: &LocalSlot) -> Self::Address; - /// Loads the address of the table element at a given index. - /// Returns a register that contains address of the table element. + /// Loads the address of the table element at a given index. Returns the + /// address of the table element using the provided register as base. fn table_elem_address( &mut self, index: Reg, - size: OperandSize, + base: Reg, table_data: &TableData, context: &mut CodeGenContext, - ) -> Reg; + ) -> Self::Address; + + /// Retrieves the size of the table, pushing the result to the value stack. + fn table_size(&mut self, table_data: &TableData, context: &mut CodeGenContext); /// Constructs an address with an offset that is relative to the /// current position of the stack pointer (e.g. [sp + (sp_offset - @@ -349,6 +352,10 @@ pub(crate) trait MacroAssembler { /// to the pointer size of the target. fn load_ptr(&mut self, src: Self::Address, dst: Reg); + /// Alias for `MacroAssembler::store` with the operand size corresponding + /// to the pointer size of the target. + fn store_ptr(&mut self, src: Reg, dst: Self::Address); + /// Pop a value from the machine stack into the given register. fn pop(&mut self, dst: Reg, size: OperandSize); @@ -517,4 +524,7 @@ pub(crate) trait MacroAssembler { /// Traps if the condition code is met. fn trapif(&mut self, cc: CmpKind, code: TrapCode); + + /// Trap if the source register is zero. + fn trapz(&mut self, src: Reg, code: TrapCode); } diff --git a/winch/codegen/src/stack.rs b/winch/codegen/src/stack.rs index 203eeab01742..2a993ae95e78 100644 --- a/winch/codegen/src/stack.rs +++ b/winch/codegen/src/stack.rs @@ -27,6 +27,14 @@ impl TypedReg { reg, } } + + /// Create an i64 [`TypedReg`]. + pub fn i32(reg: Reg) -> Self { + Self { + ty: WasmType::I32, + reg, + } + } } impl From for Reg { @@ -225,9 +233,22 @@ impl Stack { } } - /// Insert a new value at the specified index. - pub fn insert(&mut self, at: usize, val: Val) { - self.inner.insert(at, val); + /// Extend the stack with the given elements. + pub fn extend(&mut self, values: impl IntoIterator) { + self.inner.extend(values); + } + + /// Inserts many values at the given index. + pub fn insert_many(&mut self, at: usize, values: impl IntoIterator) { + debug_assert!(at <= self.len()); + // If last, simply extend. + if at == self.inner.len() { + self.inner.extend(values); + } else { + let mut tail = self.inner.split_off(at); + self.inner.extend(values); + self.inner.append(&mut tail); + } } /// Get the length of the stack. diff --git a/winch/codegen/src/visitor.rs b/winch/codegen/src/visitor.rs index fc96b7d00d72..40a290666920 100644 --- a/winch/codegen/src/visitor.rs +++ b/winch/codegen/src/visitor.rs @@ -10,11 +10,13 @@ use crate::masm::{ CmpKind, DivKind, MacroAssembler, OperandSize, RegImm, RemKind, RoundingMode, ShiftKind, }; use crate::stack::{TypedReg, Val}; +use cranelift_codegen::ir::TrapCode; use smallvec::SmallVec; use wasmparser::BrTable; use wasmparser::{BlockType, Ieee32, Ieee64, VisitOperator}; use wasmtime_environ::{ - FuncIndex, GlobalIndex, TableIndex, TableStyle, TypeIndex, WasmType, FUNCREF_MASK, + FuncIndex, GlobalIndex, TableIndex, TableStyle, TypeIndex, WasmHeapType, WasmType, + FUNCREF_INIT_BIT, }; /// A macro to define unsupported WebAssembly operators. @@ -132,7 +134,14 @@ macro_rules! def_unsupported { (emit Drop $($rest:tt)*) => {}; (emit BrTable $($rest:tt)*) => {}; (emit CallIndirect $($rest:tt)*) => {}; - + (emit TableInit $($rest:tt)*) => {}; + (emit TableCopy $($rest:tt)*) => {}; + (emit TableGet $($rest:tt)*) => {}; + (emit TableSet $($rest:tt)*) => {}; + (emit TableGrow $($rest:tt)*) => {}; + (emit TableSize $($rest:tt)*) => {}; + (emit TableFill $($rest:tt)*) => {}; + (emit ElemDrop $($rest:tt)*) => {}; (emit $unsupported:tt $($rest:tt)*) => {$($rest)*}; } @@ -608,7 +617,11 @@ where .unwrap_or_else(|| panic!("valid local at slot = {}", index)); match slot.ty { I32 | I64 | F32 | F64 => context.stack.push(Val::local(index, slot.ty)), - _ => panic!("Unsupported type {:?} for local", slot.ty), + Ref(rt) => match rt.heap_type { + WasmHeapType::Func => context.stack.push(Val::local(index, slot.ty)), + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, + t => unimplemented!("Support local type: {t}"), } } @@ -639,55 +652,15 @@ where &mut self.context, &builtin, |cx, masm, call, callee| { - // Calculate the table element address. - let index = cx.pop_to_reg(masm, None); - let elem_addr = - masm.table_elem_address(index.into(), index.ty.into(), &table_data, cx); - - let defined = masm.get_label(); - let cont = masm.get_label(); - - // Preemptively move the table element address to the - // result register, to avoid conflicts at the control flow merge. - let result = call.abi_sig.result.result_reg().unwrap(); - masm.mov(elem_addr.into(), result, ptr_type.into()); - cx.free_reg(result); - - // Push the builtin function arguments to the stack. - cx.stack - .push(TypedReg::new(ptr_type, ::vmctx_reg()).into()); - cx.stack.push(table_index.as_u32().try_into().unwrap()); - cx.stack.push(index.into()); - - masm.branch( - CmpKind::Ne, - elem_addr.into(), - elem_addr, - defined, - ptr_type.into(), + CodeGen::emit_lazy_init_funcref( + &table_data, + table_index, + ptr_type, + cx, + masm, + call, + callee, ); - - call.calculate_call_stack_space(cx).reg(masm, cx, callee); - // We know the signature of the libcall in this case, so we assert that there's - // one element in the stack and that it's the ABI signature's result register. - let top = cx.stack.peek().unwrap(); - let top = top.get_reg(); - debug_assert!(top.reg == result); - masm.jmp(cont); - - // In the defined case, mask the funcref address in place, by peeking into the - // last element of the value stack, which was pushed by the `indirect` function - // call above. - masm.bind(defined); - let imm = RegImm::i64(FUNCREF_MASK as i64); - let dst = top.into(); - masm.and(dst, dst, imm, top.ty.into()); - - masm.bind(cont); - // The indirect call above, will take care of freeing the registers used as - // params. - // So we only free the params used to lazily initialize the func ref. - cx.free_reg(elem_addr); }, ); @@ -695,6 +668,8 @@ where match self.env.translation.module.table_plans[table_index].style { TableStyle::CallerChecksSignature => { let funcref_ptr = self.context.stack.peek().map(|v| v.get_reg()).unwrap(); + self.masm + .trapz(funcref_ptr.into(), TrapCode::IndirectCallToNull); self.emit_typecheck_funcref(funcref_ptr.into(), type_index); } } @@ -705,6 +680,213 @@ where self.emit_call(self.env.funcref(type_index)); } + fn visit_table_init(&mut self, elem: u32, table: u32) { + let ptr_type = self.env.ptr_type(); + let table_init = self.env.builtins.table_init::(); + let vmctx = TypedReg::new(ptr_type, ::vmctx_reg()); + + FnCall::new(&table_init.sig).with_lib( + self.masm, + &mut self.context, + &table_init, + |cx, masm, call, callee| { + // table.init requires at least 3 elements on the value stack. + debug_assert!(cx.stack.len() >= 3); + let extra_args = [ + vmctx.into(), + table.try_into().unwrap(), + elem.try_into().unwrap(), + ]; + let at = cx.stack.len() - 3; + cx.stack.insert_many(at, extra_args); + // Finalize the call. + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + }, + ); + } + + fn visit_table_copy(&mut self, dst: u32, src: u32) { + let ptr_type = self.env.ptr_type(); + let table_copy = self.env.builtins.table_copy::(); + let vmctx = TypedReg::new(ptr_type, ::vmctx_reg()); + + FnCall::new(&table_copy.sig).with_lib( + self.masm, + &mut self.context, + &table_copy, + |cx, masm, call, callee| { + // table.copy requires at least 3 elemenents in the value stack. + debug_assert!(cx.stack.len() >= 3); + let at = cx.stack.len() - 3; + cx.stack.insert_many( + at, + [ + vmctx.into(), + dst.try_into().unwrap(), + src.try_into().unwrap(), + ], + ); + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + }, + ) + } + + fn visit_table_get(&mut self, table: u32) { + let ptr_type = self.env.ptr_type(); + let table_index = TableIndex::from_u32(table); + let table_data = self.env.resolve_table_data(table_index); + let plan = self.env.table_plan(table_index); + let heap_type = plan.table.wasm_ty.heap_type; + let style = plan.style.clone(); + let table_get = self + .env + .builtins + .table_get_lazy_init_func_ref::(); + + FnCall::new(&table_get.sig).with_lib( + self.masm, + &mut self.context, + &table_get, + |cx, masm, call, callee| { + match heap_type { + WasmHeapType::Func => match style { + TableStyle::CallerChecksSignature => { + CodeGen::emit_lazy_init_funcref( + &table_data, + table_index, + ptr_type, + cx, + masm, + call, + callee, + ); + } + }, + t => unimplemented!("Support for WasmHeapType: {t}"), + }; + }, + ); + } + + fn visit_table_grow(&mut self, table: u32) { + let ptr_type = self.env.ptr_type(); + let table_index = TableIndex::from_u32(table); + let table_plan = self.env.table_plan(table_index); + let vmctx = TypedReg::new(ptr_type, ::vmctx_reg()); + let builtin = match table_plan.table.wasm_ty.heap_type { + WasmHeapType::Func => self.env.builtins.table_grow_func_ref::(), + ty => unimplemented!("Support for HeapType: {ty}"), + }; + + FnCall::new(&builtin.sig).with_lib( + self.masm, + &mut self.context, + &builtin, + |cx, masm, call, callee| { + let len = cx.stack.len(); + // table.grow requires at least 2 elements on the value stack. + debug_assert!(len >= 2); + // The table_grow builtin expects the parameters in a different + // order. + // The value stack at this point should contain: + // [ init_value | delta ] (stack top) + // but the builtin function expects the init value as the last + // argument. + cx.stack.inner_mut().swap(len - 1, len - 2); + let at = len - 2; + cx.stack + .insert_many(at, [vmctx.into(), table.try_into().unwrap()]); + + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + }, + ); + } + + fn visit_table_size(&mut self, table: u32) { + let table_index = TableIndex::from_u32(table); + let table_data = self.env.resolve_table_data(table_index); + self.masm.table_size(&table_data, &mut self.context); + } + + fn visit_table_fill(&mut self, table: u32) { + let ptr_type = self.env.ptr_type(); + let vmctx = TypedReg::new(ptr_type, ::vmctx_reg()); + let table_index = TableIndex::from_u32(table); + let table_plan = self.env.table_plan(table_index); + let builtin = match table_plan.table.wasm_ty.heap_type { + WasmHeapType::Func => self.env.builtins.table_fill_func_ref::(), + ty => unimplemented!("Support for heap type: {ty}"), + }; + + FnCall::new(&builtin.sig).with_lib( + self.masm, + &mut self.context, + &builtin, + |cx, masm, call, callee| { + // table.fill requires at least 3 values on the value stack. + debug_assert!(cx.stack.len() >= 3); + let at = cx.stack.len() - 3; + cx.stack + .insert_many(at, [vmctx.into(), table.try_into().unwrap()]); + + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + }, + ); + } + + fn visit_table_set(&mut self, table: u32) { + let ptr_type = self.env.ptr_type(); + let table_index = TableIndex::from_u32(table); + let table_data = self.env.resolve_table_data(table_index); + let plan = self.env.table_plan(table_index); + match plan.table.wasm_ty.heap_type { + WasmHeapType::Func => match plan.style { + TableStyle::CallerChecksSignature => { + let value = self.context.pop_to_reg(self.masm, None); + let index = self.context.pop_to_reg(self.masm, None); + let base = self.context.any_gpr(self.masm); + let elem_addr = self.masm.table_elem_address( + index.into(), + base, + &table_data, + &mut self.context, + ); + + // Set the initialized bit. + self.masm.or( + value.into(), + value.into(), + RegImm::i64(FUNCREF_INIT_BIT as i64), + ptr_type.into(), + ); + + self.masm.store_ptr(value.into(), elem_addr); + + self.context.free_reg(value); + self.context.free_reg(index); + self.context.free_reg(base); + } + }, + ty => unimplemented!("Support for WasmHeapType: {ty}"), + }; + } + + fn visit_elem_drop(&mut self, index: u32) { + let ptr_type = self.env.ptr_type(); + let elem_drop = self.env.builtins.elem_drop::(); + let vmctx = TypedReg::new(ptr_type, ::vmctx_reg()); + + FnCall::new(&elem_drop.sig).with_lib( + self.masm, + &mut self.context, + &elem_drop, + |cx, masm, call, callee| { + cx.stack.extend([vmctx.into(), index.try_into().unwrap()]); + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + }, + ); + } + fn visit_nop(&mut self) {} fn visit_if(&mut self, blockty: BlockType) { @@ -916,7 +1098,17 @@ impl From for OperandSize { match ty { WasmType::I32 | WasmType::F32 => OperandSize::S32, WasmType::I64 | WasmType::F64 => OperandSize::S64, - ty => todo!("unsupported type {:?}", ty), + WasmType::Ref(rt) => { + match rt.heap_type { + // TODO: Harcoded size, assuming 64-bit support only. Once + // Wasmtime supports 32-bit architectures, this will need + // to be updated in such a way that the calculation of the + // OperandSize will depend on the target's pointer size. + WasmHeapType::Func => OperandSize::S64, + t => unimplemented!("Support for WasmHeapType: {t}"), + } + } + ty => unimplemented!("Support for WasmType {ty}"), } } } diff --git a/winch/filetests/filetests/x64/call_indirect/call_indirect.wat b/winch/filetests/filetests/x64/call_indirect/call_indirect.wat index b2dd60224111..2719ef8f9009 100644 --- a/winch/filetests/filetests/x64/call_indirect/call_indirect.wat +++ b/winch/filetests/filetests/x64/call_indirect/call_indirect.wat @@ -41,7 +41,7 @@ ;; 21: 85c0 test eax, eax ;; 23: 0f840a000000 je 0x33 ;; 29: b801000000 mov eax, 1 -;; 2e: e913010000 jmp 0x146 +;; 2e: e925010000 jmp 0x158 ;; 33: 8b44240c mov eax, dword ptr [rsp + 0xc] ;; 37: 83e802 sub eax, 2 ;; 3a: 50 push rax @@ -49,19 +49,19 @@ ;; 3f: 498b4b48 mov rcx, qword ptr [r11 + 0x48] ;; 43: bb00000000 mov ebx, 0 ;; 48: 4d89f1 mov r9, r14 -;; 4b: 4d8b4150 mov r8, qword ptr [r9 + 0x50] -;; 4f: 4439c3 cmp ebx, r8d -;; 52: 0f83f4000000 jae 0x14c +;; 4b: 458b5150 mov r10d, dword ptr [r9 + 0x50] +;; 4f: 4439d3 cmp ebx, r10d +;; 52: 0f8306010000 jae 0x15e ;; 58: 4189db mov r11d, ebx ;; 5b: 4d6bdb08 imul r11, r11, 8 ;; 5f: 4d8b4948 mov r9, qword ptr [r9 + 0x48] -;; 63: 4d89ca mov r10, r9 +;; 63: 4d89cc mov r12, r9 ;; 66: 4d01d9 add r9, r11 -;; 69: 4439c3 cmp ebx, r8d -;; 6c: 4d0f43ca cmovae r9, r10 -;; 70: 4d8b09 mov r9, qword ptr [r9] -;; 73: 4c89c8 mov rax, r9 -;; 76: 4d85c9 test r9, r9 +;; 69: 4439d3 cmp ebx, r10d +;; 6c: 4d0f43cc cmovae r9, r12 +;; 70: 4d8b01 mov r8, qword ptr [r9] +;; 73: 4c89c0 mov rax, r8 +;; 76: 4d85c0 test r8, r8 ;; 79: 0f8519000000 jne 0x98 ;; 7f: 4883ec08 sub rsp, 8 ;; 83: 4c89f7 mov rdi, r14 @@ -71,60 +71,66 @@ ;; 8f: 4883c408 add rsp, 8 ;; 93: e904000000 jmp 0x9c ;; 98: 4883e0fe and rax, 0xfffffffffffffffe -;; 9c: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] -;; a0: 418b0b mov ecx, dword ptr [r11] -;; a3: 8b5018 mov edx, dword ptr [rax + 0x18] -;; a6: 39d1 cmp ecx, edx -;; a8: 0f85a0000000 jne 0x14e -;; ae: 488b4810 mov rcx, qword ptr [rax + 0x10] -;; b2: 4883ec08 sub rsp, 8 -;; b6: 8b7c2408 mov edi, dword ptr [rsp + 8] -;; ba: ffd1 call rcx -;; bc: 4883c410 add rsp, 0x10 -;; c0: 8b4c240c mov ecx, dword ptr [rsp + 0xc] -;; c4: 83e901 sub ecx, 1 -;; c7: 50 push rax -;; c8: 51 push rcx -;; c9: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] -;; cd: 498b4b48 mov rcx, qword ptr [r11 + 0x48] -;; d1: bb00000000 mov ebx, 0 -;; d6: 4d89f1 mov r9, r14 -;; d9: 4d8b4150 mov r8, qword ptr [r9 + 0x50] -;; dd: 4439c3 cmp ebx, r8d -;; e0: 0f836a000000 jae 0x150 -;; e6: 4189db mov r11d, ebx -;; e9: 4d6bdb08 imul r11, r11, 8 -;; ed: 4d8b4948 mov r9, qword ptr [r9 + 0x48] -;; f1: 4d89ca mov r10, r9 -;; f4: 4d01d9 add r9, r11 -;; f7: 4439c3 cmp ebx, r8d -;; fa: 4d0f43ca cmovae r9, r10 -;; fe: 4d8b09 mov r9, qword ptr [r9] -;; 101: 4c89c8 mov rax, r9 -;; 104: 4d85c9 test r9, r9 -;; 107: 0f8511000000 jne 0x11e -;; 10d: 4c89f7 mov rdi, r14 -;; 110: be00000000 mov esi, 0 -;; 115: 89da mov edx, ebx -;; 117: ffd1 call rcx -;; 119: e904000000 jmp 0x122 -;; 11e: 4883e0fe and rax, 0xfffffffffffffffe -;; 122: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] -;; 126: 418b0b mov ecx, dword ptr [r11] -;; 129: 8b5018 mov edx, dword ptr [rax + 0x18] -;; 12c: 39d1 cmp ecx, edx -;; 12e: 0f851e000000 jne 0x152 -;; 134: 488b4810 mov rcx, qword ptr [rax + 0x10] -;; 138: 8b3c24 mov edi, dword ptr [rsp] -;; 13b: ffd1 call rcx -;; 13d: 4883c408 add rsp, 8 -;; 141: 59 pop rcx -;; 142: 01c1 add ecx, eax -;; 144: 89c8 mov eax, ecx -;; 146: 4883c410 add rsp, 0x10 -;; 14a: 5d pop rbp -;; 14b: c3 ret -;; 14c: 0f0b ud2 -;; 14e: 0f0b ud2 -;; 150: 0f0b ud2 -;; 152: 0f0b ud2 +;; 9c: 4885c0 test rax, rax +;; 9f: 0f84bb000000 je 0x160 +;; a5: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] +;; a9: 418b0b mov ecx, dword ptr [r11] +;; ac: 8b5018 mov edx, dword ptr [rax + 0x18] +;; af: 39d1 cmp ecx, edx +;; b1: 0f85ab000000 jne 0x162 +;; b7: 488b4810 mov rcx, qword ptr [rax + 0x10] +;; bb: 4883ec08 sub rsp, 8 +;; bf: 8b7c2408 mov edi, dword ptr [rsp + 8] +;; c3: ffd1 call rcx +;; c5: 4883c410 add rsp, 0x10 +;; c9: 8b4c240c mov ecx, dword ptr [rsp + 0xc] +;; cd: 83e901 sub ecx, 1 +;; d0: 50 push rax +;; d1: 51 push rcx +;; d2: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; d6: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; da: bb00000000 mov ebx, 0 +;; df: 4d89f1 mov r9, r14 +;; e2: 458b5150 mov r10d, dword ptr [r9 + 0x50] +;; e6: 4439d3 cmp ebx, r10d +;; e9: 0f8375000000 jae 0x164 +;; ef: 4189db mov r11d, ebx +;; f2: 4d6bdb08 imul r11, r11, 8 +;; f6: 4d8b4948 mov r9, qword ptr [r9 + 0x48] +;; fa: 4d89cc mov r12, r9 +;; fd: 4d01d9 add r9, r11 +;; 100: 4439d3 cmp ebx, r10d +;; 103: 4d0f43cc cmovae r9, r12 +;; 107: 4d8b01 mov r8, qword ptr [r9] +;; 10a: 4c89c0 mov rax, r8 +;; 10d: 4d85c0 test r8, r8 +;; 110: 0f8511000000 jne 0x127 +;; 116: 4c89f7 mov rdi, r14 +;; 119: be00000000 mov esi, 0 +;; 11e: 89da mov edx, ebx +;; 120: ffd1 call rcx +;; 122: e904000000 jmp 0x12b +;; 127: 4883e0fe and rax, 0xfffffffffffffffe +;; 12b: 4885c0 test rax, rax +;; 12e: 0f8432000000 je 0x166 +;; 134: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] +;; 138: 418b0b mov ecx, dword ptr [r11] +;; 13b: 8b5018 mov edx, dword ptr [rax + 0x18] +;; 13e: 39d1 cmp ecx, edx +;; 140: 0f8522000000 jne 0x168 +;; 146: 488b4810 mov rcx, qword ptr [rax + 0x10] +;; 14a: 8b3c24 mov edi, dword ptr [rsp] +;; 14d: ffd1 call rcx +;; 14f: 4883c408 add rsp, 8 +;; 153: 59 pop rcx +;; 154: 01c1 add ecx, eax +;; 156: 89c8 mov eax, ecx +;; 158: 4883c410 add rsp, 0x10 +;; 15c: 5d pop rbp +;; 15d: c3 ret +;; 15e: 0f0b ud2 +;; 160: 0f0b ud2 +;; 162: 0f0b ud2 +;; 164: 0f0b ud2 +;; 166: 0f0b ud2 +;; 168: 0f0b ud2 diff --git a/winch/filetests/filetests/x64/table/fill.wat b/winch/filetests/filetests/x64/table/fill.wat new file mode 100644 index 000000000000..0b7960d823ca --- /dev/null +++ b/winch/filetests/filetests/x64/table/fill.wat @@ -0,0 +1,99 @@ +;;! target = "x86_64" +(module + (type $t0 (func)) + (func $f1 (type $t0)) + (func $f2 (type $t0)) + (func $f3 (type $t0)) + + ;; Define two tables of funcref + (table $t1 3 funcref) + (table $t2 10 funcref) + + ;; Initialize table $t1 with functions $f1, $f2, $f3 + (elem (i32.const 0) $f1 $f2 $f3) + + ;; Function to fill table $t1 using a function reference from table $t2 + (func (export "fill") (param $i i32) (param $r i32) (param $n i32) + (local $ref funcref) + (local.set $ref (table.get $t1 (local.get $r))) + (table.fill $t2 (local.get $i) (local.get $ref) (local.get $n)) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4883c408 add rsp, 8 +;; 10: 5d pop rbp +;; 11: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4883c408 add rsp, 8 +;; 10: 5d pop rbp +;; 11: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4883c408 add rsp, 8 +;; 10: 5d pop rbp +;; 11: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec20 sub rsp, 0x20 +;; 8: 897c241c mov dword ptr [rsp + 0x1c], edi +;; c: 89742418 mov dword ptr [rsp + 0x18], esi +;; 10: 89542414 mov dword ptr [rsp + 0x14], edx +;; 14: 4c89742404 mov qword ptr [rsp + 4], r14 +;; 19: 448b5c2418 mov r11d, dword ptr [rsp + 0x18] +;; 1e: 4153 push r11 +;; 20: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 24: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; 28: 5b pop rbx +;; 29: 4d89f1 mov r9, r14 +;; 2c: 458b5150 mov r10d, dword ptr [r9 + 0x50] +;; 30: 4439d3 cmp ebx, r10d +;; 33: 0f8384000000 jae 0xbd +;; 39: 4189db mov r11d, ebx +;; 3c: 4d6bdb08 imul r11, r11, 8 +;; 40: 4d8b4948 mov r9, qword ptr [r9 + 0x48] +;; 44: 4d89cc mov r12, r9 +;; 47: 4d01d9 add r9, r11 +;; 4a: 4439d3 cmp ebx, r10d +;; 4d: 4d0f43cc cmovae r9, r12 +;; 51: 4d8b01 mov r8, qword ptr [r9] +;; 54: 4c89c0 mov rax, r8 +;; 57: 4d85c0 test r8, r8 +;; 5a: 0f8511000000 jne 0x71 +;; 60: 4c89f7 mov rdi, r14 +;; 63: be00000000 mov esi, 0 +;; 68: 89da mov edx, ebx +;; 6a: ffd1 call rcx +;; 6c: e904000000 jmp 0x75 +;; 71: 4883e0fe and rax, 0xfffffffffffffffe +;; 75: 488944240c mov qword ptr [rsp + 0xc], rax +;; 7a: 448b5c241c mov r11d, dword ptr [rsp + 0x1c] +;; 7f: 4153 push r11 +;; 81: 4c8b5c2414 mov r11, qword ptr [rsp + 0x14] +;; 86: 4153 push r11 +;; 88: 448b5c2424 mov r11d, dword ptr [rsp + 0x24] +;; 8d: 4153 push r11 +;; 8f: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 93: 498b4368 mov rax, qword ptr [r11 + 0x68] +;; 97: 4883ec08 sub rsp, 8 +;; 9b: 4c89f7 mov rdi, r14 +;; 9e: be01000000 mov esi, 1 +;; a3: 8b542418 mov edx, dword ptr [rsp + 0x18] +;; a7: 488b4c2410 mov rcx, qword ptr [rsp + 0x10] +;; ac: 448b442408 mov r8d, dword ptr [rsp + 8] +;; b1: ffd0 call rax +;; b3: 4883c420 add rsp, 0x20 +;; b7: 4883c420 add rsp, 0x20 +;; bb: 5d pop rbp +;; bc: c3 ret +;; bd: 0f0b ud2 diff --git a/winch/filetests/filetests/x64/table/get.wat b/winch/filetests/filetests/x64/table/get.wat new file mode 100644 index 000000000000..2c28b304d48d --- /dev/null +++ b/winch/filetests/filetests/x64/table/get.wat @@ -0,0 +1,54 @@ +;;! target = "x86_64" +(module + (table $t3 3 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) +) + + +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4883c408 add rsp, 8 +;; 10: 5d pop rbp +;; 11: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec10 sub rsp, 0x10 +;; 8: 897c240c mov dword ptr [rsp + 0xc], edi +;; c: 4c89742404 mov qword ptr [rsp + 4], r14 +;; 11: 448b5c240c mov r11d, dword ptr [rsp + 0xc] +;; 16: 4153 push r11 +;; 18: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 1c: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; 20: 5b pop rbx +;; 21: 4d89f1 mov r9, r14 +;; 24: 458b5150 mov r10d, dword ptr [r9 + 0x50] +;; 28: 4439d3 cmp ebx, r10d +;; 2b: 0f8342000000 jae 0x73 +;; 31: 4189db mov r11d, ebx +;; 34: 4d6bdb08 imul r11, r11, 8 +;; 38: 4d8b4948 mov r9, qword ptr [r9 + 0x48] +;; 3c: 4d89cc mov r12, r9 +;; 3f: 4d01d9 add r9, r11 +;; 42: 4439d3 cmp ebx, r10d +;; 45: 4d0f43cc cmovae r9, r12 +;; 49: 4d8b01 mov r8, qword ptr [r9] +;; 4c: 4c89c0 mov rax, r8 +;; 4f: 4d85c0 test r8, r8 +;; 52: 0f8511000000 jne 0x69 +;; 58: 4c89f7 mov rdi, r14 +;; 5b: be00000000 mov esi, 0 +;; 60: 89da mov edx, ebx +;; 62: ffd1 call rcx +;; 64: e904000000 jmp 0x6d +;; 69: 4883e0fe and rax, 0xfffffffffffffffe +;; 6d: 4883c410 add rsp, 0x10 +;; 71: 5d pop rbp +;; 72: c3 ret +;; 73: 0f0b ud2 diff --git a/winch/filetests/filetests/x64/table/grow.wat b/winch/filetests/filetests/x64/table/grow.wat new file mode 100644 index 000000000000..b5f97c33fc94 --- /dev/null +++ b/winch/filetests/filetests/x64/table/grow.wat @@ -0,0 +1,30 @@ +;;! target = "x86_64" + +(module + (table $t1 0 funcref) + + (func (export "grow-by-10") (param $r funcref) (result i32) + (table.grow $t1 (local.get $r) (i32.const 10)) + ) +) + + +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec10 sub rsp, 0x10 +;; 8: 48897c2408 mov qword ptr [rsp + 8], rdi +;; d: 4c893424 mov qword ptr [rsp], r14 +;; 11: 4c8b5c2408 mov r11, qword ptr [rsp + 8] +;; 16: 4153 push r11 +;; 18: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 1c: 498b5b50 mov rbx, qword ptr [r11 + 0x50] +;; 20: 4883ec08 sub rsp, 8 +;; 24: 4c89f7 mov rdi, r14 +;; 27: be00000000 mov esi, 0 +;; 2c: ba0a000000 mov edx, 0xa +;; 31: 488b4c2408 mov rcx, qword ptr [rsp + 8] +;; 36: ffd3 call rbx +;; 38: 4883c410 add rsp, 0x10 +;; 3c: 4883c410 add rsp, 0x10 +;; 40: 5d pop rbp +;; 41: c3 ret diff --git a/winch/filetests/filetests/x64/table/init_copy_drop.wat b/winch/filetests/filetests/x64/table/init_copy_drop.wat new file mode 100644 index 000000000000..523f2a9838d9 --- /dev/null +++ b/winch/filetests/filetests/x64/table/init_copy_drop.wat @@ -0,0 +1,225 @@ +;;! target = "x86_64" + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t0 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (elem.drop 1) + (table.init $t0 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (elem.drop 3) + (table.copy $t0 0 (i32.const 20) (i32.const 15) (i32.const 5)) + (table.copy $t0 0 (i32.const 21) (i32.const 29) (i32.const 1)) + (table.copy $t0 0 (i32.const 24) (i32.const 10) (i32.const 1)) + (table.copy $t0 0 (i32.const 13) (i32.const 11) (i32.const 4)) + (table.copy $t0 0 (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "check") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: b805000000 mov eax, 5 +;; 11: 4883c408 add rsp, 8 +;; 15: 5d pop rbp +;; 16: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: b806000000 mov eax, 6 +;; 11: 4883c408 add rsp, 8 +;; 15: 5d pop rbp +;; 16: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: b807000000 mov eax, 7 +;; 11: 4883c408 add rsp, 8 +;; 15: 5d pop rbp +;; 16: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: b808000000 mov eax, 8 +;; 11: 4883c408 add rsp, 8 +;; 15: 5d pop rbp +;; 16: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: b809000000 mov eax, 9 +;; 11: 4883c408 add rsp, 8 +;; 15: 5d pop rbp +;; 16: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 10: 498b4310 mov rax, qword ptr [r11 + 0x10] +;; 14: 4883ec08 sub rsp, 8 +;; 18: 4c89f7 mov rdi, r14 +;; 1b: be00000000 mov esi, 0 +;; 20: ba01000000 mov edx, 1 +;; 25: b907000000 mov ecx, 7 +;; 2a: 41b800000000 mov r8d, 0 +;; 30: 41b904000000 mov r9d, 4 +;; 36: ffd0 call rax +;; 38: 4883c408 add rsp, 8 +;; 3c: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 40: 498b4318 mov rax, qword ptr [r11 + 0x18] +;; 44: 4883ec08 sub rsp, 8 +;; 48: 4c89f7 mov rdi, r14 +;; 4b: be01000000 mov esi, 1 +;; 50: ffd0 call rax +;; 52: 4883c408 add rsp, 8 +;; 56: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 5a: 498b4310 mov rax, qword ptr [r11 + 0x10] +;; 5e: 4883ec08 sub rsp, 8 +;; 62: 4c89f7 mov rdi, r14 +;; 65: be00000000 mov esi, 0 +;; 6a: ba03000000 mov edx, 3 +;; 6f: b90f000000 mov ecx, 0xf +;; 74: 41b801000000 mov r8d, 1 +;; 7a: 41b903000000 mov r9d, 3 +;; 80: ffd0 call rax +;; 82: 4883c408 add rsp, 8 +;; 86: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 8a: 498b4318 mov rax, qword ptr [r11 + 0x18] +;; 8e: 4883ec08 sub rsp, 8 +;; 92: 4c89f7 mov rdi, r14 +;; 95: be03000000 mov esi, 3 +;; 9a: ffd0 call rax +;; 9c: 4883c408 add rsp, 8 +;; a0: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; a4: 498b4308 mov rax, qword ptr [r11 + 8] +;; a8: 4883ec08 sub rsp, 8 +;; ac: 4c89f7 mov rdi, r14 +;; af: be00000000 mov esi, 0 +;; b4: ba00000000 mov edx, 0 +;; b9: b914000000 mov ecx, 0x14 +;; be: 41b80f000000 mov r8d, 0xf +;; c4: 41b905000000 mov r9d, 5 +;; ca: ffd0 call rax +;; cc: 4883c408 add rsp, 8 +;; d0: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; d4: 498b4308 mov rax, qword ptr [r11 + 8] +;; d8: 4883ec08 sub rsp, 8 +;; dc: 4c89f7 mov rdi, r14 +;; df: be00000000 mov esi, 0 +;; e4: ba00000000 mov edx, 0 +;; e9: b915000000 mov ecx, 0x15 +;; ee: 41b81d000000 mov r8d, 0x1d +;; f4: 41b901000000 mov r9d, 1 +;; fa: ffd0 call rax +;; fc: 4883c408 add rsp, 8 +;; 100: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 104: 498b4308 mov rax, qword ptr [r11 + 8] +;; 108: 4883ec08 sub rsp, 8 +;; 10c: 4c89f7 mov rdi, r14 +;; 10f: be00000000 mov esi, 0 +;; 114: ba00000000 mov edx, 0 +;; 119: b918000000 mov ecx, 0x18 +;; 11e: 41b80a000000 mov r8d, 0xa +;; 124: 41b901000000 mov r9d, 1 +;; 12a: ffd0 call rax +;; 12c: 4883c408 add rsp, 8 +;; 130: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 134: 498b4308 mov rax, qword ptr [r11 + 8] +;; 138: 4883ec08 sub rsp, 8 +;; 13c: 4c89f7 mov rdi, r14 +;; 13f: be00000000 mov esi, 0 +;; 144: ba00000000 mov edx, 0 +;; 149: b90d000000 mov ecx, 0xd +;; 14e: 41b80b000000 mov r8d, 0xb +;; 154: 41b904000000 mov r9d, 4 +;; 15a: ffd0 call rax +;; 15c: 4883c408 add rsp, 8 +;; 160: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 164: 498b4308 mov rax, qword ptr [r11 + 8] +;; 168: 4883ec08 sub rsp, 8 +;; 16c: 4c89f7 mov rdi, r14 +;; 16f: be00000000 mov esi, 0 +;; 174: ba00000000 mov edx, 0 +;; 179: b913000000 mov ecx, 0x13 +;; 17e: 41b814000000 mov r8d, 0x14 +;; 184: 41b905000000 mov r9d, 5 +;; 18a: ffd0 call rax +;; 18c: 4883c408 add rsp, 8 +;; 190: 4883c408 add rsp, 8 +;; 194: 5d pop rbp +;; 195: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec10 sub rsp, 0x10 +;; 8: 897c240c mov dword ptr [rsp + 0xc], edi +;; c: 4c89742404 mov qword ptr [rsp + 4], r14 +;; 11: 448b5c240c mov r11d, dword ptr [rsp + 0xc] +;; 16: 4153 push r11 +;; 18: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 1c: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; 20: 5b pop rbx +;; 21: 4d89f1 mov r9, r14 +;; 24: 458b91f0000000 mov r10d, dword ptr [r9 + 0xf0] +;; 2b: 4439d3 cmp ebx, r10d +;; 2e: 0f8366000000 jae 0x9a +;; 34: 4189db mov r11d, ebx +;; 37: 4d6bdb08 imul r11, r11, 8 +;; 3b: 4d8b89e8000000 mov r9, qword ptr [r9 + 0xe8] +;; 42: 4d89cc mov r12, r9 +;; 45: 4d01d9 add r9, r11 +;; 48: 4439d3 cmp ebx, r10d +;; 4b: 4d0f43cc cmovae r9, r12 +;; 4f: 4d8b01 mov r8, qword ptr [r9] +;; 52: 4c89c0 mov rax, r8 +;; 55: 4d85c0 test r8, r8 +;; 58: 0f8511000000 jne 0x6f +;; 5e: 4c89f7 mov rdi, r14 +;; 61: be00000000 mov esi, 0 +;; 66: 89da mov edx, ebx +;; 68: ffd1 call rcx +;; 6a: e904000000 jmp 0x73 +;; 6f: 4883e0fe and rax, 0xfffffffffffffffe +;; 73: 4885c0 test rax, rax +;; 76: 0f8420000000 je 0x9c +;; 7c: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] +;; 80: 418b0b mov ecx, dword ptr [r11] +;; 83: 8b5018 mov edx, dword ptr [rax + 0x18] +;; 86: 39d1 cmp ecx, edx +;; 88: 0f8510000000 jne 0x9e +;; 8e: 488b4810 mov rcx, qword ptr [rax + 0x10] +;; 92: ffd1 call rcx +;; 94: 4883c410 add rsp, 0x10 +;; 98: 5d pop rbp +;; 99: c3 ret +;; 9a: 0f0b ud2 +;; 9c: 0f0b ud2 +;; 9e: 0f0b ud2 diff --git a/winch/filetests/filetests/x64/table/set.wat b/winch/filetests/filetests/x64/table/set.wat new file mode 100644 index 000000000000..55c325fcb011 --- /dev/null +++ b/winch/filetests/filetests/x64/table/set.wat @@ -0,0 +1,105 @@ +;;! target = "x86_64" + + +(module + (table $t3 2 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "set-funcref") (param $i i32) (param $r funcref) + (table.set $t3 (local.get $i) (local.get $r)) + ) + (func (export "set-funcref-from") (param $i i32) (param $j i32) + (table.set $t3 (local.get $i) (table.get $t3 (local.get $j))) + ) +) + +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4883c408 add rsp, 8 +;; 10: 5d pop rbp +;; 11: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec18 sub rsp, 0x18 +;; 8: 897c2414 mov dword ptr [rsp + 0x14], edi +;; c: 4889742408 mov qword ptr [rsp + 8], rsi +;; 11: 4c893424 mov qword ptr [rsp], r14 +;; 15: 488b442408 mov rax, qword ptr [rsp + 8] +;; 1a: 8b4c2414 mov ecx, dword ptr [rsp + 0x14] +;; 1e: 4c89f2 mov rdx, r14 +;; 21: 8b5a50 mov ebx, dword ptr [rdx + 0x50] +;; 24: 39d9 cmp ecx, ebx +;; 26: 0f8324000000 jae 0x50 +;; 2c: 4189cb mov r11d, ecx +;; 2f: 4d6bdb08 imul r11, r11, 8 +;; 33: 488b5248 mov rdx, qword ptr [rdx + 0x48] +;; 37: 4889d6 mov rsi, rdx +;; 3a: 4c01da add rdx, r11 +;; 3d: 39d9 cmp ecx, ebx +;; 3f: 480f43d6 cmovae rdx, rsi +;; 43: 4883c801 or rax, 1 +;; 47: 488902 mov qword ptr [rdx], rax +;; 4a: 4883c418 add rsp, 0x18 +;; 4e: 5d pop rbp +;; 4f: c3 ret +;; 50: 0f0b ud2 +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec10 sub rsp, 0x10 +;; 8: 897c240c mov dword ptr [rsp + 0xc], edi +;; c: 89742408 mov dword ptr [rsp + 8], esi +;; 10: 4c893424 mov qword ptr [rsp], r14 +;; 14: 448b5c240c mov r11d, dword ptr [rsp + 0xc] +;; 19: 4153 push r11 +;; 1b: 448b5c2410 mov r11d, dword ptr [rsp + 0x10] +;; 20: 4153 push r11 +;; 22: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 26: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; 2a: 5b pop rbx +;; 2b: 4d89f1 mov r9, r14 +;; 2e: 458b5150 mov r10d, dword ptr [r9 + 0x50] +;; 32: 4439d3 cmp ebx, r10d +;; 35: 0f8377000000 jae 0xb2 +;; 3b: 4189db mov r11d, ebx +;; 3e: 4d6bdb08 imul r11, r11, 8 +;; 42: 4d8b4948 mov r9, qword ptr [r9 + 0x48] +;; 46: 4d89cc mov r12, r9 +;; 49: 4d01d9 add r9, r11 +;; 4c: 4439d3 cmp ebx, r10d +;; 4f: 4d0f43cc cmovae r9, r12 +;; 53: 4d8b01 mov r8, qword ptr [r9] +;; 56: 4c89c0 mov rax, r8 +;; 59: 4d85c0 test r8, r8 +;; 5c: 0f8519000000 jne 0x7b +;; 62: 4883ec08 sub rsp, 8 +;; 66: 4c89f7 mov rdi, r14 +;; 69: be00000000 mov esi, 0 +;; 6e: 89da mov edx, ebx +;; 70: ffd1 call rcx +;; 72: 4883c408 add rsp, 8 +;; 76: e904000000 jmp 0x7f +;; 7b: 4883e0fe and rax, 0xfffffffffffffffe +;; 7f: 59 pop rcx +;; 80: 4c89f2 mov rdx, r14 +;; 83: 8b5a50 mov ebx, dword ptr [rdx + 0x50] +;; 86: 39d9 cmp ecx, ebx +;; 88: 0f8326000000 jae 0xb4 +;; 8e: 4189cb mov r11d, ecx +;; 91: 4d6bdb08 imul r11, r11, 8 +;; 95: 488b5248 mov rdx, qword ptr [rdx + 0x48] +;; 99: 4889d6 mov rsi, rdx +;; 9c: 4c01da add rdx, r11 +;; 9f: 39d9 cmp ecx, ebx +;; a1: 480f43d6 cmovae rdx, rsi +;; a5: 4883c801 or rax, 1 +;; a9: 488902 mov qword ptr [rdx], rax +;; ac: 4883c410 add rsp, 0x10 +;; b0: 5d pop rbp +;; b1: c3 ret +;; b2: 0f0b ud2 +;; b4: 0f0b ud2 diff --git a/winch/filetests/filetests/x64/table/size.wat b/winch/filetests/filetests/x64/table/size.wat new file mode 100644 index 000000000000..8c0f47f0ace2 --- /dev/null +++ b/winch/filetests/filetests/x64/table/size.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" +(module + (table $t1 0 funcref) + (func (export "size") (result i32) + (table.size $t1)) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4d89f3 mov r11, r14 +;; f: 418b4350 mov eax, dword ptr [r11 + 0x50] +;; 13: 4883c408 add rsp, 8 +;; 17: 5d pop rbp +;; 18: c3 ret From f466aa26e1b054a9b2d83ddbd1b66b14d887cb27 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 6 Oct 2023 15:31:23 -0700 Subject: [PATCH 078/199] Skeleton and initial support for proof-carrying code. (#7165) * WIP veriwasm 2.0 Co-Authored-By: Chris Fallin * PCC: successfully parse some simple facts. Co-authored-by: Nick Fitzgerald * PCC: plumb facts through VCode and add framework on LowerBackend to check them. Co-authored-by: Nick Fitzgerald * PCC: code is carrying some proofs! Very simple test-case. Co-authored-by: Nick Fitzgerald * PCC: add a `safe` flag for checked memory accesses. * PCC: add pretty-printing of facts to CLIF output. * PCC: misc. cleanups. * PCC: lots of cleanup. * Post-rebase fixups and some misc. fixes. * Add serde traits to facts. * PCC: add succeed and fail tests. * Review feedback: rename `safe` memflag to `checked`. * Review feedback. --------- Co-authored-by: Nick Fitzgerald --- cranelift/codegen/meta/src/shared/settings.rs | 9 + cranelift/codegen/src/context.rs | 2 + cranelift/codegen/src/ir/dfg.rs | 5 + cranelift/codegen/src/ir/memflags.rs | 46 +- cranelift/codegen/src/ir/mod.rs | 1 + cranelift/codegen/src/ir/pcc.rs | 399 ++++++++++++++++++ cranelift/codegen/src/isa/aarch64/inst/mod.rs | 28 ++ cranelift/codegen/src/isa/aarch64/lower.rs | 7 +- cranelift/codegen/src/isa/aarch64/mod.rs | 1 + cranelift/codegen/src/isa/aarch64/pcc.rs | 193 +++++++++ cranelift/codegen/src/isa/riscv64/inst/mod.rs | 4 + cranelift/codegen/src/isa/s390x/inst/mod.rs | 4 + cranelift/codegen/src/isa/x64/inst/mod.rs | 4 + cranelift/codegen/src/machinst/compile.rs | 7 + cranelift/codegen/src/machinst/lower.rs | 11 +- cranelift/codegen/src/machinst/mod.rs | 3 + cranelift/codegen/src/machinst/vcode.rs | 69 +++ cranelift/codegen/src/result.rs | 6 + cranelift/codegen/src/settings.rs | 1 + cranelift/codegen/src/write.rs | 10 +- .../filetests/filetests/pcc/fail/simple.clif | 15 + .../filetests/pcc/succeed/simple.clif | 11 + cranelift/reader/src/lexer.rs | 4 +- cranelift/reader/src/parser.rs | 96 ++++- crates/wasmtime/src/engine.rs | 1 + 25 files changed, 919 insertions(+), 18 deletions(-) create mode 100644 cranelift/codegen/src/ir/pcc.rs create mode 100644 cranelift/codegen/src/isa/aarch64/pcc.rs create mode 100644 cranelift/filetests/filetests/pcc/fail/simple.clif create mode 100644 cranelift/filetests/filetests/pcc/succeed/simple.clif diff --git a/cranelift/codegen/meta/src/shared/settings.rs b/cranelift/codegen/meta/src/shared/settings.rs index e4e511b5e8cb..e977088731a8 100644 --- a/cranelift/codegen/meta/src/shared/settings.rs +++ b/cranelift/codegen/meta/src/shared/settings.rs @@ -63,6 +63,15 @@ pub(crate) fn define() -> SettingGroup { true, ); + settings.add_bool( + "enable_pcc", + "Enable proof-carrying code translation validation.", + r#" + This adds a proof-carrying code mode. TODO ADD MORE + "#, + false, + ); + // Note that Cranelift doesn't currently need an is_pie flag, because PIE is // just PIC where symbols can't be pre-empted, which can be expressed with the // `colocated` flag on external functions and global values. diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs index 2dc62ee77fd5..ca3d5fb73628 100644 --- a/cranelift/codegen/src/context.rs +++ b/cranelift/codegen/src/context.rs @@ -237,6 +237,8 @@ impl Context { /// Run the verifier on the function. /// /// Also check that the dominator tree and control flow graph are consistent with the function. + /// + /// TODO: rename to "CLIF validate" or similar. pub fn verify<'a, FOI: Into>>(&self, fisa: FOI) -> VerifierResult<()> { let mut errors = VerifierErrors::default(); let _ = verify_context(&self.func, &self.cfg, &self.domtree, fisa, &mut errors); diff --git a/cranelift/codegen/src/ir/dfg.rs b/cranelift/codegen/src/ir/dfg.rs index 76b1b08cbeea..ca02aee99dad 100644 --- a/cranelift/codegen/src/ir/dfg.rs +++ b/cranelift/codegen/src/ir/dfg.rs @@ -5,6 +5,7 @@ use crate::ir; use crate::ir::builder::ReplaceBuilder; use crate::ir::dynamic_type::{DynamicTypeData, DynamicTypes}; use crate::ir::instructions::{CallInfo, InstructionData}; +use crate::ir::pcc::Fact; use crate::ir::{ types, Block, BlockCall, ConstantData, ConstantPool, DynamicType, ExtFuncData, FuncRef, Immediate, Inst, JumpTables, RelSourceLoc, SigRef, Signature, Type, Value, @@ -125,6 +126,9 @@ pub struct DataFlowGraph { /// Primary value table with entries for all values. values: PrimaryMap, + /// Facts: proof-carrying-code assertions about values. + pub facts: SecondaryMap>, + /// Function signature table. These signatures are referenced by indirect call instructions as /// well as the external function references. pub signatures: PrimaryMap, @@ -158,6 +162,7 @@ impl DataFlowGraph { dynamic_types: DynamicTypes::new(), value_lists: ValueListPool::new(), values: PrimaryMap::new(), + facts: SecondaryMap::new(), signatures: PrimaryMap::new(), old_signatures: SecondaryMap::new(), ext_funcs: PrimaryMap::new(), diff --git a/cranelift/codegen/src/ir/memflags.rs b/cranelift/codegen/src/ir/memflags.rs index 87165ba69117..546e11fa3703 100644 --- a/cranelift/codegen/src/ir/memflags.rs +++ b/cranelift/codegen/src/ir/memflags.rs @@ -6,10 +6,19 @@ use core::fmt; use serde_derive::{Deserialize, Serialize}; enum FlagBit { + /// Guaranteed not to trap. This may enable additional + /// optimizations to be performed. Notrap, + /// Guaranteed to use "natural alignment" for the given type. This + /// may enable better instruction selection. Aligned, + /// A load that reads data in memory that does not change for the + /// duration of the function's execution. This may enable + /// additional optimizations to be performed. Readonly, + /// Load multi-byte values from memory in a little-endian format. LittleEndian, + /// Load multi-byte values from memory in a big-endian format. BigEndian, /// Accesses only the "heap" part of abstract state. Used for /// alias analysis. Mutually exclusive with "table" and "vmctx". @@ -20,10 +29,15 @@ enum FlagBit { /// Accesses only the "vmctx" part of abstract state. Used for /// alias analysis. Mutually exclusive with "heap" and "table". Vmctx, + /// Check this load or store for safety when using the + /// proof-carrying-code framework. The address must have a + /// `PointsTo` fact attached with a sufficiently large valid range + /// for the accessed size. + Checked, } -const NAMES: [&str; 8] = [ - "notrap", "aligned", "readonly", "little", "big", "heap", "table", "vmctx", +const NAMES: [&str; 9] = [ + "notrap", "aligned", "readonly", "little", "big", "heap", "table", "vmctx", "checked", ]; /// Endianness of a memory access. @@ -48,7 +62,7 @@ pub enum Endianness { #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct MemFlags { - bits: u8, + bits: u16, } impl MemFlags { @@ -265,6 +279,32 @@ impl MemFlags { self.set_vmctx(); self } + + /// Test if the `checked` bit is set. + /// + /// Loads and stores with this flag are verified to access + /// pointers only with a validated `PointsTo` fact attached, and + /// with that fact validated, when using the proof-carrying-code + /// framework. If initial facts on program inputs are correct + /// (i.e., correctly denote the shape and types of data structures + /// in memory), and if PCC validates the compiled output, then all + /// `checked`-marked memory accesses are guaranteed (up to the + /// checker's correctness) to access valid memory. This can be + /// used to ensure memory safety and sandboxing. + pub fn checked(self) -> bool { + self.read(FlagBit::Checked) + } + + /// Set the `checked` bit. + pub fn set_checked(&mut self) { + self.set(FlagBit::Checked); + } + + /// Set the `checked` bit, returning new flags. + pub fn with_checked(mut self) -> Self { + self.set_checked(); + self + } } impl fmt::Display for MemFlags { diff --git a/cranelift/codegen/src/ir/mod.rs b/cranelift/codegen/src/ir/mod.rs index 986f8003722c..c5cd502200f7 100644 --- a/cranelift/codegen/src/ir/mod.rs +++ b/cranelift/codegen/src/ir/mod.rs @@ -18,6 +18,7 @@ pub(crate) mod known_symbol; pub mod layout; pub(crate) mod libcall; mod memflags; +pub mod pcc; mod progpoint; mod sourceloc; pub mod stackslot; diff --git a/cranelift/codegen/src/ir/pcc.rs b/cranelift/codegen/src/ir/pcc.rs new file mode 100644 index 000000000000..3fc2662a87b1 --- /dev/null +++ b/cranelift/codegen/src/ir/pcc.rs @@ -0,0 +1,399 @@ +//! Proof-carrying code. We attach "facts" to values and then check +//! that they remain true after compilation. +//! +//! A few key design principle of this approach are: +//! +//! - The producer of the IR provides the axioms. All "ground truth", +//! such as what memory is accessible -- is meant to come by way of +//! facts on the function arguments and global values. In some +//! sense, all we are doing here is validating the "internal +//! consistency" of the facts that are provided on values, and the +//! actions performed on those values. +//! +//! - We do not derive and forward-propagate facts eagerly. Rather, +//! the producer needs to provide breadcrumbs -- a "proof witness" +//! of sorts -- to allow the checking to complete. That means that +//! as an address is computed, or pointer chains are dereferenced, +//! each intermediate value will likely have some fact attached. +//! +//! This does create more verbose IR, but a significant positive +//! benefit is that it avoids unnecessary work: we do not build up a +//! knowledge base that effectively encodes the integer ranges of +//! many or most values in the program. Rather, we only check +//! specifically the memory-access sequences. In practice, each such +//! sequence is likely to be a carefully-controlled sequence of IR +//! operations from, e.g., a sandboxing compiler (such as +//! `cranelift-wasm`) so adding annotations here to communicate +//! intent (ranges, bounds-checks, and the like) is no problem. +//! +//! Facts are attached to SSA values in CLIF, and are maintained +//! through optimizations and through lowering. They are thus also +//! present on VRegs in the VCode. In theory, facts could be checked +//! at either level, though in practice it is most useful to check +//! them at the VCode level if the goal is an end-to-end verification +//! of certain properties (e.g., memory sandboxing). +//! +//! Checking facts entails visiting each instruction that defines a +//! value with a fact, and checking the result's fact against the +//! facts on arguments and the operand. For VCode, this is +//! fundamentally a question of the target ISA's semantics, so we call +//! into the `LowerBackend` for this. Note that during checking there +//! is also limited forward propagation / inference, but only within +//! an instruction: for example, an addressing mode commonly can +//! include an addition, multiplication/shift, or extend operation, +//! and there is no way to attach facts to the intermediate values +//! "inside" the instruction, so instead the backend can use +//! `FactContext::add()` and friends to forward-propagate facts. +//! +//! TODO: +//! - Propagate facts through optimization (egraph layer). +//! - Generate facts in cranelift-wasm frontend when lowering memory ops. +//! - Implement richer "points-to" facts that describe the pointed-to +//! memory, so the loaded values can also have facts. +//! - Support bounds-checking-type operations for dynamic memories and +//! tables. +//! - Implement checking at the CLIF level as well. +//! - Check instructions that can trap as well? + +use crate::ir; +use crate::isa::TargetIsa; +use crate::machinst::{InsnIndex, LowerBackend, MachInst, VCode}; +use std::fmt; + +#[cfg(feature = "enable-serde")] +use serde_derive::{Deserialize, Serialize}; + +/// The result of checking proof-carrying-code facts. +pub type PccResult = std::result::Result; + +/// An error or inconsistency discovered when checking proof-carrying +/// code. +#[derive(Debug, Clone)] +pub enum PccError { + /// An operation wraps around, invalidating the stated value + /// range. + Overflow, + /// An input to an operator that produces a fact-annotated value + /// does not have a fact describing it, and one is needed. + MissingFact, + /// A memory access is out of bounds. + OutOfBounds, + /// Proof-carrying-code checking is not implemented for a + /// particular compiler backend. + UnimplementedBackend, + /// Proof-carrying-code checking is not implemented for a + /// particular instruction that instruction-selection chose. This + /// is an internal compiler error. + UnimplementedInst, +} + +/// A fact on a value. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub enum Fact { + /// A bitslice of a value (up to a bitwidth) is less than or equal + /// to a given maximum value. + /// + /// The slicing behavior is needed because this fact can describe + /// both an SSA `Value`, whose entire value is well-defined, and a + /// `VReg` in VCode, whose bits beyond the type stored in that + /// register are don't-care (undefined). + ValueMax { + /// The bitwidth of bits we care about, from the LSB upward. + bit_width: u16, + /// The maximum value that the bitslice can take + /// (inclusive). The range is unsigned: the bits of the value + /// will be within the range `0..=max`. + max: u64, + }, + + /// A pointer value to a memory region that can be accessed. + PointsTo { + /// A description of the memory region this pointer is allowed + /// to access (size, etc). + region: MemoryRegion, + }, +} + +/// A memory region that can be accessed. This description is attached +/// to a particular base pointer. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub struct MemoryRegion { + /// Includes both the actual memory bound as well as any guard + /// pages. Inclusive, so we can represent the full range of a + /// `u64`. The range is unsigned. + pub max: u64, +} + +impl fmt::Display for Fact { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Fact::ValueMax { bit_width, max } => write!(f, "max({}, 0x{:x})", bit_width, max), + Fact::PointsTo { + region: MemoryRegion { max }, + } => write!(f, "points_to(0x{:x})", max), + } + } +} + +macro_rules! ensure { + ( $condition:expr, $err:tt $(,)? ) => { + if !$condition { + return Err(PccError::$err); + } + }; +} + +macro_rules! bail { + ( $err:tt ) => {{ + return Err(PccError::$err); + }}; +} + +/// A "context" in which we can evaluate and derive facts. This +/// context carries environment/global properties, such as the machine +/// pointer width. +#[derive(Debug)] +pub struct FactContext { + pointer_width: u16, +} + +impl FactContext { + /// Create a new "fact context" in which to evaluate facts. + pub fn new(pointer_width: u16) -> Self { + FactContext { pointer_width } + } + + /// Computes whether `lhs` "subsumes" (implies) `rhs`. + pub fn subsumes(&self, lhs: &Fact, rhs: &Fact) -> bool { + match (lhs, rhs) { + ( + Fact::ValueMax { + bit_width: bw_lhs, + max: max_lhs, + }, + Fact::ValueMax { + bit_width: bw_rhs, + max: max_rhs, + }, + ) => { + // If the bitwidths we're claiming facts about are the + // same, and if the value is less than or equal to + // `max_lhs`, and if `max_rhs` is less than `max_lhs`, + // then it is certainly less than or equal to + // `max_rhs`. + // + // In other words, we can always expand the claimed + // possible value range. + bw_lhs == bw_rhs && max_lhs <= max_rhs + } + ( + Fact::PointsTo { + region: MemoryRegion { max: max_lhs }, + }, + Fact::PointsTo { + region: MemoryRegion { max: max_rhs }, + }, + ) => { + // If the pointer is valid up to `max_lhs`, and + // `max_rhs` is less than or equal to `max_lhs`, then + // it is certainly valid up to `max_rhs`. + // + // In other words, we can always shrink the valid + // addressable region. + max_rhs <= max_lhs + } + _ => false, + } + } + + /// Computes whatever fact can be known about the sum of two + /// values with attached facts. The add is performed to the given + /// bit-width. Note that this is distinct from the machine or + /// pointer width: e.g., many 64-bit machines can still do 32-bit + /// adds that wrap at 2^32. + pub fn add(&self, lhs: &Fact, rhs: &Fact, add_width: u16) -> Option { + match (lhs, rhs) { + ( + Fact::ValueMax { + bit_width: bw_lhs, + max: lhs, + }, + Fact::ValueMax { + bit_width: bw_rhs, + max: rhs, + }, + ) if bw_lhs == bw_rhs && add_width >= *bw_lhs => { + let computed_max = lhs.checked_add(*rhs)?; + Some(Fact::ValueMax { + bit_width: *bw_lhs, + max: computed_max, + }) + } + + ( + Fact::ValueMax { + bit_width: bw_max, + max, + }, + Fact::PointsTo { region }, + ) + | ( + Fact::PointsTo { region }, + Fact::ValueMax { + bit_width: bw_max, + max, + }, + ) if *bw_max >= self.pointer_width && add_width >= *bw_max => { + let region = MemoryRegion { + max: region.max.checked_sub(*max)?, + }; + Some(Fact::PointsTo { region }) + } + + _ => None, + } + } + + /// Computes the `uextend` of a value with the given facts. + pub fn uextend(&self, fact: &Fact, from_width: u16, to_width: u16) -> Option { + match fact { + // If we have a defined value in bits 0..bit_width, and we + // are filling zeroes into from_bits..to_bits, and + // bit_width and from_bits are exactly contiguous, then we + // have defined values in 0..to_bits (and because this is + // a zero-extend, the max value is the same). + Fact::ValueMax { bit_width, max } if *bit_width == from_width => Some(Fact::ValueMax { + bit_width: to_width, + max: *max, + }), + // Otherwise, we can at least claim that the value is + // within the range of `to_width`. + Fact::ValueMax { .. } => Some(Fact::ValueMax { + bit_width: to_width, + max: max_value_for_width(to_width), + }), + _ => None, + } + } + + /// Computes the `sextend` of a value with the given facts. + pub fn sextend(&self, fact: &Fact, from_width: u16, to_width: u16) -> Option { + match fact { + // If we have a defined value in bits 0..bit_width, and + // the MSB w.r.t. `from_width` is *not* set, then we can + // do the same as `uextend`. + Fact::ValueMax { bit_width, max } + if *bit_width == from_width && (*max & (1 << (*bit_width - 1)) == 0) => + { + self.uextend(fact, from_width, to_width) + } + _ => None, + } + } + + /// Scales a value with a fact by a known constant. + pub fn scale(&self, fact: &Fact, width: u16, factor: u32) -> Option { + // The minimal (loosest) fact we can claim: the value will be + // within the range implied by its bitwidth. + let minimal_fact = Fact::ValueMax { + bit_width: width, + max: max_value_for_width(width), + }; + match fact { + Fact::ValueMax { bit_width, max } if *bit_width == width => { + let max = match max.checked_mul(u64::from(factor)) { + Some(max) => max, + None => return Some(minimal_fact), + }; + if *bit_width < 64 && max > max_value_for_width(width) { + return Some(minimal_fact); + } + Some(minimal_fact) + } + _ => None, + } + } + + /// Offsets a value with a fact by a known amount. + pub fn offset(&self, fact: &Fact, width: u16, offset: i64) -> Option { + // If we eventually support two-sided ranges, we can + // represent (0..n) + m -> ((0+m)..(n+m)). However, + // right now, all ranges start with zero, so any + // negative offset could underflow, and removes all + // claims of constrained range. + let offset = u64::try_from(offset).ok()?; + + match fact { + Fact::ValueMax { bit_width, max } if *bit_width == width => { + let max = match max.checked_add(offset) { + Some(max) => max, + None => { + return Some(Fact::ValueMax { + bit_width: width, + max: max_value_for_width(width), + }) + } + }; + + Some(Fact::ValueMax { + bit_width: *bit_width, + max, + }) + } + Fact::PointsTo { + region: MemoryRegion { max }, + } => { + let max = max.checked_sub(offset)?; + Some(Fact::PointsTo { + region: MemoryRegion { max }, + }) + } + _ => None, + } + } + + /// Check that accessing memory via a pointer with this fact, with + /// a memory access of the given size, is valid. + pub fn check_address(&self, fact: &Fact, size: u32) -> PccResult<()> { + match fact { + Fact::PointsTo { + region: MemoryRegion { max }, + } => ensure!(u64::from(size) <= *max, OutOfBounds), + _ => bail!(OutOfBounds), + } + + Ok(()) + } +} + +fn max_value_for_width(bits: u16) -> u64 { + assert!(bits <= 64); + if bits == 64 { + u64::MAX + } else { + (1u64 << bits) - 1 + } +} + +/// Top-level entry point after compilation: this checks the facts in +/// VCode. +pub fn check_vcode_facts( + _f: &ir::Function, + vcode: &VCode, + backend: &B, +) -> PccResult<()> { + for inst in 0..vcode.num_insts() { + let inst = InsnIndex::new(inst); + if vcode.inst_defines_facts(inst) || vcode[inst].is_mem_access() { + // This instruction defines a register with a new fact, or + // has some side-effect we want to be careful to + // verify. We'll call into the backend to validate this + // fact with respect to the instruction and the input + // facts. + backend.check_fact(&vcode[inst], vcode)?; + } + } + Ok(()) +} diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs index e916d9e72359..aaed2245500e 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs @@ -1045,6 +1045,34 @@ impl MachInst for Inst { } } + fn is_mem_access(&self) -> bool { + match self { + &Inst::ULoad8 { .. } + | &Inst::SLoad8 { .. } + | &Inst::ULoad16 { .. } + | &Inst::SLoad16 { .. } + | &Inst::ULoad32 { .. } + | &Inst::SLoad32 { .. } + | &Inst::ULoad64 { .. } + | &Inst::LoadP64 { .. } + | &Inst::FpuLoad32 { .. } + | &Inst::FpuLoad64 { .. } + | &Inst::FpuLoad128 { .. } + | &Inst::FpuLoadP64 { .. } + | &Inst::FpuLoadP128 { .. } + | &Inst::Store8 { .. } + | &Inst::Store16 { .. } + | &Inst::Store32 { .. } + | &Inst::Store64 { .. } + | &Inst::StoreP64 { .. } + | &Inst::FpuStore32 { .. } + | &Inst::FpuStore64 { .. } + | &Inst::FpuStore128 { .. } => true, + // TODO: verify this carefully + _ => false, + } + } + fn gen_move(to_reg: Writable, from_reg: Reg, ty: Type) -> Inst { let bits = ty.bits(); diff --git a/cranelift/codegen/src/isa/aarch64/lower.rs b/cranelift/codegen/src/isa/aarch64/lower.rs index 02c841d1b2f9..0ab57ec664b2 100644 --- a/cranelift/codegen/src/isa/aarch64/lower.rs +++ b/cranelift/codegen/src/isa/aarch64/lower.rs @@ -8,12 +8,13 @@ //! - Floating-point immediates (FIMM instruction). use crate::ir::condcodes::{FloatCC, IntCC}; +use crate::ir::pcc::PccResult; use crate::ir::Inst as IRInst; use crate::ir::{Opcode, Value}; use crate::isa::aarch64::inst::*; +use crate::isa::aarch64::pcc; use crate::isa::aarch64::AArch64Backend; use crate::machinst::lower::*; -use crate::machinst::Reg; use crate::machinst::*; pub mod isle; @@ -129,4 +130,8 @@ impl LowerBackend for AArch64Backend { fn maybe_pinned_reg(&self) -> Option { Some(regs::pinned_reg()) } + + fn check_fact(&self, inst: &Self::MInst, vcode: &VCode) -> PccResult<()> { + pcc::check(inst, vcode) + } } diff --git a/cranelift/codegen/src/isa/aarch64/mod.rs b/cranelift/codegen/src/isa/aarch64/mod.rs index ec724abbf444..59541d9d1517 100644 --- a/cranelift/codegen/src/isa/aarch64/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/mod.rs @@ -21,6 +21,7 @@ use target_lexicon::{Aarch64Architecture, Architecture, OperatingSystem, Triple} mod abi; pub mod inst; mod lower; +mod pcc; pub mod settings; use self::inst::EmitInfo; diff --git a/cranelift/codegen/src/isa/aarch64/pcc.rs b/cranelift/codegen/src/isa/aarch64/pcc.rs new file mode 100644 index 000000000000..d6427a8c1787 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/pcc.rs @@ -0,0 +1,193 @@ +//! Proof-carrying code checking for AArch64 VCode. + +use crate::ir::pcc::*; +use crate::ir::MemFlags; +use crate::isa::aarch64::inst::args::PairAMode; +use crate::isa::aarch64::inst::Inst; +use crate::isa::aarch64::inst::{AMode, ExtendOp}; +use crate::machinst::Reg; +use crate::machinst::VCode; +use crate::trace; + +pub(crate) fn check(inst: &Inst, vcode: &VCode) -> PccResult<()> { + // Create a new fact context with the machine's pointer width. + let ctx = FactContext::new(64); + + trace!("Checking facts on inst: {:?}", inst); + + match inst { + Inst::Args { .. } => { + // Defs on the args have "axiomatic facts": we trust the + // ABI code to pass through the values unharmed, so the + // facts given to us in the CLIF should still be true. + Ok(()) + } + Inst::ULoad8 { mem, flags, .. } | Inst::SLoad8 { mem, flags, .. } => { + check_addr(&ctx, *flags, mem, vcode, 1) + } + Inst::ULoad16 { mem, flags, .. } | Inst::SLoad16 { mem, flags, .. } => { + check_addr(&ctx, *flags, mem, vcode, 2) + } + Inst::ULoad32 { mem, flags, .. } | Inst::SLoad32 { mem, flags, .. } => { + check_addr(&ctx, *flags, mem, vcode, 4) + } + Inst::ULoad64 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 8), + Inst::FpuLoad32 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 4), + Inst::FpuLoad64 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 8), + Inst::FpuLoad128 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 16), + Inst::LoadP64 { mem, flags, .. } => check_addr_pair(&ctx, *flags, mem, vcode, 16), + Inst::FpuLoadP64 { mem, flags, .. } => check_addr_pair(&ctx, *flags, mem, vcode, 16), + Inst::FpuLoadP128 { mem, flags, .. } => check_addr_pair(&ctx, *flags, mem, vcode, 32), + Inst::VecLoadReplicate { rn, flags, .. } => check_scalar_addr(&ctx, *flags, *rn, vcode, 16), + Inst::LoadAcquire { + access_ty, + rn, + flags, + .. + } => check_scalar_addr(&ctx, *flags, *rn, vcode, access_ty.bytes() as u8), + + Inst::Store8 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 1), + Inst::Store16 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 2), + Inst::Store32 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 4), + Inst::Store64 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 8), + Inst::FpuStore32 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 4), + Inst::FpuStore64 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 8), + Inst::FpuStore128 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 16), + Inst::StoreP64 { mem, flags, .. } => check_addr_pair(&ctx, *flags, mem, vcode, 16), + Inst::FpuStoreP64 { mem, flags, .. } => check_addr_pair(&ctx, *flags, mem, vcode, 16), + Inst::FpuStoreP128 { mem, flags, .. } => check_addr_pair(&ctx, *flags, mem, vcode, 32), + Inst::StoreRelease { + access_ty, + rn, + flags, + .. + } => check_scalar_addr(&ctx, *flags, *rn, vcode, access_ty.bytes() as u8), + + i => { + panic!("Fact on unknown inst: {:?}", i); + } + } +} + +fn amode_extend(ctx: &FactContext, value: &Fact, mode: ExtendOp) -> Option { + match mode { + ExtendOp::UXTB => ctx.uextend(value, 8, 64), + ExtendOp::UXTH => ctx.uextend(value, 16, 64), + ExtendOp::UXTW => ctx.uextend(value, 32, 64), + ExtendOp::UXTX => Some(value.clone()), + ExtendOp::SXTB => ctx.sextend(value, 8, 64), + ExtendOp::SXTH => ctx.uextend(value, 16, 64), + ExtendOp::SXTW => ctx.uextend(value, 32, 64), + ExtendOp::SXTX => None, + } +} + +fn check_addr( + ctx: &FactContext, + flags: MemFlags, + addr: &AMode, + vcode: &VCode, + size: u8, +) -> PccResult<()> { + if !flags.checked() { + return Ok(()); + } + + match addr { + &AMode::RegReg { rn, rm } => { + let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; + let rm = vcode.vreg_fact(rm.into()).ok_or(PccError::MissingFact)?; + let sum = ctx.add(&rn, &rm, 64).ok_or(PccError::MissingFact)?; + ctx.check_address(&sum, size as u32) + } + &AMode::RegScaled { rn, rm, ty } => { + let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; + let rm = vcode.vreg_fact(rm.into()).ok_or(PccError::MissingFact)?; + let rm_scaled = ctx.scale(&rm, 64, ty.bytes()).ok_or(PccError::Overflow)?; + let sum = ctx.add(&rn, &rm_scaled, 64).ok_or(PccError::MissingFact)?; + ctx.check_address(&sum, size as u32) + } + &AMode::RegScaledExtended { + rn, + rm, + ty, + extendop, + } => { + let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; + let rm = vcode.vreg_fact(rm.into()).ok_or(PccError::MissingFact)?; + let rm_extended = amode_extend(ctx, rm, extendop).ok_or(PccError::MissingFact)?; + let rm_scaled = ctx + .scale(&rm_extended, 64, ty.bytes()) + .ok_or(PccError::Overflow)?; + let sum = ctx.add(&rn, &rm_scaled, 64).ok_or(PccError::MissingFact)?; + ctx.check_address(&sum, size as u32) + } + &AMode::RegExtended { rn, rm, extendop } => { + let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; + let rm = vcode.vreg_fact(rm.into()).ok_or(PccError::MissingFact)?; + let rm_extended = amode_extend(ctx, rm, extendop).ok_or(PccError::MissingFact)?; + let sum = ctx + .add(&rn, &rm_extended, 64) + .ok_or(PccError::MissingFact)?; + ctx.check_address(&sum, size as u32) + } + &AMode::Unscaled { rn, simm9 } => { + let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; + let sum = ctx + .offset(&rn, 64, simm9.value as i64) + .ok_or(PccError::MissingFact)?; + ctx.check_address(&sum, size as u32) + } + &AMode::UnsignedOffset { rn, uimm12 } => { + let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; + let offset = (uimm12.value as u64) * (size as u64); + let sum = ctx + .offset(&rn, 64, offset as i64) + .ok_or(PccError::MissingFact)?; + ctx.check_address(&sum, size as u32) + } + &AMode::Label { .. } | &AMode::Const { .. } => { + // Always accept: labels and constants must be within the + // generated code (else they won't be resolved). + Ok(()) + } + &AMode::RegOffset { rn, off, .. } => { + let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; + let sum = ctx.offset(&rn, 64, off).ok_or(PccError::MissingFact)?; + ctx.check_address(&sum, size as u32) + } + &AMode::SPOffset { .. } + | &AMode::FPOffset { .. } + | &AMode::NominalSPOffset { .. } + | &AMode::SPPostIndexed { .. } + | &AMode::SPPreIndexed { .. } => { + // We trust ABI code (for now!) and no lowering rules + // lower input value accesses directly to these. + Ok(()) + } + } +} + +fn check_addr_pair( + _ctx: &FactContext, + _flags: MemFlags, + _addr: &PairAMode, + _vcode: &VCode, + _size: u8, +) -> PccResult<()> { + Err(PccError::UnimplementedInst) +} + +fn check_scalar_addr( + ctx: &FactContext, + flags: MemFlags, + reg: Reg, + vcode: &VCode, + size: u8, +) -> PccResult<()> { + if !flags.checked() { + return Ok(()); + } + let fact = vcode.vreg_fact(reg.into()).ok_or(PccError::MissingFact)?; + ctx.check_address(&fact, size as u32) +} diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs index 4c10191eccfc..e484195e7565 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs @@ -889,6 +889,10 @@ impl MachInst for Inst { } } + fn is_mem_access(&self) -> bool { + panic!("TODO FILL ME OUT") + } + fn gen_move(to_reg: Writable, from_reg: Reg, ty: Type) -> Inst { let x = Inst::Mov { rd: to_reg, diff --git a/cranelift/codegen/src/isa/s390x/inst/mod.rs b/cranelift/codegen/src/isa/s390x/inst/mod.rs index 0ece48c7b664..bec240f8b960 100644 --- a/cranelift/codegen/src/isa/s390x/inst/mod.rs +++ b/cranelift/codegen/src/isa/s390x/inst/mod.rs @@ -1064,6 +1064,10 @@ impl MachInst for Inst { } } + fn is_mem_access(&self) -> bool { + panic!("TODO FILL ME OUT") + } + fn is_safepoint(&self) -> bool { match self { &Inst::Call { .. } diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index 2a321c7f44f6..19bda166c19f 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -2584,6 +2584,10 @@ impl MachInst for Inst { } } + fn is_mem_access(&self) -> bool { + panic!("TODO FILL ME OUT") + } + fn gen_move(dst_reg: Writable, src_reg: Reg, ty: Type) -> Inst { trace!( "Inst::gen_move {:?} -> {:?} (type: {:?})", diff --git a/cranelift/codegen/src/machinst/compile.rs b/cranelift/codegen/src/machinst/compile.rs index 07440489a91c..87481218246c 100644 --- a/cranelift/codegen/src/machinst/compile.rs +++ b/cranelift/codegen/src/machinst/compile.rs @@ -1,11 +1,13 @@ //! Compilation backend pipeline: optimized IR to VCode / binemit. use crate::dominator_tree::DominatorTree; +use crate::ir::pcc; use crate::ir::Function; use crate::isa::TargetIsa; use crate::machinst::*; use crate::timing; use crate::trace; +use crate::CodegenError; use regalloc2::RegallocOptions; @@ -45,6 +47,11 @@ pub fn compile( log::debug!("Number of lowered vcode blocks: {}", vcode.num_blocks()); trace!("vcode from lowering: \n{:?}", vcode); + // Perform validation of proof-carrying-code facts, if requested. + if b.flags().enable_pcc() { + pcc::check_vcode_facts(f, &vcode, b).map_err(CodegenError::Pcc)?; + } + // Perform register allocation. let regalloc_result = { let _tt = timing::regalloc(); diff --git a/cranelift/codegen/src/machinst/lower.rs b/cranelift/codegen/src/machinst/lower.rs index f4f02890e7de..38fac43b4ee7 100644 --- a/cranelift/codegen/src/machinst/lower.rs +++ b/cranelift/codegen/src/machinst/lower.rs @@ -8,6 +8,7 @@ use crate::entity::SecondaryMap; use crate::fx::{FxHashMap, FxHashSet}; use crate::inst_predicates::{has_lowering_side_effect, is_constant_64bit}; +use crate::ir::pcc::{PccError, PccResult}; use crate::ir::{ ArgumentPurpose, Block, Constant, ConstantData, DataFlowGraph, ExternalName, Function, GlobalValue, GlobalValueData, Immediate, Inst, InstructionData, MemFlags, RelSourceLoc, Type, @@ -145,6 +146,12 @@ pub trait LowerBackend { fn maybe_pinned_reg(&self) -> Option { None } + + /// Check any facts about an instruction, given VCode with facts + /// on VRegs. + fn check_fact(&self, _inst: &Self::MInst, _vcode: &VCode) -> PccResult<()> { + Err(PccError::UnimplementedBackend) + } } /// Machine-independent lowering driver / machine-instruction container. Maintains a correspondence @@ -349,7 +356,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { for ¶m in f.dfg.block_params(bb) { let ty = f.dfg.value_type(param); if value_regs[param].is_invalid() { - let regs = vregs.alloc(ty)?; + let regs = vregs.alloc_with_maybe_fact(ty, f.dfg.facts[param].clone())?; value_regs[param] = regs; trace!("bb {} param {}: regs {:?}", bb, param, regs); } @@ -358,7 +365,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { for &result in f.dfg.inst_results(inst) { let ty = f.dfg.value_type(result); if value_regs[result].is_invalid() && !ty.is_invalid() { - let regs = vregs.alloc(ty)?; + let regs = vregs.alloc_with_maybe_fact(ty, f.dfg.facts[result].clone())?; value_regs[result] = regs; trace!( "bb {} inst {} ({:?}): result {} regs {:?}", diff --git a/cranelift/codegen/src/machinst/mod.rs b/cranelift/codegen/src/machinst/mod.rs index a8d3ce376d12..c587d64adfb6 100644 --- a/cranelift/codegen/src/machinst/mod.rs +++ b/cranelift/codegen/src/machinst/mod.rs @@ -112,6 +112,9 @@ pub trait MachInst: Clone + Debug { /// Should this instruction be included in the clobber-set? fn is_included_in_clobbers(&self) -> bool; + /// Does this instruction access memory? + fn is_mem_access(&self) -> bool; + /// Generate a move. fn gen_move(to_reg: Writable, from_reg: Reg, ty: Type) -> Self; diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs index 7e88902634d2..1a09bc59f97c 100644 --- a/cranelift/codegen/src/machinst/vcode.rs +++ b/cranelift/codegen/src/machinst/vcode.rs @@ -19,6 +19,7 @@ use crate::fx::FxHashMap; use crate::fx::FxHashSet; +use crate::ir::pcc::*; use crate::ir::RelSourceLoc; use crate::ir::{self, types, Constant, ConstantData, DynamicStackSlot, ValueLabel}; use crate::machinst::*; @@ -172,6 +173,9 @@ pub struct VCode { debug_value_labels: Vec<(VReg, InsnIndex, InsnIndex, u32)>, pub(crate) sigs: SigSet, + + /// Facts on VRegs, for proof-carrying code verification. + facts: Vec>, } /// The result of `VCode::emit`. Contains all information computed @@ -586,6 +590,7 @@ impl VCodeBuilder { /// Build the final VCode. pub fn build(mut self, vregs: VRegAllocator) -> VCode { self.vcode.vreg_types = vregs.vreg_types; + self.vcode.facts = vregs.facts; self.vcode.reftyped_vregs = vregs.reftyped_vregs; if self.direction == VCodeBuildDirection::Backward { @@ -654,6 +659,7 @@ impl VCode { constants, debug_value_labels: vec![], vreg_aliases: FxHashMap::with_capacity_and_hasher(10 * n_blocks, Default::default()), + facts: vec![], } } @@ -1277,6 +1283,27 @@ impl VCode { self.assert_not_vreg_alias(op.vreg()); op } + + /// Get the fact, if any, for a given VReg. + pub fn vreg_fact(&self, vreg: VReg) -> Option<&Fact> { + self.facts[vreg.vreg()].as_ref() + } + + /// Does a given instruction define any facts? + pub fn inst_defines_facts(&self, inst: InsnIndex) -> bool { + self.inst_operands(inst) + .iter() + .filter(|o| o.kind() == OperandKind::Def) + .map(|o| o.vreg()) + .any(|vreg| self.vreg_fact(vreg).is_some()) + } +} + +impl std::ops::Index for VCode { + type Output = I; + fn index(&self, idx: InsnIndex) -> &Self::Output { + &self.insts[idx.index()] + } } impl RegallocFunction for VCode { @@ -1431,6 +1458,13 @@ impl fmt::Debug for VCode { inst, self.insts[inst].pretty_print_inst(&[], &mut state) )?; + for operand in self.inst_operands(InsnIndex::new(inst)) { + if operand.kind() == OperandKind::Def { + if let Some(fact) = &self.facts[operand.vreg().vreg()] { + writeln!(f, " v{} ! {:?}", operand.vreg().vreg(), fact)?; + } + } + } } } @@ -1462,6 +1496,9 @@ pub struct VRegAllocator { /// lowering rules) or some ABI code. deferred_error: Option, + /// Facts on VRegs, for proof-carrying code. + facts: Vec>, + /// The type of instruction that this allocator makes registers for. _inst: core::marker::PhantomData, } @@ -1472,6 +1509,7 @@ impl VRegAllocator { Self { next_vreg: first_user_vreg_index(), vreg_types: vec![], + facts: vec![], reftyped_vregs_set: FxHashSet::default(), reftyped_vregs: vec![], deferred_error: None, @@ -1502,6 +1540,11 @@ impl VRegAllocator { for (®_ty, ®) in tys.iter().zip(regs.regs().iter()) { self.set_vreg_type(reg.to_virtual_reg().unwrap(), reg_ty); } + + // Create empty facts for each allocated vreg. + self.facts + .resize(usize::try_from(self.next_vreg).unwrap(), None); + Ok(regs) } @@ -1550,6 +1593,32 @@ impl VRegAllocator { } } } + + /// Set the proof-carrying code fact on a given virtual register. + /// + /// Returns the old fact, if any (only one fact can be stored). + pub fn set_fact(&mut self, vreg: VirtualReg, fact: Fact) -> Option { + self.facts[vreg.index()].replace(fact) + } + + /// Allocate a fresh ValueRegs, with a given fact to apply if + /// the value fits in one VReg. + pub fn alloc_with_maybe_fact( + &mut self, + ty: Type, + fact: Option, + ) -> CodegenResult> { + let result = self.alloc(ty)?; + + // Ensure that we don't lose a fact on a value that splits + // into multiple VRegs. + assert!(result.len() == 1 || fact.is_none()); + if let Some(fact) = fact { + self.set_fact(result.regs()[0].to_virtual_reg().unwrap(), fact); + } + + Ok(result) + } } /// This structure tracks the large constants used in VCode that will be emitted separately by the diff --git a/cranelift/codegen/src/result.rs b/cranelift/codegen/src/result.rs index d14049c0b6b9..9cbef7979551 100644 --- a/cranelift/codegen/src/result.rs +++ b/cranelift/codegen/src/result.rs @@ -2,6 +2,7 @@ use regalloc2::checker::CheckerErrors; +use crate::ir::pcc::PccError; use crate::{ir::Function, verifier::VerifierErrors}; use std::string::String; @@ -41,6 +42,9 @@ pub enum CodegenError { /// Register allocator internal error discovered by the symbolic checker. Regalloc(CheckerErrors), + + /// Proof-carrying-code validation error. + Pcc(PccError), } /// A convenient alias for a `Result` that uses `CodegenError` as the error type. @@ -58,6 +62,7 @@ impl std::error::Error for CodegenError { #[cfg(feature = "unwind")] CodegenError::RegisterMappingError { .. } => None, CodegenError::Regalloc(..) => None, + CodegenError::Pcc(..) => None, } } } @@ -72,6 +77,7 @@ impl std::fmt::Display for CodegenError { #[cfg(feature = "unwind")] CodegenError::RegisterMappingError(_0) => write!(f, "Register mapping error"), CodegenError::Regalloc(errors) => write!(f, "Regalloc validation errors: {:?}", errors), + CodegenError::Pcc(e) => write!(f, "Proof-carrying-code validation error: {:?}", e), } } } diff --git a/cranelift/codegen/src/settings.rs b/cranelift/codegen/src/settings.rs index bb4af16d1fb6..9c52203365cf 100644 --- a/cranelift/codegen/src/settings.rs +++ b/cranelift/codegen/src/settings.rs @@ -530,6 +530,7 @@ regalloc_checker = false regalloc_verbose_logs = false enable_alias_analysis = true enable_verifier = true +enable_pcc = false is_pic = false use_colocated_libcalls = false enable_float = true diff --git a/cranelift/codegen/src/write.rs b/cranelift/codegen/src/write.rs index 39025fd7addc..20239d1355de 100644 --- a/cranelift/codegen/src/write.rs +++ b/cranelift/codegen/src/write.rs @@ -199,7 +199,12 @@ fn write_spec(w: &mut dyn Write, func: &Function) -> fmt::Result { // Basic blocks fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result { - write!(w, "{}: {}", arg, func.dfg.value_type(arg)) + let ty = func.dfg.value_type(arg); + if let Some(f) = &func.dfg.facts[arg] { + write!(w, "{} ! {}: {}", arg, f, ty) + } else { + write!(w, "{}: {}", arg, ty) + } } /// Write out the basic block header, outdented: @@ -346,6 +351,9 @@ fn write_instruction( } else { write!(w, ", {}", r)?; } + if let Some(f) = &func.dfg.facts[*r] { + write!(w, " ! {}", f)?; + } } if has_results { write!(w, " = ")?; diff --git a/cranelift/filetests/filetests/pcc/fail/simple.clif b/cranelift/filetests/filetests/pcc/fail/simple.clif new file mode 100644 index 000000000000..5aac4f8601f3 --- /dev/null +++ b/cranelift/filetests/filetests/pcc/fail/simple.clif @@ -0,0 +1,15 @@ +test compile expect-fail +set enable_pcc=true +target aarch64 + +;; The `points_to` annotation is not large enough here -- the +;; 4GiB-range 32-bit offset could go out of range. PCC should catch +;; this. + +function %simple1(i64 vmctx, i32) -> i8 { +block0(v0 ! points_to(0x8000_0000): i64, v1 ! max(32, 0xffff_ffff): i32): + v2 ! max(64, 0xffff_ffff) = uextend.i64 v1 + v3 ! points_to(0x1) = iadd.i64 v0, v2 + v4 = load.i8 checked v3 + return v4 +} diff --git a/cranelift/filetests/filetests/pcc/succeed/simple.clif b/cranelift/filetests/filetests/pcc/succeed/simple.clif new file mode 100644 index 000000000000..cf1793e5a583 --- /dev/null +++ b/cranelift/filetests/filetests/pcc/succeed/simple.clif @@ -0,0 +1,11 @@ +test compile +set enable_pcc=true +target aarch64 + +function %simple1(i64 vmctx, i32) -> i8 { +block0(v0 ! points_to(0x1_0000_0000): i64, v1 ! max(32, 0xffff_ffff): i32): + v2 ! max(64, 0xffff_ffff) = uextend.i64 v1 + v3 ! points_to(0x1) = iadd.i64 v0, v2 + v4 = load.i8 checked v3 + return v4 +} diff --git a/cranelift/reader/src/lexer.rs b/cranelift/reader/src/lexer.rs index e2c80cf93db9..0367f4b7b9b5 100644 --- a/cranelift/reader/src/lexer.rs +++ b/cranelift/reader/src/lexer.rs @@ -28,7 +28,7 @@ pub enum Token<'a> { Dot, // '.' Colon, // ':' Equal, // '=' - Not, // '!' + Bang, // '!' Arrow, // '->' Float(&'a str), // Floating point immediate Integer(&'a str), // Integer immediate @@ -474,7 +474,7 @@ impl<'a> Lexer<'a> { Some('.') => Some(self.scan_char(Token::Dot)), Some(':') => Some(self.scan_char(Token::Colon)), Some('=') => Some(self.scan_char(Token::Equal)), - Some('!') => Some(self.scan_char(Token::Not)), + Some('!') => Some(self.scan_char(Token::Bang)), Some('+') => Some(self.scan_number()), Some('*') => Some(self.scan_char(Token::Multiply)), Some('-') => { diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 28b6ae18600c..100ef20ec332 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -12,6 +12,7 @@ use cranelift_codegen::entity::{EntityRef, PrimaryMap}; use cranelift_codegen::ir::entities::{AnyEntity, DynamicType}; use cranelift_codegen::ir::immediates::{Ieee32, Ieee64, Imm64, Offset32, Uimm32, Uimm64}; use cranelift_codegen::ir::instructions::{InstructionData, InstructionFormat, VariableArgs}; +use cranelift_codegen::ir::pcc::{Fact, MemoryRegion}; use cranelift_codegen::ir::types; use cranelift_codegen::ir::types::INVALID; use cranelift_codegen::ir::types::*; @@ -1135,7 +1136,7 @@ impl<'a> Parser<'a> { let mut list = Vec::new(); while self.token() == Some(Token::Identifier("feature")) { self.consume(); - let has = !self.optional(Token::Not); + let has = !self.optional(Token::Bang); match (self.token(), has) { (Some(Token::String(flag)), true) => list.push(Feature::With(flag)), (Some(Token::String(flag)), false) => list.push(Feature::Without(flag)), @@ -1943,7 +1944,7 @@ impl<'a> Parser<'a> { // between the parsing of value aliases and the parsing of instructions. // // inst-results ::= Value(v) { "," Value(v) } - let results = self.parse_inst_results()?; + let results = self.parse_inst_results(ctx)?; for result in &results { while ctx.function.dfg.num_values() <= result.index() { @@ -1993,16 +1994,23 @@ impl<'a> Parser<'a> { // Parse a single block parameter declaration, and append it to `block`. // - // block-param ::= * Value(v) ":" Type(t) arg-loc? + // block-param ::= * Value(v) [ "!" fact ] ":" Type(t) arg-loc? // arg-loc ::= "[" value-location "]" // fn parse_block_param(&mut self, ctx: &mut Context, block: Block) -> ParseResult<()> { - // block-param ::= * Value(v) ":" Type(t) arg-loc? + // block-param ::= * Value(v) [ "!" fact ] ":" Type(t) arg-loc? let v = self.match_value("block argument must be a value")?; let v_location = self.loc; - // block-param ::= Value(v) * ":" Type(t) arg-loc? + // block-param ::= Value(v) * [ "!" fact ] ":" Type(t) arg-loc? + let fact = if self.token() == Some(Token::Bang) { + self.consume(); + // block-param ::= Value(v) [ "!" * fact ] ":" Type(t) arg-loc? + Some(self.parse_fact()?) + } else { + None + }; self.match_token(Token::Colon, "expected ':' after block argument")?; - // block-param ::= Value(v) ":" * Type(t) arg-loc? + // block-param ::= Value(v) [ "!" fact ] ":" * Type(t) arg-loc? while ctx.function.dfg.num_values() <= v.index() { ctx.function.dfg.make_invalid_value_for_parser(); @@ -2012,15 +2020,66 @@ impl<'a> Parser<'a> { // Allocate the block argument. ctx.function.dfg.append_block_param_for_parser(block, t, v); ctx.map.def_value(v, v_location)?; + ctx.function.dfg.facts[v] = fact; Ok(()) } + // Parse a "fact" for proof-carrying code, attached to a value. + // + // fact ::= "max" "(" bit-width "," max-value ")" + // | "points_to" "(" valid-range ")" + // bit-width ::= uimm64 + // max-value ::= uimm64 + // valid-range ::= uimm64 + fn parse_fact(&mut self) -> ParseResult { + match self.token() { + Some(Token::Identifier("max")) => { + self.consume(); + self.match_token(Token::LPar, "`max` fact needs an opening `(`")?; + let bit_width: u64 = self + .match_uimm64("expected a bit-width value for `max` fact")? + .into(); + self.match_token(Token::Comma, "expected a comma")?; + let max: u64 = self + .match_uimm64("expected a max value for `max` fact")? + .into(); + self.match_token(Token::RPar, "`max` fact needs a closing `)`")?; + let bit_width_max = match bit_width { + x if x > 64 => { + return Err(self.error("bitwidth must be <= 64 bits on a `max` fact")); + } + 64 => u64::MAX, + x => (1u64 << x) - 1, + }; + if max > bit_width_max { + return Err( + self.error("max value is out of range for bitwidth on a `max` fact") + ); + } + Ok(Fact::ValueMax { + bit_width: u16::try_from(bit_width).unwrap(), + max: max.into(), + }) + } + Some(Token::Identifier("points_to")) => { + self.consume(); + self.match_token(Token::LPar, "expected a `(`")?; + let max = self.match_uimm64("expected a max offset for `points_to` fact")?; + self.match_token(Token::RPar, "expected a `)`")?; + Ok(Fact::PointsTo { + region: MemoryRegion { max: max.into() }, + }) + } + _ => Err(self.error("expected a `max` or `points_to` fact")), + } + } + // Parse instruction results and return them. // // inst-results ::= Value(v) { "," Value(v) } // - fn parse_inst_results(&mut self) -> ParseResult> { + fn parse_inst_results(&mut self, ctx: &mut Context) -> ParseResult> { // Result value numbers. let mut results = SmallVec::new(); @@ -2031,10 +2090,29 @@ impl<'a> Parser<'a> { results.push(v); + let fact = if self.token() == Some(Token::Bang) { + self.consume(); + // block-param ::= Value(v) [ "!" * fact ] ":" Type(t) arg-loc? + Some(self.parse_fact()?) + } else { + None + }; + ctx.function.dfg.facts[v] = fact; + // inst-results ::= Value(v) * { "," Value(v) } while self.optional(Token::Comma) { // inst-results ::= Value(v) { "," * Value(v) } - results.push(self.match_value("expected result value")?); + let v = self.match_value("expected result value")?; + results.push(v); + + let fact = if self.token() == Some(Token::Bang) { + self.consume(); + // block-param ::= Value(v) [ "!" * fact ] ":" Type(t) arg-loc? + Some(self.parse_fact()?) + } else { + None + }; + ctx.function.dfg.facts[v] = fact; } } @@ -2368,7 +2446,7 @@ impl<'a> Parser<'a> { if self.optional(Token::Equal) { self.match_token(Token::Equal, "expected another =")?; Ok(Comparison::Equals) - } else if self.optional(Token::Not) { + } else if self.optional(Token::Bang) { self.match_token(Token::Equal, "expected a =")?; Ok(Comparison::NotEquals) } else { diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs index 82dd651e093d..336048e79f4a 100644 --- a/crates/wasmtime/src/engine.rs +++ b/crates/wasmtime/src/engine.rs @@ -416,6 +416,7 @@ impl Engine { | "enable_jump_tables" | "enable_float" | "enable_verifier" + | "enable_pcc" | "regalloc_checker" | "regalloc_verbose_logs" | "is_pic" From 099b66358b1d45af1a172c0a7f592d4b5ab2334d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 6 Oct 2023 17:59:37 -0500 Subject: [PATCH 079/199] Update syntax for `trappable_error_type` in `bindgen!` (#7170) * Update syntax for `trappable_error_type` in `bindgen!` Following through on a suggestion from #7152 this makes the syntax a bit more consistent with the rest of `bindgen!` today. This additionally updates the documentation on the `bindgen!` macro itself. * Update parsing to afford for versions --- crates/component-macro/src/bindgen.rs | 23 +---- crates/component-macro/tests/codegen.rs | 4 +- crates/wasi/src/preview2/mod.rs | 14 ++- crates/wasmtime/src/component/mod.rs | 11 +-- crates/wit-bindgen/src/lib.rs | 95 +++++--------------- tests/all/component_model/bindgen/results.rs | 10 +-- 6 files changed, 43 insertions(+), 114 deletions(-) diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs index e3aca760c1cc..a508d1e99483 100644 --- a/crates/component-macro/src/bindgen.rs +++ b/crates/component-macro/src/bindgen.rs @@ -318,28 +318,11 @@ impl Parse for Opt { } fn trappable_error_field_parse(input: ParseStream<'_>) -> Result { - // Accept a Rust identifier or a string literal. This is required - // because not all wit identifiers are Rust identifiers, so we can - // smuggle the invalid ones inside quotes. - fn ident_or_str(input: ParseStream<'_>) -> Result { - let l = input.lookahead1(); - if l.peek(syn::LitStr) { - Ok(input.parse::()?.value()) - } else if l.peek(syn::Ident) { - Ok(input.parse::()?.to_string()) - } else { - Err(l.error()) - } - } - - let wit_package_path = input.parse::()?.value(); - input.parse::()?; - let wit_type_name = ident_or_str(input)?; - input.parse::()?; + let wit_path = input.parse::()?.value(); + input.parse::]>()?; let rust_type_name = input.parse::()?.to_token_stream().to_string(); Ok(TrappableError { - wit_package_path, - wit_type_name, + wit_path, rust_type_name, }) } diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs index 5b6292989d82..66f9fb1c26bb 100644 --- a/crates/component-macro/tests/codegen.rs +++ b/crates/component-macro/tests/codegen.rs @@ -140,8 +140,8 @@ mod trappable_errors { } ", trappable_error_type: { - "demo:pkg/a"::"b": MyX, - "demo:pkg/c"::"b": MyX, + "demo:pkg/a/b" => MyX, + "demo:pkg/c/b" => MyX, }, }); diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index de6f8687a5b8..8b188e686873 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -71,8 +71,8 @@ pub mod bindings { ", tracing: true, trappable_error_type: { - "wasi:io/streams"::"stream-error": StreamError, - "wasi:filesystem/types"::"error-code": FsError, + "wasi:io/streams/stream-error" => StreamError, + "wasi:filesystem/types/error-code" => FsError, }, with: { "wasi:clocks/wall-clock": crate::preview2::bindings::clocks::wall_clock, @@ -149,17 +149,15 @@ pub mod bindings { "poll-one", ], }, - with: { - "wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream, - }, trappable_error_type: { - "wasi:io/streams"::"stream-error": crate::preview2::StreamError, - "wasi:filesystem/types"::"error-code": crate::preview2::FsError, - "wasi:sockets/network"::"error-code": crate::preview2::SocketError, + "wasi:io/streams/stream-error" => crate::preview2::StreamError, + "wasi:filesystem/types/error-code" => crate::preview2::FsError, + "wasi:sockets/network/error-code" => crate::preview2::SocketError, }, with: { "wasi:sockets/network/network": super::network::Network, "wasi:sockets/tcp/tcp-socket": super::tcp::TcpSocket, + "wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream, "wasi:filesystem/types/directory-entry-stream": super::filesystem::ReaddirIterator, "wasi:filesystem/types/descriptor": super::filesystem::Descriptor, "wasi:io/streams/input-stream": super::stream::InputStream, diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs index b44d4f5ca2f7..e2028a2bebb5 100644 --- a/crates/wasmtime/src/component/mod.rs +++ b/crates/wasmtime/src/component/mod.rs @@ -299,14 +299,15 @@ pub(crate) use self::store::ComponentStoreData; /// /// // This can be used to translate WIT return values of the form /// // `result` into `Result` in Rust. -/// // The `RustErrorType` structure will have an automatically generated -/// // implementation of `From for RustErrorType`. The -/// // `RustErrorType` additionally can also represent a trap to -/// // conveniently flatten all errors into one container. +/// // Users must define `RustErrorType` and the `Host` trait for the +/// // interface which defines `error-type` will have a method +/// // called `convert_error_type` which converts `RustErrorType` +/// // into `wasmtime::Result`. This conversion can either +/// // return the raw WIT error (`ErrorType` here) or a trap. /// // /// // By default this option is not specified. /// trappable_error_type: { -/// interface::ErrorType: RustErrorType, +/// "wasi:io/streams/stream-error" => RustErrorType, /// }, /// /// // All generated bindgen types are "owned" meaning types like `String` diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index 7183e0bf232c..c432140892f1 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -1,6 +1,6 @@ use crate::rust::{to_rust_ident, to_rust_upper_camel_case, RustGenerator, TypeMode}; use crate::types::{TypeInfo, Types}; -use anyhow::{anyhow, bail, Context}; +use anyhow::{anyhow, Context}; use heck::*; use indexmap::{IndexMap, IndexSet}; use std::collections::{BTreeMap, HashMap, HashSet}; @@ -114,11 +114,8 @@ pub struct Opts { #[derive(Debug, Clone)] pub struct TrappableError { - /// The package and interface that define the error type being mapped. - pub wit_package_path: String, - - /// The name of the error type in WIT that is being mapped. - pub wit_type_name: String, + /// Full path to the error, such as `wasi:io/streams/error`. + pub wit_path: String, /// The name, in Rust, of the error type to generate. pub rust_type_name: String, @@ -220,7 +217,7 @@ impl Wasmtime { fn generate(&mut self, resolve: &Resolve, id: WorldId) -> String { self.types.analyze(resolve, id); for (i, te) in self.opts.trappable_error_type.iter().enumerate() { - let id = resolve_type_in_package(resolve, &te.wit_package_path, &te.wit_type_name) + let id = resolve_type_in_package(resolve, &te.wit_path) .context(format!("resolving {:?}", te)) .unwrap(); let name = format!("_TrappableError{i}"); @@ -770,77 +767,29 @@ impl Wasmtime { } } -fn resolve_type_in_package( - resolve: &Resolve, - package_path: &str, - type_name: &str, -) -> anyhow::Result { +fn resolve_type_in_package(resolve: &Resolve, wit_path: &str) -> anyhow::Result { // foo:bar/baz - let (namespace, rest) = package_path - .split_once(':') - .ok_or_else(|| anyhow!("Invalid package path: missing package identifier"))?; - - let (package_name, iface_name) = rest - .split_once('/') - .ok_or_else(|| anyhow!("Invalid package path: missing namespace separator"))?; - - // TODO: we should handle version annotations - if package_name.contains('@') { - bail!("Invalid package path: version parsing is not currently handled"); - } - - let packages = Vec::from_iter( - resolve - .package_names - .iter() - .filter(|(pname, _)| pname.namespace == namespace && pname.name == package_name), - ); - - if packages.len() != 1 { - if packages.is_empty() { - bail!("No package named `{}`", namespace); - } else { - // Getting here is a bug, parsing version identifiers would disambiguate the intended - // package. - bail!( - "Multiple packages named `{}` found ({:?})", - namespace, - packages - ); - } - } - - let (_, &package_id) = packages[0]; - let package = &resolve.packages[package_id]; - - let (_, &iface_id) = package - .interfaces + let (_, interface) = resolve + .packages .iter() - .find(|(name, _)| name.as_str() == iface_name) - .ok_or_else(|| { - anyhow!( - "Unknown interface `{}` in package `{}`", - iface_name, - package_path - ) - })?; - - let iface = &resolve.interfaces[iface_id]; - - let (_, &type_id) = iface + .flat_map(|(_, p)| p.interfaces.iter()) + .find(|(_, id)| wit_path.starts_with(&resolve.id_of(**id).unwrap())) + .ok_or_else(|| anyhow!("no package/interface found to match `{wit_path}`"))?; + + let wit_path = wit_path + .strip_prefix(&resolve.id_of(*interface).unwrap()) + .unwrap(); + let wit_path = wit_path + .strip_prefix('/') + .ok_or_else(|| anyhow!("expected `/` after interface name"))?; + + let (_, id) = resolve.interfaces[*interface] .types .iter() - .find(|(n, _)| n.as_str() == type_name) - .ok_or_else(|| { - anyhow!( - "No type named `{}` in package `{}`", - package_name, - package_path - ) - })?; - - Ok(type_id) + .find(|(name, _)| wit_path == name.as_str()) + .ok_or_else(|| anyhow!("no types found to match `{wit_path}` in interface"))?; + Ok(*id) } struct InterfaceGenerator<'a> { diff --git a/tests/all/component_model/bindgen/results.rs b/tests/all/component_model/bindgen/results.rs index bc491ced250e..29f60fe8b45a 100644 --- a/tests/all/component_model/bindgen/results.rs +++ b/tests/all/component_model/bindgen/results.rs @@ -238,7 +238,7 @@ mod enum_error { enum-error: func(a: float64) -> result; } }", - trappable_error_type: { "inline:inline/imports"::e1: TrappableE1 } + trappable_error_type: { "inline:inline/imports/e1" => TrappableE1 } }); // You can create concrete trap types which make it all the way out to the @@ -418,9 +418,7 @@ mod record_error { record-error: func(a: float64) -> result; } }", - // Literal strings can be used for the interface and typename fields instead of - // identifiers, because wit identifiers arent always Rust identifiers. - trappable_error_type: { "inline:inline/imports"::"e2": TrappableE2 } + trappable_error_type: { "inline:inline/imports/e2" => TrappableE2 } }); pub enum TrappableE2 { @@ -591,7 +589,7 @@ mod variant_error { variant-error: func(a: float64) -> result; } }", - trappable_error_type: { "inline:inline/imports"::e3: TrappableE3 } + trappable_error_type: { "inline:inline/imports/e3" => TrappableE3 } }); pub enum TrappableE3 { @@ -786,7 +784,7 @@ mod multiple_interfaces_error { enum-error: func(a: float64) -> result; } }", - trappable_error_type: { "inline:inline/types"::e1: TrappableE1 } + trappable_error_type: { "inline:inline/types/e1" => TrappableE1 } }); pub enum TrappableE1 { From b1f7ff3b356f777e87da107d1c1535677aa258fb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 6 Oct 2023 18:00:12 -0500 Subject: [PATCH 080/199] Support multiple versions in `bindgen!` (#7172) This commit fixes a bug in the `bindgen!` macro where when faced with multiple packages that differ only in version number invalid bindings were generated. The fix here is to add version number information to package module names if necessary in situations such as this. This required some refactoring internally to have a single source of truth for what the name of a module should be and avoid having it implicitly calculated in two locations. --- .../component-macro/test-helpers/src/lib.rs | 2 +- .../codegen/multiversion/deps/v1/root.wit | 5 + .../codegen/multiversion/deps/v2/root.wit | 5 + .../tests/codegen/multiversion/root.wit | 6 + crates/wit-bindgen/src/lib.rs | 235 ++++++++++-------- 5 files changed, 149 insertions(+), 104 deletions(-) create mode 100644 crates/component-macro/tests/codegen/multiversion/deps/v1/root.wit create mode 100644 crates/component-macro/tests/codegen/multiversion/deps/v2/root.wit create mode 100644 crates/component-macro/tests/codegen/multiversion/root.wit diff --git a/crates/component-macro/test-helpers/src/lib.rs b/crates/component-macro/test-helpers/src/lib.rs index 28ea3ca4652f..9029b90bc686 100644 --- a/crates/component-macro/test-helpers/src/lib.rs +++ b/crates/component-macro/test-helpers/src/lib.rs @@ -10,7 +10,7 @@ pub fn foreach(input: TokenStream) -> TokenStream { let mut result = Vec::new(); for f in cwd.read_dir().unwrap() { let f = f.unwrap().path(); - if f.extension().and_then(|s| s.to_str()) == Some("wit") { + if f.extension().and_then(|s| s.to_str()) == Some("wit") || f.is_dir() { let name = f.file_stem().unwrap().to_str().unwrap(); let ident = Ident::new(&name.replace("-", "_"), Span::call_site()); let path = f.to_str().unwrap(); diff --git a/crates/component-macro/tests/codegen/multiversion/deps/v1/root.wit b/crates/component-macro/tests/codegen/multiversion/deps/v1/root.wit new file mode 100644 index 000000000000..cdda0a38b4ba --- /dev/null +++ b/crates/component-macro/tests/codegen/multiversion/deps/v1/root.wit @@ -0,0 +1,5 @@ +package my:dep@0.1.0; + +interface a { + x: func(); +} diff --git a/crates/component-macro/tests/codegen/multiversion/deps/v2/root.wit b/crates/component-macro/tests/codegen/multiversion/deps/v2/root.wit new file mode 100644 index 000000000000..fa8fbeb2df6b --- /dev/null +++ b/crates/component-macro/tests/codegen/multiversion/deps/v2/root.wit @@ -0,0 +1,5 @@ +package my:dep@0.2.0; + +interface a { + x: func(); +} diff --git a/crates/component-macro/tests/codegen/multiversion/root.wit b/crates/component-macro/tests/codegen/multiversion/root.wit new file mode 100644 index 000000000000..ff23acf81cc9 --- /dev/null +++ b/crates/component-macro/tests/codegen/multiversion/root.wit @@ -0,0 +1,6 @@ +package foo:bar; + +world foo { + import my:dep/a@0.1.0; + import my:dep/a@0.2.0; +} diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index c432140892f1..636858e1ff06 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -27,20 +27,22 @@ mod source; mod types; use source::Source; -struct InterfaceName { - /// True when this interface name has been remapped through the use of `with` in the `bindgen!` - /// macro invocation. - remapped: bool, - - /// The string name for this interface. - path: String, +#[derive(Clone)] +enum InterfaceName { + /// This interface was remapped using `with` to some other Rust code. + Remapped { + name_at_root: String, + local_path: Vec, + }, + /// This interface is generated in the module hierarchy specified. + Path(Vec), } #[derive(Default)] struct Wasmtime { src: Source, opts: Opts, - import_interfaces: BTreeMap, Vec>, + import_interfaces: Vec<(String, InterfaceName)>, import_functions: Vec, exports: Exports, types: Types, @@ -51,10 +53,6 @@ struct Wasmtime { trappable_errors: IndexMap, } -struct ImportInterface { - snake: String, - module: String, -} struct ImportFunction { add_to_linker: String, sig: String, @@ -63,7 +61,7 @@ struct ImportFunction { #[derive(Default)] struct Exports { fields: BTreeMap, - modules: BTreeMap, Vec>, + modules: Vec<(String, InterfaceName)>, funcs: Vec, } @@ -175,45 +173,85 @@ impl Wasmtime { is_export: bool, ) -> bool { let with_name = resolve.name_world_key(name); + + let mut path = Vec::new(); + if is_export { + path.push("exports".to_string()); + } + match name { + WorldKey::Name(name) => { + path.push(name.to_snake_case()); + } + WorldKey::Interface(_) => { + let iface = &resolve.interfaces[id]; + let pkgname = &resolve.packages[iface.package.unwrap()].name; + path.push(pkgname.namespace.to_snake_case()); + path.push(self.name_package_module(resolve, iface.package.unwrap())); + path.push(iface.name.as_ref().unwrap().to_snake_case()); + } + } let entry = if let Some(remapped_path) = self.opts.with.get(&with_name) { let name = format!("__with_name{}", self.with_name_counter); self.with_name_counter += 1; uwriteln!(self.src, "use {remapped_path} as {name};"); - InterfaceName { - remapped: true, - path: name, + InterfaceName::Remapped { + name_at_root: name, + local_path: path, } } else { - let path = match name { - WorldKey::Name(name) => name.to_snake_case(), - WorldKey::Interface(_) => { - let iface = &resolve.interfaces[id]; - let pkgname = &resolve.packages[iface.package.unwrap()].name; - format!( - "{}::{}::{}", - pkgname.namespace.to_snake_case(), - pkgname.name.to_snake_case(), - iface.name.as_ref().unwrap().to_snake_case() - ) - } - }; - let path = if is_export { - format!("exports::{path}") - } else { - path - }; - InterfaceName { - remapped: false, - path, - } + InterfaceName::Path(path) }; - let remapped = entry.remapped; + let remapped = matches!(entry, InterfaceName::Remapped { .. }); self.interface_names.insert(id, entry); - remapped } + /// If the package `id` is the only package with its namespace/name combo + /// then pass through the name unmodified. If, however, there are multiple + /// versions of this package then the package module is going to get version + /// information. + fn name_package_module(&self, resolve: &Resolve, id: PackageId) -> String { + let pkg = &resolve.packages[id]; + let versions_with_same_name = resolve + .packages + .iter() + .filter_map(|(_, p)| { + if p.name.namespace == pkg.name.namespace && p.name.name == pkg.name.name { + Some(&p.name.version) + } else { + None + } + }) + .collect::>(); + let base = pkg.name.name.to_snake_case(); + if versions_with_same_name.len() == 1 { + return base; + } + + let version = match &pkg.name.version { + Some(version) => version, + // If this package didn't have a version then don't mangle its name + // and other packages with the same name but with versions present + // will have their names mangled. + None => return base, + }; + + // Here there's multiple packages with the same name that differ only in + // version, so the version needs to be mangled into the Rust module name + // that we're generating. This in theory could look at all of + // `versions_with_same_name` and produce a minimal diff, e.g. for 0.1.0 + // and 0.2.0 this could generate "foo1" and "foo2", but for now + // a simpler path is chosen to generate "foo0_1_0" and "foo0_2_0". + let version = version + .to_string() + .replace('.', "_") + .replace('-', "_") + .replace('+', "_") + .to_snake_case(); + format!("{base}{version}") + } + fn generate(&mut self, resolve: &Resolve, id: WorldId) -> String { self.types.analyze(resolve, id); for (i, te) in self.opts.trappable_error_type.iter().enumerate() { @@ -232,6 +270,7 @@ impl Wasmtime { self.import(resolve, id, name, import); } } + for (name, export) in world.exports.iter() { if !self.opts.only_interfaces || matches!(export, WorldItem::Interface(_)) { self.export(resolve, name, export); @@ -288,15 +327,8 @@ impl Wasmtime { }} " ); - let pkg = resolve.interfaces[*id].package.unwrap(); - let pkgname = match name { - WorldKey::Name(_) => None, - WorldKey::Interface(_) => Some(resolve.packages[pkg].name.clone()), - }; self.import_interfaces - .entry(pkgname) - .or_insert(Vec::new()) - .push(ImportInterface { snake, module }); + .push((module, self.interface_names[id].clone())); } WorldItem::Type(ty) => { let name = match name { @@ -430,9 +462,7 @@ impl Wasmtime { }; self.exports .modules - .entry(pkgname.clone()) - .or_insert(Vec::new()) - .push(module); + .push((module, self.interface_names[id].clone())); let name = resolve.name_world_key(name); let (path, method_name) = match pkgname { @@ -562,18 +592,10 @@ impl Wasmtime { } let imports = mem::take(&mut self.import_interfaces); - self.emit_modules( - &imports - .into_iter() - .map(|(k, v)| (k, v.into_iter().map(|m| m.module).collect())) - .collect(), - ); - if !self.exports.modules.is_empty() { - uwriteln!(self.src, "pub mod exports {{"); - let exports = mem::take(&mut self.exports.modules); - self.emit_modules(&exports); - uwriteln!(self.src, "}}"); - } + self.emit_modules(imports); + + let exports = mem::take(&mut self.exports.modules); + self.emit_modules(exports); let mut src = mem::take(&mut self.src); if self.opts.rustfmt { @@ -603,34 +625,39 @@ impl Wasmtime { src.into() } - fn emit_modules(&mut self, modules: &BTreeMap, Vec>) { - let mut map = BTreeMap::new(); - for (pkg, modules) in modules { - match pkg { - Some(pkg) => { - let prev = map - .entry(&pkg.namespace) - .or_insert(BTreeMap::new()) - .insert(&pkg.name, modules); - assert!(prev.is_none()); - } - None => { - for module in modules { - uwriteln!(self.src, "{module}"); - } - } + fn emit_modules(&mut self, modules: Vec<(String, InterfaceName)>) { + #[derive(Default)] + struct Module { + submodules: BTreeMap, + contents: Vec, + } + let mut map = Module::default(); + for (module, name) in modules { + let path = match name { + InterfaceName::Remapped { local_path, .. } => local_path, + InterfaceName::Path(path) => path, + }; + let mut cur = &mut map; + for name in path[..path.len() - 1].iter() { + cur = cur + .submodules + .entry(name.clone()) + .or_insert(Module::default()); } + cur.contents.push(module); } - for (ns, pkgs) in map { - uwriteln!(self.src, "pub mod {} {{", ns.to_snake_case()); - for (pkg, modules) in pkgs { - uwriteln!(self.src, "pub mod {} {{", pkg.to_snake_case()); - for module in modules { - uwriteln!(self.src, "{module}"); - } - uwriteln!(self.src, "}}"); + + emit(&mut self.src, map); + + fn emit(me: &mut Source, module: Module) { + for (name, submodule) in module.submodules { + uwriteln!(me, "pub mod {name} {{"); + emit(me, submodule); + uwriteln!(me, "}}"); + } + for submodule in module.contents { + uwriteln!(me, "{submodule}"); } - uwriteln!(self.src, "}}"); } } } @@ -672,19 +699,12 @@ impl Wasmtime { return; } let mut interfaces = Vec::new(); - for (pkg, imports) in self.import_interfaces.iter() { - for import in imports { - let mut path = String::new(); - if let Some(pkg) = pkg { - path.push_str(&pkg.namespace.to_snake_case()); - path.push_str("::"); - path.push_str(&pkg.name.to_snake_case()); - path.push_str("::"); - } - - path.push_str(&import.snake); - interfaces.push(path); - } + for (_, name) in self.import_interfaces.iter() { + let path = match name { + InterfaceName::Remapped { .. } => unreachable!("imported a remapped module"), + InterfaceName::Path(path) => path, + }; + interfaces.push(path.join("::")); } uwrite!( @@ -1900,8 +1920,17 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { } } let mut path_to_root = self.path_to_root(); - let InterfaceName { path, .. } = &self.gen.interface_names[&interface]; - path_to_root.push_str(path); + match &self.gen.interface_names[&interface] { + InterfaceName::Remapped { name_at_root, .. } => path_to_root.push_str(name_at_root), + InterfaceName::Path(path) => { + for (i, name) in path.iter().enumerate() { + if i > 0 { + path_to_root.push_str("::"); + } + path_to_root.push_str(name); + } + } + } Some(path_to_root) } From de98ede953e6584e6f87533869cb2cb3956f1501 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sat, 7 Oct 2023 01:45:26 +0100 Subject: [PATCH 081/199] riscv64: Improve `f{min,max}` (#7181) --- cranelift/codegen/src/isa/riscv64/inst.isle | 33 ++-- .../codegen/src/isa/riscv64/inst/args.rs | 47 ------ .../codegen/src/isa/riscv64/inst/emit.rs | 137 ---------------- .../src/isa/riscv64/inst/emit_tests.rs | 9 -- cranelift/codegen/src/isa/riscv64/inst/mod.rs | 32 +--- cranelift/codegen/src/isa/riscv64/lower.isle | 21 ++- .../filetests/isa/riscv64/float.clif | 147 ------------------ .../filetests/filetests/isa/riscv64/fmax.clif | 62 ++++++++ .../filetests/filetests/isa/riscv64/fmin.clif | 62 ++++++++ 9 files changed, 154 insertions(+), 396 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/riscv64/fmax.clif create mode 100644 cranelift/filetests/filetests/isa/riscv64/fmin.clif diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 7ea81927dad5..4c99f7c8f2aa 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -274,15 +274,6 @@ (f_tmp WritableReg) (rs Reg) (ty Type)) - ;;;; FMax - (FloatSelect - (op FloatSelectOP) - (rd WritableReg) - ;; a integer register - (tmp WritableReg) - (rs1 Reg) - (rs2 Reg) - (ty Type)) ;; popcnt if target doesn't support extension B ;; use iteration to implement. @@ -391,11 +382,6 @@ )) -(type FloatSelectOP (enum - (Max) - (Min) -)) - (type FloatRoundOP (enum (Nearest) (Ceil) @@ -1098,15 +1084,6 @@ (_ Unit (emit (MInst.FloatRound op rd tmp tmp2 rs ty)))) (writable_reg_to_reg rd))) -(decl gen_float_select (FloatSelectOP Reg Reg Type) Reg) -(rule - (gen_float_select op x y ty) - (let - ((rd WritableReg (temp_writable_reg ty)) - (tmp WritableXReg (temp_writable_xreg)) - (_ Unit (emit (MInst.FloatSelect op rd tmp x y ty)))) - (writable_reg_to_reg rd))) - ;;;; Instruction Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1527,6 +1504,16 @@ (decl rv_fge (Type FReg FReg) XReg) (rule (rv_fge ty rs1 rs2) (rv_fle ty rs2 rs1)) +;; Helper for emitting the `fmin` instruction. +(decl rv_fmin (Type FReg FReg) FReg) +(rule (rv_fmin $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FminS) $F32 rs1 rs2)) +(rule (rv_fmin $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FminD) $F64 rs1 rs2)) + +;; Helper for emitting the `fmax` instruction. +(decl rv_fmax (Type FReg FReg) FReg) +(rule (rv_fmax $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FmaxS) $F32 rs1 rs2)) +(rule (rv_fmax $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FmaxD) $F64 rs1 rs2)) + ;; `Zba` Extension Instructions diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs index 0ce56295f849..7539e3172833 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/args.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs @@ -1695,53 +1695,6 @@ impl FloatRoundOP { } } -impl FloatSelectOP { - pub(crate) fn op_name(self) -> &'static str { - match self { - FloatSelectOP::Max => "max", - FloatSelectOP::Min => "min", - } - } - - pub(crate) fn to_fpuoprrr(self, ty: Type) -> FpuOPRRR { - match self { - FloatSelectOP::Max => { - if ty == F32 { - FpuOPRRR::FmaxS - } else { - FpuOPRRR::FmaxD - } - } - FloatSelectOP::Min => { - if ty == F32 { - FpuOPRRR::FminS - } else { - FpuOPRRR::FminD - } - } - } - } - // move qnan bits into int register. - pub(crate) fn snan_bits(self, rd: Writable, ty: Type) -> SmallInstVec { - let mut insts = SmallInstVec::new(); - insts.push(Inst::load_imm12(rd, Imm12::from_i16(-1))); - let x = if ty == F32 { 22 } else { 51 }; - insts.push(Inst::AluRRImm12 { - alu_op: AluOPRRI::Srli, - rd: rd, - rs: rd.to_reg(), - imm12: Imm12::from_i16(x), - }); - insts.push(Inst::AluRRImm12 { - alu_op: AluOPRRI::Slli, - rd: rd, - rs: rd.to_reg(), - imm12: Imm12::from_i16(x), - }); - insts - } -} - pub(crate) fn f32_bits(f: f32) -> u32 { u32::from_le_bytes(f.to_le_bytes()) } diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs index 130eeaa12142..2fc5749deea4 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs @@ -368,7 +368,6 @@ impl Inst { | Inst::Unwind { .. } | Inst::DummyUse { .. } | Inst::FloatRound { .. } - | Inst::FloatSelect { .. } | Inst::Popcnt { .. } | Inst::Rev8 { .. } | Inst::Cltz { .. } @@ -2587,126 +2586,6 @@ impl Inst { sink.bind_label(label_jump_over, &mut state.ctrl_plane); } - &Inst::FloatSelect { - op, - rd, - tmp, - rs1, - rs2, - ty, - } => { - let label_nan = sink.get_label(); - let label_jump_over = sink.get_label(); - // check if rs1 is nan. - Inst::emit_not_nan(tmp, rs1, ty).emit(&[], sink, emit_info, state); - Inst::CondBr { - taken: CondBrTarget::Label(label_nan), - not_taken: CondBrTarget::Fallthrough, - kind: IntegerCompare { - kind: IntCC::Equal, - rs1: tmp.to_reg(), - rs2: zero_reg(), - }, - } - .emit(&[], sink, emit_info, state); - // check if rs2 is nan. - Inst::emit_not_nan(tmp, rs2, ty).emit(&[], sink, emit_info, state); - Inst::CondBr { - taken: CondBrTarget::Label(label_nan), - not_taken: CondBrTarget::Fallthrough, - kind: IntegerCompare { - kind: IntCC::Equal, - rs1: tmp.to_reg(), - rs2: zero_reg(), - }, - } - .emit(&[], sink, emit_info, state); - // here rs1 and rs2 is not nan. - Inst::FpuRRR { - alu_op: op.to_fpuoprrr(ty), - frm: None, - rd: rd, - rs1: rs1, - rs2: rs2, - } - .emit(&[], sink, emit_info, state); - // special handle for +0 or -0. - { - // check is rs1 and rs2 all equal to zero. - let label_done = sink.get_label(); - { - // if rs1 == 0 - let mut insts = Inst::emit_if_float_not_zero( - tmp, - rs1, - ty, - CondBrTarget::Label(label_done), - CondBrTarget::Fallthrough, - ); - insts.extend(Inst::emit_if_float_not_zero( - tmp, - rs2, - ty, - CondBrTarget::Label(label_done), - CondBrTarget::Fallthrough, - )); - insts - .iter() - .for_each(|i| i.emit(&[], sink, emit_info, state)); - } - Inst::FpuRR { - alu_op: FpuOPRR::move_f_to_x_op(ty), - frm: None, - rd: tmp, - rs: rs1, - } - .emit(&[], sink, emit_info, state); - Inst::FpuRR { - alu_op: FpuOPRR::move_f_to_x_op(ty), - frm: None, - rd: writable_spilltmp_reg(), - rs: rs2, - } - .emit(&[], sink, emit_info, state); - Inst::AluRRR { - alu_op: if op == FloatSelectOP::Max { - AluOPRRR::And - } else { - AluOPRRR::Or - }, - rd: tmp, - rs1: tmp.to_reg(), - rs2: spilltmp_reg(), - } - .emit(&[], sink, emit_info, state); - // move back to rd. - Inst::FpuRR { - alu_op: FpuOPRR::move_x_to_f_op(ty), - frm: None, - rd, - rs: tmp.to_reg(), - } - .emit(&[], sink, emit_info, state); - // - sink.bind_label(label_done, &mut state.ctrl_plane); - } - // we have the reuslt,jump over. - Inst::gen_jump(label_jump_over).emit(&[], sink, emit_info, state); - // here is nan. - sink.bind_label(label_nan, &mut state.ctrl_plane); - op.snan_bits(tmp, ty) - .into_iter() - .for_each(|i| i.emit(&[], sink, emit_info, state)); - // move to rd. - Inst::FpuRR { - alu_op: FpuOPRR::move_x_to_f_op(ty), - frm: None, - rd, - rs: tmp.to_reg(), - } - .emit(&[], sink, emit_info, state); - sink.bind_label(label_jump_over, &mut state.ctrl_plane); - } &Inst::Popcnt { sum, tmp, @@ -3708,22 +3587,6 @@ impl Inst { rd: allocs.next_writable(rd), }, - Inst::FloatSelect { - op, - rd, - tmp, - rs1, - rs2, - ty, - } => Inst::FloatSelect { - op, - ty, - rs1: allocs.next(rs1), - rs2: allocs.next(rs2), - tmp: allocs.next_writable(tmp), - rd: allocs.next_writable(rd), - }, - Inst::Popcnt { sum, tmp, diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs b/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs index 111259278469..d42312e60f4b 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs @@ -2287,15 +2287,6 @@ fn riscv64_worst_case_instruction_size() { ty: F64, }); - candidates.push(Inst::FloatSelect { - op: FloatSelectOP::Max, - rd: writable_fa0(), - tmp: writable_a0(), - rs1: fa0(), - rs2: fa0(), - ty: F64, - }); - let mut max: (u32, MInst) = (0, Inst::Nop0); for i in candidates { let mut buffer = MachBuffer::new(); diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs index e484195e7565..fed91b230321 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs @@ -55,7 +55,7 @@ pub(crate) type VecWritableReg = Vec>; pub use crate::isa::riscv64::lower::isle::generated_code::{ AluOPRRI, AluOPRRR, AtomicOP, CsrImmOP, CsrRegOP, FClassResult, FFlagsException, FloatRoundOP, - FloatSelectOP, FpuOPRR, FpuOPRRR, FpuOPRRRR, LoadOP, MInst as Inst, StoreOP, CSR, FRM, + FpuOPRR, FpuOPRRR, FpuOPRRRR, LoadOP, MInst as Inst, StoreOP, CSR, FRM, }; use crate::isa::riscv64::lower::isle::generated_code::{CjOp, MInst, VecAluOpRRImm5, VecAluOpRRR}; @@ -609,13 +609,6 @@ fn riscv64_get_operands VReg>(inst: &Inst, collector: &mut Operan collector.reg_early_def(f_tmp); collector.reg_early_def(rd); } - &Inst::FloatSelect { - rd, tmp, rs1, rs2, .. - } => { - collector.reg_uses(&[rs1, rs2]); - collector.reg_early_def(tmp); - collector.reg_early_def(rd); - } &Inst::Popcnt { sum, step, rs, tmp, .. } => { @@ -1113,29 +1106,6 @@ impl Inst { ty ) } - &Inst::FloatSelect { - op, - rd, - tmp, - rs1, - rs2, - ty, - } => { - let rs1 = format_reg(rs1, allocs); - let rs2 = format_reg(rs2, allocs); - let tmp = format_reg(tmp.to_reg(), allocs); - let rd = format_reg(rd.to_reg(), allocs); - format!( - "f{}.{} {},{},{}##tmp={} ty={}", - op.op_name(), - if ty == F32 { "s" } else { "d" }, - rd, - rs1, - rs2, - tmp, - ty - ) - } &Inst::AtomicStore { src, ty, p } => { let src = format_reg(src, allocs); let p = format_reg(p, allocs); diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index 53a2d65f6a05..5106ee4fa2d4 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -1478,8 +1478,16 @@ ;;;; Rules for `fmin` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; RISC-V's `fmin` instruction returns the number input if one of inputs is a +;; NaN. We handle this by manually checking if one of the inputs is a NaN +;; and selecting based on that result. (rule 0 (lower (has_type (ty_scalar_float ty) (fmin x y))) - (gen_float_select (FloatSelectOP.Min) x y ty)) + (let (;; Check if both inputs are not nan. + (is_ordered FCmp (emit_fcmp (FloatCC.Ordered) ty x y)) + ;; `fadd` returns a nan if any of the inputs is a NaN. + (nan FReg (rv_fadd ty x y)) + (min FReg (rv_fmin ty x y))) + (gen_select_freg is_ordered min nan))) ;; vfmin does almost the right thing, but it does not handle NaN's correctly. ;; We should return a NaN if any of the inputs is a NaN, but vfmin returns the @@ -1496,8 +1504,17 @@ ;;;; Rules for `fmax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; RISC-V's `fmax` instruction returns the number input if one of inputs is a +;; NaN. We handle this by manually checking if one of the inputs is a NaN +;; and selecting based on that result. (rule 0 (lower (has_type (ty_scalar_float ty) (fmax x y))) - (gen_float_select (FloatSelectOP.Max) x y ty)) + (let (;; Check if both inputs are not nan. + (is_ordered FCmp (emit_fcmp (FloatCC.Ordered) ty x y)) + ;; `fadd` returns a NaN if any of the inputs is a NaN. + (nan FReg (rv_fadd ty x y)) + (max FReg (rv_fmax ty x y))) + (gen_select_freg is_ordered max nan))) + ;; vfmax does almost the right thing, but it does not handle NaN's correctly. ;; We should return a NaN if any of the inputs is a NaN, but vfmax returns the diff --git a/cranelift/filetests/filetests/isa/riscv64/float.clif b/cranelift/filetests/filetests/isa/riscv64/float.clif index 2b5cd9564023..fc14272e316e 100644 --- a/cranelift/filetests/filetests/isa/riscv64/float.clif +++ b/cranelift/filetests/filetests/isa/riscv64/float.clif @@ -130,153 +130,6 @@ block0(v0: f64, v1: f64): ; fdiv.d fa0, fa0, fa1 ; ret -function %f9(f32, f32) -> f32 { -block0(v0: f32, v1: f32): - v2 = fmin v0, v1 - return v2 -} - -; VCode: -; block0: -; fmv.d fa5,fa0 -; fmin.s fa0,fa5,fa1##tmp=a4 ty=f32 -; ret -; -; Disassembled: -; block0: ; offset 0x0 -; fmv.d fa5, fa0 -; feq.s a4, fa5, fa5 -; beqz a4, 0x3c -; feq.s a4, fa1, fa1 -; beqz a4, 0x34 -; fmin.s fa0, fa5, fa1 -; fclass.s a4, fa5 -; andi a4, a4, 0x18 -; beqz a4, 0x34 -; fclass.s a4, fa1 -; andi a4, a4, 0x18 -; beqz a4, 0x28 -; fmv.x.w a4, fa5 -; fmv.x.w t6, fa1 -; or a4, a4, t6 -; fmv.w.x fa0, a4 -; j 0x14 -; addi a4, zero, -1 -; srli a4, a4, 0x16 -; slli a4, a4, 0x16 -; fmv.w.x fa0, a4 -; ret - -function %f10(f64, f64) -> f64 { -block0(v0: f64, v1: f64): - v2 = fmin v0, v1 - return v2 -} - -; VCode: -; block0: -; fmv.d fa5,fa0 -; fmin.d fa0,fa5,fa1##tmp=a4 ty=f64 -; ret -; -; Disassembled: -; block0: ; offset 0x0 -; fmv.d fa5, fa0 -; feq.d a4, fa5, fa5 -; beqz a4, 0x3c -; feq.d a4, fa1, fa1 -; beqz a4, 0x34 -; fmin.d fa0, fa5, fa1 -; fclass.d a4, fa5 -; andi a4, a4, 0x18 -; beqz a4, 0x34 -; fclass.d a4, fa1 -; andi a4, a4, 0x18 -; beqz a4, 0x28 -; fmv.x.d a4, fa5 -; fmv.x.d t6, fa1 -; or a4, a4, t6 -; fmv.d.x fa0, a4 -; j 0x14 -; addi a4, zero, -1 -; srli a4, a4, 0x33 -; slli a4, a4, 0x33 -; fmv.d.x fa0, a4 -; ret - -function %f11(f32, f32) -> f32 { -block0(v0: f32, v1: f32): - v2 = fmax v0, v1 - return v2 -} - -; VCode: -; block0: -; fmv.d fa5,fa0 -; fmax.s fa0,fa5,fa1##tmp=a4 ty=f32 -; ret -; -; Disassembled: -; block0: ; offset 0x0 -; fmv.d fa5, fa0 -; feq.s a4, fa5, fa5 -; beqz a4, 0x3c -; feq.s a4, fa1, fa1 -; beqz a4, 0x34 -; fmax.s fa0, fa5, fa1 -; fclass.s a4, fa5 -; andi a4, a4, 0x18 -; beqz a4, 0x34 -; fclass.s a4, fa1 -; andi a4, a4, 0x18 -; beqz a4, 0x28 -; fmv.x.w a4, fa5 -; fmv.x.w t6, fa1 -; and a4, a4, t6 -; fmv.w.x fa0, a4 -; j 0x14 -; addi a4, zero, -1 -; srli a4, a4, 0x16 -; slli a4, a4, 0x16 -; fmv.w.x fa0, a4 -; ret - -function %f12(f64, f64) -> f64 { -block0(v0: f64, v1: f64): - v2 = fmax v0, v1 - return v2 -} - -; VCode: -; block0: -; fmv.d fa5,fa0 -; fmax.d fa0,fa5,fa1##tmp=a4 ty=f64 -; ret -; -; Disassembled: -; block0: ; offset 0x0 -; fmv.d fa5, fa0 -; feq.d a4, fa5, fa5 -; beqz a4, 0x3c -; feq.d a4, fa1, fa1 -; beqz a4, 0x34 -; fmax.d fa0, fa5, fa1 -; fclass.d a4, fa5 -; andi a4, a4, 0x18 -; beqz a4, 0x34 -; fclass.d a4, fa1 -; andi a4, a4, 0x18 -; beqz a4, 0x28 -; fmv.x.d a4, fa5 -; fmv.x.d t6, fa1 -; and a4, a4, t6 -; fmv.d.x fa0, a4 -; j 0x14 -; addi a4, zero, -1 -; srli a4, a4, 0x33 -; slli a4, a4, 0x33 -; fmv.d.x fa0, a4 -; ret function %f13(f32) -> f32 { block0(v0: f32): diff --git a/cranelift/filetests/filetests/isa/riscv64/fmax.clif b/cranelift/filetests/filetests/isa/riscv64/fmax.clif new file mode 100644 index 000000000000..04fcdc2622fb --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/fmax.clif @@ -0,0 +1,62 @@ +test compile precise-output +set unwind_info=false +target riscv64 + +function %fmax_f32(f32, f32) -> f32 { +block0(v0: f32, v1: f32): + v2 = fmax v0, v1 + return v2 +} + +; VCode: +; block0: +; feq.s a3,fa0,fa0 +; feq.s a5,fa1,fa1 +; and a1,a3,a5 +; fadd.s fa3,fa0,fa1 +; fmax.s fa5,fa0,fa1 +; select fa0,fa5,fa3##condition=(a1 ne zero) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.s a3, fa0, fa0 +; feq.s a5, fa1, fa1 +; and a1, a3, a5 +; fadd.s fa3, fa0, fa1 +; fmax.s fa5, fa0, fa1 +; beqz a1, 0xc +; fmv.d fa0, fa5 +; j 8 +; fmv.d fa0, fa3 +; ret + +function %fmax_f64(f64, f64) -> f64 { +block0(v0: f64, v1: f64): + v2 = fmax v0, v1 + return v2 +} + +; VCode: +; block0: +; feq.d a3,fa0,fa0 +; feq.d a5,fa1,fa1 +; and a1,a3,a5 +; fadd.d fa3,fa0,fa1 +; fmax.d fa5,fa0,fa1 +; select fa0,fa5,fa3##condition=(a1 ne zero) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.d a3, fa0, fa0 +; feq.d a5, fa1, fa1 +; and a1, a3, a5 +; fadd.d fa3, fa0, fa1 +; fmax.d fa5, fa0, fa1 +; beqz a1, 0xc +; fmv.d fa0, fa5 +; j 8 +; fmv.d fa0, fa3 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/fmin.clif b/cranelift/filetests/filetests/isa/riscv64/fmin.clif new file mode 100644 index 000000000000..eb68c53f96d0 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/fmin.clif @@ -0,0 +1,62 @@ +test compile precise-output +set unwind_info=false +target riscv64 + +function %fmin_f32(f32, f32) -> f32 { +block0(v0: f32, v1: f32): + v2 = fmin v0, v1 + return v2 +} + +; VCode: +; block0: +; feq.s a3,fa0,fa0 +; feq.s a5,fa1,fa1 +; and a1,a3,a5 +; fadd.s fa3,fa0,fa1 +; fmin.s fa5,fa0,fa1 +; select fa0,fa5,fa3##condition=(a1 ne zero) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.s a3, fa0, fa0 +; feq.s a5, fa1, fa1 +; and a1, a3, a5 +; fadd.s fa3, fa0, fa1 +; fmin.s fa5, fa0, fa1 +; beqz a1, 0xc +; fmv.d fa0, fa5 +; j 8 +; fmv.d fa0, fa3 +; ret + +function %fmin_f64(f64, f64) -> f64 { +block0(v0: f64, v1: f64): + v2 = fmin v0, v1 + return v2 +} + +; VCode: +; block0: +; feq.d a3,fa0,fa0 +; feq.d a5,fa1,fa1 +; and a1,a3,a5 +; fadd.d fa3,fa0,fa1 +; fmin.d fa5,fa0,fa1 +; select fa0,fa5,fa3##condition=(a1 ne zero) +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; feq.d a3, fa0, fa0 +; feq.d a5, fa1, fa1 +; and a1, a3, a5 +; fadd.d fa3, fa0, fa1 +; fmin.d fa5, fa0, fa1 +; beqz a1, 0xc +; fmv.d fa0, fa5 +; j 8 +; fmv.d fa0, fa3 +; ret + From fef8a90f258483ac6c2e470d3de277a743d5d392 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 6 Oct 2023 18:42:53 -0700 Subject: [PATCH 082/199] PCC: add semantics for core add/shift/extend/amode ops on AArch64. (#7180) * PCC: add semantics for core add/shift/extend/amode ops on AArch64. This PR adds verification of facts on values produced by adds, shifts, and extends on AArch64, handling the various combination instructions (adds with builtin extends or shifts, for example), and also adds verification of all addressing modes, including those with builtin extends and shifts. It also splits the test suite into"succeed" and "fail" sets, and provides cases that PCC should catch. * Review feedback. --- cranelift/codegen/src/ir/pcc.rs | 17 +- .../codegen/src/isa/aarch64/inst/args.rs | 10 +- .../codegen/src/isa/aarch64/inst/imms.rs | 10 + cranelift/codegen/src/isa/aarch64/pcc.rs | 266 ++++++++++++++---- cranelift/codegen/src/machinst/compile.rs | 7 +- cranelift/codegen/src/machinst/lower.rs | 15 +- cranelift/codegen/src/machinst/reg.rs | 5 + cranelift/codegen/src/machinst/vcode.rs | 77 ++--- .../filetests/filetests/pcc/fail/add.clif | 50 ++++ .../filetests/filetests/pcc/fail/extend.clif | 20 ++ .../filetests/filetests/pcc/fail/load.clif | 59 ++++ .../filetests/filetests/pcc/fail/shift.clif | 10 + .../filetests/filetests/pcc/succeed/add.clif | 60 ++++ .../filetests/pcc/succeed/extend.clif | 9 + .../filetests/filetests/pcc/succeed/load.clif | 60 ++++ .../filetests/pcc/succeed/shift.clif | 17 ++ 16 files changed, 599 insertions(+), 93 deletions(-) create mode 100644 cranelift/filetests/filetests/pcc/fail/add.clif create mode 100644 cranelift/filetests/filetests/pcc/fail/extend.clif create mode 100644 cranelift/filetests/filetests/pcc/fail/load.clif create mode 100644 cranelift/filetests/filetests/pcc/fail/shift.clif create mode 100644 cranelift/filetests/filetests/pcc/succeed/add.clif create mode 100644 cranelift/filetests/filetests/pcc/succeed/extend.clif create mode 100644 cranelift/filetests/filetests/pcc/succeed/load.clif create mode 100644 cranelift/filetests/filetests/pcc/succeed/shift.clif diff --git a/cranelift/codegen/src/ir/pcc.rs b/cranelift/codegen/src/ir/pcc.rs index 3fc2662a87b1..08f2133cfbd1 100644 --- a/cranelift/codegen/src/ir/pcc.rs +++ b/cranelift/codegen/src/ir/pcc.rs @@ -76,6 +76,9 @@ pub enum PccError { /// An input to an operator that produces a fact-annotated value /// does not have a fact describing it, and one is needed. MissingFact, + /// A derivation of an output fact is unsupported (incorrect or + /// not derivable). + UnsupportedFact, /// A memory access is out of bounds. OutOfBounds, /// Proof-carrying-code checking is not implemented for a @@ -310,12 +313,24 @@ impl FactContext { if *bit_width < 64 && max > max_value_for_width(width) { return Some(minimal_fact); } - Some(minimal_fact) + Some(Fact::ValueMax { + bit_width: *bit_width, + max, + }) } _ => None, } } + /// Left-shifts a value with a fact by a known constant. + pub fn shl(&self, fact: &Fact, width: u16, amount: u16) -> Option { + if amount >= 32 { + return None; + } + let factor: u32 = 1 << amount; + self.scale(fact, width, factor) + } + /// Offsets a value with a fact by a known amount. pub fn offset(&self, fact: &Fact, width: u16, offset: i64) -> Option { // If we eventually support two-sided ranges, we can diff --git a/cranelift/codegen/src/isa/aarch64/inst/args.rs b/cranelift/codegen/src/isa/aarch64/inst/args.rs index cf1d6d05a671..9abb50150090 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/args.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/args.rs @@ -11,7 +11,7 @@ use std::string::String; // Instruction sub-components: shift and extend descriptors /// A shift operator for a register or immediate. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum ShiftOp { /// Logical shift left. @@ -580,6 +580,14 @@ impl OperandSize { OperandSize::Size64 => 1, } } + + /// The maximum unsigned value representable in a value of this size. + pub fn max_value(&self) -> u64 { + match self { + OperandSize::Size32 => u32::MAX as u64, + OperandSize::Size64 => u64::MAX, + } + } } /// Type used to communicate the size of a scalar SIMD & FP operand. diff --git a/cranelift/codegen/src/isa/aarch64/inst/imms.rs b/cranelift/codegen/src/isa/aarch64/inst/imms.rs index 7617d1be3258..54f6bd804bcf 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/imms.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/imms.rs @@ -307,6 +307,16 @@ impl Imm12 { pub fn imm_bits(&self) -> u32 { self.bits as u32 } + + /// Get the actual value that this immediate corresponds to. + pub fn value(&self) -> u64 { + let base = self.bits as u64; + if self.shift12 { + base << 12 + } else { + base + } + } } /// An immediate for logical instructions. diff --git a/cranelift/codegen/src/isa/aarch64/pcc.rs b/cranelift/codegen/src/isa/aarch64/pcc.rs index d6427a8c1787..50c3acfbecc3 100644 --- a/cranelift/codegen/src/isa/aarch64/pcc.rs +++ b/cranelift/codegen/src/isa/aarch64/pcc.rs @@ -2,13 +2,66 @@ use crate::ir::pcc::*; use crate::ir::MemFlags; -use crate::isa::aarch64::inst::args::PairAMode; +use crate::isa::aarch64::inst::args::{PairAMode, ShiftOp}; +use crate::isa::aarch64::inst::ALUOp; use crate::isa::aarch64::inst::Inst; use crate::isa::aarch64::inst::{AMode, ExtendOp}; use crate::machinst::Reg; use crate::machinst::VCode; use crate::trace; +fn get_fact(vcode: &VCode, reg: Reg) -> PccResult<&Fact> { + vcode.vreg_fact(reg.into()).ok_or(PccError::MissingFact) +} + +fn has_fact(vcode: &VCode, reg: Reg) -> bool { + vcode.vreg_fact(reg.into()).is_some() +} + +fn fail_if_missing(fact: Option) -> PccResult { + fact.ok_or(PccError::UnsupportedFact) +} + +fn check_subsumes(ctx: &FactContext, subsumer: &Fact, subsumee: &Fact) -> PccResult<()> { + trace!( + "checking if derived fact {:?} subsumes stated fact {:?}", + subsumer, + subsumee + ); + if ctx.subsumes(subsumer, subsumee) { + Ok(()) + } else { + Err(PccError::UnsupportedFact) + } +} + +fn extend_fact(ctx: &FactContext, value: &Fact, mode: ExtendOp) -> Option { + match mode { + ExtendOp::UXTB => ctx.uextend(value, 8, 64), + ExtendOp::UXTH => ctx.uextend(value, 16, 64), + ExtendOp::UXTW => ctx.uextend(value, 32, 64), + ExtendOp::UXTX => Some(value.clone()), + ExtendOp::SXTB => ctx.sextend(value, 8, 64), + ExtendOp::SXTH => ctx.sextend(value, 16, 64), + ExtendOp::SXTW => ctx.sextend(value, 32, 64), + ExtendOp::SXTX => None, + } +} + +fn check_output PccResult>( + ctx: &FactContext, + vcode: &VCode, + out: Reg, + f: F, +) -> PccResult<()> { + if let Some(fact) = vcode.vreg_fact(out.into()) { + let result = f()?; + check_subsumes(ctx, &result, fact) + } else { + Ok(()) + } +} + pub(crate) fn check(inst: &Inst, vcode: &VCode) -> PccResult<()> { // Create a new fact context with the machine's pointer width. let ctx = FactContext::new(64); @@ -44,8 +97,16 @@ pub(crate) fn check(inst: &Inst, vcode: &VCode) -> PccResult<()> { rn, flags, .. - } => check_scalar_addr(&ctx, *flags, *rn, vcode, access_ty.bytes() as u8), + } => check_scalar_addr( + &ctx, + *flags, + *rn, + vcode, + u8::try_from(access_ty.bytes()).unwrap(), + ), + // TODO: stores: once we have memcaps, check that we aren't + // overwriting a field that has a pointee type. Inst::Store8 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 1), Inst::Store16 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 2), Inst::Store32 { mem, flags, .. } => check_addr(&ctx, *flags, mem, vcode, 4), @@ -61,7 +122,112 @@ pub(crate) fn check(inst: &Inst, vcode: &VCode) -> PccResult<()> { rn, flags, .. - } => check_scalar_addr(&ctx, *flags, *rn, vcode, access_ty.bytes() as u8), + } => check_scalar_addr( + &ctx, + *flags, + *rn, + vcode, + u8::try_from(access_ty.bytes()).unwrap(), + ), + + Inst::AluRRR { + alu_op: ALUOp::Add, + size, + rd, + rn, + rm, + } if has_fact(vcode, *rn) && has_fact(vcode, *rm) => { + check_output(&ctx, vcode, rd.to_reg(), || { + let rn = get_fact(vcode, *rn)?; + let rm = get_fact(vcode, *rm)?; + fail_if_missing(ctx.add(rn, rm, size.bits().into())) + }) + } + Inst::AluRRImm12 { + alu_op: ALUOp::Add, + size, + rd, + rn, + imm12, + } if has_fact(vcode, *rn) => check_output(&ctx, vcode, rd.to_reg(), || { + let rn = get_fact(vcode, *rn)?; + let imm_fact = Fact::ValueMax { + bit_width: size.bits().into(), + max: imm12.value(), + }; + fail_if_missing(ctx.add(&rn, &imm_fact, size.bits().into())) + }), + Inst::AluRRRShift { + alu_op: ALUOp::Add, + size, + rd, + rn, + rm, + shiftop, + } if shiftop.op() == ShiftOp::LSL && has_fact(vcode, *rn) && has_fact(vcode, *rm) => { + check_output(&ctx, vcode, rd.to_reg(), || { + let rn = get_fact(vcode, *rn)?; + let rm = get_fact(vcode, *rm)?; + let rm_shifted = fail_if_missing(ctx.shl( + &rm, + size.bits().into(), + shiftop.amt().value().into(), + ))?; + fail_if_missing(ctx.add(&rn, &rm_shifted, size.bits().into())) + }) + } + Inst::AluRRRExtend { + alu_op: ALUOp::Add, + size, + rd, + rn, + rm, + extendop, + } if has_fact(vcode, *rn) && has_fact(vcode, *rm) => { + check_output(&ctx, vcode, rd.to_reg(), || { + let rn = get_fact(vcode, *rn)?; + let rm = get_fact(vcode, *rm)?; + let rm_extended = fail_if_missing(extend_fact(&ctx, rm, *extendop))?; + fail_if_missing(ctx.add(&rn, &rm_extended, size.bits().into())) + }) + } + Inst::AluRRImmShift { + alu_op: ALUOp::Lsl, + size, + rd, + rn, + immshift, + } if has_fact(vcode, *rn) && has_fact(vcode, *rn) => { + check_output(&ctx, vcode, rd.to_reg(), || { + let rn = get_fact(vcode, *rn)?; + fail_if_missing(ctx.shl(&rn, size.bits().into(), immshift.value().into())) + }) + } + Inst::Extend { + rd, + rn, + signed: false, + from_bits, + to_bits, + } if has_fact(vcode, *rn) => check_output(&ctx, vcode, rd.to_reg(), || { + let rn = get_fact(vcode, *rn)?; + fail_if_missing(ctx.uextend(&rn, (*from_bits).into(), (*to_bits).into())) + }), + Inst::AluRRR { size, rd, .. } + | Inst::AluRRImm12 { rd, size, .. } + | Inst::AluRRRShift { rd, size, .. } + | Inst::AluRRRExtend { rd, size, .. } + | Inst::AluRRImmLogic { rd, size, .. } + | Inst::AluRRImmShift { rd, size, .. } => { + // Any ALU op can validate a max-value fact where the + // value is the maximum for its bit-width. + check_output(&ctx, vcode, rd.to_reg(), || { + Ok(Fact::ValueMax { + bit_width: size.bits().into(), + max: size.max_value(), + }) + }) + } i => { panic!("Fact on unknown inst: {:?}", i); @@ -69,19 +235,6 @@ pub(crate) fn check(inst: &Inst, vcode: &VCode) -> PccResult<()> { } } -fn amode_extend(ctx: &FactContext, value: &Fact, mode: ExtendOp) -> Option { - match mode { - ExtendOp::UXTB => ctx.uextend(value, 8, 64), - ExtendOp::UXTH => ctx.uextend(value, 16, 64), - ExtendOp::UXTW => ctx.uextend(value, 32, 64), - ExtendOp::UXTX => Some(value.clone()), - ExtendOp::SXTB => ctx.sextend(value, 8, 64), - ExtendOp::SXTH => ctx.uextend(value, 16, 64), - ExtendOp::SXTW => ctx.uextend(value, 32, 64), - ExtendOp::SXTX => None, - } -} - fn check_addr( ctx: &FactContext, flags: MemFlags, @@ -93,19 +246,21 @@ fn check_addr( return Ok(()); } + trace!("check_addr: {:?}", addr); + match addr { &AMode::RegReg { rn, rm } => { - let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; - let rm = vcode.vreg_fact(rm.into()).ok_or(PccError::MissingFact)?; - let sum = ctx.add(&rn, &rm, 64).ok_or(PccError::MissingFact)?; - ctx.check_address(&sum, size as u32) + let rn = get_fact(vcode, rn)?; + let rm = get_fact(vcode, rm)?; + let sum = fail_if_missing(ctx.add(&rn, &rm, 64))?; + ctx.check_address(&sum, size.into()) } &AMode::RegScaled { rn, rm, ty } => { - let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; - let rm = vcode.vreg_fact(rm.into()).ok_or(PccError::MissingFact)?; - let rm_scaled = ctx.scale(&rm, 64, ty.bytes()).ok_or(PccError::Overflow)?; - let sum = ctx.add(&rn, &rm_scaled, 64).ok_or(PccError::MissingFact)?; - ctx.check_address(&sum, size as u32) + let rn = get_fact(vcode, rn)?; + let rm = get_fact(vcode, rm)?; + let rm_scaled = fail_if_missing(ctx.scale(&rm, 64, ty.bytes()))?; + let sum = fail_if_missing(ctx.add(&rn, &rm_scaled, 64))?; + ctx.check_address(&sum, size.into()) } &AMode::RegScaledExtended { rn, @@ -113,38 +268,37 @@ fn check_addr( ty, extendop, } => { - let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; - let rm = vcode.vreg_fact(rm.into()).ok_or(PccError::MissingFact)?; - let rm_extended = amode_extend(ctx, rm, extendop).ok_or(PccError::MissingFact)?; - let rm_scaled = ctx - .scale(&rm_extended, 64, ty.bytes()) - .ok_or(PccError::Overflow)?; - let sum = ctx.add(&rn, &rm_scaled, 64).ok_or(PccError::MissingFact)?; - ctx.check_address(&sum, size as u32) + let rn = get_fact(vcode, rn)?; + let rm = get_fact(vcode, rm)?; + let rm_extended = fail_if_missing(extend_fact(ctx, rm, extendop))?; + let rm_scaled = fail_if_missing(ctx.scale(&rm_extended, 64, ty.bytes()))?; + let sum = fail_if_missing(ctx.add(&rn, &rm_scaled, 64))?; + ctx.check_address(&sum, size.into()) } &AMode::RegExtended { rn, rm, extendop } => { - let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; - let rm = vcode.vreg_fact(rm.into()).ok_or(PccError::MissingFact)?; - let rm_extended = amode_extend(ctx, rm, extendop).ok_or(PccError::MissingFact)?; - let sum = ctx - .add(&rn, &rm_extended, 64) - .ok_or(PccError::MissingFact)?; - ctx.check_address(&sum, size as u32) + let rn = get_fact(vcode, rn)?; + let rm = get_fact(vcode, rm)?; + let rm_extended = fail_if_missing(extend_fact(ctx, rm, extendop))?; + let sum = fail_if_missing(ctx.add(&rn, &rm_extended, 64))?; + ctx.check_address(&sum, size.into()) } &AMode::Unscaled { rn, simm9 } => { - let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; - let sum = ctx - .offset(&rn, 64, simm9.value as i64) - .ok_or(PccError::MissingFact)?; - ctx.check_address(&sum, size as u32) + let rn = get_fact(vcode, rn)?; + let sum = fail_if_missing(ctx.offset(&rn, 64, simm9.value.into()))?; + ctx.check_address(&sum, size.into()) } &AMode::UnsignedOffset { rn, uimm12 } => { - let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; - let offset = (uimm12.value as u64) * (size as u64); - let sum = ctx - .offset(&rn, 64, offset as i64) - .ok_or(PccError::MissingFact)?; - ctx.check_address(&sum, size as u32) + let rn = get_fact(vcode, rn)?; + // Safety: this will not overflow: `size` should be at + // most 32 or 64 for large vector ops, and the `uimm12`'s + // value is at most 4095. + let uimm12: u64 = uimm12.value.into(); + let offset: u64 = uimm12.checked_mul(size.into()).unwrap(); + // This `unwrap()` will always succeed because the value + // will always be positive and much smaller than + // `i64::MAX` (see above). + let sum = fail_if_missing(ctx.offset(&rn, 64, i64::try_from(offset).unwrap()))?; + ctx.check_address(&sum, size.into()) } &AMode::Label { .. } | &AMode::Const { .. } => { // Always accept: labels and constants must be within the @@ -152,9 +306,9 @@ fn check_addr( Ok(()) } &AMode::RegOffset { rn, off, .. } => { - let rn = vcode.vreg_fact(rn.into()).ok_or(PccError::MissingFact)?; - let sum = ctx.offset(&rn, 64, off).ok_or(PccError::MissingFact)?; - ctx.check_address(&sum, size as u32) + let rn = get_fact(vcode, rn)?; + let sum = fail_if_missing(ctx.offset(&rn, 64, off))?; + ctx.check_address(&sum, size.into()) } &AMode::SPOffset { .. } | &AMode::FPOffset { .. } @@ -188,6 +342,6 @@ fn check_scalar_addr( if !flags.checked() { return Ok(()); } - let fact = vcode.vreg_fact(reg.into()).ok_or(PccError::MissingFact)?; - ctx.check_address(&fact, size as u32) + let fact = get_fact(vcode, reg)?; + ctx.check_address(fact, size.into()) } diff --git a/cranelift/codegen/src/machinst/compile.rs b/cranelift/codegen/src/machinst/compile.rs index 87481218246c..757c36a3fdcb 100644 --- a/cranelift/codegen/src/machinst/compile.rs +++ b/cranelift/codegen/src/machinst/compile.rs @@ -75,8 +75,11 @@ pub fn compile( .expect("register allocation") }; - // Run the regalloc checker, if requested. - if b.flags().regalloc_checker() { + // Run the regalloc checker, if requested. Also run the checker if + // PCC is enabled (PCC only validates up to pre-regalloc VCode, so + // for a full guarantee we need to ensure that regalloc did a + // faithful translation to allocated machine code.) + if b.flags().regalloc_checker() || b.flags().enable_pcc() { let _tt = timing::regalloc_checker(); let mut checker = regalloc2::checker::Checker::new(&vcode, vcode.machine_env()); checker.prepare(®alloc_result); diff --git a/cranelift/codegen/src/machinst/lower.rs b/cranelift/codegen/src/machinst/lower.rs index 38fac43b4ee7..0df5e7e73666 100644 --- a/cranelift/codegen/src/machinst/lower.rs +++ b/cranelift/codegen/src/machinst/lower.rs @@ -771,6 +771,19 @@ impl<'func, I: VCodeInst> Lower<'func, I> { debug_assert_eq!(regs.len(), dsts.len()); for (dst, temp) in dsts.regs().iter().zip(regs.regs().iter()) { self.set_vreg_alias(*dst, *temp); + + // If there was any PCC fact about the + // original VReg, move it to the aliased reg + // instead. Lookup goes through the alias, but + // we want to preserve whatever was stated + // about the vreg before its producer was + // lowered. + if let Some(fact) = + self.vregs.take_fact(dst.to_virtual_reg().unwrap().into()) + { + self.vregs + .set_fact(temp.to_virtual_reg().unwrap().into(), fact); + } } } } @@ -963,7 +976,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { let arg = self.f.dfg.resolve_aliases(arg); let regs = self.put_value_in_regs(arg); for &vreg in regs.regs() { - let vreg = self.vcode.resolve_vreg_alias(vreg.into()); + let vreg = self.vcode.vcode.resolve_vreg_alias(vreg.into()); branch_arg_vregs.push(vreg.into()); } } diff --git a/cranelift/codegen/src/machinst/reg.rs b/cranelift/codegen/src/machinst/reg.rs index 4fd9b1e177c4..8aadc11b540e 100644 --- a/cranelift/codegen/src/machinst/reg.rs +++ b/cranelift/codegen/src/machinst/reg.rs @@ -219,6 +219,11 @@ impl std::convert::From for regalloc2::VReg { reg.0 } } +impl std::convert::From<&Reg> for regalloc2::VReg { + fn from(reg: &Reg) -> regalloc2::VReg { + reg.0 + } +} impl std::convert::From for regalloc2::VReg { fn from(reg: VirtualReg) -> regalloc2::VReg { diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs index 1a09bc59f97c..aa88e19c6b68 100644 --- a/cranelift/codegen/src/machinst/vcode.rs +++ b/cranelift/codegen/src/machinst/vcode.rs @@ -408,37 +408,12 @@ impl VCodeBuilder { pub fn set_vreg_alias(&mut self, from: Reg, to: Reg) { let from = from.into(); - let resolved_to = self.resolve_vreg_alias(to.into()); + let resolved_to = self.vcode.resolve_vreg_alias(to.into()); // Disallow cycles (see below). assert_ne!(resolved_to, from); self.vcode.vreg_aliases.insert(from, resolved_to); } - pub fn resolve_vreg_alias(&self, from: regalloc2::VReg) -> regalloc2::VReg { - Self::resolve_vreg_alias_impl(&self.vcode.vreg_aliases, from) - } - - fn resolve_vreg_alias_impl( - aliases: &FxHashMap, - from: regalloc2::VReg, - ) -> regalloc2::VReg { - // We prevent cycles from existing by resolving targets of - // aliases eagerly before setting them. If the target resolves - // to the origin of the alias, then a cycle would be created - // and the alias is disallowed. Because of the structure of - // SSA code (one instruction can refer to another's defs but - // not vice-versa, except indirectly through - // phis/blockparams), cycles should not occur as we use - // aliases to redirect vregs to the temps that actually define - // them. - - let mut vreg = from; - while let Some(to) = aliases.get(&vreg) { - vreg = *to; - } - vreg - } - /// Access the constants. pub fn constants(&mut self) -> &mut VCodeConstants { &mut self.vcode.constants @@ -520,7 +495,7 @@ impl VCodeBuilder { // Generate debug-value labels based on per-label maps. for (label, tuples) in &self.debug_info { for &(start, end, vreg) in tuples { - let vreg = self.resolve_vreg_alias(vreg); + let vreg = self.vcode.resolve_vreg_alias(vreg); let fwd_start = translate(end); let fwd_end = translate(start); self.vcode @@ -553,7 +528,7 @@ impl VCodeBuilder { let allocatable = PRegSet::from(self.vcode.machine_env()); let mut op_collector = OperandCollector::new(&mut self.vcode.operands, allocatable, |vreg| { - Self::resolve_vreg_alias_impl(vreg_aliases, vreg) + VCode::::resolve_vreg_alias_impl(vreg_aliases, vreg) }); insn.get_operands(&mut op_collector); let (ops, clobbers) = op_collector.finish(); @@ -581,7 +556,7 @@ impl VCodeBuilder { // Translate blockparam args via the vreg aliases table as well. for arg in &mut self.vcode.branch_block_args { - let new_arg = Self::resolve_vreg_alias_impl(&self.vcode.vreg_aliases, *arg); + let new_arg = VCode::::resolve_vreg_alias_impl(&self.vcode.vreg_aliases, *arg); trace!("operandcollector: block arg {:?} -> {:?}", arg, new_arg); *arg = new_arg; } @@ -606,7 +581,7 @@ impl VCodeBuilder { // Also note that `reftyped_vregs` can't have duplicates, so after the // aliases are applied duplicates are removed. for reg in self.vcode.reftyped_vregs.iter_mut() { - *reg = Self::resolve_vreg_alias_impl(&self.vcode.vreg_aliases, *reg); + *reg = VCode::::resolve_vreg_alias_impl(&self.vcode.vreg_aliases, *reg); } self.vcode.reftyped_vregs.sort(); self.vcode.reftyped_vregs.dedup(); @@ -1261,6 +1236,34 @@ impl VCode { self.block_order.lowered_order()[block.index()].orig_block() } + pub fn resolve_vreg_alias(&self, from: regalloc2::VReg) -> regalloc2::VReg { + Self::resolve_vreg_alias_impl(&self.vreg_aliases, from) + } + + /// Implementation of alias resolution. Separate helper that does + /// not borrow `self` in order to allow working around borrowing + /// restrictions. + fn resolve_vreg_alias_impl( + aliases: &FxHashMap, + from: regalloc2::VReg, + ) -> regalloc2::VReg { + // We prevent cycles from existing by resolving targets of + // aliases eagerly before setting them. If the target resolves + // to the origin of the alias, then a cycle would be created + // and the alias is disallowed. Because of the structure of + // SSA code (one instruction can refer to another's defs but + // not vice-versa, except indirectly through + // phis/blockparams), cycles should not occur as we use + // aliases to redirect vregs to the temps that actually define + // them. + + let mut vreg = from; + while let Some(to) = aliases.get(&vreg) { + vreg = *to; + } + vreg + } + #[inline] fn assert_no_vreg_aliases<'a>(&self, list: &'a [VReg]) -> &'a [VReg] { for vreg in list { @@ -1271,7 +1274,7 @@ impl VCode { #[inline] fn assert_not_vreg_alias(&self, vreg: VReg) -> VReg { - debug_assert!(VCodeBuilder::::resolve_vreg_alias_impl(&self.vreg_aliases, vreg) == vreg); + debug_assert!(self.resolve_vreg_alias(vreg) == vreg); vreg } @@ -1286,6 +1289,7 @@ impl VCode { /// Get the fact, if any, for a given VReg. pub fn vreg_fact(&self, vreg: VReg) -> Option<&Fact> { + let vreg = self.resolve_vreg_alias(vreg); self.facts[vreg.vreg()].as_ref() } @@ -1461,7 +1465,7 @@ impl fmt::Debug for VCode { for operand in self.inst_operands(InsnIndex::new(inst)) { if operand.kind() == OperandKind::Def { if let Some(fact) = &self.facts[operand.vreg().vreg()] { - writeln!(f, " v{} ! {:?}", operand.vreg().vreg(), fact)?; + writeln!(f, " v{} ! {}", operand.vreg().vreg(), fact)?; } } } @@ -1598,9 +1602,18 @@ impl VRegAllocator { /// /// Returns the old fact, if any (only one fact can be stored). pub fn set_fact(&mut self, vreg: VirtualReg, fact: Fact) -> Option { + trace!("vreg {:?} has fact: {:?}", vreg, fact); self.facts[vreg.index()].replace(fact) } + /// Take (and remove) a fact about a VReg. Used when setting up + /// aliases: we want to move a fact from the alias vreg to the + /// aliased vreg, to preserve facts about a value that were stated + /// before we lowered its producer. + pub fn take_fact(&mut self, vreg: VirtualReg) -> Option { + self.facts[vreg.index()].take() + } + /// Allocate a fresh ValueRegs, with a given fact to apply if /// the value fits in one VReg. pub fn alloc_with_maybe_fact( diff --git a/cranelift/filetests/filetests/pcc/fail/add.clif b/cranelift/filetests/filetests/pcc/fail/add.clif new file mode 100644 index 000000000000..e32ed55d66ed --- /dev/null +++ b/cranelift/filetests/filetests/pcc/fail/add.clif @@ -0,0 +1,50 @@ +test compile expect-fail +set enable_pcc=true +target aarch64 + +function %f0(i32, i32) -> i32 { +block0(v0 ! max(32, 0x100): i32, v1 ! max(32, 0x80): i32): + v2 ! max(32, 0x17f) = iadd.i32 v0, v1 + return v2 +} + +function %f1(i32) -> i32 { +block0(v0 ! max(32, 0x100): i32): + v1 ! max(32, 1) = iconst.i32 1 + v2 ! max(32, 0x100) = iadd.i32 v0, v1 + return v2 +} + +function %f3(i32) -> i64 { +block0(v0: i32): + v1 ! max(32, 1) = iconst.i32 1 + v2 ! max(32, 0xffff_fffe) = iadd.i32 v0, v1 + v3 ! max(64, 0xffff_fffe) = uextend.i64 v2 + return v3 +} + +function %f3(i32) -> i64 { +block0(v0: i32): + v1 ! max(32, 1) = iconst.i32 1 + v2 ! max(32, 0xffff_ffff) = iadd.i32 v0, v1 + v3 ! max(64, 0xffff_ffff) = uextend.i64 v2 + v4 ! max(64, 0x1) = iconst.i64 1 + v5 ! max(64, 0xffff_ffff) = iadd.i64 v3, v4 + return v5 +} + +;; check merged ops: +function %f4(i32, i32) -> i32 { +block0(v0 ! max(32, 0x100): i32, v1 ! max(32, 0x200): i32): + v2 = iconst.i32 2 + v3 ! max(32, 0x400) = ishl.i32 v0, v2 + v4 ! max(32, 0x5ff) = iadd.i32 v1, v3 + return v4 +} + +function %f5(i32, i64) -> i64 { +block0(v0 ! max(32, 0x100): i32, v1 ! max(64, 0x200): i64): + v2 ! max(64, 0x100) = uextend.i64 v0 + v3 ! max(64, 0x2ff) = iadd.i64 v1, v2 + return v3 +} diff --git a/cranelift/filetests/filetests/pcc/fail/extend.clif b/cranelift/filetests/filetests/pcc/fail/extend.clif new file mode 100644 index 000000000000..dab088874d17 --- /dev/null +++ b/cranelift/filetests/filetests/pcc/fail/extend.clif @@ -0,0 +1,20 @@ +test compile expect-fail +set enable_pcc=true +target aarch64 + +function %f0(i32) -> i64 { +block0(v0 ! max(32, 0xffff_ffff): i32): + v1 ! max(64, 0xffff_0000) = uextend.i64 v0 + return v1 +} + +;; This one is actually true, but we don't support the reasoning it +;; would need: one needs to assume the "don't-care" bits in the upper +;; half of the i32 are the worst case (all ones), so `0xffff_ffff` is +;; possible. If the `i32` were taken through another 32-bit operation +;; and we asserted its 32-bit range at that point, it would work. +function %f1(i32) -> i64 { +block0(v0 ! max(16, 0xffff): i32): + v1 ! max(64, 0xffff_ffff) = uextend.i64 v0 + return v1 +} diff --git a/cranelift/filetests/filetests/pcc/fail/load.clif b/cranelift/filetests/filetests/pcc/fail/load.clif new file mode 100644 index 000000000000..17a189fa05fc --- /dev/null +++ b/cranelift/filetests/filetests/pcc/fail/load.clif @@ -0,0 +1,59 @@ +test compile expect-fail +set enable_pcc=true +target aarch64 + +function %f0(i64, i32) -> i64 { +block0(v0 ! points_to(0x1000): i64, v1 ! max(32, 0x1000): i32): + v2 ! max(64, 0x100) = uextend.i64 v1 + v3 ! points_to(0x8) = iadd.i64 v0, v2 + v4 = load.i64 checked v3 + return v4 +} + +;; Insufficient guard region: the 8-byte load could go off the end. +function %f1(i64, i32) -> i64 { +block0(v0 ! points_to(0x1_0000_0000): i64, v1 ! max(32, 0xffff_ffff): i32): + v2 ! max(64, 0xffff_ffff) = uextend.i64 v1 + ;; 8 bytes of valid range is all we actually need, so let's only claim that. + v3 ! points_to(0x8) = iadd.i64 v0, v2 + v4 = load.i64 checked v3 + return v4 +} + +;; RegRegExtend mode on aarch64. +function %f2(i64, i32) -> i8 { +block0(v0 ! points_to(0x1000): i64, v1 ! max(32, 0x1000): i32): + v2 ! max(64, 0x100) = uextend.i64 v1 + v3 ! points_to(0x1) = iadd.i64 v0, v2 + v4 = load.i8 checked v3 + return v4 +} + +;; RegReg mode on aarch64. +function %f3(i64, i64) -> i8 { +block0(v0 ! points_to(0x100): i64, v1 ! max(64, 0xfff): i64): + v2 ! points_to(0x1) = iadd.i64 v0, v1 + v3 = load.i8 checked v2 + return v3 +} + +;; RegScaledExtended mode on aarch64. +function %f4(i64, i32) -> i64 { +block0(v0 ! points_to(0x7000): i64, v1 ! max(32, 0xfff): i32): + v2 ! max(64, 0xfff) = uextend.i64 v1 + v3 = iconst.i32 3 + v4 ! max(64, 0x7ff8) = ishl.i64 v2, v3 + v5 ! points_to(0x8) = iadd.i64 v0, v4 + v6 = load.i64 checked v5 + return v6 +} + +;; RegScaled mode on aarch64. +function %f5(i64, i64) -> i64 { +block0(v0 ! points_to(0x7000): i64, v1 ! max(64, 0xfff): i64): + v2 = iconst.i32 3 + v3 ! max(64, 0x7ff8) = ishl.i64 v1, v2 + v4 ! points_to(0x8) = iadd.i64 v0, v3 + v5 = load.i64 checked v4 + return v5 +} diff --git a/cranelift/filetests/filetests/pcc/fail/shift.clif b/cranelift/filetests/filetests/pcc/fail/shift.clif new file mode 100644 index 000000000000..fba82900bc99 --- /dev/null +++ b/cranelift/filetests/filetests/pcc/fail/shift.clif @@ -0,0 +1,10 @@ +test compile expect-fail +set enable_pcc=true +target aarch64 + +function %f0(i32) -> i32 { +block0(v0 ! max(32, 0x100): i32): + v1 = iconst.i32 2 + v2 ! max(32, 0x3ff) = ishl.i32 v0, v1 + return v2 +} diff --git a/cranelift/filetests/filetests/pcc/succeed/add.clif b/cranelift/filetests/filetests/pcc/succeed/add.clif new file mode 100644 index 000000000000..26e72c7c18ae --- /dev/null +++ b/cranelift/filetests/filetests/pcc/succeed/add.clif @@ -0,0 +1,60 @@ +test compile +set enable_pcc=true +target aarch64 + +function %f0(i32, i32) -> i32 { +block0(v0 ! max(32, 0x100): i32, v1 ! max(32, 0x80): i32): + v2 ! max(32, 0x180) = iadd.i32 v0, v1 + return v2 +} + +function %f1(i32) -> i32 { +block0(v0 ! max(32, 0x100): i32): + v1 ! max(32, 1) = iconst.i32 1 + v2 ! max(32, 0x101) = iadd.i32 v0, v1 + return v2 +} + +;; a looser but still accurate bound should check too: +function %f2(i32) -> i32 { +block0(v0 ! max(32, 0x100): i32): + v1 ! max(32, 1) = iconst.i32 1 + v2 ! max(32, 0x102) = iadd.i32 v0, v1 + return v2 +} + +;; we should be able to verify a range based on the type alone: +function %f3(i32) -> i64 { +block0(v0: i32): + v1 ! max(32, 1) = iconst.i32 1 + v2 ! max(32, 0xffff_ffff) = iadd.i32 v0, v1 + v3 ! max(64, 0xffff_ffff) = uextend.i64 v2 + return v3 +} + +;; we should be able to verify a range based on the type alone: +function %f3(i32) -> i64 { +block0(v0: i32): + v1 ! max(32, 1) = iconst.i32 1 + v2 ! max(32, 0xffff_ffff) = iadd.i32 v0, v1 + v3 ! max(64, 0xffff_ffff) = uextend.i64 v2 + v4 ! max(64, 0x1) = iconst.i64 1 + v5 ! max(64, 0x1_0000_0000) = iadd.i64 v3, v4 + return v5 +} + +;; check merged ops: +function %f4(i32, i32) -> i32 { +block0(v0 ! max(32, 0x100): i32, v1 ! max(32, 0x200): i32): + v2 = iconst.i32 2 + v3 ! max(32, 0x400) = ishl.i32 v0, v2 + v4 ! max(32, 0x600) = iadd.i32 v1, v3 + return v4 +} + +function %f5(i32, i64) -> i64 { +block0(v0 ! max(32, 0x100): i32, v1 ! max(64, 0x200): i64): + v2 ! max(64, 0x100) = uextend.i64 v0 + v3 ! max(64, 0x300) = iadd.i64 v1, v2 + return v3 +} diff --git a/cranelift/filetests/filetests/pcc/succeed/extend.clif b/cranelift/filetests/filetests/pcc/succeed/extend.clif new file mode 100644 index 000000000000..6f89f930928c --- /dev/null +++ b/cranelift/filetests/filetests/pcc/succeed/extend.clif @@ -0,0 +1,9 @@ +test compile +set enable_pcc=true +target aarch64 + +function %f0(i32) -> i64 { +block0(v0 ! max(32, 0xffff_ffff): i32): + v1 ! max(64, 0xffff_ffff) = uextend.i64 v0 + return v1 +} diff --git a/cranelift/filetests/filetests/pcc/succeed/load.clif b/cranelift/filetests/filetests/pcc/succeed/load.clif new file mode 100644 index 000000000000..e863cb059ffa --- /dev/null +++ b/cranelift/filetests/filetests/pcc/succeed/load.clif @@ -0,0 +1,60 @@ +test compile +set enable_pcc=true +target aarch64 + +function %f0(i64, i32) -> i64 { +block0(v0 ! points_to(0x1_0000_0000): i64, v1 ! max(32, 0x100): i32): + v2 ! max(64, 0x100) = uextend.i64 v1 + ;; 8 bytes of valid range is all we actually need, so let's only claim that. + v3 ! points_to(0x8) = iadd.i64 v0, v2 + v4 = load.i64 checked v3 + return v4 +} + +;; Note the guard region of 8 bytes -- just enough for the below! +function %f1(i64, i32) -> i64 { +block0(v0 ! points_to(0x1_0000_0008): i64, v1 ! max(32, 0xffff_ffff): i32): + v2 ! max(64, 0xffff_ffff) = uextend.i64 v1 + ;; 8 bytes of valid range is all we actually need, so let's only claim that. + v3 ! points_to(0x8) = iadd.i64 v0, v2 + v4 = load.i64 checked v3 + return v4 +} + +;; RegRegExtend mode on aarch64. +function %f2(i64, i32) -> i8 { +block0(v0 ! points_to(0x1000): i64, v1 ! max(32, 0xfff): i32): + v2 ! max(64, 0x100) = uextend.i64 v1 + v3 ! points_to(0x1) = iadd.i64 v0, v2 + v4 = load.i8 checked v3 + return v4 +} + +;; RegReg mode on aarch64. +function %f3(i64, i64) -> i8 { +block0(v0 ! points_to(0x1000): i64, v1 ! max(64, 0xfff): i64): + v2 ! points_to(0x1) = iadd.i64 v0, v1 + v3 = load.i8 checked v2 + return v3 +} + +;; RegScaledExtended mode on aarch64. +function %f4(i64, i32) -> i64 { +block0(v0 ! points_to(0x8000): i64, v1 ! max(32, 0xfff): i32): + v2 ! max(64, 0xfff) = uextend.i64 v1 + v3 = iconst.i32 3 + v4 ! max(64, 0x7ff8) = ishl.i64 v2, v3 + v5 ! points_to(0x8) = iadd.i64 v0, v4 + v6 = load.i64 checked v5 + return v6 +} + +;; RegScaled mode on aarch64. +function %f5(i64, i64) -> i64 { +block0(v0 ! points_to(0x8000): i64, v1 ! max(64, 0xfff): i64): + v2 = iconst.i32 3 + v3 ! max(64, 0x7ff8) = ishl.i64 v1, v2 + v4 ! points_to(0x8) = iadd.i64 v0, v3 + v5 = load.i64 checked v4 + return v5 +} diff --git a/cranelift/filetests/filetests/pcc/succeed/shift.clif b/cranelift/filetests/filetests/pcc/succeed/shift.clif new file mode 100644 index 000000000000..d4ebd25fcc92 --- /dev/null +++ b/cranelift/filetests/filetests/pcc/succeed/shift.clif @@ -0,0 +1,17 @@ +test compile +set enable_pcc=true +target aarch64 + +function %f0(i32) -> i32 { +block0(v0 ! max(32, 0x100): i32): + v1 = iconst.i32 2 + v2 ! max(32, 0x400) = ishl.i32 v0, v1 + return v2 +} + +function %f0(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 2 + v2 ! max(32, 0xffff_ffff) = ishl.i32 v0, v1 + return v2 +} From 9fc4a7106f2d77bb5d15659c20154cfea0374d7f Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Mon, 9 Oct 2023 04:11:00 +0100 Subject: [PATCH 083/199] cranelift: Group labels by FuncId instead of Name (#7183) This prevents confusing label locations when there are multiple functions with the same Name. --- cranelift/jit/src/backend.rs | 4 ++-- cranelift/module/src/module.rs | 12 ++++++++---- cranelift/object/src/backend.rs | 16 ++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cranelift/jit/src/backend.rs b/cranelift/jit/src/backend.rs index 438c60d49e27..f04b89d84cce 100644 --- a/cranelift/jit/src/backend.rs +++ b/cranelift/jit/src/backend.rs @@ -712,7 +712,7 @@ impl Module for JITModule { .buffer .relocs() .iter() - .map(|reloc| ModuleReloc::from_mach_reloc(reloc, &ctx.func)) + .map(|reloc| ModuleReloc::from_mach_reloc(reloc, &ctx.func, id)) .collect(); self.record_function_for_perf(ptr, size, &decl.linkage_name(id)); @@ -797,7 +797,7 @@ impl Module for JITModule { size, relocs: relocs .iter() - .map(|reloc| ModuleReloc::from_mach_reloc(reloc, func)) + .map(|reloc| ModuleReloc::from_mach_reloc(reloc, func, id)) .collect(), }); diff --git a/cranelift/module/src/module.rs b/cranelift/module/src/module.rs index cae49ef17e97..76661fd86481 100644 --- a/cranelift/module/src/module.rs +++ b/cranelift/module/src/module.rs @@ -11,7 +11,7 @@ use core::fmt::Display; use cranelift_codegen::binemit::{CodeOffset, Reloc}; use cranelift_codegen::entity::{entity_impl, PrimaryMap}; use cranelift_codegen::ir::function::{Function, VersionMarker}; -use cranelift_codegen::ir::{ExternalName, UserFuncName}; +use cranelift_codegen::ir::ExternalName; use cranelift_codegen::settings::SetError; use cranelift_codegen::{ ir, isa, CodegenError, CompileError, Context, FinalizedMachReloc, FinalizedRelocTarget, @@ -36,7 +36,11 @@ pub struct ModuleReloc { impl ModuleReloc { /// Converts a `FinalizedMachReloc` produced from a `Function` into a `ModuleReloc`. - pub fn from_mach_reloc(mach_reloc: &FinalizedMachReloc, func: &Function) -> Self { + pub fn from_mach_reloc( + mach_reloc: &FinalizedMachReloc, + func: &Function, + func_id: FuncId, + ) -> Self { let name = match mach_reloc.target { FinalizedRelocTarget::ExternalName(ExternalName::User(reff)) => { let name = &func.params.user_named_funcs()[reff]; @@ -50,7 +54,7 @@ impl ModuleReloc { ModuleRelocTarget::KnownSymbol(ks) } FinalizedRelocTarget::Func(offset) => { - ModuleRelocTarget::FunctionOffset(func.name.clone(), offset) + ModuleRelocTarget::FunctionOffset(func_id, offset) } }; Self { @@ -430,7 +434,7 @@ pub enum ModuleRelocTarget { /// Symbols known to the linker. KnownSymbol(ir::KnownSymbol), /// A offset inside a function - FunctionOffset(UserFuncName, CodeOffset), + FunctionOffset(FuncId, CodeOffset), } impl ModuleRelocTarget { diff --git a/cranelift/object/src/backend.rs b/cranelift/object/src/backend.rs index d083d84ce12f..435ff39856b8 100644 --- a/cranelift/object/src/backend.rs +++ b/cranelift/object/src/backend.rs @@ -3,7 +3,6 @@ use anyhow::anyhow; use cranelift_codegen::binemit::{Addend, CodeOffset, Reloc}; use cranelift_codegen::entity::SecondaryMap; -use cranelift_codegen::ir::UserFuncName; use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::{self, ir, FinalizedMachReloc}; use cranelift_control::ControlPlane; @@ -132,7 +131,7 @@ pub struct ObjectModule { libcalls: HashMap, libcall_names: Box String + Send + Sync>, known_symbols: HashMap, - known_labels: HashMap<(UserFuncName, CodeOffset), SymbolId>, + known_labels: HashMap<(FuncId, CodeOffset), SymbolId>, per_function_section: bool, } @@ -372,7 +371,9 @@ impl Module for ObjectModule { if !relocs.is_empty() { let relocs = relocs .iter() - .map(|record| self.process_reloc(&ModuleReloc::from_mach_reloc(&record, func))) + .map(|record| { + self.process_reloc(&ModuleReloc::from_mach_reloc(&record, func, func_id)) + }) .collect(); self.relocs.push(SymbolRelocs { section, @@ -594,15 +595,10 @@ impl ObjectModule { } } - ModuleRelocTarget::FunctionOffset(ref fname, offset) => { - match self.known_labels.entry((fname.clone(), offset)) { + ModuleRelocTarget::FunctionOffset(func_id, offset) => { + match self.known_labels.entry((func_id, offset)) { Entry::Occupied(o) => *o.get(), Entry::Vacant(v) => { - let func_user_name = fname.get_user().unwrap(); - let func_id = FuncId::from_name(&ModuleRelocTarget::user( - func_user_name.namespace, - func_user_name.index, - )); let func_symbol_id = self.functions[func_id].unwrap().0; let func_symbol = self.object.symbol(func_symbol_id); From 89449b68414a241be359201caf17932e410e74f0 Mon Sep 17 00:00:00 2001 From: Dave Bakker Date: Mon, 9 Oct 2023 05:22:36 +0200 Subject: [PATCH 084/199] wasi-sockets: Simplify & clarify TCP errors (#7120) * Use Rustix::Errno to unify error code mapping. * Clarify Connect failure state * Allow accept() to return transient errors. The original provision was added to align with preview3 streams that may only fail once. However, after discussing with Dan Gohman, we came to the conclusion that a stream of result<> could do the trick fine too. Fixes: https://github.com/WebAssembly/wasi-sockets/issues/22 * Fold `ephemeral-ports-exhausted` into `address-in-use` There is no cross-platform way to know the distinction between them * Clarify `local-address` behavior on unbound socket * Remove `concurrency-conflict` clutter, and just document it to be always possible. * Simplify state errors. They were unnecessarily detailed and mostly have no standardized equivalent in POSIX, so wasi-libc will probably just map them all back into a single EOPNOTSUPP or EINVAL or ... EISCONN/ENOTCONN can be derived in wasi-libc based on context and/or by checking `remote-address`. For example, `shutdown` can only be called on connected sockets, so if it returns `invalid-state` it can be unambiguously mapped to ENOTCONN. * Document that connect may return ECONNABORTED * Remove create-tcp/udp-socket not supported errors. These stem from back when the entire wasi-sockets proposal was one big single thing. In this day and age, when an implementation doesn't want to support TCP and/or UDP, it can simply _not_ implement that interface, rather than returning an error at runtime. * Simplify "not supported" and "invalid argument" error cases. There is a myriad of reasons why an argument might be invalid or an operation might be not supported. But there is few cross platform consistency in which of those error cases result in which error codes. The error codes that have been removed were fairly specific, but: - Were still missing error cases. So additional error codes would have needed to be created. - Implementations would have to bend over backwards to make it work cross platform, especially beyond just Win/Mac/Linux. - Didn't all have an equivalent in POSIX, so they would map back into a generic EINVAL anyways. * Move example_body out of lib.rs into its own test-case make room for other tests. * Refactor TCP integration tests: - Ad-hoc skeleton implementation of resources. - Add blocking wrappers around async operations. * Fix get/set_unicast_hop_limit on Linux * Test TCP socket states * Keep track of address family ourselves. Because I need the family for input validation. And the resulting code is more straightforward. * Add more tests and make it work on Linux * Fix Windows * Simplify integration tests. All platforms supported by wasmtime also support dualstack sockets. * Test ipv6_only inheritence * Test that socket options keep being respected, even if listen() has already been called * cargo fmt * Duplicate .wit changes to wasi-http * prtest:full * Fix BSD behavior of SO_SNDBUF/SO_RCVBUF * fmt * Fix type error * Got lost during merge * Implement listen backlog tests * Manually inherit buffer size from listener on MacOS. I suspect that these changes apply to any BSD platform, but I can't test that. * Keep track of IPV6_V6ONLY ourselves. - This provides cross-platform behaviour for `ipv6-only` - This eliminates the syscall in `validate_address_family` * Reject IPv4-compatible IPv6 addresses. * Remove intermediate SystemError trait * Fix ambiguous .into()'s * Fix IPV6_UNICAST_HOPS inheritance on MacOS --- crates/test-programs/tests/wasi-sockets.rs | 23 +- .../src/bin/ip_name_lookup.rs | 2 +- .../wasi-sockets-tests/src/bin/tcp_bind.rs | 151 ++++++ .../wasi-sockets-tests/src/bin/tcp_connect.rs | 119 +++++ .../src/bin/tcp_sample_application.rs | 74 +++ .../src/bin/tcp_sockopts.rs | 189 ++++++++ .../wasi-sockets-tests/src/bin/tcp_states.rs | 264 +++++++++++ .../wasi-sockets-tests/src/bin/tcp_v4.rs | 28 -- .../wasi-sockets-tests/src/bin/tcp_v6.rs | 30 -- .../wasi-sockets-tests/src/lib.rs | 222 ++++++--- .../wit/deps/sockets/ip-name-lookup.wit | 6 +- crates/wasi-http/wit/deps/sockets/network.wit | 61 +-- .../wit/deps/sockets/tcp-create-socket.wit | 5 +- crates/wasi-http/wit/deps/sockets/tcp.wit | 103 +++-- .../wit/deps/sockets/udp-create-socket.wit | 5 +- crates/wasi-http/wit/deps/sockets/udp.wit | 53 ++- crates/wasi/src/preview2/host/network.rs | 112 +++-- crates/wasi/src/preview2/host/tcp.rs | 432 +++++++++++++----- crates/wasi/src/preview2/ip_name_lookup.rs | 23 +- crates/wasi/src/preview2/tcp.rs | 51 ++- .../wasi/wit/deps/sockets/ip-name-lookup.wit | 6 +- crates/wasi/wit/deps/sockets/network.wit | 61 +-- .../wit/deps/sockets/tcp-create-socket.wit | 5 +- crates/wasi/wit/deps/sockets/tcp.wit | 103 +++-- .../wit/deps/sockets/udp-create-socket.wit | 5 +- crates/wasi/wit/deps/sockets/udp.wit | 53 ++- 26 files changed, 1654 insertions(+), 532 deletions(-) create mode 100644 crates/test-programs/wasi-sockets-tests/src/bin/tcp_bind.rs create mode 100644 crates/test-programs/wasi-sockets-tests/src/bin/tcp_connect.rs create mode 100644 crates/test-programs/wasi-sockets-tests/src/bin/tcp_sample_application.rs create mode 100644 crates/test-programs/wasi-sockets-tests/src/bin/tcp_sockopts.rs create mode 100644 crates/test-programs/wasi-sockets-tests/src/bin/tcp_states.rs delete mode 100644 crates/test-programs/wasi-sockets-tests/src/bin/tcp_v4.rs delete mode 100644 crates/test-programs/wasi-sockets-tests/src/bin/tcp_v6.rs diff --git a/crates/test-programs/tests/wasi-sockets.rs b/crates/test-programs/tests/wasi-sockets.rs index 8484a4f98f77..c94243a2a866 100644 --- a/crates/test-programs/tests/wasi-sockets.rs +++ b/crates/test-programs/tests/wasi-sockets.rs @@ -67,13 +67,28 @@ async fn run(name: &str) -> anyhow::Result<()> { } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn tcp_v4() { - run("tcp_v4").await.unwrap(); +async fn tcp_sample_application() { + run("tcp_sample_application").await.unwrap(); } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn tcp_v6() { - run("tcp_v6").await.unwrap(); +async fn tcp_bind() { + run("tcp_bind").await.unwrap(); +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn tcp_connect() { + run("tcp_connect").await.unwrap(); +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn tcp_states() { + run("tcp_states").await.unwrap(); +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn tcp_sockopts() { + run("tcp_sockopts").await.unwrap(); } #[test_log::test(tokio::test(flavor = "multi_thread"))] diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs b/crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs index 50932e057c65..8e8869109d1c 100644 --- a/crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs +++ b/crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs @@ -12,7 +12,7 @@ fn main() { assert!(addresses.resolve_next_address().is_ok()); let result = ip_name_lookup::resolve_addresses(&network, "a.b<&>", None, false); - assert!(matches!(result, Err(network::ErrorCode::InvalidName))); + assert!(matches!(result, Err(network::ErrorCode::InvalidArgument))); // Try resolving a valid address and ensure that it eventually terminates. // To help prevent this test from being flaky this additionally times out diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_bind.rs b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_bind.rs new file mode 100644 index 000000000000..8c6ce3604d56 --- /dev/null +++ b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_bind.rs @@ -0,0 +1,151 @@ +use wasi::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network}; +use wasi::sockets::tcp::TcpSocket; +use wasi_sockets_tests::*; + +/// Bind a socket and let the system determine a port. +fn test_tcp_bind_ephemeral_port(net: &Network, ip: IpAddress) { + let bind_addr = IpSocketAddress::new(ip, 0); + + let sock = TcpSocket::new(ip.family()).unwrap(); + sock.blocking_bind(net, bind_addr).unwrap(); + + let bound_addr = sock.local_address().unwrap(); + + assert_eq!(bind_addr.ip(), bound_addr.ip()); + assert_ne!(bind_addr.port(), bound_addr.port()); +} + +/// Bind a socket on a specified port. +fn test_tcp_bind_specific_port(net: &Network, ip: IpAddress) { + const PORT: u16 = 54321; + + let bind_addr = IpSocketAddress::new(ip, PORT); + + let sock = TcpSocket::new(ip.family()).unwrap(); + sock.blocking_bind(net, bind_addr).unwrap(); + + let bound_addr = sock.local_address().unwrap(); + + assert_eq!(bind_addr.ip(), bound_addr.ip()); + assert_eq!(bind_addr.port(), bound_addr.port()); +} + +/// Two sockets may not be actively bound to the same address at the same time. +fn test_tcp_bind_addrinuse(net: &Network, ip: IpAddress) { + let bind_addr = IpSocketAddress::new(ip, 0); + + let sock1 = TcpSocket::new(ip.family()).unwrap(); + sock1.blocking_bind(net, bind_addr).unwrap(); + sock1.blocking_listen().unwrap(); + + let bound_addr = sock1.local_address().unwrap(); + + let sock2 = TcpSocket::new(ip.family()).unwrap(); + assert_eq!( + sock2.blocking_bind(net, bound_addr), + Err(ErrorCode::AddressInUse) + ); +} + +// Try binding to an address that is not configured on the system. +fn test_tcp_bind_addrnotavail(net: &Network, ip: IpAddress) { + let bind_addr = IpSocketAddress::new(ip, 0); + + let sock = TcpSocket::new(ip.family()).unwrap(); + + assert_eq!( + sock.blocking_bind(net, bind_addr), + Err(ErrorCode::AddressNotBindable) + ); +} + +/// Bind should validate the address family. +fn test_tcp_bind_wrong_family(net: &Network, family: IpAddressFamily) { + let wrong_ip = match family { + IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK, + IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK, + }; + + let sock = TcpSocket::new(family).unwrap(); + let result = sock.blocking_bind(net, IpSocketAddress::new(wrong_ip, 0)); + + assert!(matches!(result, Err(ErrorCode::InvalidArgument))); +} + +/// Bind only works on unicast addresses. +fn test_tcp_bind_non_unicast(net: &Network) { + let ipv4_broadcast = IpSocketAddress::new(IpAddress::IPV4_BROADCAST, 0); + let ipv4_multicast = IpSocketAddress::new(IpAddress::Ipv4((224, 254, 0, 0)), 0); + let ipv6_multicast = IpSocketAddress::new(IpAddress::Ipv6((0xff00, 0, 0, 0, 0, 0, 0, 0)), 0); + + let sock_v4 = TcpSocket::new(IpAddressFamily::Ipv4).unwrap(); + let sock_v6 = TcpSocket::new(IpAddressFamily::Ipv6).unwrap(); + + assert!(matches!( + sock_v4.blocking_bind(net, ipv4_broadcast), + Err(ErrorCode::InvalidArgument) + )); + assert!(matches!( + sock_v4.blocking_bind(net, ipv4_multicast), + Err(ErrorCode::InvalidArgument) + )); + assert!(matches!( + sock_v6.blocking_bind(net, ipv6_multicast), + Err(ErrorCode::InvalidArgument) + )); +} + +fn test_tcp_bind_dual_stack(net: &Network) { + let sock = TcpSocket::new(IpAddressFamily::Ipv6).unwrap(); + let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0); + + // Even on platforms that don't support dualstack sockets, + // setting ipv6_only to true (disabling dualstack mode) should work. + sock.set_ipv6_only(true).unwrap(); + + // Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail: + assert!(matches!( + sock.blocking_bind(net, addr), + Err(ErrorCode::InvalidArgument) + )); + + sock.set_ipv6_only(false).unwrap(); + + sock.blocking_bind(net, addr).unwrap(); + + let bound_addr = sock.local_address().unwrap(); + + assert_eq!(bound_addr.family(), IpAddressFamily::Ipv6); +} + +fn main() { + const RESERVED_IPV4_ADDRESS: IpAddress = IpAddress::Ipv4((192, 0, 2, 0)); // Reserved for documentation and examples. + const RESERVED_IPV6_ADDRESS: IpAddress = IpAddress::Ipv6((0x2001, 0x0db8, 0, 0, 0, 0, 0, 0)); // Reserved for documentation and examples. + + let net = Network::default(); + + test_tcp_bind_ephemeral_port(&net, IpAddress::IPV4_LOOPBACK); + test_tcp_bind_ephemeral_port(&net, IpAddress::IPV6_LOOPBACK); + test_tcp_bind_ephemeral_port(&net, IpAddress::IPV4_UNSPECIFIED); + test_tcp_bind_ephemeral_port(&net, IpAddress::IPV6_UNSPECIFIED); + + test_tcp_bind_specific_port(&net, IpAddress::IPV4_LOOPBACK); + test_tcp_bind_specific_port(&net, IpAddress::IPV6_LOOPBACK); + test_tcp_bind_specific_port(&net, IpAddress::IPV4_UNSPECIFIED); + test_tcp_bind_specific_port(&net, IpAddress::IPV6_UNSPECIFIED); + + test_tcp_bind_addrinuse(&net, IpAddress::IPV4_LOOPBACK); + test_tcp_bind_addrinuse(&net, IpAddress::IPV6_LOOPBACK); + test_tcp_bind_addrinuse(&net, IpAddress::IPV4_UNSPECIFIED); + test_tcp_bind_addrinuse(&net, IpAddress::IPV6_UNSPECIFIED); + + test_tcp_bind_addrnotavail(&net, RESERVED_IPV4_ADDRESS); + test_tcp_bind_addrnotavail(&net, RESERVED_IPV6_ADDRESS); + + test_tcp_bind_wrong_family(&net, IpAddressFamily::Ipv4); + test_tcp_bind_wrong_family(&net, IpAddressFamily::Ipv6); + + test_tcp_bind_non_unicast(&net); + + test_tcp_bind_dual_stack(&net); +} diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_connect.rs b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_connect.rs new file mode 100644 index 000000000000..3629ad2bfca1 --- /dev/null +++ b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_connect.rs @@ -0,0 +1,119 @@ +use wasi::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network}; +use wasi::sockets::tcp::TcpSocket; +use wasi_sockets_tests::*; + +const SOME_PORT: u16 = 47; // If the tests pass, this will never actually be connected to. + +/// `0.0.0.0` / `::` is not a valid remote address in WASI. +fn test_tcp_connect_unspec(net: &Network, family: IpAddressFamily) { + let addr = IpSocketAddress::new(IpAddress::new_unspecified(family), SOME_PORT); + let sock = TcpSocket::new(family).unwrap(); + + assert!(matches!( + sock.blocking_connect(net, addr), + Err(ErrorCode::InvalidArgument) + )); +} + +/// 0 is not a valid remote port. +fn test_tcp_connect_port_0(net: &Network, family: IpAddressFamily) { + let addr = IpSocketAddress::new(IpAddress::new_loopback(family), 0); + let sock = TcpSocket::new(family).unwrap(); + + assert!(matches!( + sock.blocking_connect(net, addr), + Err(ErrorCode::InvalidArgument) + )); +} + +/// Bind should validate the address family. +fn test_tcp_connect_wrong_family(net: &Network, family: IpAddressFamily) { + let wrong_ip = match family { + IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK, + IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK, + }; + let remote_addr = IpSocketAddress::new(wrong_ip, SOME_PORT); + + let sock = TcpSocket::new(family).unwrap(); + + assert!(matches!( + sock.blocking_connect(net, remote_addr), + Err(ErrorCode::InvalidArgument) + )); +} + +/// Can only connect to unicast addresses. +fn test_tcp_connect_non_unicast(net: &Network) { + let ipv4_broadcast = IpSocketAddress::new(IpAddress::IPV4_BROADCAST, SOME_PORT); + let ipv4_multicast = IpSocketAddress::new(IpAddress::Ipv4((224, 254, 0, 0)), SOME_PORT); + let ipv6_multicast = + IpSocketAddress::new(IpAddress::Ipv6((0xff00, 0, 0, 0, 0, 0, 0, 0)), SOME_PORT); + + let sock_v4 = TcpSocket::new(IpAddressFamily::Ipv4).unwrap(); + let sock_v6 = TcpSocket::new(IpAddressFamily::Ipv6).unwrap(); + + assert!(matches!( + sock_v4.blocking_connect(net, ipv4_broadcast), + Err(ErrorCode::InvalidArgument) + )); + assert!(matches!( + sock_v4.blocking_connect(net, ipv4_multicast), + Err(ErrorCode::InvalidArgument) + )); + assert!(matches!( + sock_v6.blocking_connect(net, ipv6_multicast), + Err(ErrorCode::InvalidArgument) + )); +} + +fn test_tcp_connect_dual_stack(net: &Network) { + // Set-up: + let v4_listener = TcpSocket::new(IpAddressFamily::Ipv4).unwrap(); + v4_listener + .blocking_bind(&net, IpSocketAddress::new(IpAddress::IPV4_LOOPBACK, 0)) + .unwrap(); + v4_listener.blocking_listen().unwrap(); + + let v4_listener_addr = v4_listener.local_address().unwrap(); + let v6_listener_addr = + IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, v4_listener_addr.port()); + + let v6_client = TcpSocket::new(IpAddressFamily::Ipv6).unwrap(); + + // Tests: + + // Even on platforms that don't support dualstack sockets, + // setting ipv6_only to true (disabling dualstack mode) should work. + v6_client.set_ipv6_only(true).unwrap(); + + // Connecting to an IPv4-mapped-IPv6 address on an ipv6-only socket should fail: + assert!(matches!( + v6_client.blocking_connect(net, v6_listener_addr), + Err(ErrorCode::InvalidArgument) + )); + + v6_client.set_ipv6_only(false).unwrap(); + + v6_client.blocking_connect(net, v6_listener_addr).unwrap(); + + let connected_addr = v6_client.local_address().unwrap(); + + assert_eq!(connected_addr.family(), IpAddressFamily::Ipv6); +} + +fn main() { + let net = Network::default(); + + test_tcp_connect_unspec(&net, IpAddressFamily::Ipv4); + test_tcp_connect_unspec(&net, IpAddressFamily::Ipv6); + + test_tcp_connect_port_0(&net, IpAddressFamily::Ipv4); + test_tcp_connect_port_0(&net, IpAddressFamily::Ipv6); + + test_tcp_connect_wrong_family(&net, IpAddressFamily::Ipv4); + test_tcp_connect_wrong_family(&net, IpAddressFamily::Ipv6); + + test_tcp_connect_non_unicast(&net); + + test_tcp_connect_dual_stack(&net); +} diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_sample_application.rs b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_sample_application.rs new file mode 100644 index 000000000000..9f182f2c4660 --- /dev/null +++ b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_sample_application.rs @@ -0,0 +1,74 @@ +use wasi::sockets::network::{ + IpAddressFamily, IpSocketAddress, Ipv4SocketAddress, Ipv6SocketAddress, Network, +}; +use wasi::sockets::tcp::TcpSocket; +use wasi_sockets_tests::*; + +fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddress) { + let first_message = b"Hello, world!"; + let second_message = b"Greetings, planet!"; + + let net = Network::default(); + let listener = TcpSocket::new(family).unwrap(); + + listener.blocking_bind(&net, bind_address).unwrap(); + listener.set_listen_backlog_size(32).unwrap(); + listener.blocking_listen().unwrap(); + + let addr = listener.local_address().unwrap(); + + { + let client = TcpSocket::new(family).unwrap(); + let (_client_input, client_output) = client.blocking_connect(&net, addr).unwrap(); + + client_output.blocking_write_util(&[]).unwrap(); + client_output.blocking_write_util(first_message).unwrap(); + } + + { + let (_accepted, input, _output) = listener.accept().unwrap(); + + let empty_data = input.read(0).unwrap(); + assert!(empty_data.is_empty()); + + let data = input.blocking_read(first_message.len() as u64).unwrap(); + + // Check that we sent and recieved our message! + assert_eq!(data, first_message); // Not guaranteed to work but should work in practice. + } + + // Another client + { + let client = TcpSocket::new(family).unwrap(); + let (_client_input, client_output) = client.blocking_connect(&net, addr).unwrap(); + + client_output.blocking_write_util(second_message).unwrap(); + } + + { + let (_accepted, input, _output) = listener.accept().unwrap(); + let data = input.blocking_read(second_message.len() as u64).unwrap(); + + // Check that we sent and recieved our message! + assert_eq!(data, second_message); // Not guaranteed to work but should work in practice. + } +} + +fn main() { + test_sample_application( + IpAddressFamily::Ipv4, + IpSocketAddress::Ipv4(Ipv4SocketAddress { + port: 0, // use any free port + address: (127, 0, 0, 1), // localhost + }), + ); + test_sample_application( + IpAddressFamily::Ipv6, + IpSocketAddress::Ipv6(Ipv6SocketAddress { + port: 0, // use any free port + address: (0, 0, 0, 0, 0, 0, 0, 1), // localhost + flow_info: 0, + scope_id: 0, + }), + ); +} diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_sockopts.rs b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_sockopts.rs new file mode 100644 index 000000000000..c1a5df8ea7a5 --- /dev/null +++ b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_sockopts.rs @@ -0,0 +1,189 @@ +use wasi::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network}; +use wasi::sockets::tcp::TcpSocket; +use wasi_sockets_tests::*; + +fn test_tcp_sockopt_defaults(family: IpAddressFamily) { + let sock = TcpSocket::new(family).unwrap(); + + assert_eq!(sock.address_family(), family); + + if family == IpAddressFamily::Ipv6 { + sock.ipv6_only().unwrap(); // Only verify that it has a default value at all, but either value is valid. + } + + sock.keep_alive().unwrap(); // Only verify that it has a default value at all, but either value is valid. + assert_eq!(sock.no_delay().unwrap(), false); + assert!(sock.unicast_hop_limit().unwrap() > 0); + assert!(sock.receive_buffer_size().unwrap() > 0); + assert!(sock.send_buffer_size().unwrap() > 0); +} + +fn test_tcp_sockopt_input_ranges(family: IpAddressFamily) { + let sock = TcpSocket::new(family).unwrap(); + + if family == IpAddressFamily::Ipv6 { + assert!(matches!(sock.set_ipv6_only(true), Ok(_))); + assert!(matches!(sock.set_ipv6_only(false), Ok(_))); + } + + assert!(matches!(sock.set_listen_backlog_size(0), Ok(_))); // Unsupported sizes should be silently capped. + assert!(matches!(sock.set_listen_backlog_size(u64::MAX), Ok(_))); // Unsupported sizes should be silently capped. + + assert!(matches!(sock.set_keep_alive(true), Ok(_))); + assert!(matches!(sock.set_keep_alive(false), Ok(_))); + + assert!(matches!(sock.set_no_delay(true), Ok(_))); + assert!(matches!(sock.set_no_delay(false), Ok(_))); + + assert!(matches!( + sock.set_unicast_hop_limit(0), + Err(ErrorCode::InvalidArgument) + )); + assert!(matches!(sock.set_unicast_hop_limit(1), Ok(_))); + assert!(matches!(sock.set_unicast_hop_limit(u8::MAX), Ok(_))); + + assert!(matches!(sock.set_receive_buffer_size(0), Ok(_))); // Unsupported sizes should be silently capped. + assert!(matches!(sock.set_receive_buffer_size(u64::MAX), Ok(_))); // Unsupported sizes should be silently capped. + assert!(matches!(sock.set_send_buffer_size(0), Ok(_))); // Unsupported sizes should be silently capped. + assert!(matches!(sock.set_send_buffer_size(u64::MAX), Ok(_))); // Unsupported sizes should be silently capped. +} + +fn test_tcp_sockopt_readback(family: IpAddressFamily) { + let sock = TcpSocket::new(family).unwrap(); + + if family == IpAddressFamily::Ipv6 { + sock.set_ipv6_only(true).unwrap(); + assert_eq!(sock.ipv6_only().unwrap(), true); + sock.set_ipv6_only(false).unwrap(); + assert_eq!(sock.ipv6_only().unwrap(), false); + } + + sock.set_keep_alive(true).unwrap(); + assert_eq!(sock.keep_alive().unwrap(), true); + sock.set_keep_alive(false).unwrap(); + assert_eq!(sock.keep_alive().unwrap(), false); + + sock.set_no_delay(true).unwrap(); + assert_eq!(sock.no_delay().unwrap(), true); + sock.set_no_delay(false).unwrap(); + assert_eq!(sock.no_delay().unwrap(), false); + + sock.set_unicast_hop_limit(42).unwrap(); + assert_eq!(sock.unicast_hop_limit().unwrap(), 42); + + sock.set_receive_buffer_size(0x10000).unwrap(); + assert_eq!(sock.receive_buffer_size().unwrap(), 0x10000); + + sock.set_send_buffer_size(0x10000).unwrap(); + assert_eq!(sock.send_buffer_size().unwrap(), 0x10000); +} + +fn test_tcp_sockopt_inheritance(net: &Network, family: IpAddressFamily) { + let bind_addr = IpSocketAddress::new(IpAddress::new_loopback(family), 0); + let listener = TcpSocket::new(family).unwrap(); + + let default_ipv6_only = listener.ipv6_only().unwrap_or(false); + let default_keep_alive = listener.keep_alive().unwrap(); + + // Configure options on listener: + { + if family == IpAddressFamily::Ipv6 { + listener.set_ipv6_only(!default_ipv6_only).unwrap(); + } + + listener.set_keep_alive(!default_keep_alive).unwrap(); + listener.set_no_delay(true).unwrap(); + listener.set_unicast_hop_limit(42).unwrap(); + listener.set_receive_buffer_size(0x10000).unwrap(); + listener.set_send_buffer_size(0x10000).unwrap(); + } + + listener.blocking_bind(&net, bind_addr).unwrap(); + listener.blocking_listen().unwrap(); + let bound_addr = listener.local_address().unwrap(); + let client = TcpSocket::new(family).unwrap(); + client.blocking_connect(&net, bound_addr).unwrap(); + let (accepted_client, _, _) = listener.accept().unwrap(); + + // Verify options on accepted socket: + { + if family == IpAddressFamily::Ipv6 { + assert_eq!(accepted_client.ipv6_only().unwrap(), !default_ipv6_only); + } + + assert_eq!(accepted_client.keep_alive().unwrap(), !default_keep_alive); + assert_eq!(accepted_client.no_delay().unwrap(), true); + assert_eq!(accepted_client.unicast_hop_limit().unwrap(), 42); + assert_eq!(accepted_client.receive_buffer_size().unwrap(), 0x10000); + assert_eq!(accepted_client.send_buffer_size().unwrap(), 0x10000); + } + + // Update options on listener to something else: + { + listener.set_keep_alive(default_keep_alive).unwrap(); + listener.set_no_delay(false).unwrap(); + listener.set_unicast_hop_limit(43).unwrap(); + listener.set_receive_buffer_size(0x20000).unwrap(); + listener.set_send_buffer_size(0x20000).unwrap(); + } + + // Verify that the already accepted socket was not affected: + { + assert_eq!(accepted_client.keep_alive().unwrap(), !default_keep_alive); + assert_eq!(accepted_client.no_delay().unwrap(), true); + assert_eq!(accepted_client.unicast_hop_limit().unwrap(), 42); + assert_eq!(accepted_client.receive_buffer_size().unwrap(), 0x10000); + assert_eq!(accepted_client.send_buffer_size().unwrap(), 0x10000); + } +} + +fn test_tcp_sockopt_after_listen(net: &Network, family: IpAddressFamily) { + let bind_addr = IpSocketAddress::new(IpAddress::new_loopback(family), 0); + let listener = TcpSocket::new(family).unwrap(); + listener.blocking_bind(&net, bind_addr).unwrap(); + listener.blocking_listen().unwrap(); + let bound_addr = listener.local_address().unwrap(); + + let default_keep_alive = listener.keep_alive().unwrap(); + + // Update options while the socket is already listening: + { + listener.set_keep_alive(!default_keep_alive).unwrap(); + listener.set_no_delay(true).unwrap(); + listener.set_unicast_hop_limit(42).unwrap(); + listener.set_receive_buffer_size(0x10000).unwrap(); + listener.set_send_buffer_size(0x10000).unwrap(); + } + + let client = TcpSocket::new(family).unwrap(); + client.blocking_connect(&net, bound_addr).unwrap(); + let (accepted_client, _, _) = listener.accept().unwrap(); + + // Verify options on accepted socket: + { + assert_eq!(accepted_client.keep_alive().unwrap(), !default_keep_alive); + assert_eq!(accepted_client.no_delay().unwrap(), true); + assert_eq!(accepted_client.unicast_hop_limit().unwrap(), 42); + assert_eq!(accepted_client.receive_buffer_size().unwrap(), 0x10000); + assert_eq!(accepted_client.send_buffer_size().unwrap(), 0x10000); + } +} + +fn main() { + let net = Network::default(); + + test_tcp_sockopt_defaults(IpAddressFamily::Ipv4); + test_tcp_sockopt_defaults(IpAddressFamily::Ipv6); + + test_tcp_sockopt_input_ranges(IpAddressFamily::Ipv4); + test_tcp_sockopt_input_ranges(IpAddressFamily::Ipv6); + + test_tcp_sockopt_readback(IpAddressFamily::Ipv4); + test_tcp_sockopt_readback(IpAddressFamily::Ipv6); + + test_tcp_sockopt_inheritance(&net, IpAddressFamily::Ipv4); + test_tcp_sockopt_inheritance(&net, IpAddressFamily::Ipv6); + + test_tcp_sockopt_after_listen(&net, IpAddressFamily::Ipv4); + test_tcp_sockopt_after_listen(&net, IpAddressFamily::Ipv6); +} diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_states.rs b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_states.rs new file mode 100644 index 000000000000..8547749e5e68 --- /dev/null +++ b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_states.rs @@ -0,0 +1,264 @@ +use wasi::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network}; +use wasi::sockets::tcp::{ShutdownType, TcpSocket}; +use wasi_sockets_tests::*; + +fn test_tcp_unbound_state_invariants(family: IpAddressFamily) { + let sock = TcpSocket::new(family).unwrap(); + + // Skipping: tcp::start_bind + assert!(matches!(sock.finish_bind(), Err(ErrorCode::NotInProgress))); + // Skipping: tcp::start_connect + assert!(matches!( + sock.finish_connect(), + Err(ErrorCode::NotInProgress) + )); + assert!(matches!( + sock.start_listen(), + Err(ErrorCode::InvalidState) // Unlike POSIX, trying to listen without an explicit bind should fail in WASI. + )); + assert!(matches!( + sock.finish_listen(), + Err(ErrorCode::NotInProgress) + )); + assert!(matches!(sock.accept(), Err(ErrorCode::InvalidState))); + assert!(matches!( + sock.shutdown(ShutdownType::Both), + Err(ErrorCode::InvalidState) + )); + + assert!(matches!(sock.local_address(), Err(ErrorCode::InvalidState))); + assert!(matches!( + sock.remote_address(), + Err(ErrorCode::InvalidState) + )); + assert_eq!(sock.address_family(), family); + + if family == IpAddressFamily::Ipv6 { + assert!(matches!(sock.ipv6_only(), Ok(_))); + + // Even on platforms that don't support dualstack sockets, + // setting ipv6_only to true (disabling dualstack mode) should work. + assert!(matches!(sock.set_ipv6_only(true), Ok(_))); + } else { + assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); + assert!(matches!( + sock.set_ipv6_only(true), + Err(ErrorCode::NotSupported) + )); + } + + assert!(matches!(sock.set_listen_backlog_size(32), Ok(_))); + assert!(matches!(sock.keep_alive(), Ok(_))); + assert!(matches!(sock.set_keep_alive(false), Ok(_))); + assert!(matches!(sock.no_delay(), Ok(_))); + assert!(matches!(sock.set_no_delay(false), Ok(_))); + assert!(matches!(sock.unicast_hop_limit(), Ok(_))); + assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_))); + assert!(matches!(sock.receive_buffer_size(), Ok(_))); + assert!(matches!(sock.set_receive_buffer_size(16000), Ok(_))); + assert!(matches!(sock.send_buffer_size(), Ok(_))); + assert!(matches!(sock.set_send_buffer_size(16000), Ok(_))); +} + +fn test_tcp_bound_state_invariants(net: &Network, family: IpAddressFamily) { + let bind_address = IpSocketAddress::new(IpAddress::new_loopback(family), 0); + let sock = TcpSocket::new(family).unwrap(); + sock.blocking_bind(net, bind_address).unwrap(); + + assert!(matches!( + sock.start_bind(net, bind_address), + Err(ErrorCode::InvalidState) + )); + assert!(matches!(sock.finish_bind(), Err(ErrorCode::NotInProgress))); + // Skipping: tcp::start_connect + assert!(matches!( + sock.finish_connect(), + Err(ErrorCode::NotInProgress) + )); + // Skipping: tcp::start_listen + assert!(matches!( + sock.finish_listen(), + Err(ErrorCode::NotInProgress) + )); + assert!(matches!(sock.accept(), Err(ErrorCode::InvalidState))); + assert!(matches!( + sock.shutdown(ShutdownType::Both), + Err(ErrorCode::InvalidState) + )); + + assert!(matches!(sock.local_address(), Ok(_))); + assert!(matches!( + sock.remote_address(), + Err(ErrorCode::InvalidState) + )); + assert_eq!(sock.address_family(), family); + + if family == IpAddressFamily::Ipv6 { + assert!(matches!(sock.ipv6_only(), Ok(_))); + assert!(matches!( + sock.set_ipv6_only(true), + Err(ErrorCode::InvalidState) + )); + } else { + assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); + assert!(matches!( + sock.set_ipv6_only(true), + Err(ErrorCode::NotSupported) + )); + } + + assert!(matches!(sock.set_listen_backlog_size(32), Ok(_))); + assert!(matches!(sock.keep_alive(), Ok(_))); + assert!(matches!(sock.set_keep_alive(false), Ok(_))); + assert!(matches!(sock.no_delay(), Ok(_))); + assert!(matches!(sock.set_no_delay(false), Ok(_))); + assert!(matches!(sock.unicast_hop_limit(), Ok(_))); + assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_))); + assert!(matches!(sock.receive_buffer_size(), Ok(_))); + assert!(matches!(sock.set_receive_buffer_size(16000), Ok(_))); + assert!(matches!(sock.send_buffer_size(), Ok(_))); + assert!(matches!(sock.set_send_buffer_size(16000), Ok(_))); +} + +fn test_tcp_listening_state_invariants(net: &Network, family: IpAddressFamily) { + let bind_address = IpSocketAddress::new(IpAddress::new_loopback(family), 0); + let sock = TcpSocket::new(family).unwrap(); + sock.blocking_bind(net, bind_address).unwrap(); + sock.blocking_listen().unwrap(); + + assert!(matches!( + sock.start_bind(net, bind_address), + Err(ErrorCode::InvalidState) + )); + assert!(matches!(sock.finish_bind(), Err(ErrorCode::NotInProgress))); + assert!(matches!( + sock.start_connect(net, bind_address), // Actual address shouldn't matter + Err(ErrorCode::InvalidState) + )); + assert!(matches!( + sock.finish_connect(), + Err(ErrorCode::NotInProgress) + )); + assert!(matches!(sock.start_listen(), Err(ErrorCode::InvalidState))); + assert!(matches!( + sock.finish_listen(), + Err(ErrorCode::NotInProgress) + )); + // Skipping: tcp::accept + assert!(matches!( + sock.shutdown(ShutdownType::Both), + Err(ErrorCode::InvalidState) + )); + + assert!(matches!(sock.local_address(), Ok(_))); + assert!(matches!( + sock.remote_address(), + Err(ErrorCode::InvalidState) + )); + assert_eq!(sock.address_family(), family); + + if family == IpAddressFamily::Ipv6 { + assert!(matches!(sock.ipv6_only(), Ok(_))); + assert!(matches!( + sock.set_ipv6_only(true), + Err(ErrorCode::InvalidState) + )); + } else { + assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); + assert!(matches!( + sock.set_ipv6_only(true), + Err(ErrorCode::NotSupported) + )); + } + + assert!(matches!( + sock.set_listen_backlog_size(32), + Ok(_) | Err(ErrorCode::NotSupported) + )); + assert!(matches!(sock.keep_alive(), Ok(_))); + assert!(matches!(sock.set_keep_alive(false), Ok(_))); + assert!(matches!(sock.no_delay(), Ok(_))); + assert!(matches!(sock.set_no_delay(false), Ok(_))); + assert!(matches!(sock.unicast_hop_limit(), Ok(_))); + assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_))); + assert!(matches!(sock.receive_buffer_size(), Ok(_))); + assert!(matches!(sock.set_receive_buffer_size(16000), Ok(_))); + assert!(matches!(sock.send_buffer_size(), Ok(_))); + assert!(matches!(sock.set_send_buffer_size(16000), Ok(_))); +} + +fn test_tcp_connected_state_invariants(net: &Network, family: IpAddressFamily) { + let bind_address = IpSocketAddress::new(IpAddress::new_loopback(family), 0); + let sock_listener = TcpSocket::new(family).unwrap(); + sock_listener.blocking_bind(net, bind_address).unwrap(); + sock_listener.blocking_listen().unwrap(); + let addr_listener = sock_listener.local_address().unwrap(); + let sock = TcpSocket::new(family).unwrap(); + let (_input, _output) = sock.blocking_connect(net, addr_listener).unwrap(); + + assert!(matches!( + sock.start_bind(net, bind_address), + Err(ErrorCode::InvalidState) + )); + assert!(matches!(sock.finish_bind(), Err(ErrorCode::NotInProgress))); + assert!(matches!( + sock.start_connect(net, addr_listener), + Err(ErrorCode::InvalidState) + )); + assert!(matches!( + sock.finish_connect(), + Err(ErrorCode::NotInProgress) + )); + assert!(matches!(sock.start_listen(), Err(ErrorCode::InvalidState))); + assert!(matches!( + sock.finish_listen(), + Err(ErrorCode::NotInProgress) + )); + assert!(matches!(sock.accept(), Err(ErrorCode::InvalidState))); + // Skipping: tcp::shutdown + + assert!(matches!(sock.local_address(), Ok(_))); + assert!(matches!(sock.remote_address(), Ok(_))); + assert_eq!(sock.address_family(), family); + + if family == IpAddressFamily::Ipv6 { + assert!(matches!(sock.ipv6_only(), Ok(_))); + assert!(matches!( + sock.set_ipv6_only(true), + Err(ErrorCode::InvalidState) + )); + } else { + assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported))); + assert!(matches!( + sock.set_ipv6_only(true), + Err(ErrorCode::NotSupported) + )); + } + + assert!(matches!(sock.keep_alive(), Ok(_))); + assert!(matches!(sock.set_keep_alive(false), Ok(_))); + assert!(matches!(sock.no_delay(), Ok(_))); + assert!(matches!(sock.set_no_delay(false), Ok(_))); + assert!(matches!(sock.unicast_hop_limit(), Ok(_))); + assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_))); + assert!(matches!(sock.receive_buffer_size(), Ok(_))); + assert!(matches!(sock.set_receive_buffer_size(16000), Ok(_))); + assert!(matches!(sock.send_buffer_size(), Ok(_))); + assert!(matches!(sock.set_send_buffer_size(16000), Ok(_))); +} + +fn main() { + let net = Network::default(); + + test_tcp_unbound_state_invariants(IpAddressFamily::Ipv4); + test_tcp_unbound_state_invariants(IpAddressFamily::Ipv6); + + test_tcp_bound_state_invariants(&net, IpAddressFamily::Ipv4); + test_tcp_bound_state_invariants(&net, IpAddressFamily::Ipv6); + + test_tcp_listening_state_invariants(&net, IpAddressFamily::Ipv4); + test_tcp_listening_state_invariants(&net, IpAddressFamily::Ipv6); + + test_tcp_connected_state_invariants(&net, IpAddressFamily::Ipv4); + test_tcp_connected_state_invariants(&net, IpAddressFamily::Ipv6); +} diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v4.rs b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v4.rs deleted file mode 100644 index cf02fdd79663..000000000000 --- a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v4.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! A simple TCP testcase, using IPv4. - -use wasi::io::poll; -use wasi::sockets::network::{IpAddressFamily, IpSocketAddress, Ipv4SocketAddress}; -use wasi::sockets::{instance_network, tcp_create_socket}; -use wasi_sockets_tests::*; - -fn main() { - let net = instance_network::instance_network(); - - let sock = tcp_create_socket::create_tcp_socket(IpAddressFamily::Ipv4).unwrap(); - - let addr = IpSocketAddress::Ipv4(Ipv4SocketAddress { - port: 0, // use any free port - address: (127, 0, 0, 1), // localhost - }); - - let sub = sock.subscribe(); - - sock.start_bind(&net, addr).unwrap(); - - poll::poll_one(&sub); - drop(sub); - - sock.finish_bind().unwrap(); - - example_body(net, sock, IpAddressFamily::Ipv4) -} diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v6.rs b/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v6.rs deleted file mode 100644 index 807db9825f1e..000000000000 --- a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_v6.rs +++ /dev/null @@ -1,30 +0,0 @@ -//! Like v4.rs, but with IPv6. - -use wasi::io::poll; -use wasi::sockets::network::{IpAddressFamily, IpSocketAddress, Ipv6SocketAddress}; -use wasi::sockets::{instance_network, tcp_create_socket}; -use wasi_sockets_tests::*; - -fn main() { - let net = instance_network::instance_network(); - - let sock = tcp_create_socket::create_tcp_socket(IpAddressFamily::Ipv6).unwrap(); - - let addr = IpSocketAddress::Ipv6(Ipv6SocketAddress { - port: 0, // use any free port - address: (0, 0, 0, 0, 0, 0, 0, 1), // localhost - flow_info: 0, - scope_id: 0, - }); - - let sub = sock.subscribe(); - - sock.start_bind(&net, addr).unwrap(); - - poll::poll_one(&sub); - drop(sub); - - sock.finish_bind().unwrap(); - - example_body(net, sock, IpAddressFamily::Ipv6) -} diff --git a/crates/test-programs/wasi-sockets-tests/src/lib.rs b/crates/test-programs/wasi-sockets-tests/src/lib.rs index a46cff830c0f..4d90e914ddda 100644 --- a/crates/test-programs/wasi-sockets-tests/src/lib.rs +++ b/crates/test-programs/wasi-sockets-tests/src/lib.rs @@ -1,97 +1,191 @@ wit_bindgen::generate!("test-command-with-sockets" in "../../wasi/wit"); -use wasi::io::poll; -use wasi::io::streams; -use wasi::sockets::{network, tcp, tcp_create_socket}; +use wasi::io::poll::{self, Pollable}; +use wasi::io::streams::{InputStream, OutputStream, StreamError}; +use wasi::sockets::instance_network; +use wasi::sockets::network::{ + ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Ipv4SocketAddress, Ipv6SocketAddress, + Network, +}; +use wasi::sockets::tcp::TcpSocket; +use wasi::sockets::tcp_create_socket; + +impl Pollable { + pub fn wait(&self) { + poll::poll_one(self); + } +} -pub fn write(output: &streams::OutputStream, mut bytes: &[u8]) -> Result<(), streams::StreamError> { - let pollable = output.subscribe(); +impl OutputStream { + pub fn blocking_write_util(&self, mut bytes: &[u8]) -> Result<(), StreamError> { + let pollable = self.subscribe(); - while !bytes.is_empty() { - poll::poll_list(&[&pollable]); + while !bytes.is_empty() { + pollable.wait(); - let permit = output.check_write()?; + let permit = self.check_write()?; - let len = bytes.len().min(permit as usize); - let (chunk, rest) = bytes.split_at(len); + let len = bytes.len().min(permit as usize); + let (chunk, rest) = bytes.split_at(len); - output.write(chunk)?; + self.write(chunk)?; - output.blocking_flush()?; + self.blocking_flush()?; - bytes = rest; + bytes = rest; + } + Ok(()) } - Ok(()) } -pub fn example_body(net: tcp::Network, sock: tcp::TcpSocket, family: network::IpAddressFamily) { - let first_message = b"Hello, world!"; - let second_message = b"Greetings, planet!"; - - let sub = sock.subscribe(); +impl Network { + pub fn default() -> Network { + instance_network::instance_network() + } +} - sock.set_listen_backlog_size(32).unwrap(); +impl TcpSocket { + pub fn new(address_family: IpAddressFamily) -> Result { + tcp_create_socket::create_tcp_socket(address_family) + } - sock.start_listen().unwrap(); - poll::poll_one(&sub); - sock.finish_listen().unwrap(); + pub fn blocking_bind( + &self, + network: &Network, + local_address: IpSocketAddress, + ) -> Result<(), ErrorCode> { + let sub = self.subscribe(); + + self.start_bind(&network, local_address)?; + + loop { + match self.finish_bind() { + Err(ErrorCode::WouldBlock) => sub.wait(), + result => return result, + } + } + } - let addr = sock.local_address().unwrap(); + pub fn blocking_listen(&self) -> Result<(), ErrorCode> { + let sub = self.subscribe(); - let client = tcp_create_socket::create_tcp_socket(family).unwrap(); - let client_sub = client.subscribe(); + self.start_listen()?; - client.start_connect(&net, addr).unwrap(); - poll::poll_one(&client_sub); - let (client_input, client_output) = client.finish_connect().unwrap(); + loop { + match self.finish_listen() { + Err(ErrorCode::WouldBlock) => sub.wait(), + result => return result, + } + } + } - write(&client_output, &[]).unwrap(); + pub fn blocking_connect( + &self, + network: &Network, + remote_address: IpSocketAddress, + ) -> Result<(InputStream, OutputStream), ErrorCode> { + let sub = self.subscribe(); + + self.start_connect(&network, remote_address)?; + + loop { + match self.finish_connect() { + Err(ErrorCode::WouldBlock) => sub.wait(), + result => return result, + } + } + } - write(&client_output, first_message).unwrap(); + pub fn blocking_accept(&self) -> Result<(TcpSocket, InputStream, OutputStream), ErrorCode> { + let sub = self.subscribe(); - drop(client_input); - drop(client_output); - drop(client_sub); - drop(client); + loop { + match self.accept() { + Err(ErrorCode::WouldBlock) => sub.wait(), + result => return result, + } + } + } +} - poll::poll_one(&sub); - let (accepted, input, output) = sock.accept().unwrap(); +impl IpAddress { + pub const IPV4_BROADCAST: IpAddress = IpAddress::Ipv4((255, 255, 255, 255)); - let empty_data = input.read(0).unwrap(); - assert!(empty_data.is_empty()); + pub const IPV4_LOOPBACK: IpAddress = IpAddress::Ipv4((127, 0, 0, 1)); + pub const IPV6_LOOPBACK: IpAddress = IpAddress::Ipv6((0, 0, 0, 0, 0, 0, 0, 1)); - let data = input.blocking_read(first_message.len() as u64).unwrap(); + pub const IPV4_UNSPECIFIED: IpAddress = IpAddress::Ipv4((0, 0, 0, 0)); + pub const IPV6_UNSPECIFIED: IpAddress = IpAddress::Ipv6((0, 0, 0, 0, 0, 0, 0, 0)); - drop(input); - drop(output); - drop(accepted); + pub const IPV4_MAPPED_LOOPBACK: IpAddress = + IpAddress::Ipv6((0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 0x0001)); - // Check that we sent and recieved our message! - assert_eq!(data, first_message); // Not guaranteed to work but should work in practice. + pub const fn new_loopback(family: IpAddressFamily) -> IpAddress { + match family { + IpAddressFamily::Ipv4 => Self::IPV4_LOOPBACK, + IpAddressFamily::Ipv6 => Self::IPV6_LOOPBACK, + } + } - // Another client - let client = tcp_create_socket::create_tcp_socket(family).unwrap(); - let client_sub = client.subscribe(); + pub const fn new_unspecified(family: IpAddressFamily) -> IpAddress { + match family { + IpAddressFamily::Ipv4 => Self::IPV4_UNSPECIFIED, + IpAddressFamily::Ipv6 => Self::IPV6_UNSPECIFIED, + } + } - client.start_connect(&net, addr).unwrap(); - poll::poll_one(&client_sub); - let (client_input, client_output) = client.finish_connect().unwrap(); + pub const fn family(&self) -> IpAddressFamily { + match self { + IpAddress::Ipv4(_) => IpAddressFamily::Ipv4, + IpAddress::Ipv6(_) => IpAddressFamily::Ipv6, + } + } +} - write(&client_output, second_message).unwrap(); +impl PartialEq for IpAddress { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Ipv4(left), Self::Ipv4(right)) => left == right, + (Self::Ipv6(left), Self::Ipv6(right)) => left == right, + _ => false, + } + } +} - drop(client_input); - drop(client_output); - drop(client_sub); - drop(client); +impl IpSocketAddress { + pub const fn new(ip: IpAddress, port: u16) -> IpSocketAddress { + match ip { + IpAddress::Ipv4(addr) => IpSocketAddress::Ipv4(Ipv4SocketAddress { + port: port, + address: addr, + }), + IpAddress::Ipv6(addr) => IpSocketAddress::Ipv6(Ipv6SocketAddress { + port: port, + address: addr, + flow_info: 0, + scope_id: 0, + }), + } + } - poll::poll_one(&sub); - let (accepted, input, output) = sock.accept().unwrap(); - let data = input.blocking_read(second_message.len() as u64).unwrap(); + pub const fn ip(&self) -> IpAddress { + match self { + IpSocketAddress::Ipv4(addr) => IpAddress::Ipv4(addr.address), + IpSocketAddress::Ipv6(addr) => IpAddress::Ipv6(addr.address), + } + } - drop(input); - drop(output); - drop(accepted); + pub const fn port(&self) -> u16 { + match self { + IpSocketAddress::Ipv4(addr) => addr.port, + IpSocketAddress::Ipv6(addr) => addr.port, + } + } - // Check that we sent and recieved our message! - assert_eq!(data, second_message); // Not guaranteed to work but should work in practice. + pub const fn family(&self) -> IpAddressFamily { + match self { + IpSocketAddress::Ipv4(_) => IpAddressFamily::Ipv4, + IpSocketAddress::Ipv6(_) => IpAddressFamily::Ipv6, + } + } } diff --git a/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit index da9b435d9ef9..8fc3074af6d5 100644 --- a/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit +++ b/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit @@ -25,9 +25,9 @@ interface ip-name-lookup { /// to `resolve-next-address` never returns `ok(none)`. This may change in the future. /// /// # Typical errors - /// - `invalid-name`: `name` is a syntactically invalid domain name. - /// - `invalid-name`: `name` is an IP address. - /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) + /// - `invalid-argument`: `name` is a syntactically invalid domain name. + /// - `invalid-argument`: `name` is an IP address. + /// - `not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) /// /// # References: /// - diff --git a/crates/wasi-http/wit/deps/sockets/network.wit b/crates/wasi-http/wit/deps/sockets/network.wit index 03755253b294..861ec673de68 100644 --- a/crates/wasi-http/wit/deps/sockets/network.wit +++ b/crates/wasi-http/wit/deps/sockets/network.wit @@ -14,6 +14,7 @@ interface network { /// - `access-denied` /// - `not-supported` /// - `out-of-memory` + /// - `concurrency-conflict` /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. enum error-code { @@ -32,6 +33,11 @@ interface network { /// POSIX equivalent: EOPNOTSUPP not-supported, + /// One of the arguments is invalid. + /// + /// POSIX equivalent: EINVAL + invalid-argument, + /// Not enough memory to complete the operation. /// /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY @@ -41,6 +47,8 @@ interface network { timeout, /// This operation is incompatible with another asynchronous operation that is already in progress. + /// + /// POSIX equivalent: EALREADY concurrency-conflict, /// Trying to finish an asynchronous operation that: @@ -56,72 +64,36 @@ interface network { would-block, - // ### IP ERRORS ### - - /// The specified address-family is not supported. - address-family-not-supported, - - /// An IPv4 address was passed to an IPv6 resource, or vice versa. - address-family-mismatch, - - /// The socket address is not a valid remote address. E.g. the IP address is set to INADDR_ANY, or the port is set to 0. - invalid-remote-address, - - /// The operation is only supported on IPv4 resources. - ipv4-only-operation, - - /// The operation is only supported on IPv6 resources. - ipv6-only-operation, - - // ### TCP & UDP SOCKET ERRORS ### + /// The operation is not valid in the socket's current state. + invalid-state, + /// A new socket resource could not be created because of a system limit. new-socket-limit, - /// The socket is already attached to another network. - already-attached, - - /// The socket is already bound. - already-bound, - - /// The socket is already in the Connection state. - already-connected, - - /// The socket is not bound to any local address. - not-bound, - - /// The socket is not in the Connection state. - not-connected, - /// A bind operation failed because the provided address is not an address that the `network` can bind to. address-not-bindable, - /// A bind operation failed because the provided address is already in use. + /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. address-in-use, - /// A bind operation failed because there are no ephemeral ports available. - ephemeral-ports-exhausted, - /// The remote address is not reachable remote-unreachable, // ### TCP SOCKET ERRORS ### - /// The socket is already in the Listener state. - already-listening, - - /// The socket is already in the Listener state. - not-listening, - /// The connection was forcefully rejected connection-refused, /// The connection was reset. connection-reset, + /// A connection was aborted. + connection-aborted, + // ### UDP SOCKET ERRORS ### datagram-too-large, @@ -129,9 +101,6 @@ interface network { // ### NAME LOOKUP ERRORS ### - /// The provided name is a syntactically invalid domain name. - invalid-name, - /// Name does not exist or has no suitable associated IP addresses. name-unresolvable, diff --git a/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit b/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit index b64cabba7993..a9a33738b20d 100644 --- a/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit +++ b/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit @@ -14,9 +14,8 @@ interface tcp-create-socket { /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. /// /// # Typical errors - /// - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP) - /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) - /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) /// /// # References /// - diff --git a/crates/wasi-http/wit/deps/sockets/tcp.wit b/crates/wasi-http/wit/deps/sockets/tcp.wit index 0ae7c05e8fcb..62a9068716b2 100644 --- a/crates/wasi-http/wit/deps/sockets/tcp.wit +++ b/crates/wasi-http/wit/deps/sockets/tcp.wit @@ -30,12 +30,13 @@ interface tcp { /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. /// /// # Typical `start` errors - /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) - /// - `already-bound`: The socket is already bound. (EINVAL) - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL) + /// - `invalid-state`: The socket is already bound. (EINVAL) /// /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) /// - `address-in-use`: Address is already in use. (EADDRINUSE) /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) /// - `not-in-progress`: A `bind` operation is not in progress. @@ -55,21 +56,33 @@ interface tcp { /// - the socket is transitioned into the Connection state /// - a pair of streams is returned that can be used to read & write to the connection /// + /// POSIX mentions: + /// > If connect() fails, the state of the socket is unspecified. Conforming applications should + /// > close the file descriptor and create a new socket before attempting to reconnect. + /// + /// WASI prescribes the following behavior: + /// - If `connect` fails because an input/state validation error, the socket should remain usable. + /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication. + /// Besides `drop`, any method after such a failure may return an error. + /// /// # Typical `start` errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) - /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. - /// - `already-connected`: The socket is already in the Connection state. (EISCONN) - /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos) + /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `invalid-state`: The socket is already in the Connection state. (EISCONN) + /// - `invalid-state`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) /// /// # Typical `finish` errors /// - `timeout`: Connection timed out. (ETIMEDOUT) /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) /// - `not-in-progress`: A `connect` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) /// @@ -90,13 +103,12 @@ interface tcp { /// - the socket must already be explicitly bound. /// /// # Typical `start` errors - /// - `not-bound`: The socket is not bound to any local address. (EDESTADDRREQ) - /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) - /// - `already-listening`: The socket is already in the Listener state. - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) + /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `invalid-state`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) + /// - `invalid-state`: The socket is already in the Listener state. /// /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) /// - `not-in-progress`: A `listen` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) /// @@ -110,16 +122,23 @@ interface tcp { /// Accept a new client socket. /// - /// The returned socket is bound and in the Connection state. + /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket: + /// - `address-family` + /// - `ipv6-only` + /// - `keep-alive` + /// - `no-delay` + /// - `unicast-hop-limit` + /// - `receive-buffer-size` + /// - `send-buffer-size` /// /// On success, this function returns the newly accepted client socket along with /// a pair of streams that can be used to read & write to the connection. /// /// # Typical errors - /// - `not-listening`: Socket is not in the Listener state. (EINVAL) - /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) - /// - /// Host implementations must skip over transient errors returned by the native accept syscall. + /// - `invalid-state`: Socket is not in the Listener state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) /// /// # References /// - @@ -130,8 +149,14 @@ interface tcp { /// Get the bound local address. /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. + /// - `invalid-state`: The socket is not bound to any local address. /// /// # References /// - @@ -140,10 +165,10 @@ interface tcp { /// - local-address: func() -> result; - /// Get the bound remote address. + /// Get the remote address. /// /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) /// /// # References /// - @@ -162,40 +187,35 @@ interface tcp { /// Equivalent to the IPV6_V6ONLY socket option. /// /// # Typical errors - /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. - /// - `already-bound`: (set) The socket is already bound. + /// - `invalid-state`: (set) The socket is already bound. + /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) ipv6-only: func() -> result; set-ipv6-only: func(value: bool) -> result<_, error-code>; /// Hints the desired listen queue size. Implementations are free to ignore this. /// /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. + /// - `invalid-state`: (set) The socket is already in the Connection state. set-listen-backlog-size: func(value: u64) -> result<_, error-code>; /// Equivalent to the SO_KEEPALIVE socket option. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) keep-alive: func() -> result; set-keep-alive: func(value: bool) -> result<_, error-code>; /// Equivalent to the TCP_NODELAY socket option. /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// The default value is `false`. no-delay: func() -> result; set-no-delay: func(value: bool) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `already-listening`: (set) The socket is already in the Listener state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + /// - `invalid-state`: (set) The socket is already in the Connection state. + /// - `invalid-state`: (set) The socket is already in the Listener state. unicast-hop-limit: func() -> result; set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; @@ -211,9 +231,8 @@ interface tcp { /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. /// /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `already-listening`: (set) The socket is already in the Listener state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// - `invalid-state`: (set) The socket is already in the Connection state. + /// - `invalid-state`: (set) The socket is already in the Listener state. receive-buffer-size: func() -> result; set-receive-buffer-size: func(value: u64) -> result<_, error-code>; send-buffer-size: func() -> result; @@ -237,7 +256,7 @@ interface tcp { /// The shutdown function does not close (drop) the socket. /// /// # Typical errors - /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) + /// - `invalid-state`: The socket is not in the Connection state. (ENOTCONN) /// /// # References /// - diff --git a/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit b/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit index 64d899456ca8..e026359fd90f 100644 --- a/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit +++ b/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit @@ -14,9 +14,8 @@ interface udp-create-socket { /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. /// /// # Typical errors - /// - `not-supported`: The host does not support UDP sockets. (EOPNOTSUPP) - /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) - /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) /// /// # References: /// - diff --git a/crates/wasi-http/wit/deps/sockets/udp.wit b/crates/wasi-http/wit/deps/sockets/udp.wit index a29250caa9af..7a9d7f72c4fa 100644 --- a/crates/wasi-http/wit/deps/sockets/udp.wit +++ b/crates/wasi-http/wit/deps/sockets/udp.wit @@ -31,12 +31,11 @@ interface udp { /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. /// /// # Typical `start` errors - /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) - /// - `already-bound`: The socket is already bound. (EINVAL) - /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-state`: The socket is already bound. (EINVAL) /// /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) /// - `address-in-use`: Address is already in use. (EADDRINUSE) /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) /// - `not-in-progress`: A `bind` operation is not in progress. @@ -63,14 +62,14 @@ interface udp { /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. /// /// # Typical `start` errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. - /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. /// /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) /// - `not-in-progress`: A `connect` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) /// @@ -89,7 +88,7 @@ interface udp { /// If `max-results` is 0, this function returns successfully with an empty list. /// /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. (EINVAL) + /// - `invalid-state`: The socket is not bound to any local address. (EINVAL) /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) /// @@ -119,11 +118,12 @@ interface udp { /// call `remote-address` to get their address. /// /// # Typical errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) - /// - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) + /// - `invalid-state`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) @@ -141,8 +141,14 @@ interface udp { /// Get the current bound address. /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. + /// - `invalid-state`: The socket is not bound to any local address. /// /// # References /// - @@ -154,7 +160,7 @@ interface udp { /// Get the address set with `connect`. /// /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) /// /// # References /// - @@ -173,17 +179,13 @@ interface udp { /// Equivalent to the IPV6_V6ONLY socket option. /// /// # Typical errors - /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. - /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. + /// - `invalid-state`: (set) The socket is already bound. /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) ipv6-only: func() -> result; set-ipv6-only: func(value: bool) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) unicast-hop-limit: func() -> result; set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; @@ -197,9 +199,6 @@ interface udp { /// for internal metadata structures. /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) receive-buffer-size: func() -> result; set-receive-buffer-size: func(value: u64) -> result<_, error-code>; send-buffer-size: func() -> result; diff --git a/crates/wasi/src/preview2/host/network.rs b/crates/wasi/src/preview2/host/network.rs index b2a8ff59a700..bd97df613820 100644 --- a/crates/wasi/src/preview2/host/network.rs +++ b/crates/wasi/src/preview2/host/network.rs @@ -3,6 +3,7 @@ use crate::preview2::bindings::sockets::network::{ Ipv6SocketAddress, }; use crate::preview2::{SocketError, WasiView}; +use rustix::io::Errno; use std::io; use wasmtime::component::Resource; @@ -23,47 +24,83 @@ impl crate::preview2::bindings::sockets::network::HostNetwork for T } impl From for ErrorCode { - fn from(error: io::Error) -> Self { - match error.kind() { - // Errors that we can directly map. - io::ErrorKind::PermissionDenied => ErrorCode::AccessDenied, - io::ErrorKind::ConnectionRefused => ErrorCode::ConnectionRefused, - io::ErrorKind::ConnectionReset => ErrorCode::ConnectionReset, - io::ErrorKind::NotConnected => ErrorCode::NotConnected, - io::ErrorKind::AddrInUse => ErrorCode::AddressInUse, - io::ErrorKind::AddrNotAvailable => ErrorCode::AddressNotBindable, - io::ErrorKind::WouldBlock => ErrorCode::WouldBlock, - io::ErrorKind::TimedOut => ErrorCode::Timeout, - io::ErrorKind::Unsupported => ErrorCode::NotSupported, - io::ErrorKind::OutOfMemory => ErrorCode::OutOfMemory, - - // Errors that don't correspond to a Rust `io::ErrorKind`. - io::ErrorKind::Other => match error.raw_os_error() { - Some(libc::ENOBUFS) | Some(libc::ENOMEM) => ErrorCode::OutOfMemory, - Some(libc::EOPNOTSUPP) => ErrorCode::NotSupported, - Some(libc::ENETUNREACH) | Some(libc::EHOSTUNREACH) | Some(libc::ENETDOWN) => { - ErrorCode::RemoteUnreachable - } - Some(libc::ECONNRESET) => ErrorCode::ConnectionReset, - Some(libc::ECONNREFUSED) => ErrorCode::ConnectionRefused, - Some(libc::EADDRINUSE) => ErrorCode::AddressInUse, - Some(_) | None => { - log::debug!("unknown I/O error: {error}"); - ErrorCode::Unknown - } - }, + fn from(value: io::Error) -> Self { + // Attempt the more detailed native error code first: + if let Some(errno) = Errno::from_io_error(&value) { + return errno.into(); + } + + match value.kind() { + std::io::ErrorKind::AddrInUse => ErrorCode::AddressInUse, + std::io::ErrorKind::AddrNotAvailable => ErrorCode::AddressNotBindable, + std::io::ErrorKind::ConnectionAborted => ErrorCode::ConnectionAborted, + std::io::ErrorKind::ConnectionRefused => ErrorCode::ConnectionRefused, + std::io::ErrorKind::ConnectionReset => ErrorCode::ConnectionReset, + std::io::ErrorKind::Interrupted => ErrorCode::WouldBlock, + std::io::ErrorKind::InvalidInput => ErrorCode::InvalidArgument, + std::io::ErrorKind::NotConnected => ErrorCode::InvalidState, + std::io::ErrorKind::OutOfMemory => ErrorCode::OutOfMemory, + std::io::ErrorKind::PermissionDenied => ErrorCode::AccessDenied, + std::io::ErrorKind::TimedOut => ErrorCode::Timeout, + std::io::ErrorKind::Unsupported => ErrorCode::NotSupported, + std::io::ErrorKind::WouldBlock => ErrorCode::WouldBlock, _ => { - log::debug!("unknown I/O error: {error}"); + log::debug!("unknown I/O error: {value}"); ErrorCode::Unknown } } } } -impl From for ErrorCode { - fn from(error: rustix::io::Errno) -> Self { - std::io::Error::from(error).into() +impl From for ErrorCode { + fn from(value: Errno) -> Self { + match value { + Errno::WOULDBLOCK => ErrorCode::WouldBlock, + #[allow(unreachable_patterns)] // EWOULDBLOCK and EAGAIN can have the same value. + Errno::AGAIN => ErrorCode::WouldBlock, + Errno::INTR => ErrorCode::WouldBlock, + #[cfg(not(windows))] + Errno::PERM => ErrorCode::AccessDenied, + Errno::ACCESS => ErrorCode::AccessDenied, + Errno::ADDRINUSE => ErrorCode::AddressInUse, + Errno::ADDRNOTAVAIL => ErrorCode::AddressNotBindable, + Errno::ALREADY => ErrorCode::ConcurrencyConflict, + Errno::TIMEDOUT => ErrorCode::Timeout, + Errno::CONNREFUSED => ErrorCode::ConnectionRefused, + Errno::CONNRESET => ErrorCode::ConnectionReset, + Errno::CONNABORTED => ErrorCode::ConnectionAborted, + Errno::INVAL => ErrorCode::InvalidArgument, + Errno::HOSTUNREACH => ErrorCode::RemoteUnreachable, + Errno::HOSTDOWN => ErrorCode::RemoteUnreachable, + Errno::NETDOWN => ErrorCode::RemoteUnreachable, + Errno::NETUNREACH => ErrorCode::RemoteUnreachable, + #[cfg(target_os = "linux")] + Errno::NONET => ErrorCode::RemoteUnreachable, + Errno::ISCONN => ErrorCode::InvalidState, + Errno::NOTCONN => ErrorCode::InvalidState, + Errno::DESTADDRREQ => ErrorCode::InvalidState, + #[cfg(not(windows))] + Errno::NFILE => ErrorCode::NewSocketLimit, + Errno::MFILE => ErrorCode::NewSocketLimit, + Errno::MSGSIZE => ErrorCode::DatagramTooLarge, + #[cfg(not(windows))] + Errno::NOMEM => ErrorCode::OutOfMemory, + Errno::NOBUFS => ErrorCode::OutOfMemory, + Errno::OPNOTSUPP => ErrorCode::NotSupported, + Errno::NOPROTOOPT => ErrorCode::NotSupported, + Errno::PFNOSUPPORT => ErrorCode::NotSupported, + Errno::PROTONOSUPPORT => ErrorCode::NotSupported, + Errno::PROTOTYPE => ErrorCode::NotSupported, + Errno::SOCKTNOSUPPORT => ErrorCode::NotSupported, + Errno::AFNOSUPPORT => ErrorCode::NotSupported, + + // FYI, EINPROGRESS should have already been handled by connect. + _ => { + log::debug!("unknown I/O error: {value}"); + ErrorCode::Unknown + } + } } } @@ -174,3 +211,12 @@ impl From for cap_net_ext::AddressFamily { } } } + +impl From for IpAddressFamily { + fn from(family: cap_net_ext::AddressFamily) -> Self { + match family { + cap_net_ext::AddressFamily::Ipv4 => IpAddressFamily::Ipv4, + cap_net_ext::AddressFamily::Ipv6 => IpAddressFamily::Ipv6, + } + } +} diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index 0cf88b9100a4..82e824b3a141 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -1,15 +1,19 @@ -use crate::preview2::bindings::{ - io::streams::{InputStream, OutputStream}, - sockets::network::{ErrorCode, IpAddressFamily, IpSocketAddress, Network}, - sockets::tcp::{self, ShutdownType}, -}; use crate::preview2::tcp::{TcpSocket, TcpState}; +use crate::preview2::{ + bindings::{ + io::streams::{InputStream, OutputStream}, + sockets::network::{ErrorCode, IpAddressFamily, IpSocketAddress, Network}, + sockets::tcp::{self, ShutdownType}, + }, + tcp::SocketAddressFamily, +}; use crate::preview2::{Pollable, SocketResult, WasiView}; use cap_net_ext::{Blocking, PoolExt, TcpListenerExt}; use cap_std::net::TcpListener; use io_lifetimes::AsSocketlike; use rustix::io::Errno; use rustix::net::sockopt; +use std::net::{IpAddr, Ipv6Addr, SocketAddr}; use tokio::io::Interest; use wasmtime::component::Resource; @@ -24,19 +28,29 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { ) -> SocketResult<()> { let table = self.table_mut(); let socket = table.get_resource(&this)?; + let network = table.get_resource(&network)?; + let local_address: SocketAddr = local_address.into(); match socket.tcp_state { TcpState::Default => {} - _ => return Err(ErrorCode::NotInProgress.into()), + TcpState::BindStarted => return Err(ErrorCode::ConcurrencyConflict.into()), + _ => return Err(ErrorCode::InvalidState.into()), } - let network = table.get_resource(&network)?; + validate_unicast(&local_address)?; + validate_address_family(&socket, &local_address)?; + let binder = network.pool.tcp_binder(local_address)?; // Perform the OS bind call. - binder.bind_existing_tcp_listener( - &*socket.tcp_socket().as_socketlike_view::(), - )?; + binder + .bind_existing_tcp_listener(&*socket.tcp_socket().as_socketlike_view::()) + .map_err(|error| match Errno::from_io_error(&error) { + Some(Errno::AFNOSUPPORT) => ErrorCode::InvalidArgument, // Just in case our own validations weren't sufficient. + #[cfg(windows)] + Some(Errno::NOBUFS) => ErrorCode::AddressInUse, // Windows returns WSAENOBUFS when the ephemeral ports have been exhausted. + _ => ErrorCode::from(error), + })?; let socket = table.get_resource_mut(&this)?; socket.tcp_state = TcpState::BindStarted; @@ -67,14 +81,25 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { let table = self.table_mut(); let r = { let socket = table.get_resource(&this)?; + let network = table.get_resource(&network)?; + let remote_address: SocketAddr = remote_address.into(); match socket.tcp_state { TcpState::Default => {} - TcpState::Connected => return Err(ErrorCode::AlreadyConnected.into()), - _ => return Err(ErrorCode::NotInProgress.into()), + TcpState::Bound + | TcpState::Connected + | TcpState::ConnectFailed + | TcpState::Listening => return Err(ErrorCode::InvalidState.into()), + TcpState::Connecting + | TcpState::ConnectReady + | TcpState::ListenStarted + | TcpState::BindStarted => return Err(ErrorCode::ConcurrencyConflict.into()), } - let network = table.get_resource(&network)?; + validate_unicast(&remote_address)?; + validate_remote_address(&remote_address)?; + validate_address_family(&socket, &remote_address)?; + let connecter = network.pool.tcp_connecter(remote_address)?; // Do an OS `connect`. Our socket is non-blocking, so it'll either... @@ -93,9 +118,14 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { return Ok(()); } // continue in progress, - Err(err) if err.raw_os_error() == Some(INPROGRESS.raw_os_error()) => {} + Err(err) if Errno::from_io_error(&err) == Some(INPROGRESS) => {} // or fail immediately. - Err(err) => return Err(err.into()), + Err(err) => { + return Err(match Errno::from_io_error(&err) { + Some(Errno::AFNOSUPPORT) => ErrorCode::InvalidArgument.into(), // Just in case our own validations weren't sufficient. + _ => err.into(), + }); + } } let socket = table.get_resource_mut(&this)?; @@ -131,7 +161,10 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { // Check whether the connect succeeded. match sockopt::get_socket_error(socket.tcp_socket()) { Ok(Ok(())) => {} - Err(err) | Ok(Err(err)) => return Err(err.into()), + Err(err) | Ok(Err(err)) => { + socket.tcp_state = TcpState::ConnectFailed; + return Err(err.into()); + } } } _ => return Err(ErrorCode::NotInProgress.into()), @@ -151,15 +184,25 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { match socket.tcp_state { TcpState::Bound => {} - TcpState::ListenStarted => return Err(ErrorCode::AlreadyListening.into()), - TcpState::Connected => return Err(ErrorCode::AlreadyConnected.into()), - _ => return Err(ErrorCode::NotInProgress.into()), + TcpState::Default + | TcpState::Connected + | TcpState::ConnectFailed + | TcpState::Listening => return Err(ErrorCode::InvalidState.into()), + TcpState::ListenStarted + | TcpState::Connecting + | TcpState::ConnectReady + | TcpState::BindStarted => return Err(ErrorCode::ConcurrencyConflict.into()), } socket .tcp_socket() .as_socketlike_view::() - .listen(socket.listen_backlog_size)?; + .listen(socket.listen_backlog_size) + .map_err(|error| match Errno::from_io_error(&error) { + #[cfg(windows)] + Some(Errno::MFILE) => ErrorCode::OutOfMemory, // We're not trying to create a new socket. Rewrite it to less surprising error code. + _ => ErrorCode::from(error), + })?; socket.tcp_state = TcpState::ListenStarted; @@ -193,18 +236,63 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { match socket.tcp_state { TcpState::Listening => {} - TcpState::Connected => return Err(ErrorCode::AlreadyConnected.into()), - _ => return Err(ErrorCode::NotInProgress.into()), + _ => return Err(ErrorCode::InvalidState.into()), } // Do the OS accept call. let tcp_socket = socket.tcp_socket(); - let (connection, _addr) = tcp_socket.try_io(Interest::READABLE, || { - tcp_socket - .as_socketlike_view::() - .accept_with(Blocking::No) - })?; - let mut tcp_socket = TcpSocket::from_tcp_stream(connection)?; + let (connection, _addr) = tcp_socket + .try_io(Interest::READABLE, || { + tcp_socket + .as_socketlike_view::() + .accept_with(Blocking::No) + }) + .map_err(|error| match Errno::from_io_error(&error) { + #[cfg(windows)] + Some(Errno::INPROGRESS) => ErrorCode::WouldBlock, // "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function." + + // Normalize Linux' non-standard behavior. + // "Linux accept() passes already-pending network errors on the new socket as an error code from accept(). This behavior differs from other BSD socket implementations." + #[cfg(target_os = "linux")] + Some( + Errno::CONNRESET + | Errno::NETRESET + | Errno::HOSTUNREACH + | Errno::HOSTDOWN + | Errno::NETDOWN + | Errno::NETUNREACH + | Errno::PROTO + | Errno::NOPROTOOPT + | Errno::NONET + | Errno::OPNOTSUPP, + ) => ErrorCode::ConnectionAborted, + + _ => ErrorCode::from(error), + })?; + + #[cfg(target_os = "macos")] + { + // Manually inherit socket options from listener. We only have to + // do this on platforms that don't already do this automatically + // and only if a specific value was explicitly set on the listener. + + if let Some(size) = socket.receive_buffer_size { + _ = sockopt::set_socket_recv_buffer_size(&connection, size); // Ignore potential error. + } + + if let Some(size) = socket.send_buffer_size { + _ = sockopt::set_socket_send_buffer_size(&connection, size); // Ignore potential error. + } + + // For some reason, IP_TTL is inherited, but IPV6_UNICAST_HOPS isn't. + if let (SocketAddressFamily::Ipv6 { .. }, Some(ttl)) = (socket.family, socket.hop_limit) + { + _ = sockopt::set_ipv6_unicast_hops(&connection, Some(ttl)); + // Ignore potential error. + } + } + + let mut tcp_socket = TcpSocket::from_tcp_stream(connection, socket.family)?; // Mark the socket as connected so that we can exit early from methods like `start-bind`. tcp_socket.tcp_state = TcpState::Connected; @@ -222,6 +310,13 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { fn local_address(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; + + match socket.tcp_state { + TcpState::Default => return Err(ErrorCode::InvalidState.into()), + TcpState::BindStarted => return Err(ErrorCode::ConcurrencyConflict.into()), + _ => {} + } + let addr = socket .tcp_socket() .as_socketlike_view::() @@ -232,6 +327,12 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { fn remote_address(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; + + match socket.tcp_state { + TcpState::Connected => {} + _ => return Err(ErrorCode::InvalidState.into()), + } + let addr = socket .tcp_socket() .as_socketlike_view::() @@ -246,57 +347,41 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { let table = self.table(); let socket = table.get_resource(&this)?; - // If `SO_DOMAIN` is available, use it. - // - // TODO: OpenBSD also supports this; upstream PRs are posted. - #[cfg(not(any( - windows, - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" - )))] - { - use rustix::net::AddressFamily; - - let family = sockopt::get_socket_domain(socket.tcp_socket())?; - let family = match family { - AddressFamily::INET => IpAddressFamily::Ipv4, - AddressFamily::INET6 => IpAddressFamily::Ipv6, - _ => return Err(ErrorCode::NotSupported.into()), - }; - Ok(family) - } - - // When `SO_DOMAIN` is not available, emulate it. - #[cfg(any( - windows, - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" - ))] - { - if let Ok(_) = sockopt::get_ipv6_unicast_hops(socket.tcp_socket()) { - return Ok(IpAddressFamily::Ipv6); - } - if let Ok(_) = sockopt::get_ip_ttl(socket.tcp_socket()) { - return Ok(IpAddressFamily::Ipv4); - } - Err(ErrorCode::NotSupported.into()) + match socket.family { + SocketAddressFamily::Ipv4 => Ok(IpAddressFamily::Ipv4), + SocketAddressFamily::Ipv6 { .. } => Ok(IpAddressFamily::Ipv6), } } fn ipv6_only(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; - Ok(sockopt::get_ipv6_v6only(socket.tcp_socket())?) + + // Instead of just calling the OS we return our own internal state, because + // MacOS doesn't propogate the V6ONLY state on to accepted client sockets. + + match socket.family { + SocketAddressFamily::Ipv4 => Err(ErrorCode::NotSupported.into()), + SocketAddressFamily::Ipv6 { v6only } => Ok(v6only), + } } fn set_ipv6_only(&mut self, this: Resource, value: bool) -> SocketResult<()> { - let table = self.table(); - let socket = table.get_resource(&this)?; - Ok(sockopt::set_ipv6_v6only(socket.tcp_socket(), value)?) + let table = self.table_mut(); + let socket = table.get_resource_mut(&this)?; + + match socket.family { + SocketAddressFamily::Ipv4 => Err(ErrorCode::NotSupported.into()), + SocketAddressFamily::Ipv6 { .. } => match socket.tcp_state { + TcpState::Default => { + sockopt::set_ipv6_v6only(socket.tcp_socket(), value)?; + socket.family = SocketAddressFamily::Ipv6 { v6only: value }; + Ok(()) + } + TcpState::BindStarted => Err(ErrorCode::ConcurrencyConflict.into()), + _ => Err(ErrorCode::InvalidState.into()), + }, + } } fn set_listen_backlog_size( @@ -328,13 +413,13 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { // Not all platforms support this. We'll only update our own value if the OS supports changing the backlog size after the fact. rustix::net::listen(socket.tcp_socket(), value) - .map_err(|_| ErrorCode::AlreadyListening)?; + .map_err(|_| ErrorCode::NotSupported)?; socket.listen_backlog_size = Some(value); Ok(()) } - TcpState::Connected => Err(ErrorCode::AlreadyConnected.into()), + TcpState::Connected | TcpState::ConnectFailed => Err(ErrorCode::InvalidState.into()), TcpState::Connecting | TcpState::ConnectReady | TcpState::ListenStarted => { Err(ErrorCode::ConcurrencyConflict.into()) } @@ -369,17 +454,16 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { let table = self.table(); let socket = table.get_resource(&this)?; - // We don't track whether the socket is IPv4 or IPv6 so try one and - // fall back to the other. - match sockopt::get_ipv6_unicast_hops(socket.tcp_socket()) { - Ok(value) => Ok(value), - Err(Errno::NOPROTOOPT) => { - let value = sockopt::get_ip_ttl(socket.tcp_socket())?; - let value = value.try_into().unwrap(); - Ok(value) + let ttl = match socket.family { + SocketAddressFamily::Ipv4 => sockopt::get_ip_ttl(socket.tcp_socket())? + .try_into() + .unwrap(), + SocketAddressFamily::Ipv6 { .. } => { + sockopt::get_ipv6_unicast_hops(socket.tcp_socket())? } - Err(err) => Err(err.into()), - } + }; + + Ok(ttl) } fn set_unicast_hop_limit( @@ -387,22 +471,37 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, value: u8, ) -> SocketResult<()> { - let table = self.table(); - let socket = table.get_resource(&this)?; + let table = self.table_mut(); + let socket = table.get_resource_mut(&this)?; + + if value == 0 { + // A well-behaved IP application should never send out new packets with TTL 0. + // We validate the value ourselves because OS'es are not consistent in this. + // On Linux the validation is even inconsistent between their IPv4 and IPv6 implementation. + return Err(ErrorCode::InvalidArgument.into()); + } + + match socket.family { + SocketAddressFamily::Ipv4 => sockopt::set_ip_ttl(socket.tcp_socket(), value.into())?, + SocketAddressFamily::Ipv6 { .. } => { + sockopt::set_ipv6_unicast_hops(socket.tcp_socket(), Some(value))? + } + } - // We don't track whether the socket is IPv4 or IPv6 so try one and - // fall back to the other. - match sockopt::set_ipv6_unicast_hops(socket.tcp_socket(), Some(value)) { - Ok(()) => Ok(()), - Err(Errno::NOPROTOOPT) => Ok(sockopt::set_ip_ttl(socket.tcp_socket(), value.into())?), - Err(err) => Err(err.into()), + #[cfg(target_os = "macos")] + { + socket.hop_limit = Some(value); } + + Ok(()) } fn receive_buffer_size(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; - Ok(sockopt::get_socket_recv_buffer_size(socket.tcp_socket())? as u64) + + let value = sockopt::get_socket_recv_buffer_size(socket.tcp_socket())? as u64; + Ok(normalize_getsockopt_buffer_size(value)) } fn set_receive_buffer_size( @@ -410,19 +509,37 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, value: u64, ) -> SocketResult<()> { - let table = self.table(); - let socket = table.get_resource(&this)?; - let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; - Ok(sockopt::set_socket_recv_buffer_size( - socket.tcp_socket(), - value, - )?) + let table = self.table_mut(); + let socket = table.get_resource_mut(&this)?; + let value = normalize_setsockopt_buffer_size(value); + + match sockopt::set_socket_recv_buffer_size(socket.tcp_socket(), value) { + // Most platforms (Linux, Windows, Fuchsia, Solaris, Illumos, Haiku, ESP-IDF, ..and more?) treat the value + // passed to SO_SNDBUF/SO_RCVBUF as a performance tuning hint and silently clamp the input if it exceeds + // their capability. + // As far as I can see, only the *BSD family views this option as a hard requirement and fails when the + // value is out of range. We normalize this behavior in favor of the more commonly understood + // "performance hint" semantics. In other words; even ENOBUFS is "Ok". + // A future improvement could be to query the corresponding sysctl on *BSD platforms and clamp the input + // `size` ourselves, to completely close the gap with other platforms. + Err(Errno::NOBUFS) => Ok(()), + r => r, + }?; + + #[cfg(target_os = "macos")] + { + socket.receive_buffer_size = Some(value); + } + + Ok(()) } fn send_buffer_size(&mut self, this: Resource) -> SocketResult { let table = self.table(); let socket = table.get_resource(&this)?; - Ok(sockopt::get_socket_send_buffer_size(socket.tcp_socket())? as u64) + + let value = sockopt::get_socket_send_buffer_size(socket.tcp_socket())? as u64; + Ok(normalize_getsockopt_buffer_size(value)) } fn set_send_buffer_size( @@ -430,13 +547,21 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { this: Resource, value: u64, ) -> SocketResult<()> { - let table = self.table(); - let socket = table.get_resource(&this)?; - let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; - Ok(sockopt::set_socket_send_buffer_size( - socket.tcp_socket(), - value, - )?) + let table = self.table_mut(); + let socket = table.get_resource_mut(&this)?; + let value = normalize_setsockopt_buffer_size(value); + + match sockopt::set_socket_send_buffer_size(socket.tcp_socket(), value) { + Err(Errno::NOBUFS) => Ok(()), // See `set_receive_buffer_size` + r => r, + }?; + + #[cfg(target_os = "macos")] + { + socket.send_buffer_size = Some(value); + } + + Ok(()) } fn subscribe(&mut self, this: Resource) -> anyhow::Result> { @@ -451,6 +576,14 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { let table = self.table(); let socket = table.get_resource(&this)?; + match socket.tcp_state { + TcpState::Connected => {} + TcpState::Connecting | TcpState::ConnectReady => { + return Err(ErrorCode::ConcurrencyConflict.into()) + } + _ => return Err(ErrorCode::InvalidState.into()), + } + let how = match shutdown_type { ShutdownType::Receive => std::net::Shutdown::Read, ShutdownType::Send => std::net::Shutdown::Write, @@ -479,6 +612,7 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { | TcpState::BindStarted | TcpState::Bound | TcpState::ListenStarted + | TcpState::ConnectFailed | TcpState::ConnectReady => {} TcpState::Listening | TcpState::Connecting | TcpState::Connected => { @@ -505,3 +639,89 @@ const INPROGRESS: Errno = Errno::INPROGRESS; // #[cfg(windows)] const INPROGRESS: Errno = Errno::WOULDBLOCK; + +fn validate_unicast(addr: &SocketAddr) -> SocketResult<()> { + match to_canonical(&addr.ip()) { + IpAddr::V4(ipv4) => { + if ipv4.is_multicast() || ipv4.is_broadcast() { + Err(ErrorCode::InvalidArgument.into()) + } else { + Ok(()) + } + } + IpAddr::V6(ipv6) => { + if ipv6.is_multicast() { + Err(ErrorCode::InvalidArgument.into()) + } else { + Ok(()) + } + } + } +} + +fn validate_remote_address(addr: &SocketAddr) -> SocketResult<()> { + if to_canonical(&addr.ip()).is_unspecified() { + return Err(ErrorCode::InvalidArgument.into()); + } + + if addr.port() == 0 { + return Err(ErrorCode::InvalidArgument.into()); + } + + Ok(()) +} + +fn validate_address_family(socket: &TcpSocket, addr: &SocketAddr) -> SocketResult<()> { + match (socket.family, addr.ip()) { + (SocketAddressFamily::Ipv4, IpAddr::V4(_)) => Ok(()), + (SocketAddressFamily::Ipv6 { v6only }, IpAddr::V6(ipv6)) => { + if is_deprecated_ipv4_compatible(&ipv6) { + // Reject IPv4-*compatible* IPv6 addresses. They have been deprecated + // since 2006, OS handling of them is inconsistent and our own + // validations don't take them into account either. + // Note that these are not the same as IPv4-*mapped* IPv6 addresses. + Err(ErrorCode::InvalidArgument.into()) + } else if v6only && ipv6.to_ipv4_mapped().is_some() { + Err(ErrorCode::InvalidArgument.into()) + } else { + Ok(()) + } + } + _ => Err(ErrorCode::InvalidArgument.into()), + } +} + +// Can be removed once `IpAddr::to_canonical` becomes stable. +fn to_canonical(addr: &IpAddr) -> IpAddr { + match addr { + IpAddr::V4(ipv4) => IpAddr::V4(*ipv4), + IpAddr::V6(ipv6) => { + if let Some(ipv4) = ipv6.to_ipv4_mapped() { + IpAddr::V4(ipv4) + } else { + IpAddr::V6(*ipv6) + } + } + } +} + +fn is_deprecated_ipv4_compatible(addr: &Ipv6Addr) -> bool { + matches!(addr.segments(), [0, 0, 0, 0, 0, 0, _, _]) + && *addr != Ipv6Addr::UNSPECIFIED + && *addr != Ipv6Addr::LOCALHOST +} + +fn normalize_setsockopt_buffer_size(value: u64) -> usize { + value.clamp(1, i32::MAX as u64).try_into().unwrap() +} + +fn normalize_getsockopt_buffer_size(value: u64) -> u64 { + if cfg!(target_os = "linux") { + // Linux doubles the value passed to setsockopt to allow space for bookkeeping overhead. + // getsockopt returns this internally doubled value. + // We'll half the value to at least get it back into the same ballpark that the application requested it in. + value / 2 + } else { + value + } +} diff --git a/crates/wasi/src/preview2/ip_name_lookup.rs b/crates/wasi/src/preview2/ip_name_lookup.rs index ce7cde93b9b9..e3f9e1a61b15 100644 --- a/crates/wasi/src/preview2/ip_name_lookup.rs +++ b/crates/wasi/src/preview2/ip_name_lookup.rs @@ -3,7 +3,6 @@ use crate::preview2::bindings::sockets::network::{ErrorCode, IpAddress, IpAddres use crate::preview2::poll::{subscribe, Pollable, Subscribe}; use crate::preview2::{spawn_blocking, AbortOnDropJoinHandle, SocketError, WasiView}; use anyhow::Result; -use std::io; use std::mem; use std::net::{SocketAddr, ToSocketAddrs}; use std::pin::Pin; @@ -11,8 +10,8 @@ use std::vec; use wasmtime::component::Resource; pub enum ResolveAddressStream { - Waiting(AbortOnDropJoinHandle>>), - Done(io::Result>), + Waiting(AbortOnDropJoinHandle, SocketError>>), + Done(Result, SocketError>), } #[async_trait::async_trait] @@ -29,10 +28,10 @@ impl Host for T { // `Host::parse` serves us two functions: // 1. validate the input is not an IP address, // 2. convert unicode domains to punycode. - let name = match url::Host::parse(&name).map_err(|_| ErrorCode::InvalidName)? { + let name = match url::Host::parse(&name).map_err(|_| ErrorCode::InvalidArgument)? { url::Host::Domain(name) => name, - url::Host::Ipv4(_) => return Err(ErrorCode::InvalidName.into()), - url::Host::Ipv6(_) => return Err(ErrorCode::InvalidName.into()), + url::Host::Ipv4(_) => return Err(ErrorCode::InvalidArgument.into()), + url::Host::Ipv6(_) => return Err(ErrorCode::InvalidArgument.into()), }; if !network.allow_ip_name_lookup { @@ -48,8 +47,10 @@ impl Host for T { // the usage of the `ToSocketAddrs` trait. This blocks the current // thread, so use `spawn_blocking`. Finally note that this is only // resolving names, not ports, so force the port to be 0. - let task = spawn_blocking(move || -> io::Result> { - let result = (name.as_str(), 0).to_socket_addrs()?; + let task = spawn_blocking(move || -> Result, SocketError> { + let result = (name.as_str(), 0) + .to_socket_addrs() + .map_err(|_| ErrorCode::NameUnresolvable)?; // If/when we use `getaddrinfo` directly, map the error properly. Ok(result .filter_map(|addr| { // In lieu of preventing these addresses from being resolved @@ -98,12 +99,6 @@ impl HostResolveAddressStream for T { } } ResolveAddressStream::Done(slot @ Err(_)) => { - // TODO: this `?` is what converts `io::Error` into `Error` - // and the conversion is not great right now. The standard - // library doesn't expose a ton of information through the - // return value of `getaddrinfo` right now so supporting a - // richer conversion here will probably require calling - // `getaddrinfo` directly. mem::replace(slot, Ok(Vec::new().into_iter()))?; unreachable!(); } diff --git a/crates/wasi/src/preview2/tcp.rs b/crates/wasi/src/preview2/tcp.rs index 434eeb3aeead..dd782c4c66ce 100644 --- a/crates/wasi/src/preview2/tcp.rs +++ b/crates/wasi/src/preview2/tcp.rs @@ -6,6 +6,7 @@ use anyhow::{Error, Result}; use cap_net_ext::{AddressFamily, Blocking, TcpListenerExt}; use cap_std::net::TcpListener; use io_lifetimes::raw::{FromRawSocketlike, IntoRawSocketlike}; +use rustix::net::sockopt; use std::io; use std::mem; use std::sync::Arc; @@ -38,6 +39,9 @@ pub(crate) enum TcpState { /// An outgoing connection is ready to be established. ConnectReady, + /// An outgoing connection was attempted but failed. + ConnectFailed, + /// An outgoing connection has been established. Connected, } @@ -56,6 +60,24 @@ pub struct TcpSocket { /// The desired listen queue size. Set to None to use the system's default. pub(crate) listen_backlog_size: Option, + + pub(crate) family: SocketAddressFamily, + + /// The manually configured buffer size. `None` means: no preference, use system default. + #[cfg(target_os = "macos")] + pub(crate) receive_buffer_size: Option, + /// The manually configured buffer size. `None` means: no preference, use system default. + #[cfg(target_os = "macos")] + pub(crate) send_buffer_size: Option, + /// The manually configured TTL. `None` means: no preference, use system default. + #[cfg(target_os = "macos")] + pub(crate) hop_limit: Option, +} + +#[derive(Copy, Clone)] +pub(crate) enum SocketAddressFamily { + Ipv4, + Ipv6 { v6only: bool }, } pub(crate) struct TcpReadStream { @@ -243,18 +265,32 @@ impl TcpSocket { // Create a new host socket and set it to non-blocking, which is needed // by our async implementation. let tcp_listener = TcpListener::new(family, Blocking::No)?; - Self::from_tcp_listener(tcp_listener) + + let socket_address_family = match family { + AddressFamily::Ipv4 => SocketAddressFamily::Ipv4, + AddressFamily::Ipv6 => SocketAddressFamily::Ipv6 { + v6only: sockopt::get_ipv6_v6only(&tcp_listener)?, + }, + }; + + Self::from_tcp_listener(tcp_listener, socket_address_family) } /// Create a `TcpSocket` from an existing socket. /// /// The socket must be in non-blocking mode. - pub fn from_tcp_stream(tcp_socket: cap_std::net::TcpStream) -> io::Result { + pub(crate) fn from_tcp_stream( + tcp_socket: cap_std::net::TcpStream, + family: SocketAddressFamily, + ) -> io::Result { let tcp_listener = TcpListener::from(rustix::fd::OwnedFd::from(tcp_socket)); - Self::from_tcp_listener(tcp_listener) + Self::from_tcp_listener(tcp_listener, family) } - pub fn from_tcp_listener(tcp_listener: cap_std::net::TcpListener) -> io::Result { + pub(crate) fn from_tcp_listener( + tcp_listener: cap_std::net::TcpListener, + family: SocketAddressFamily, + ) -> io::Result { let fd = tcp_listener.into_raw_socketlike(); let std_stream = unsafe { std::net::TcpStream::from_raw_socketlike(fd) }; let stream = with_ambient_tokio_runtime(|| tokio::net::TcpStream::try_from(std_stream))?; @@ -263,6 +299,13 @@ impl TcpSocket { inner: Arc::new(stream), tcp_state: TcpState::Default, listen_backlog_size: None, + family, + #[cfg(target_os = "macos")] + receive_buffer_size: None, + #[cfg(target_os = "macos")] + send_buffer_size: None, + #[cfg(target_os = "macos")] + hop_limit: None, }) } diff --git a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit index da9b435d9ef9..8fc3074af6d5 100644 --- a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit +++ b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit @@ -25,9 +25,9 @@ interface ip-name-lookup { /// to `resolve-next-address` never returns `ok(none)`. This may change in the future. /// /// # Typical errors - /// - `invalid-name`: `name` is a syntactically invalid domain name. - /// - `invalid-name`: `name` is an IP address. - /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) + /// - `invalid-argument`: `name` is a syntactically invalid domain name. + /// - `invalid-argument`: `name` is an IP address. + /// - `not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) /// /// # References: /// - diff --git a/crates/wasi/wit/deps/sockets/network.wit b/crates/wasi/wit/deps/sockets/network.wit index 03755253b294..861ec673de68 100644 --- a/crates/wasi/wit/deps/sockets/network.wit +++ b/crates/wasi/wit/deps/sockets/network.wit @@ -14,6 +14,7 @@ interface network { /// - `access-denied` /// - `not-supported` /// - `out-of-memory` + /// - `concurrency-conflict` /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. enum error-code { @@ -32,6 +33,11 @@ interface network { /// POSIX equivalent: EOPNOTSUPP not-supported, + /// One of the arguments is invalid. + /// + /// POSIX equivalent: EINVAL + invalid-argument, + /// Not enough memory to complete the operation. /// /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY @@ -41,6 +47,8 @@ interface network { timeout, /// This operation is incompatible with another asynchronous operation that is already in progress. + /// + /// POSIX equivalent: EALREADY concurrency-conflict, /// Trying to finish an asynchronous operation that: @@ -56,72 +64,36 @@ interface network { would-block, - // ### IP ERRORS ### - - /// The specified address-family is not supported. - address-family-not-supported, - - /// An IPv4 address was passed to an IPv6 resource, or vice versa. - address-family-mismatch, - - /// The socket address is not a valid remote address. E.g. the IP address is set to INADDR_ANY, or the port is set to 0. - invalid-remote-address, - - /// The operation is only supported on IPv4 resources. - ipv4-only-operation, - - /// The operation is only supported on IPv6 resources. - ipv6-only-operation, - - // ### TCP & UDP SOCKET ERRORS ### + /// The operation is not valid in the socket's current state. + invalid-state, + /// A new socket resource could not be created because of a system limit. new-socket-limit, - /// The socket is already attached to another network. - already-attached, - - /// The socket is already bound. - already-bound, - - /// The socket is already in the Connection state. - already-connected, - - /// The socket is not bound to any local address. - not-bound, - - /// The socket is not in the Connection state. - not-connected, - /// A bind operation failed because the provided address is not an address that the `network` can bind to. address-not-bindable, - /// A bind operation failed because the provided address is already in use. + /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. address-in-use, - /// A bind operation failed because there are no ephemeral ports available. - ephemeral-ports-exhausted, - /// The remote address is not reachable remote-unreachable, // ### TCP SOCKET ERRORS ### - /// The socket is already in the Listener state. - already-listening, - - /// The socket is already in the Listener state. - not-listening, - /// The connection was forcefully rejected connection-refused, /// The connection was reset. connection-reset, + /// A connection was aborted. + connection-aborted, + // ### UDP SOCKET ERRORS ### datagram-too-large, @@ -129,9 +101,6 @@ interface network { // ### NAME LOOKUP ERRORS ### - /// The provided name is a syntactically invalid domain name. - invalid-name, - /// Name does not exist or has no suitable associated IP addresses. name-unresolvable, diff --git a/crates/wasi/wit/deps/sockets/tcp-create-socket.wit b/crates/wasi/wit/deps/sockets/tcp-create-socket.wit index b64cabba7993..a9a33738b20d 100644 --- a/crates/wasi/wit/deps/sockets/tcp-create-socket.wit +++ b/crates/wasi/wit/deps/sockets/tcp-create-socket.wit @@ -14,9 +14,8 @@ interface tcp-create-socket { /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. /// /// # Typical errors - /// - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP) - /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) - /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) /// /// # References /// - diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit index 0ae7c05e8fcb..62a9068716b2 100644 --- a/crates/wasi/wit/deps/sockets/tcp.wit +++ b/crates/wasi/wit/deps/sockets/tcp.wit @@ -30,12 +30,13 @@ interface tcp { /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. /// /// # Typical `start` errors - /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) - /// - `already-bound`: The socket is already bound. (EINVAL) - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL) + /// - `invalid-state`: The socket is already bound. (EINVAL) /// /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) /// - `address-in-use`: Address is already in use. (EADDRINUSE) /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) /// - `not-in-progress`: A `bind` operation is not in progress. @@ -55,21 +56,33 @@ interface tcp { /// - the socket is transitioned into the Connection state /// - a pair of streams is returned that can be used to read & write to the connection /// + /// POSIX mentions: + /// > If connect() fails, the state of the socket is unspecified. Conforming applications should + /// > close the file descriptor and create a new socket before attempting to reconnect. + /// + /// WASI prescribes the following behavior: + /// - If `connect` fails because an input/state validation error, the socket should remain usable. + /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication. + /// Besides `drop`, any method after such a failure may return an error. + /// /// # Typical `start` errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) - /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. - /// - `already-connected`: The socket is already in the Connection state. (EISCONN) - /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos) + /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `invalid-state`: The socket is already in the Connection state. (EISCONN) + /// - `invalid-state`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) /// /// # Typical `finish` errors /// - `timeout`: Connection timed out. (ETIMEDOUT) /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) /// - `not-in-progress`: A `connect` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) /// @@ -90,13 +103,12 @@ interface tcp { /// - the socket must already be explicitly bound. /// /// # Typical `start` errors - /// - `not-bound`: The socket is not bound to any local address. (EDESTADDRREQ) - /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) - /// - `already-listening`: The socket is already in the Listener state. - /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) + /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `invalid-state`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) + /// - `invalid-state`: The socket is already in the Listener state. /// /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) /// - `not-in-progress`: A `listen` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) /// @@ -110,16 +122,23 @@ interface tcp { /// Accept a new client socket. /// - /// The returned socket is bound and in the Connection state. + /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket: + /// - `address-family` + /// - `ipv6-only` + /// - `keep-alive` + /// - `no-delay` + /// - `unicast-hop-limit` + /// - `receive-buffer-size` + /// - `send-buffer-size` /// /// On success, this function returns the newly accepted client socket along with /// a pair of streams that can be used to read & write to the connection. /// /// # Typical errors - /// - `not-listening`: Socket is not in the Listener state. (EINVAL) - /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) - /// - /// Host implementations must skip over transient errors returned by the native accept syscall. + /// - `invalid-state`: Socket is not in the Listener state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) /// /// # References /// - @@ -130,8 +149,14 @@ interface tcp { /// Get the bound local address. /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. + /// - `invalid-state`: The socket is not bound to any local address. /// /// # References /// - @@ -140,10 +165,10 @@ interface tcp { /// - local-address: func() -> result; - /// Get the bound remote address. + /// Get the remote address. /// /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) /// /// # References /// - @@ -162,40 +187,35 @@ interface tcp { /// Equivalent to the IPV6_V6ONLY socket option. /// /// # Typical errors - /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. - /// - `already-bound`: (set) The socket is already bound. + /// - `invalid-state`: (set) The socket is already bound. + /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) ipv6-only: func() -> result; set-ipv6-only: func(value: bool) -> result<_, error-code>; /// Hints the desired listen queue size. Implementations are free to ignore this. /// /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. + /// - `invalid-state`: (set) The socket is already in the Connection state. set-listen-backlog-size: func(value: u64) -> result<_, error-code>; /// Equivalent to the SO_KEEPALIVE socket option. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) keep-alive: func() -> result; set-keep-alive: func(value: bool) -> result<_, error-code>; /// Equivalent to the TCP_NODELAY socket option. /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// The default value is `false`. no-delay: func() -> result; set-no-delay: func(value: bool) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `already-listening`: (set) The socket is already in the Listener state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + /// - `invalid-state`: (set) The socket is already in the Connection state. + /// - `invalid-state`: (set) The socket is already in the Listener state. unicast-hop-limit: func() -> result; set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; @@ -211,9 +231,8 @@ interface tcp { /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. /// /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `already-listening`: (set) The socket is already in the Listener state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// - `invalid-state`: (set) The socket is already in the Connection state. + /// - `invalid-state`: (set) The socket is already in the Listener state. receive-buffer-size: func() -> result; set-receive-buffer-size: func(value: u64) -> result<_, error-code>; send-buffer-size: func() -> result; @@ -237,7 +256,7 @@ interface tcp { /// The shutdown function does not close (drop) the socket. /// /// # Typical errors - /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) + /// - `invalid-state`: The socket is not in the Connection state. (ENOTCONN) /// /// # References /// - diff --git a/crates/wasi/wit/deps/sockets/udp-create-socket.wit b/crates/wasi/wit/deps/sockets/udp-create-socket.wit index 64d899456ca8..e026359fd90f 100644 --- a/crates/wasi/wit/deps/sockets/udp-create-socket.wit +++ b/crates/wasi/wit/deps/sockets/udp-create-socket.wit @@ -14,9 +14,8 @@ interface udp-create-socket { /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. /// /// # Typical errors - /// - `not-supported`: The host does not support UDP sockets. (EOPNOTSUPP) - /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) - /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) /// /// # References: /// - diff --git a/crates/wasi/wit/deps/sockets/udp.wit b/crates/wasi/wit/deps/sockets/udp.wit index a29250caa9af..7a9d7f72c4fa 100644 --- a/crates/wasi/wit/deps/sockets/udp.wit +++ b/crates/wasi/wit/deps/sockets/udp.wit @@ -31,12 +31,11 @@ interface udp { /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. /// /// # Typical `start` errors - /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) - /// - `already-bound`: The socket is already bound. (EINVAL) - /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-state`: The socket is already bound. (EINVAL) /// /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) /// - `address-in-use`: Address is already in use. (EADDRINUSE) /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) /// - `not-in-progress`: A `bind` operation is not in progress. @@ -63,14 +62,14 @@ interface udp { /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. /// /// # Typical `start` errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. - /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. /// /// # Typical `finish` errors - /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) /// - `not-in-progress`: A `connect` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) /// @@ -89,7 +88,7 @@ interface udp { /// If `max-results` is 0, this function returns successfully with an empty list. /// /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. (EINVAL) + /// - `invalid-state`: The socket is not bound to any local address. (EINVAL) /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) /// @@ -119,11 +118,12 @@ interface udp { /// call `remote-address` to get their address. /// /// # Typical errors - /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) - /// - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) + /// - `invalid-state`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) @@ -141,8 +141,14 @@ interface udp { /// Get the current bound address. /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. + /// - `invalid-state`: The socket is not bound to any local address. /// /// # References /// - @@ -154,7 +160,7 @@ interface udp { /// Get the address set with `connect`. /// /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) /// /// # References /// - @@ -173,17 +179,13 @@ interface udp { /// Equivalent to the IPV6_V6ONLY socket option. /// /// # Typical errors - /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. - /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. + /// - `invalid-state`: (set) The socket is already bound. /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) ipv6-only: func() -> result; set-ipv6-only: func(value: bool) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) unicast-hop-limit: func() -> result; set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; @@ -197,9 +199,6 @@ interface udp { /// for internal metadata structures. /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) receive-buffer-size: func() -> result; set-receive-buffer-size: func(value: u64) -> result<_, error-code>; send-buffer-size: func() -> result; From ecc1b794f8bfa64b64fedbf8f63e60f39c319bb4 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Mon, 9 Oct 2023 04:25:14 +0100 Subject: [PATCH 085/199] riscv64: Add GOT relocations for PIC code (#7184) * riscv64: Use `CALL_PLT` relocation for all calls * riscv64: Set the ELF RVC flag only when the C extension is enabled * riscv64: Use PC Relative Relocations for all Calls * riscv64: Add support for PIC symbols --- cranelift/codegen/src/binemit/mod.rs | 23 ++- .../codegen/src/isa/riscv64/inst/emit.rs | 136 +++++++++--------- cranelift/codegen/src/isa/riscv64/inst/mod.rs | 2 +- .../filetests/filetests/isa/riscv64/call.clif | 2 +- .../filetests/isa/riscv64/return-call.clif | 2 +- .../isa/riscv64/simd-abi-large-spill.clif | 2 +- .../filetests/isa/riscv64/stack-limit.clif | 20 +-- .../filetests/isa/riscv64/stack.clif | 50 ++----- .../isa/riscv64/symbol-value-pic.clif | 24 ++++ .../filetests/isa/riscv64/tls-elf.clif | 18 +-- cranelift/jit/src/compiled_blob.rs | 4 +- cranelift/object/src/backend.rs | 35 ++++- 12 files changed, 172 insertions(+), 146 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/riscv64/symbol-value-pic.clif diff --git a/cranelift/codegen/src/binemit/mod.rs b/cranelift/codegen/src/binemit/mod.rs index d5582d87f3be..d8d804bbdc28 100644 --- a/cranelift/codegen/src/binemit/mod.rs +++ b/cranelift/codegen/src/binemit/mod.rs @@ -85,12 +85,14 @@ pub enum Reloc { /// This is equivalent to `R_AARCH64_LD64_GOT_LO12_NC` (312) in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#static-aarch64-relocations) Aarch64Ld64GotLo12Nc, - /// procedure call. - /// call symbol - /// expands to the following assembly and relocation: - /// auipc ra, 0 - /// jalr ra, ra, 0 - RiscvCall, + /// RISC-V Call PLT: 32-bit PC-relative function call, macros call, tail (PIC) + /// + /// Despite having PLT in the name, this relocation is also used for normal calls. + /// The non-PLT version of this relocation has been deprecated. + /// + /// This is the `R_RISCV_CALL_PLT` relocation from the RISC-V ELF psABI document. + /// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#procedure-calls + RiscvCallPlt, /// RISC-V TLS GD: High 20 bits of 32-bit PC-relative TLS GD GOT reference, /// @@ -104,6 +106,12 @@ pub enum Reloc { /// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses RiscvPCRelLo12I, + /// High 20 bits of a 32-bit PC-relative GOT offset relocation + /// + /// This is the `R_RISCV_GOT_HI20` relocation from the RISC-V ELF psABI document. + /// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses + RiscvGotHi20, + /// s390x TLS GD64 - 64-bit offset of tls_index for GD symbol in GOT S390xTlsGd64, /// s390x TLS GDCall - marker to enable optimization of TLS calls @@ -125,8 +133,9 @@ impl fmt::Display for Reloc { Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"), Self::X86SecRel => write!(f, "SecRel"), Self::Arm32Call | Self::Arm64Call => write!(f, "Call"), - Self::RiscvCall => write!(f, "RiscvCall"), + Self::RiscvCallPlt => write!(f, "RiscvCallPlt"), Self::RiscvTlsGdHi20 => write!(f, "RiscvTlsGdHi20"), + Self::RiscvGotHi20 => write!(f, "RiscvGotHi20"), Self::RiscvPCRelLo12I => write!(f, "RiscvPCRelLo12I"), Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"), Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"), diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs index 2fc5749deea4..da7845de7996 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs @@ -1323,55 +1323,16 @@ impl Inst { } } &Inst::Call { ref info } => { - // call - match info.dest { - ExternalName::User { .. } => { - if info.opcode.is_call() { - sink.add_call_site(info.opcode); - } - sink.add_reloc(Reloc::RiscvCall, &info.dest, 0); - if let Some(s) = state.take_stack_map() { - sink.add_stack_map(StackMapExtent::UpcomingBytes(8), s); - } - Inst::construct_auipc_and_jalr( - Some(writable_link_reg()), - writable_link_reg(), - 0, - ) - .into_iter() - .for_each(|i| i.emit_uncompressed(sink, emit_info, state, start_off)); - } - ExternalName::LibCall(..) - | ExternalName::TestCase { .. } - | ExternalName::KnownSymbol(..) => { - // use indirect call. it is more simple. - // load ext name. - Inst::LoadExtName { - rd: writable_spilltmp_reg2(), - name: Box::new(info.dest.clone()), - offset: 0, - } - .emit(&[], sink, emit_info, state); - - // call - Inst::CallInd { - info: Box::new(CallIndInfo { - rn: spilltmp_reg2(), - // This doesen't really matter but we might as well send - // the correct info. - uses: info.uses.clone(), - defs: info.defs.clone(), - clobbers: info.clobbers, - opcode: Opcode::CallIndirect, - caller_callconv: info.caller_callconv, - callee_callconv: info.callee_callconv, - // Send this as 0 to avoid updating the pop size twice. - callee_pop_size: 0, - }), - } - .emit(&[], sink, emit_info, state); - } + if info.opcode.is_call() { + sink.add_call_site(info.opcode); } + sink.add_reloc(Reloc::RiscvCallPlt, &info.dest, 0); + if let Some(s) = state.take_stack_map() { + sink.add_stack_map(StackMapExtent::UpcomingBytes(8), s); + } + Inst::construct_auipc_and_jalr(Some(writable_link_reg()), writable_link_reg(), 0) + .into_iter() + .for_each(|i| i.emit_uncompressed(sink, emit_info, state, start_off)); let callee_pop_size = i64::from(info.callee_pop_size); state.virtual_sp_offset -= callee_pop_size; @@ -1419,7 +1380,7 @@ impl Inst { ); sink.add_call_site(ir::Opcode::ReturnCall); - sink.add_reloc(Reloc::RiscvCall, &**callee, 0); + sink.add_reloc(Reloc::RiscvCallPlt, &**callee, 0); Inst::construct_auipc_and_jalr(None, writable_spilltmp_reg(), 0) .into_iter() .for_each(|i| i.emit_uncompressed(sink, emit_info, state, start_off)); @@ -2309,26 +2270,71 @@ impl Inst { ref name, offset, } => { - let label_data = sink.get_label(); - let label_end = sink.get_label(); + if emit_info.shared_flag.is_pic() { + // Load a PC-relative address into a register. + // RISC-V does this slightly differently from other arches. We emit a relocation + // with a label, instead of the symbol itself. + // + // See: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses + // + // Emit the following code: + // label: + // auipc rd, 0 # R_RISCV_GOT_HI20 (symbol_name) + // ld rd, rd, 0 # R_RISCV_PCREL_LO12_I (label) + + // Create the lable that is going to be published to the final binary object. + let auipc_label = sink.get_label(); + sink.bind_label(auipc_label, &mut state.ctrl_plane); + + // Get the current PC. + sink.add_reloc(Reloc::RiscvGotHi20, &**name, 0); + Inst::Auipc { + rd: rd, + imm: Imm20::from_i32(0), + } + .emit_uncompressed(sink, emit_info, state, start_off); - // Load the value from a label - Inst::Load { - rd, - op: LoadOP::Ld, - flags: MemFlags::trusted(), - from: AMode::Label(label_data), - } - .emit(&[], sink, emit_info, state); + // The `ld` here, points to the `auipc` label instead of directly to the symbol. + sink.add_reloc(Reloc::RiscvPCRelLo12I, &auipc_label, 0); + Inst::Load { + rd, + op: LoadOP::Ld, + flags: MemFlags::trusted(), + from: AMode::RegOffset(rd.to_reg(), 0, I64), + } + .emit_uncompressed(sink, emit_info, state, start_off); + } else { + // In the non PIC sequence we relocate the absolute address into + // a prealocatted space, load it into a register and jump over it. + // + // Emit the following code: + // ld rd, label_data + // j label_end + // label_data: + // <8 byte space> # ABS8 + // label_end: + + let label_data = sink.get_label(); + let label_end = sink.get_label(); + + // Load the value from a label + Inst::Load { + rd, + op: LoadOP::Ld, + flags: MemFlags::trusted(), + from: AMode::Label(label_data), + } + .emit(&[], sink, emit_info, state); - // Jump over the data - Inst::gen_jump(label_end).emit(&[], sink, emit_info, state); + // Jump over the data + Inst::gen_jump(label_end).emit(&[], sink, emit_info, state); - sink.bind_label(label_data, &mut state.ctrl_plane); - sink.add_reloc(Reloc::Abs8, name.as_ref(), offset); - sink.put8(0); + sink.bind_label(label_data, &mut state.ctrl_plane); + sink.add_reloc(Reloc::Abs8, name.as_ref(), offset); + sink.put8(0); - sink.bind_label(label_end, &mut state.ctrl_plane); + sink.bind_label(label_end, &mut state.ctrl_plane); + } } &Inst::ElfTlsGetAddr { rd, ref name } => { diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs index fed91b230321..12d00c319a27 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs @@ -1991,7 +1991,7 @@ impl MachInstLabelUse for LabelUse { fn from_reloc(reloc: Reloc, addend: Addend) -> Option { match (reloc, addend) { - (Reloc::RiscvCall, _) => Some(Self::PCRel32), + (Reloc::RiscvCallPlt, _) => Some(Self::PCRel32), _ => None, } } diff --git a/cranelift/filetests/filetests/isa/riscv64/call.clif b/cranelift/filetests/filetests/isa/riscv64/call.clif index c97543c7214f..10f17f5599ba 100644 --- a/cranelift/filetests/filetests/isa/riscv64/call.clif +++ b/cranelift/filetests/filetests/isa/riscv64/call.clif @@ -847,7 +847,7 @@ block0(v0: i16): ; addi sp, sp, -0x10 ; block1: ; offset 0x18 ; mv s1, a0 -; auipc ra, 0 ; reloc_external RiscvCall u0:0 0 +; auipc ra, 0 ; reloc_external RiscvCallPlt u0:0 0 ; jalr ra ; mv a0, s1 ; addi sp, sp, 0x10 diff --git a/cranelift/filetests/filetests/isa/riscv64/return-call.clif b/cranelift/filetests/filetests/isa/riscv64/return-call.clif index a119160e1f08..7eb4190dd9f8 100644 --- a/cranelift/filetests/filetests/isa/riscv64/return-call.clif +++ b/cranelift/filetests/filetests/isa/riscv64/return-call.clif @@ -82,7 +82,7 @@ block0(v0: i64): ; ld t6, 0(s0) ; addi sp, s0, 0x10 ; mv s0, t6 -; auipc t6, 0 ; reloc_external RiscvCall %callee_i64 0 +; auipc t6, 0 ; reloc_external RiscvCallPlt %callee_i64 0 ; jr t6 ;;;; Test passing `f64`s ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-abi-large-spill.clif b/cranelift/filetests/filetests/isa/riscv64/simd-abi-large-spill.clif index f8412ce89cde..9e32f3c27075 100644 --- a/cranelift/filetests/filetests/isa/riscv64/simd-abi-large-spill.clif +++ b/cranelift/filetests/filetests/isa/riscv64/simd-abi-large-spill.clif @@ -53,7 +53,7 @@ block0: ; .byte 0x87, 0xd5, 0x0f, 0x02 ; .byte 0x57, 0x70, 0x08, 0xcc ; .byte 0xa7, 0x05, 0x01, 0x02 -; auipc ra, 0 ; reloc_external RiscvCall u2:0 0 +; auipc ra, 0 ; reloc_external RiscvCallPlt u2:0 0 ; jalr ra ; mv a0, s1 ; .byte 0x87, 0x05, 0x01, 0x02 diff --git a/cranelift/filetests/filetests/isa/riscv64/stack-limit.clif b/cranelift/filetests/filetests/isa/riscv64/stack-limit.clif index d33f83bdd740..64fd50b5b5b3 100644 --- a/cranelift/filetests/filetests/isa/riscv64/stack-limit.clif +++ b/cranelift/filetests/filetests/isa/riscv64/stack-limit.clif @@ -216,16 +216,12 @@ block0(v0: i64): ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: stk_ovf ; lui a0, 0x62 ; addi a0, a0, -0x580 -; auipc t5, 0 -; ld t5, 0xc(t5) -; j 0xc -; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %Probestack 0 -; .byte 0x00, 0x00, 0x00, 0x00 -; jalr t5 +; auipc ra, 0 ; reloc_external RiscvCallPlt %Probestack 0 +; jalr ra ; lui t6, 0xfff9e ; addi t6, t6, 0x580 ; add sp, t6, sp -; block1: ; offset 0x58 +; block1: ; offset 0x48 ; lui t6, 0x62 ; addi t6, t6, -0x580 ; add sp, t6, sp @@ -330,16 +326,12 @@ block0(v0: i64): ; .byte 0x00, 0x00, 0x00, 0x00 ; trap: stk_ovf ; lui a0, 0x62 ; addi a0, a0, -0x580 -; auipc t5, 0 -; ld t5, 0xc(t5) -; j 0xc -; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %Probestack 0 -; .byte 0x00, 0x00, 0x00, 0x00 -; jalr t5 +; auipc ra, 0 ; reloc_external RiscvCallPlt %Probestack 0 +; jalr ra ; lui t6, 0xfff9e ; addi t6, t6, 0x580 ; add sp, t6, sp -; block1: ; offset 0x60 +; block1: ; offset 0x50 ; lui t6, 0x62 ; addi t6, t6, -0x580 ; add sp, t6, sp diff --git a/cranelift/filetests/filetests/isa/riscv64/stack.clif b/cranelift/filetests/filetests/isa/riscv64/stack.clif index a5215b51f4c2..1400328f1950 100644 --- a/cranelift/filetests/filetests/isa/riscv64/stack.clif +++ b/cranelift/filetests/filetests/isa/riscv64/stack.clif @@ -74,16 +74,12 @@ block0: ; mv s0, sp ; lui a0, 0x18 ; addi a0, a0, 0x6b0 -; auipc t5, 0 -; ld t5, 0xc(t5) -; j 0xc -; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %Probestack 0 -; .byte 0x00, 0x00, 0x00, 0x00 -; jalr t5 +; auipc ra, 0 ; reloc_external RiscvCallPlt %Probestack 0 +; jalr ra ; lui t6, 0xfffe8 ; addi t6, t6, -0x6b0 ; add sp, t6, sp -; block1: ; offset 0x3c +; block1: ; offset 0x2c ; mv a0, sp ; lui t6, 0x18 ; addi t6, t6, 0x6b0 @@ -164,16 +160,12 @@ block0: ; mv s0, sp ; lui a0, 0x18 ; addi a0, a0, 0x6b0 -; auipc t5, 0 -; ld t5, 0xc(t5) -; j 0xc -; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %Probestack 0 -; .byte 0x00, 0x00, 0x00, 0x00 -; jalr t5 +; auipc ra, 0 ; reloc_external RiscvCallPlt %Probestack 0 +; jalr ra ; lui t6, 0xfffe8 ; addi t6, t6, -0x6b0 ; add sp, t6, sp -; block1: ; offset 0x3c +; block1: ; offset 0x2c ; ld a0, 0(sp) ; lui t6, 0x18 ; addi t6, t6, 0x6b0 @@ -254,16 +246,12 @@ block0(v0: i64): ; mv s0, sp ; lui a0, 0x18 ; addi a0, a0, 0x6b0 -; auipc t5, 0 -; ld t5, 0xc(t5) -; j 0xc -; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %Probestack 0 -; .byte 0x00, 0x00, 0x00, 0x00 -; jalr t5 +; auipc ra, 0 ; reloc_external RiscvCallPlt %Probestack 0 +; jalr ra ; lui t6, 0xfffe8 ; addi t6, t6, -0x6b0 ; add sp, t6, sp -; block1: ; offset 0x3c +; block1: ; offset 0x2c ; sd a0, 0(sp) ; lui t6, 0x18 ; addi t6, t6, 0x6b0 @@ -949,16 +937,12 @@ block0(v0: i128): ; mv s0, sp ; lui a0, 0x18 ; addi a0, a0, 0x6b0 -; auipc t5, 0 -; ld t5, 0xc(t5) -; j 0xc -; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %Probestack 0 -; .byte 0x00, 0x00, 0x00, 0x00 -; jalr t5 +; auipc ra, 0 ; reloc_external RiscvCallPlt %Probestack 0 +; jalr ra ; lui t6, 0xfffe8 ; addi t6, t6, -0x6b0 ; add sp, t6, sp -; block1: ; offset 0x3c +; block1: ; offset 0x2c ; sd a0, 0(sp) ; sd a1, 8(sp) ; lui t6, 0x18 @@ -1083,16 +1067,12 @@ block0: ; mv s0, sp ; lui a0, 0x18 ; addi a0, a0, 0x6b0 -; auipc t5, 0 -; ld t5, 0xc(t5) -; j 0xc -; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %Probestack 0 -; .byte 0x00, 0x00, 0x00, 0x00 -; jalr t5 +; auipc ra, 0 ; reloc_external RiscvCallPlt %Probestack 0 +; jalr ra ; lui t6, 0xfffe8 ; addi t6, t6, -0x6b0 ; add sp, t6, sp -; block1: ; offset 0x3c +; block1: ; offset 0x2c ; ld a0, 0(sp) ; ld a1, 8(sp) ; lui t6, 0x18 diff --git a/cranelift/filetests/filetests/isa/riscv64/symbol-value-pic.clif b/cranelift/filetests/filetests/isa/riscv64/symbol-value-pic.clif new file mode 100644 index 000000000000..049e9717d54e --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/symbol-value-pic.clif @@ -0,0 +1,24 @@ +test compile precise-output +set unwind_info=false +set is_pic +target riscv64 + +function %f() -> i64 { + gv0 = symbol %my_global + +block0: + v0 = symbol_value.i64 gv0 + return v0 +} + +; VCode: +; block0: +; load_sym a0,%my_global+0 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; auipc a0, 0 ; reloc_external RiscvGotHi20 %my_global 0 +; ld a0, 0(a0) ; reloc_external RiscvPCRelLo12I func+0 0 +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/tls-elf.clif b/cranelift/filetests/filetests/isa/riscv64/tls-elf.clif index ab352c4f1b75..85ae4452462a 100644 --- a/cranelift/filetests/filetests/isa/riscv64/tls-elf.clif +++ b/cranelift/filetests/filetests/isa/riscv64/tls-elf.clif @@ -41,12 +41,8 @@ block0(v0: i32): ; mv s1, a0 ; auipc a0, 0 ; reloc_external RiscvTlsGdHi20 u1:0 0 ; mv a0, a0 ; reloc_external RiscvPCRelLo12I func+28 0 -; auipc t5, 0 -; ld t5, 0xc(t5) -; j 0xc -; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %ElfTlsGetAddr 0 -; .byte 0x00, 0x00, 0x00, 0x00 -; jalr t5 +; auipc ra, 0 ; reloc_external RiscvCallPlt %ElfTlsGetAddr 0 +; jalr ra ; mv a1, a0 ; mv a0, s1 ; addi sp, sp, 0x10 @@ -56,8 +52,6 @@ block0(v0: i32): ; addi sp, sp, 0x10 ; ret - - function u0:1(i64) -> i64 system_v { gv0 = symbol colocated tls u1:0 @@ -93,12 +87,8 @@ block0(v0: i64): ; block1: ; offset 0x10 ; auipc a0, 0 ; reloc_external RiscvTlsGdHi20 u1:0 0 ; mv a0, a0 ; reloc_external RiscvPCRelLo12I func+16 0 -; auipc t5, 0 -; ld t5, 0xc(t5) -; j 0xc -; .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %ElfTlsGetAddr 0 -; .byte 0x00, 0x00, 0x00, 0x00 -; jalr t5 +; auipc ra, 0 ; reloc_external RiscvCallPlt %ElfTlsGetAddr 0 +; jalr ra ; mv a4, zero ; sb a4, 0(a0) ; mv a0, zero diff --git a/cranelift/jit/src/compiled_blob.rs b/cranelift/jit/src/compiled_blob.rs index de01855c6d7b..f4f134023ea2 100644 --- a/cranelift/jit/src/compiled_blob.rs +++ b/cranelift/jit/src/compiled_blob.rs @@ -93,8 +93,8 @@ impl CompiledBlob { let imm26 = (diff as u32) << chop >> chop; unsafe { modify_inst32(iptr, |inst| inst | imm26) }; } - Reloc::RiscvCall => { - // A R_RISCV_CALL relocation expects auipc+jalr instruction pair. + Reloc::RiscvCallPlt => { + // A R_RISCV_CALL_PLT relocation expects auipc+jalr instruction pair. // It is the equivalent of two relocations: // 1. R_RISCV_PCREL_HI20 on the `auipc` // 2. R_RISCV_PCREL_LO12_I on the `jalr` diff --git a/cranelift/object/src/backend.rs b/cranelift/object/src/backend.rs index 435ff39856b8..a0aa1e3ae7fa 100644 --- a/cranelift/object/src/backend.rs +++ b/cranelift/object/src/backend.rs @@ -79,11 +79,24 @@ impl ObjectBuilder { binary_format, ))); } - // FIXME(#4994) get the right variant from the TargetIsa + + // FIXME(#4994): Get the right float ABI variant from the TargetIsa + let mut eflags = object::elf::EF_RISCV_FLOAT_ABI_DOUBLE; + + // Set the RVC eflag if we have the C extension enabled. + let has_c = isa + .isa_flags() + .iter() + .filter(|f| f.name == "has_zca" || f.name == "has_zcd") + .all(|f| f.as_bool().unwrap_or_default()); + if has_c { + eflags |= object::elf::EF_RISCV_RVC; + } + file_flags = object::FileFlags::Elf { os_abi: object::elf::ELFOSABI_NONE, abi_version: 0, - e_flags: object::elf::EF_RISCV_RVC | object::elf::EF_RISCV_FLOAT_ABI_DOUBLE, + e_flags: eflags, }; object::Architecture::Riscv64 } @@ -791,14 +804,14 @@ impl ObjectModule { 0, ) } - Reloc::RiscvCall => { + Reloc::RiscvCallPlt => { assert_eq!( self.object.format(), object::BinaryFormat::Elf, - "RiscvCall is not supported for this file format" + "RiscvCallPlt is not supported for this file format" ); ( - RelocationKind::Elf(object::elf::R_RISCV_CALL), + RelocationKind::Elf(object::elf::R_RISCV_CALL_PLT), RelocationEncoding::Generic, 0, ) @@ -827,6 +840,18 @@ impl ObjectModule { 0, ) } + Reloc::RiscvGotHi20 => { + assert_eq!( + self.object.format(), + object::BinaryFormat::Elf, + "RiscvGotHi20 is not supported for this file format" + ); + ( + RelocationKind::Elf(object::elf::R_RISCV_GOT_HI20), + RelocationEncoding::Generic, + 0, + ) + } // FIXME reloc => unimplemented!("{:?}", reloc), }; From 654a1a1cdb0e56c941c0b125801872bb55d92dd5 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Mon, 9 Oct 2023 15:40:07 +0200 Subject: [PATCH 086/199] feat(wasi-sockets): implement UDP (#7148) * feat(wasi-sockets): implement UDP This is based on TCP implementation Signed-off-by: Roman Volosatovs * refactor(wasi-sockets): simplify UDP implementation This introduces quite a few changes compared to TCP, which should most probably be integrated there as well Signed-off-by: Roman Volosatovs * feat(wasi-sockets): store UDP connect address in state Signed-off-by: Roman Volosatovs * fix(wasi-sockets): avoid `shutdown` on `drop` Signed-off-by: Roman Volosatovs * Remove explicit bind * Simplify start_connect&finish_connect. On UDP sockets, `connect` never blocks. * Move UDP test to single file, similar to `tcp_sample_application.rs` * Update UDP tests --------- Signed-off-by: Roman Volosatovs Co-authored-by: Dave Bakker --- crates/test-programs/tests/wasi-sockets.rs | 5 + .../src/bin/udp_sample_application.rs | 89 +++++ .../wasi-sockets-tests/src/lib.rs | 125 +++++- crates/wasi-http/wit/test.wit | 2 + crates/wasi/src/preview2/command.rs | 5 + crates/wasi/src/preview2/host/mod.rs | 2 + crates/wasi/src/preview2/host/tcp.rs | 21 -- crates/wasi/src/preview2/host/udp.rs | 356 ++++++++++++++++++ .../src/preview2/host/udp_create_socket.rs | 15 + crates/wasi/src/preview2/mod.rs | 2 + crates/wasi/src/preview2/udp.rs | 92 +++++ crates/wasi/wit/test.wit | 2 + 12 files changed, 694 insertions(+), 22 deletions(-) create mode 100644 crates/test-programs/wasi-sockets-tests/src/bin/udp_sample_application.rs create mode 100644 crates/wasi/src/preview2/host/udp.rs create mode 100644 crates/wasi/src/preview2/host/udp_create_socket.rs create mode 100644 crates/wasi/src/preview2/udp.rs diff --git a/crates/test-programs/tests/wasi-sockets.rs b/crates/test-programs/tests/wasi-sockets.rs index c94243a2a866..806d21c6c5db 100644 --- a/crates/test-programs/tests/wasi-sockets.rs +++ b/crates/test-programs/tests/wasi-sockets.rs @@ -91,6 +91,11 @@ async fn tcp_sockopts() { run("tcp_sockopts").await.unwrap(); } +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn udp_sample_application() { + run("udp_sample_application").await.unwrap(); +} + #[test_log::test(tokio::test(flavor = "multi_thread"))] async fn ip_name_lookup() { run("ip_name_lookup").await.unwrap(); diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/udp_sample_application.rs b/crates/test-programs/wasi-sockets-tests/src/bin/udp_sample_application.rs new file mode 100644 index 000000000000..7dc64fa586a6 --- /dev/null +++ b/crates/test-programs/wasi-sockets-tests/src/bin/udp_sample_application.rs @@ -0,0 +1,89 @@ +use wasi::sockets::network::{ + IpAddressFamily, IpSocketAddress, Ipv4SocketAddress, Ipv6SocketAddress, Network, +}; +use wasi::sockets::udp::{Datagram, UdpSocket}; +use wasi_sockets_tests::*; + +fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddress) { + let first_message = &[]; + let second_message = b"Hello, world!"; + let third_message = b"Greetings, planet!"; + + let net = Network::default(); + + let server = UdpSocket::new(family).unwrap(); + + server.blocking_bind(&net, bind_address).unwrap(); + let addr = server.local_address().unwrap(); + + let client_addr = { + let client = UdpSocket::new(family).unwrap(); + client.blocking_connect(&net, addr).unwrap(); + + let datagrams = [ + Datagram { + data: first_message.to_vec(), + remote_address: addr, + }, + Datagram { + data: second_message.to_vec(), + remote_address: addr, + }, + ]; + client.blocking_send(&datagrams).unwrap(); + + client.local_address().unwrap() + }; + + { + // Check that we've received our sent messages. + // Not guaranteed to work but should work in practice. + let datagrams = server.blocking_receive(2..100).unwrap(); + assert_eq!(datagrams.len(), 2); + + assert_eq!(datagrams[0].data, first_message); + assert_eq!(datagrams[0].remote_address, client_addr); + + assert_eq!(datagrams[1].data, second_message); + assert_eq!(datagrams[1].remote_address, client_addr); + } + + // Another client + { + let client = UdpSocket::new(family).unwrap(); + client.blocking_connect(&net, addr).unwrap(); + + let datagrams = [Datagram { + data: third_message.to_vec(), + remote_address: addr, + }]; + client.blocking_send(&datagrams).unwrap(); + } + + { + // Check that we sent and received our message! + let datagrams = server.blocking_receive(1..100).unwrap(); + assert_eq!(datagrams.len(), 1); + + assert_eq!(datagrams[0].data, third_message); // Not guaranteed to work but should work in practice. + } +} + +fn main() { + test_sample_application( + IpAddressFamily::Ipv4, + IpSocketAddress::Ipv4(Ipv4SocketAddress { + port: 0, // use any free port + address: (127, 0, 0, 1), // localhost + }), + ); + test_sample_application( + IpAddressFamily::Ipv6, + IpSocketAddress::Ipv6(Ipv6SocketAddress { + port: 0, // use any free port + address: (0, 0, 0, 0, 0, 0, 0, 1), // localhost + flow_info: 0, + scope_id: 0, + }), + ); +} diff --git a/crates/test-programs/wasi-sockets-tests/src/lib.rs b/crates/test-programs/wasi-sockets-tests/src/lib.rs index 4d90e914ddda..258099e5a05c 100644 --- a/crates/test-programs/wasi-sockets-tests/src/lib.rs +++ b/crates/test-programs/wasi-sockets-tests/src/lib.rs @@ -1,5 +1,7 @@ wit_bindgen::generate!("test-command-with-sockets" in "../../wasi/wit"); +use std::ops::Range; +use wasi::clocks::monotonic_clock; use wasi::io::poll::{self, Pollable}; use wasi::io::streams::{InputStream, OutputStream, StreamError}; use wasi::sockets::instance_network; @@ -8,12 +10,25 @@ use wasi::sockets::network::{ Network, }; use wasi::sockets::tcp::TcpSocket; -use wasi::sockets::tcp_create_socket; +use wasi::sockets::udp::{Datagram, UdpSocket}; +use wasi::sockets::{tcp_create_socket, udp_create_socket}; + +const TIMEOUT_NS: u64 = 1_000_000_000; impl Pollable { pub fn wait(&self) { poll::poll_one(self); } + + pub fn wait_until(&self, timeout: &Pollable) -> Result<(), ErrorCode> { + let ready = poll::poll_list(&[self, timeout]); + assert!(ready.len() > 0); + match ready[0] { + 0 => Ok(()), + 1 => Err(ErrorCode::Timeout), + _ => unreachable!(), + } + } } impl OutputStream { @@ -108,6 +123,89 @@ impl TcpSocket { } } +impl UdpSocket { + pub fn new(address_family: IpAddressFamily) -> Result { + udp_create_socket::create_udp_socket(address_family) + } + + pub fn blocking_bind( + &self, + network: &Network, + local_address: IpSocketAddress, + ) -> Result<(), ErrorCode> { + let sub = self.subscribe(); + + self.start_bind(&network, local_address)?; + + loop { + match self.finish_bind() { + Err(ErrorCode::WouldBlock) => sub.wait(), + result => return result, + } + } + } + + pub fn blocking_connect( + &self, + network: &Network, + remote_address: IpSocketAddress, + ) -> Result<(), ErrorCode> { + let sub = self.subscribe(); + + self.start_connect(&network, remote_address)?; + + loop { + match self.finish_connect() { + Err(ErrorCode::WouldBlock) => sub.wait(), + result => return result, + } + } + } + + pub fn blocking_send(&self, mut datagrams: &[Datagram]) -> Result<(), ErrorCode> { + let timeout = monotonic_clock::subscribe(TIMEOUT_NS, false); + let pollable = self.subscribe(); + + while !datagrams.is_empty() { + match self.send(datagrams) { + Ok(packets_sent) => { + datagrams = &datagrams[(packets_sent as usize)..]; + } + Err(ErrorCode::WouldBlock) => pollable.wait_until(&timeout)?, + Err(err) => return Err(err), + } + } + + Ok(()) + } + + pub fn blocking_receive(&self, count: Range) -> Result, ErrorCode> { + let timeout = monotonic_clock::subscribe(TIMEOUT_NS, false); + let pollable = self.subscribe(); + let mut datagrams = vec![]; + + loop { + match self.receive(count.end - datagrams.len() as u64) { + Ok(mut chunk) => { + datagrams.append(&mut chunk); + + if datagrams.len() >= count.start as usize { + return Ok(datagrams); + } + } + Err(ErrorCode::WouldBlock) => { + if datagrams.len() >= count.start as usize { + return Ok(datagrams); + } else { + pollable.wait_until(&timeout)?; + } + } + Err(err) => return Err(err), + } + } + } +} + impl IpAddress { pub const IPV4_BROADCAST: IpAddress = IpAddress::Ipv4((255, 255, 255, 255)); @@ -189,3 +287,28 @@ impl IpSocketAddress { } } } + +impl PartialEq for Ipv4SocketAddress { + fn eq(&self, other: &Self) -> bool { + self.port == other.port && self.address == other.address + } +} + +impl PartialEq for Ipv6SocketAddress { + fn eq(&self, other: &Self) -> bool { + self.port == other.port + && self.flow_info == other.flow_info + && self.address == other.address + && self.scope_id == other.scope_id + } +} + +impl PartialEq for IpSocketAddress { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Ipv4(l0), Self::Ipv4(r0)) => l0 == r0, + (Self::Ipv6(l0), Self::Ipv6(r0)) => l0 == r0, + _ => false, + } + } +} diff --git a/crates/wasi-http/wit/test.wit b/crates/wasi-http/wit/test.wit index fc9c357522bf..a0d1d07a6c64 100644 --- a/crates/wasi-http/wit/test.wit +++ b/crates/wasi-http/wit/test.wit @@ -37,6 +37,8 @@ world test-command-with-sockets { import wasi:cli/stderr; import wasi:sockets/tcp; import wasi:sockets/tcp-create-socket; + import wasi:sockets/udp; + import wasi:sockets/udp-create-socket; import wasi:sockets/network; import wasi:sockets/instance-network; import wasi:sockets/ip-name-lookup; diff --git a/crates/wasi/src/preview2/command.rs b/crates/wasi/src/preview2/command.rs index 811e3cf18e2c..898311157354 100644 --- a/crates/wasi/src/preview2/command.rs +++ b/crates/wasi/src/preview2/command.rs @@ -48,6 +48,8 @@ pub fn add_to_linker(l: &mut wasmtime::component::Linker) -> any crate::preview2::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::tcp::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?; + crate::preview2::bindings::sockets::udp::add_to_linker(l, |t| t)?; + crate::preview2::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::instance_network::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::network::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?; @@ -65,6 +67,7 @@ pub mod sync { "wasi:filesystem/types": crate::preview2::bindings::sync_io::filesystem::types, "wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens, "wasi:sockets/tcp": crate::preview2::bindings::sockets::tcp, + "wasi:sockets/udp": crate::preview2::bindings::sockets::udp, "wasi:clocks/monotonic_clock": crate::preview2::bindings::clocks::monotonic_clock, "wasi:io/poll": crate::preview2::bindings::sync_io::io::poll, "wasi:io/streams": crate::preview2::bindings::sync_io::io::streams, @@ -107,6 +110,8 @@ pub mod sync { crate::preview2::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::tcp::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?; + crate::preview2::bindings::sockets::udp::add_to_linker(l, |t| t)?; + crate::preview2::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::instance_network::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::network::add_to_linker(l, |t| t)?; crate::preview2::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?; diff --git a/crates/wasi/src/preview2/host/mod.rs b/crates/wasi/src/preview2/host/mod.rs index 138166731565..651d2cd38e0c 100644 --- a/crates/wasi/src/preview2/host/mod.rs +++ b/crates/wasi/src/preview2/host/mod.rs @@ -8,3 +8,5 @@ mod network; mod random; mod tcp; mod tcp_create_socket; +mod udp; +mod udp_create_socket; diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index 82e824b3a141..05a203a13f80 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -603,27 +603,6 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { // As in the filesystem implementation, we assume closing a socket // doesn't block. let dropped = table.delete_resource(this)?; - - // If we might have an `event::poll` waiting on the socket, wake it up. - #[cfg(not(unix))] - { - match dropped.tcp_state { - TcpState::Default - | TcpState::BindStarted - | TcpState::Bound - | TcpState::ListenStarted - | TcpState::ConnectFailed - | TcpState::ConnectReady => {} - - TcpState::Listening | TcpState::Connecting | TcpState::Connected => { - match rustix::net::shutdown(&*dropped.inner, rustix::net::Shutdown::ReadWrite) { - Ok(()) | Err(Errno::NOTCONN) => {} - Err(err) => Err(err).unwrap(), - } - } - } - } - drop(dropped); Ok(()) diff --git a/crates/wasi/src/preview2/host/udp.rs b/crates/wasi/src/preview2/host/udp.rs new file mode 100644 index 000000000000..f05000ae676f --- /dev/null +++ b/crates/wasi/src/preview2/host/udp.rs @@ -0,0 +1,356 @@ +use std::net::SocketAddr; + +use crate::preview2::{ + bindings::{ + sockets::network::{ErrorCode, IpAddressFamily, IpSocketAddress, Network}, + sockets::udp, + }, + udp::UdpState, +}; +use crate::preview2::{Pollable, SocketResult, WasiView}; +use cap_net_ext::{AddressFamily, PoolExt}; +use io_lifetimes::AsSocketlike; +use rustix::io::Errno; +use rustix::net::sockopt; +use wasmtime::component::Resource; + +/// Theoretical maximum byte size of a UDP datagram, the real limit is lower, +/// but we do not account for e.g. the transport layer here for simplicity. +/// In practice, datagrams are typically less than 1500 bytes. +const MAX_UDP_DATAGRAM_SIZE: usize = 65535; + +impl udp::Host for T {} + +impl crate::preview2::host::udp::udp::HostUdpSocket for T { + fn start_bind( + &mut self, + this: Resource, + network: Resource, + local_address: IpSocketAddress, + ) -> SocketResult<()> { + let table = self.table_mut(); + let socket = table.get_resource(&this)?; + + match socket.udp_state { + UdpState::Default => {} + UdpState::BindStarted | UdpState::Connecting(..) => { + return Err(ErrorCode::ConcurrencyConflict.into()) + } + UdpState::Bound | UdpState::Connected(..) => return Err(ErrorCode::InvalidState.into()), + } + + let network = table.get_resource(&network)?; + let binder = network.pool.udp_binder(local_address)?; + + // Perform the OS bind call. + binder.bind_existing_udp_socket( + &*socket + .udp_socket() + .as_socketlike_view::(), + )?; + + let socket = table.get_resource_mut(&this)?; + socket.udp_state = UdpState::BindStarted; + + Ok(()) + } + + fn finish_bind(&mut self, this: Resource) -> SocketResult<()> { + let table = self.table_mut(); + let socket = table.get_resource_mut(&this)?; + + match socket.udp_state { + UdpState::BindStarted => { + socket.udp_state = UdpState::Bound; + Ok(()) + } + _ => Err(ErrorCode::NotInProgress.into()), + } + } + + fn start_connect( + &mut self, + this: Resource, + network: Resource, + remote_address: IpSocketAddress, + ) -> SocketResult<()> { + let table = self.table_mut(); + let socket = table.get_resource(&this)?; + let network = table.get_resource(&network)?; + + match socket.udp_state { + UdpState::Default | UdpState::Bound => {} + UdpState::BindStarted | UdpState::Connecting(..) => { + return Err(ErrorCode::ConcurrencyConflict.into()) + } + UdpState::Connected(..) => return Err(ErrorCode::InvalidState.into()), + } + + let connecter = network.pool.udp_connecter(remote_address)?; + + // Do an OS `connect`. + connecter.connect_existing_udp_socket( + &*socket + .udp_socket() + .as_socketlike_view::(), + )?; + + let socket = table.get_resource_mut(&this)?; + socket.udp_state = UdpState::Connecting(remote_address); + Ok(()) + } + + fn finish_connect(&mut self, this: Resource) -> SocketResult<()> { + let table = self.table_mut(); + let socket = table.get_resource_mut(&this)?; + + match socket.udp_state { + UdpState::Connecting(addr) => { + socket.udp_state = UdpState::Connected(addr); + Ok(()) + } + _ => Err(ErrorCode::NotInProgress.into()), + } + } + + fn receive( + &mut self, + this: Resource, + max_results: u64, + ) -> SocketResult> { + if max_results == 0 { + return Ok(vec![]); + } + + let table = self.table(); + let socket = table.get_resource(&this)?; + + let udp_socket = socket.udp_socket(); + let mut datagrams = vec![]; + let mut buf = [0; MAX_UDP_DATAGRAM_SIZE]; + match socket.udp_state { + UdpState::Default | UdpState::BindStarted => return Err(ErrorCode::InvalidState.into()), + UdpState::Bound | UdpState::Connecting(..) => { + for i in 0..max_results { + match udp_socket.try_recv_from(&mut buf) { + Ok((size, remote_address)) => datagrams.push(udp::Datagram { + data: buf[..size].into(), + remote_address: remote_address.into(), + }), + Err(_e) if i > 0 => { + return Ok(datagrams); + } + Err(e) => return Err(e.into()), + } + } + } + UdpState::Connected(remote_address) => { + for i in 0..max_results { + match udp_socket.try_recv(&mut buf) { + Ok(size) => datagrams.push(udp::Datagram { + data: buf[..size].into(), + remote_address, + }), + Err(_e) if i > 0 => { + return Ok(datagrams); + } + Err(e) => return Err(e.into()), + } + } + } + } + Ok(datagrams) + } + + fn send( + &mut self, + this: Resource, + datagrams: Vec, + ) -> SocketResult { + if datagrams.is_empty() { + return Ok(0); + }; + let table = self.table(); + let socket = table.get_resource(&this)?; + + let udp_socket = socket.udp_socket(); + let mut count = 0; + match socket.udp_state { + UdpState::Default | UdpState::BindStarted => return Err(ErrorCode::InvalidState.into()), + UdpState::Bound | UdpState::Connecting(..) => { + for udp::Datagram { + data, + remote_address, + } in datagrams + { + match udp_socket.try_send_to(&data, remote_address.into()) { + Ok(_size) => count += 1, + Err(_e) if count > 0 => { + return Ok(count); + } + Err(e) => return Err(e.into()), + } + } + } + UdpState::Connected(addr) => { + let addr = SocketAddr::from(addr); + for udp::Datagram { + data, + remote_address, + } in datagrams + { + if SocketAddr::from(remote_address) != addr { + // From WIT documentation: + // If at least one datagram has been sent successfully, this function never returns an error. + if count == 0 { + return Err(ErrorCode::InvalidArgument.into()); + } else { + return Ok(count); + } + } + match udp_socket.try_send(&data) { + Ok(_size) => count += 1, + Err(_e) if count > 0 => { + return Ok(count); + } + Err(e) => return Err(e.into()), + } + } + } + } + Ok(count) + } + + fn local_address(&mut self, this: Resource) -> SocketResult { + let table = self.table(); + let socket = table.get_resource(&this)?; + let addr = socket + .udp_socket() + .as_socketlike_view::() + .local_addr()?; + Ok(addr.into()) + } + + fn remote_address(&mut self, this: Resource) -> SocketResult { + let table = self.table(); + let socket = table.get_resource(&this)?; + let addr = socket + .udp_socket() + .as_socketlike_view::() + .peer_addr()?; + Ok(addr.into()) + } + + fn address_family( + &mut self, + this: Resource, + ) -> Result { + let table = self.table(); + let socket = table.get_resource(&this)?; + match socket.family { + AddressFamily::Ipv4 => Ok(IpAddressFamily::Ipv4), + AddressFamily::Ipv6 => Ok(IpAddressFamily::Ipv6), + } + } + + fn ipv6_only(&mut self, this: Resource) -> SocketResult { + let table = self.table(); + let socket = table.get_resource(&this)?; + Ok(sockopt::get_ipv6_v6only(socket.udp_socket())?) + } + + fn set_ipv6_only(&mut self, this: Resource, value: bool) -> SocketResult<()> { + let table = self.table(); + let socket = table.get_resource(&this)?; + Ok(sockopt::set_ipv6_v6only(socket.udp_socket(), value)?) + } + + fn unicast_hop_limit(&mut self, this: Resource) -> SocketResult { + let table = self.table(); + let socket = table.get_resource(&this)?; + + // We don't track whether the socket is IPv4 or IPv6 so try one and + // fall back to the other. + match sockopt::get_ipv6_unicast_hops(socket.udp_socket()) { + Ok(value) => Ok(value), + Err(Errno::NOPROTOOPT) => { + let value = sockopt::get_ip_ttl(socket.udp_socket())?; + let value = value.try_into().unwrap(); + Ok(value) + } + Err(err) => Err(err.into()), + } + } + + fn set_unicast_hop_limit( + &mut self, + this: Resource, + value: u8, + ) -> SocketResult<()> { + let table = self.table(); + let socket = table.get_resource(&this)?; + + // We don't track whether the socket is IPv4 or IPv6 so try one and + // fall back to the other. + match sockopt::set_ipv6_unicast_hops(socket.udp_socket(), Some(value)) { + Ok(()) => Ok(()), + Err(Errno::NOPROTOOPT) => Ok(sockopt::set_ip_ttl(socket.udp_socket(), value.into())?), + Err(err) => Err(err.into()), + } + } + + fn receive_buffer_size(&mut self, this: Resource) -> SocketResult { + let table = self.table(); + let socket = table.get_resource(&this)?; + Ok(sockopt::get_socket_recv_buffer_size(socket.udp_socket())? as u64) + } + + fn set_receive_buffer_size( + &mut self, + this: Resource, + value: u64, + ) -> SocketResult<()> { + let table = self.table(); + let socket = table.get_resource(&this)?; + let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; + Ok(sockopt::set_socket_recv_buffer_size( + socket.udp_socket(), + value, + )?) + } + + fn send_buffer_size(&mut self, this: Resource) -> SocketResult { + let table = self.table(); + let socket = table.get_resource(&this)?; + Ok(sockopt::get_socket_send_buffer_size(socket.udp_socket())? as u64) + } + + fn set_send_buffer_size( + &mut self, + this: Resource, + value: u64, + ) -> SocketResult<()> { + let table = self.table(); + let socket = table.get_resource(&this)?; + let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?; + Ok(sockopt::set_socket_send_buffer_size( + socket.udp_socket(), + value, + )?) + } + + fn subscribe(&mut self, this: Resource) -> anyhow::Result> { + crate::preview2::poll::subscribe(self.table_mut(), this) + } + + fn drop(&mut self, this: Resource) -> Result<(), anyhow::Error> { + let table = self.table_mut(); + + // As in the filesystem implementation, we assume closing a socket + // doesn't block. + let dropped = table.delete_resource(this)?; + drop(dropped); + + Ok(()) + } +} diff --git a/crates/wasi/src/preview2/host/udp_create_socket.rs b/crates/wasi/src/preview2/host/udp_create_socket.rs new file mode 100644 index 000000000000..7e57e19d5297 --- /dev/null +++ b/crates/wasi/src/preview2/host/udp_create_socket.rs @@ -0,0 +1,15 @@ +use crate::preview2::bindings::{sockets::network::IpAddressFamily, sockets::udp_create_socket}; +use crate::preview2::udp::UdpSocket; +use crate::preview2::{SocketResult, WasiView}; +use wasmtime::component::Resource; + +impl udp_create_socket::Host for T { + fn create_udp_socket( + &mut self, + address_family: IpAddressFamily, + ) -> SocketResult> { + let socket = UdpSocket::new(address_family.into())?; + let socket = self.table_mut().push_resource(socket)?; + Ok(socket) + } +} diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index 8b188e686873..02e6ed405686 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -36,6 +36,7 @@ mod stdio; mod stream; mod table; mod tcp; +mod udp; mod write_stream; pub use self::clocks::{HostMonotonicClock, HostWallClock}; @@ -157,6 +158,7 @@ pub mod bindings { with: { "wasi:sockets/network/network": super::network::Network, "wasi:sockets/tcp/tcp-socket": super::tcp::TcpSocket, + "wasi:sockets/udp/udp-socket": super::udp::UdpSocket, "wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream, "wasi:filesystem/types/directory-entry-stream": super::filesystem::ReaddirIterator, "wasi:filesystem/types/descriptor": super::filesystem::Descriptor, diff --git a/crates/wasi/src/preview2/udp.rs b/crates/wasi/src/preview2/udp.rs new file mode 100644 index 000000000000..2c879f99a6c9 --- /dev/null +++ b/crates/wasi/src/preview2/udp.rs @@ -0,0 +1,92 @@ +use crate::preview2::bindings::sockets::network::IpSocketAddress; +use crate::preview2::poll::Subscribe; +use crate::preview2::with_ambient_tokio_runtime; +use async_trait::async_trait; +use cap_net_ext::{AddressFamily, Blocking, UdpSocketExt}; +use io_lifetimes::raw::{FromRawSocketlike, IntoRawSocketlike}; +use std::io; +use std::sync::Arc; +use tokio::io::Interest; + +/// The state of a UDP socket. +/// +/// This represents the various states a socket can be in during the +/// activities of binding, and connecting. +pub(crate) enum UdpState { + /// The initial state for a newly-created socket. + Default, + + /// Binding started via `start_bind`. + BindStarted, + + /// Binding finished via `finish_bind`. The socket has an address but + /// is not yet listening for connections. + Bound, + + /// A connect call is in progress. + Connecting(IpSocketAddress), + + /// The socket is "connected" to a peer address. + Connected(IpSocketAddress), +} + +/// A host UDP socket, plus associated bookkeeping. +/// +/// The inner state is wrapped in an Arc because the same underlying socket is +/// used for implementing the stream types. +pub struct UdpSocket { + /// The part of a `UdpSocket` which is reference-counted so that we + /// can pass it to async tasks. + pub(crate) inner: Arc, + + /// The current state in the bind/connect progression. + pub(crate) udp_state: UdpState, + + /// Socket address family. + pub(crate) family: AddressFamily, +} + +#[async_trait] +impl Subscribe for UdpSocket { + async fn ready(&mut self) { + // Some states are ready immediately. + match self.udp_state { + UdpState::BindStarted => return, + _ => {} + } + + // FIXME: Add `Interest::ERROR` when we update to tokio 1.32. + self.inner + .ready(Interest::READABLE | Interest::WRITABLE) + .await + .expect("failed to await UDP socket readiness"); + } +} + +impl UdpSocket { + /// Create a new socket in the given family. + pub fn new(family: AddressFamily) -> io::Result { + // Create a new host socket and set it to non-blocking, which is needed + // by our async implementation. + let udp_socket = cap_std::net::UdpSocket::new(family, Blocking::No)?; + Self::from_udp_socket(udp_socket, family) + } + + pub fn from_udp_socket( + udp_socket: cap_std::net::UdpSocket, + family: AddressFamily, + ) -> io::Result { + let fd = udp_socket.into_raw_socketlike(); + let std_socket = unsafe { std::net::UdpSocket::from_raw_socketlike(fd) }; + let socket = with_ambient_tokio_runtime(|| tokio::net::UdpSocket::try_from(std_socket))?; + Ok(Self { + inner: Arc::new(socket), + udp_state: UdpState::Default, + family, + }) + } + + pub fn udp_socket(&self) -> &tokio::net::UdpSocket { + &self.inner + } +} diff --git a/crates/wasi/wit/test.wit b/crates/wasi/wit/test.wit index fc9c357522bf..a0d1d07a6c64 100644 --- a/crates/wasi/wit/test.wit +++ b/crates/wasi/wit/test.wit @@ -37,6 +37,8 @@ world test-command-with-sockets { import wasi:cli/stderr; import wasi:sockets/tcp; import wasi:sockets/tcp-create-socket; + import wasi:sockets/udp; + import wasi:sockets/udp-create-socket; import wasi:sockets/network; import wasi:sockets/instance-network; import wasi:sockets/ip-name-lookup; From 5cde281d7b0a217d2793dab262b9fe3f84deaa66 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Mon, 9 Oct 2023 17:11:18 +0200 Subject: [PATCH 087/199] Ensure exports are properly namespaced (#7196) Continuation of #7172 where imports and export _definitions_ are properly namespaced by version by the usage of exports was not. --- crates/component-macro/tests/codegen/multiversion/root.wit | 1 + crates/wit-bindgen/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/component-macro/tests/codegen/multiversion/root.wit b/crates/component-macro/tests/codegen/multiversion/root.wit index ff23acf81cc9..52225f02beb3 100644 --- a/crates/component-macro/tests/codegen/multiversion/root.wit +++ b/crates/component-macro/tests/codegen/multiversion/root.wit @@ -3,4 +3,5 @@ package foo:bar; world foo { import my:dep/a@0.1.0; import my:dep/a@0.2.0; + export my:dep/a@0.2.0; } diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index 636858e1ff06..2531d0495ea3 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -470,7 +470,7 @@ impl Wasmtime { format!( "exports::{}::{}::{snake}::{camel}", pkgname.namespace.to_snake_case(), - pkgname.name.to_snake_case(), + self.name_package_module(resolve, iface.package.unwrap()), ), format!( "{}_{}_{snake}", From 97db7c4dc5ce6f97e19c330c223fb1621474aa4a Mon Sep 17 00:00:00 2001 From: Anatol Liu Date: Mon, 9 Oct 2023 12:37:53 -0400 Subject: [PATCH 088/199] add i128 support for iabs on aarch64 (#7185) --- cranelift/codegen/src/isa/aarch64/inst.isle | 3 +++ cranelift/codegen/src/isa/aarch64/lower.isle | 20 +++++++++++++++- .../filetests/filetests/isa/aarch64/iabs.clif | 24 +++++++++++++++++++ .../filetests/runtests/i128-iabs.clif | 1 + 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle index 0e1d03334ffa..d9c064f6c6cb 100644 --- a/cranelift/codegen/src/isa/aarch64/inst.isle +++ b/cranelift/codegen/src/isa/aarch64/inst.isle @@ -2704,6 +2704,9 @@ (rule (and_vec x y size) (vec_rrr (VecALUOp.And) x y size)) ;; Helpers for generating `eor` instructions. +(decl eor (Type Reg Reg) Reg) +(rule (eor ty x y) (alu_rrr (ALUOp.Eor) ty x y)) + (decl eor_vec (Reg Reg VectorSize) Reg) (rule (eor_vec x y size) (vec_rrr (VecALUOp.Eor) x y size)) diff --git a/cranelift/codegen/src/isa/aarch64/lower.isle b/cranelift/codegen/src/isa/aarch64/lower.isle index bc548ce8f18b..4d8b29fb6fd2 100644 --- a/cranelift/codegen/src/isa/aarch64/lower.isle +++ b/cranelift/codegen/src/isa/aarch64/lower.isle @@ -339,7 +339,7 @@ ;;;; Rules for `iabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(rule (lower (has_type ty @ (multi_lane _ _) (iabs x))) +(rule -1 (lower (has_type ty @ (multi_lane _ _) (iabs x))) (vec_abs x (vector_size ty))) (rule 2 (lower (has_type $I64 (iabs x))) @@ -348,6 +348,24 @@ (rule 1 (lower (has_type (fits_in_32 ty) (iabs x))) (abs (OperandSize.Size32) (put_in_reg_sext32 x))) +; `rustc` implementation. +; - create a bitmask of all 1s if negative, or 0s if positive. +; - xor all bits by bitmask. then subtract bitmask from xor'd values. +; - if `x` is positive, the xor'd bits = x and the mask = 0, so we end up with +; `x - 0`. +; - if `x` is negative, the xor'd bits = ~x and the mask = -1, so we end up with +; `~x - (-1) = ~x + 1`, which is exactly `abs(x)`. +(rule (lower (has_type $I128 (iabs x))) + (let ((x_regs ValueRegs x) + (x_lo Reg (value_regs_get x_regs 0)) + (x_hi Reg (value_regs_get x_regs 1)) + (asr_reg Reg (asr_imm $I64 x_hi (imm_shift_from_u8 63))) + (eor_hi Reg (eor $I64 x_hi asr_reg)) + (eor_lo Reg (eor $I64 x_lo asr_reg)) + (subs_lo ProducesFlags (sub_with_flags_paired $I64 eor_lo asr_reg)) + (sbc_hi ConsumesFlags (sbc_paired $I64 eor_hi asr_reg))) + (with_flags subs_lo sbc_hi))) + ;;;; Rules for `avg_round` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (has_type $I64X2 (avg_round x y))) diff --git a/cranelift/filetests/filetests/isa/aarch64/iabs.clif b/cranelift/filetests/filetests/isa/aarch64/iabs.clif index c559a2313e11..b76916e8bba7 100644 --- a/cranelift/filetests/filetests/isa/aarch64/iabs.clif +++ b/cranelift/filetests/filetests/isa/aarch64/iabs.clif @@ -190,3 +190,27 @@ block0(v0: i64): ; cneg x0, x0, le ; ret +function %f11(i128) -> i128 { +block0(v0: i128): + v1 = iabs v0 + return v1 +} + +; VCode: +; block0: +; asr x3, x1, #63 +; eor x5, x1, x3 +; eor x7, x0, x3 +; subs x0, x7, x3 +; sbc x1, x5, x3 +; ret +; +; Disassembled: +; block0: ; offset 0x0 +; asr x3, x1, #0x3f +; eor x5, x1, x3 +; eor x7, x0, x3 +; subs x0, x7, x3 +; sbc x1, x5, x3 +; ret + diff --git a/cranelift/filetests/filetests/runtests/i128-iabs.clif b/cranelift/filetests/filetests/runtests/i128-iabs.clif index 578596c8b634..5078f8ac06ed 100644 --- a/cranelift/filetests/filetests/runtests/i128-iabs.clif +++ b/cranelift/filetests/filetests/runtests/i128-iabs.clif @@ -1,6 +1,7 @@ test interpret test run set enable_llvm_abi_extensions=true +target aarch64 target s390x target x86_64 From faa8838e60cea94f485b78420d69e3c7239a3b19 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Mon, 9 Oct 2023 18:40:01 +0200 Subject: [PATCH 089/199] Reapply #7150 (#7197) Signed-off-by: Ryan Levick --- crates/wasi/src/preview2/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index 02e6ed405686..a64efa7708a4 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -46,7 +46,9 @@ pub use self::filesystem::{DirPerms, FilePerms, FsError, FsResult}; pub use self::network::{Network, SocketError, SocketResult}; pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe}; pub use self::random::{thread_rng, Deterministic}; -pub use self::stdio::{stderr, stdin, stdout, IsATTY, Stderr, Stdin, Stdout}; +pub use self::stdio::{ + stderr, stdin, stdout, IsATTY, Stderr, Stdin, StdinStream, Stdout, StdoutStream, +}; pub use self::stream::{ HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult, }; From 23b75f6df3f59ead9474139cb67ac7aa9ef43171 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 9 Oct 2023 14:08:54 -0500 Subject: [PATCH 090/199] riscv64: Optimize `gen_bmask` slightly (#7200) If the input operand is an `icmp` or an `fcmp` there's no need to use `snez` since the output value is already guaranteed to be zero or one. --- cranelift/codegen/src/isa/riscv64/inst.isle | 10 + .../isa/riscv64/select_spectre_guard.clif | 1014 ++++++++--------- ..._guard_yes_spectre_i32_access_0_offset.wat | 54 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 62 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 76 +- ...0_guard_yes_spectre_i8_access_0_offset.wat | 48 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 62 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 72 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 48 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 56 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 56 +- ...f_guard_yes_spectre_i8_access_0_offset.wat | 48 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 56 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 56 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 46 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 50 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 64 +- ...0_guard_yes_spectre_i8_access_0_offset.wat | 34 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 50 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 46 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 34 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 50 +- ...s_spectre_i32_access_0xffff0000_offset.wat | 56 +- ...f_guard_yes_spectre_i8_access_0_offset.wat | 34 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 50 +- ...es_spectre_i8_access_0xffff0000_offset.wat | 56 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 54 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 67 +- ...0_guard_yes_spectre_i8_access_0_offset.wat | 54 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 67 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 46 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 56 +- ...0_guard_yes_spectre_i8_access_0_offset.wat | 46 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 56 +- ..._guard_yes_spectre_i32_access_0_offset.wat | 46 +- ...d_yes_spectre_i32_access_0x1000_offset.wat | 56 +- ...f_guard_yes_spectre_i8_access_0_offset.wat | 46 +- ...rd_yes_spectre_i8_access_0x1000_offset.wat | 56 +- 38 files changed, 1417 insertions(+), 1521 deletions(-) diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 4c99f7c8f2aa..40db1f1848a7 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -2902,6 +2902,9 @@ ;; Generates either 0 if `Value` is zero or -1 otherwise. (decl gen_bmask (Value) XReg) + +;; Base cases: use `snez` after a sign extension to ensure that the entire +;; register is defined. For i128 we test both the upper and lower half. (rule 0 (gen_bmask val @ (value_type (fits_in_64 _))) (let ((non_zero XReg (rv_snez (sext val)))) (rv_neg non_zero))) @@ -2909,6 +2912,13 @@ (let ((non_zero XReg (rv_snez (rv_or (value_regs_get val 0) (value_regs_get val 1))))) (rv_neg non_zero))) +;; If the input value is an `icmp` or an `fcmp` directly then the `snez` can +;; be omitted because the result of the icmp or fcmp is a 0 or 1 directly. This +;; means we can go straight to the `neg` instruction to produce the final +;; result. +(rule 2 (gen_bmask val @ (maybe_uextend (icmp _ _ _))) (rv_neg val)) +(rule 2 (gen_bmask val @ (maybe_uextend (fcmp _ _ _))) (rv_neg val)) + (decl lower_bmask (Value Type) ValueRegs) (rule 0 (lower_bmask val (fits_in_64 _)) (value_reg (gen_bmask val))) diff --git a/cranelift/filetests/filetests/isa/riscv64/select_spectre_guard.clif b/cranelift/filetests/filetests/isa/riscv64/select_spectre_guard.clif index ab91c78b6c7e..d10455421760 100644 --- a/cranelift/filetests/filetests/isa/riscv64/select_spectre_guard.clif +++ b/cranelift/filetests/filetests/isa/riscv64/select_spectre_guard.clif @@ -12,33 +12,33 @@ block0(v0: i8, v1: i8, v2: i8): ; VCode: ; block0: -; li a3,42 -; andi a5,a0,255 -; andi a3,a3,255 -; eq a3,a5,a3##ty=i8 -; sltu a3,zero,a3 -; sub a4,zero,a3 -; and a0,a1,a4 -; not a3,a4 -; and a4,a2,a3 -; or a0,a0,a4 +; mv a3,a0 +; li a0,42 +; andi a4,a3,255 +; andi a0,a0,255 +; eq a3,a4,a0##ty=i8 +; sub a3,zero,a3 +; and a4,a1,a3 +; not a0,a3 +; and a2,a2,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; andi a5, a0, 0xff -; andi a3, a3, 0xff -; bne a5, a3, 0xc +; mv a3, a0 +; addi a0, zero, 0x2a +; andi a4, a3, 0xff +; andi a0, a0, 0xff +; bne a4, a0, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; snez a3, a3 -; neg a4, a3 -; and a0, a1, a4 -; not a3, a4 -; and a4, a2, a3 -; or a0, a0, a4 +; neg a3, a3 +; and a4, a1, a3 +; not a0, a3 +; and a2, a2, a0 +; or a0, a4, a2 ; ret function %f(i8, i16, i16) -> i16 { @@ -51,33 +51,33 @@ block0(v0: i8, v1: i16, v2: i16): ; VCode: ; block0: -; li a3,42 -; andi a5,a0,255 -; andi a3,a3,255 -; eq a3,a5,a3##ty=i8 -; sltu a3,zero,a3 -; sub a4,zero,a3 -; and a0,a1,a4 -; not a3,a4 -; and a4,a2,a3 -; or a0,a0,a4 +; mv a3,a0 +; li a0,42 +; andi a4,a3,255 +; andi a0,a0,255 +; eq a3,a4,a0##ty=i8 +; sub a3,zero,a3 +; and a4,a1,a3 +; not a0,a3 +; and a2,a2,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; andi a5, a0, 0xff -; andi a3, a3, 0xff -; bne a5, a3, 0xc +; mv a3, a0 +; addi a0, zero, 0x2a +; andi a4, a3, 0xff +; andi a0, a0, 0xff +; bne a4, a0, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; snez a3, a3 -; neg a4, a3 -; and a0, a1, a4 -; not a3, a4 -; and a4, a2, a3 -; or a0, a0, a4 +; neg a3, a3 +; and a4, a1, a3 +; not a0, a3 +; and a2, a2, a0 +; or a0, a4, a2 ; ret function %f(i8, i32, i32) -> i32 { @@ -90,33 +90,33 @@ block0(v0: i8, v1: i32, v2: i32): ; VCode: ; block0: -; li a3,42 -; andi a5,a0,255 -; andi a3,a3,255 -; eq a3,a5,a3##ty=i8 -; sltu a3,zero,a3 -; sub a4,zero,a3 -; and a0,a1,a4 -; not a3,a4 -; and a4,a2,a3 -; or a0,a0,a4 +; mv a3,a0 +; li a0,42 +; andi a4,a3,255 +; andi a0,a0,255 +; eq a3,a4,a0##ty=i8 +; sub a3,zero,a3 +; and a4,a1,a3 +; not a0,a3 +; and a2,a2,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; andi a5, a0, 0xff -; andi a3, a3, 0xff -; bne a5, a3, 0xc +; mv a3, a0 +; addi a0, zero, 0x2a +; andi a4, a3, 0xff +; andi a0, a0, 0xff +; bne a4, a0, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; snez a3, a3 -; neg a4, a3 -; and a0, a1, a4 -; not a3, a4 -; and a4, a2, a3 -; or a0, a0, a4 +; neg a3, a3 +; and a4, a1, a3 +; not a0, a3 +; and a2, a2, a0 +; or a0, a4, a2 ; ret function %f(i8, i64, i64) -> i64 { @@ -129,33 +129,33 @@ block0(v0: i8, v1: i64, v2: i64): ; VCode: ; block0: -; li a3,42 -; andi a5,a0,255 -; andi a3,a3,255 -; eq a3,a5,a3##ty=i8 -; sltu a3,zero,a3 -; sub a4,zero,a3 -; and a0,a1,a4 -; not a3,a4 -; and a4,a2,a3 -; or a0,a0,a4 +; mv a3,a0 +; li a0,42 +; andi a4,a3,255 +; andi a0,a0,255 +; eq a3,a4,a0##ty=i8 +; sub a3,zero,a3 +; and a4,a1,a3 +; not a0,a3 +; and a2,a2,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a3, zero, 0x2a -; andi a5, a0, 0xff -; andi a3, a3, 0xff -; bne a5, a3, 0xc +; mv a3, a0 +; addi a0, zero, 0x2a +; andi a4, a3, 0xff +; andi a0, a0, 0xff +; bne a4, a0, 0xc ; addi a3, zero, 1 ; j 8 ; mv a3, zero -; snez a3, a3 -; neg a4, a3 -; and a0, a1, a4 -; not a3, a4 -; and a4, a2, a3 -; or a0, a0, a4 +; neg a3, a3 +; and a4, a1, a3 +; not a0, a3 +; and a2, a2, a0 +; or a0, a4, a2 ; ret function %f(i8, i128, i128) -> i128 { @@ -171,25 +171,25 @@ block0(v0: i8, v1: i128, v2: i128): ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s6,-8(sp) +; sd s10,-8(sp) ; add sp,-16 ; block0: -; li a5,42 -; andi a0,a0,255 +; mv a5,a0 +; li a0,42 ; andi a5,a5,255 -; eq a5,a0,a5##ty=i8 -; sltu a5,zero,a5 -; sub s6,zero,a5 -; and a0,a1,s6 -; and a5,a2,s6 -; not a2,s6 -; not a1,s6 -; and a2,a3,a2 -; and a1,a4,a1 -; or a0,a0,a2 -; or a1,a5,a1 +; andi a0,a0,255 +; eq a5,a5,a0##ty=i8 +; sub a5,zero,a5 +; and a0,a1,a5 +; and a2,a2,a5 +; not s10,a5 +; not a1,a5 +; and a3,a3,s10 +; and a4,a4,a1 +; or a0,a0,a3 +; or a1,a2,a4 ; add sp,+16 -; ld s6,-8(sp) +; ld s10,-8(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -201,28 +201,28 @@ block0(v0: i8, v1: i128, v2: i128): ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s6, -8(sp) +; sd s10, -8(sp) ; addi sp, sp, -0x10 ; block1: ; offset 0x18 -; addi a5, zero, 0x2a -; andi a0, a0, 0xff +; mv a5, a0 +; addi a0, zero, 0x2a ; andi a5, a5, 0xff -; bne a0, a5, 0xc +; andi a0, a0, 0xff +; bne a5, a0, 0xc ; addi a5, zero, 1 ; j 8 ; mv a5, zero -; snez a5, a5 -; neg s6, a5 -; and a0, a1, s6 -; and a5, a2, s6 -; not a2, s6 -; not a1, s6 -; and a2, a3, a2 -; and a1, a4, a1 -; or a0, a0, a2 -; or a1, a5, a1 +; neg a5, a5 +; and a0, a1, a5 +; and a2, a2, a5 +; not s10, a5 +; not a1, a5 +; and a3, a3, s10 +; and a4, a4, a1 +; or a0, a0, a3 +; or a1, a2, a4 ; addi sp, sp, 0x10 -; ld s6, -8(sp) +; ld s10, -8(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -239,36 +239,34 @@ block0(v0: i16, v1: i8, v2: i8): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,48 -; srli a4,a5,48 +; slli a4,a0,48 +; srli a0,a4,48 ; slli a3,a3,48 -; srli a5,a3,48 -; eq a3,a4,a5##ty=i16 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; srli a4,a3,48 +; eq a0,a0,a4##ty=i16 +; sub a4,zero,a0 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x30 -; srli a4, a5, 0x30 +; slli a4, a0, 0x30 +; srli a0, a4, 0x30 ; slli a3, a3, 0x30 -; srli a5, a3, 0x30 -; bne a4, a5, 0xc -; addi a3, zero, 1 +; srli a4, a3, 0x30 +; bne a0, a4, 0xc +; addi a0, zero, 1 ; j 8 -; mv a3, zero -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a0, zero +; neg a4, a0 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i16, i16, i16) -> i16 { @@ -282,36 +280,34 @@ block0(v0: i16, v1: i16, v2: i16): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,48 -; srli a4,a5,48 +; slli a4,a0,48 +; srli a0,a4,48 ; slli a3,a3,48 -; srli a5,a3,48 -; eq a3,a4,a5##ty=i16 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; srli a4,a3,48 +; eq a0,a0,a4##ty=i16 +; sub a4,zero,a0 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x30 -; srli a4, a5, 0x30 +; slli a4, a0, 0x30 +; srli a0, a4, 0x30 ; slli a3, a3, 0x30 -; srli a5, a3, 0x30 -; bne a4, a5, 0xc -; addi a3, zero, 1 +; srli a4, a3, 0x30 +; bne a0, a4, 0xc +; addi a0, zero, 1 ; j 8 -; mv a3, zero -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a0, zero +; neg a4, a0 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i16, i32, i32) -> i32 { @@ -325,36 +321,34 @@ block0(v0: i16, v1: i32, v2: i32): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,48 -; srli a4,a5,48 +; slli a4,a0,48 +; srli a0,a4,48 ; slli a3,a3,48 -; srli a5,a3,48 -; eq a3,a4,a5##ty=i16 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; srli a4,a3,48 +; eq a0,a0,a4##ty=i16 +; sub a4,zero,a0 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x30 -; srli a4, a5, 0x30 +; slli a4, a0, 0x30 +; srli a0, a4, 0x30 ; slli a3, a3, 0x30 -; srli a5, a3, 0x30 -; bne a4, a5, 0xc -; addi a3, zero, 1 +; srli a4, a3, 0x30 +; bne a0, a4, 0xc +; addi a0, zero, 1 ; j 8 -; mv a3, zero -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a0, zero +; neg a4, a0 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i16, i64, i64) -> i64 { @@ -368,36 +362,34 @@ block0(v0: i16, v1: i64, v2: i64): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,48 -; srli a4,a5,48 +; slli a4,a0,48 +; srli a0,a4,48 ; slli a3,a3,48 -; srli a5,a3,48 -; eq a3,a4,a5##ty=i16 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; srli a4,a3,48 +; eq a0,a0,a4##ty=i16 +; sub a4,zero,a0 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x30 -; srli a4, a5, 0x30 +; slli a4, a0, 0x30 +; srli a0, a4, 0x30 ; slli a3, a3, 0x30 -; srli a5, a3, 0x30 -; bne a4, a5, 0xc -; addi a3, zero, 1 +; srli a4, a3, 0x30 +; bne a0, a4, 0xc +; addi a0, zero, 1 ; j 8 -; mv a3, zero -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a0, zero +; neg a4, a0 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i16, i128, i128) -> i128 { @@ -413,7 +405,7 @@ block0(v0: i16, v1: i128, v2: i128): ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s8,-8(sp) +; sd s6,-8(sp) ; add sp,-16 ; block0: ; li a5,42 @@ -421,19 +413,18 @@ block0(v0: i16, v1: i128, v2: i128): ; srli a0,a0,48 ; slli a5,a5,48 ; srli a5,a5,48 -; eq a5,a0,a5##ty=i16 -; sltu a0,zero,a5 -; sub s8,zero,a0 -; and a5,a1,s8 -; and a1,a2,s8 -; not a0,s8 -; not a2,s8 -; and a0,a3,a0 -; and a2,a4,a2 -; or a0,a5,a0 -; or a1,a1,a2 +; eq a0,a0,a5##ty=i16 +; sub s6,zero,a0 +; and a0,a1,s6 +; and a5,a2,s6 +; not a2,s6 +; not a1,s6 +; and a2,a3,a2 +; and a1,a4,a1 +; or a0,a0,a2 +; or a1,a5,a1 ; add sp,+16 -; ld s8,-8(sp) +; ld s6,-8(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -445,7 +436,7 @@ block0(v0: i16, v1: i128, v2: i128): ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s8, -8(sp) +; sd s6, -8(sp) ; addi sp, sp, -0x10 ; block1: ; offset 0x18 ; addi a5, zero, 0x2a @@ -454,21 +445,20 @@ block0(v0: i16, v1: i128, v2: i128): ; slli a5, a5, 0x30 ; srli a5, a5, 0x30 ; bne a0, a5, 0xc -; addi a5, zero, 1 +; addi a0, zero, 1 ; j 8 -; mv a5, zero -; snez a0, a5 -; neg s8, a0 -; and a5, a1, s8 -; and a1, a2, s8 -; not a0, s8 -; not a2, s8 -; and a0, a3, a0 -; and a2, a4, a2 -; or a0, a5, a0 -; or a1, a1, a2 +; mv a0, zero +; neg s6, a0 +; and a0, a1, s6 +; and a5, a2, s6 +; not a2, s6 +; not a1, s6 +; and a2, a3, a2 +; and a1, a4, a1 +; or a0, a0, a2 +; or a1, a5, a1 ; addi sp, sp, 0x10 -; ld s8, -8(sp) +; ld s6, -8(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -485,36 +475,34 @@ block0(v0: i32, v1: i8, v2: i8): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,32 -; srli a4,a5,32 +; slli a4,a0,32 +; srli a0,a4,32 ; slli a3,a3,32 -; srli a5,a3,32 -; eq a3,a4,a5##ty=i32 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; srli a4,a3,32 +; eq a0,a0,a4##ty=i32 +; sub a4,zero,a0 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a4, a5, 0x20 +; slli a4, a0, 0x20 +; srli a0, a4, 0x20 ; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; bne a4, a5, 0xc -; addi a3, zero, 1 +; srli a4, a3, 0x20 +; bne a0, a4, 0xc +; addi a0, zero, 1 ; j 8 -; mv a3, zero -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a0, zero +; neg a4, a0 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i32, i16, i16) -> i16 { @@ -527,37 +515,35 @@ block0(v0: i32, v1: i16, v2: i16): ; VCode: ; block0: -; li a3,42 -; slli a5,a0,32 -; srli a4,a5,32 -; slli a3,a3,32 -; srli a5,a3,32 -; eq a3,a4,a5##ty=i32 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; li a3,42 +; slli a4,a0,32 +; srli a0,a4,32 +; slli a3,a3,32 +; srli a4,a3,32 +; eq a0,a0,a4##ty=i32 +; sub a4,zero,a0 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a4, a5, 0x20 +; slli a4, a0, 0x20 +; srli a0, a4, 0x20 ; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; bne a4, a5, 0xc -; addi a3, zero, 1 +; srli a4, a3, 0x20 +; bne a0, a4, 0xc +; addi a0, zero, 1 ; j 8 -; mv a3, zero -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a0, zero +; neg a4, a0 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i32, i32, i32) -> i32 { @@ -571,36 +557,34 @@ block0(v0: i32, v1: i32, v2: i32): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,32 -; srli a4,a5,32 +; slli a4,a0,32 +; srli a0,a4,32 ; slli a3,a3,32 -; srli a5,a3,32 -; eq a3,a4,a5##ty=i32 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; srli a4,a3,32 +; eq a0,a0,a4##ty=i32 +; sub a4,zero,a0 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a4, a5, 0x20 +; slli a4, a0, 0x20 +; srli a0, a4, 0x20 ; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; bne a4, a5, 0xc -; addi a3, zero, 1 +; srli a4, a3, 0x20 +; bne a0, a4, 0xc +; addi a0, zero, 1 ; j 8 -; mv a3, zero -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a0, zero +; neg a4, a0 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i32, i64, i64) -> i64 { @@ -614,36 +598,34 @@ block0(v0: i32, v1: i64, v2: i64): ; VCode: ; block0: ; li a3,42 -; slli a5,a0,32 -; srli a4,a5,32 +; slli a4,a0,32 +; srli a0,a4,32 ; slli a3,a3,32 -; srli a5,a3,32 -; eq a3,a4,a5##ty=i32 -; sltu a4,zero,a3 -; sub a0,zero,a4 -; and a3,a1,a0 -; not a4,a0 -; and a0,a2,a4 -; or a0,a3,a0 +; srli a4,a3,32 +; eq a0,a0,a4##ty=i32 +; sub a4,zero,a0 +; and a0,a1,a4 +; not a3,a4 +; and a4,a2,a3 +; or a0,a0,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 ; addi a3, zero, 0x2a -; slli a5, a0, 0x20 -; srli a4, a5, 0x20 +; slli a4, a0, 0x20 +; srli a0, a4, 0x20 ; slli a3, a3, 0x20 -; srli a5, a3, 0x20 -; bne a4, a5, 0xc -; addi a3, zero, 1 +; srli a4, a3, 0x20 +; bne a0, a4, 0xc +; addi a0, zero, 1 ; j 8 -; mv a3, zero -; snez a4, a3 -; neg a0, a4 -; and a3, a1, a0 -; not a4, a0 -; and a0, a2, a4 -; or a0, a3, a0 +; mv a0, zero +; neg a4, a0 +; and a0, a1, a4 +; not a3, a4 +; and a4, a2, a3 +; or a0, a0, a4 ; ret function %f(i32, i128, i128) -> i128 { @@ -659,7 +641,7 @@ block0(v0: i32, v1: i128, v2: i128): ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s8,-8(sp) +; sd s6,-8(sp) ; add sp,-16 ; block0: ; li a5,42 @@ -667,19 +649,18 @@ block0(v0: i32, v1: i128, v2: i128): ; srli a0,a0,32 ; slli a5,a5,32 ; srli a5,a5,32 -; eq a5,a0,a5##ty=i32 -; sltu a0,zero,a5 -; sub s8,zero,a0 -; and a5,a1,s8 -; and a1,a2,s8 -; not a0,s8 -; not a2,s8 -; and a0,a3,a0 -; and a2,a4,a2 -; or a0,a5,a0 -; or a1,a1,a2 +; eq a0,a0,a5##ty=i32 +; sub s6,zero,a0 +; and a0,a1,s6 +; and a5,a2,s6 +; not a2,s6 +; not a1,s6 +; and a2,a3,a2 +; and a1,a4,a1 +; or a0,a0,a2 +; or a1,a5,a1 ; add sp,+16 -; ld s8,-8(sp) +; ld s6,-8(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -691,7 +672,7 @@ block0(v0: i32, v1: i128, v2: i128): ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s8, -8(sp) +; sd s6, -8(sp) ; addi sp, sp, -0x10 ; block1: ; offset 0x18 ; addi a5, zero, 0x2a @@ -700,21 +681,20 @@ block0(v0: i32, v1: i128, v2: i128): ; slli a5, a5, 0x20 ; srli a5, a5, 0x20 ; bne a0, a5, 0xc -; addi a5, zero, 1 +; addi a0, zero, 1 ; j 8 -; mv a5, zero -; snez a0, a5 -; neg s8, a0 -; and a5, a1, s8 -; and a1, a2, s8 -; not a0, s8 -; not a2, s8 -; and a0, a3, a0 -; and a2, a4, a2 -; or a0, a5, a0 -; or a1, a1, a2 +; mv a0, zero +; neg s6, a0 +; and a0, a1, s6 +; and a5, a2, s6 +; not a2, s6 +; not a1, s6 +; and a2, a3, a2 +; and a1, a4, a1 +; or a0, a0, a2 +; or a1, a5, a1 ; addi sp, sp, 0x10 -; ld s8, -8(sp) +; ld s6, -8(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -730,29 +710,27 @@ block0(v0: i64, v1: i8, v2: i8): ; VCode: ; block0: -; li a5,42 -; eq a5,a0,a5##ty=i64 -; sltu a0,zero,a5 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a4,42 +; eq a4,a0,a4##ty=i64 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a5, zero, 0x2a -; bne a0, a5, 0xc -; addi a5, zero, 1 +; addi a4, zero, 0x2a +; bne a0, a4, 0xc +; addi a4, zero, 1 ; j 8 -; mv a5, zero -; snez a0, a5 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; mv a4, zero +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i64, i16, i16) -> i16 { @@ -765,29 +743,27 @@ block0(v0: i64, v1: i16, v2: i16): ; VCode: ; block0: -; li a5,42 -; eq a5,a0,a5##ty=i64 -; sltu a0,zero,a5 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a4,42 +; eq a4,a0,a4##ty=i64 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a5, zero, 0x2a -; bne a0, a5, 0xc -; addi a5, zero, 1 +; addi a4, zero, 0x2a +; bne a0, a4, 0xc +; addi a4, zero, 1 ; j 8 -; mv a5, zero -; snez a0, a5 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; mv a4, zero +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i64, i32, i32) -> i32 { @@ -800,29 +776,27 @@ block0(v0: i64, v1: i32, v2: i32): ; VCode: ; block0: -; li a5,42 -; eq a5,a0,a5##ty=i64 -; sltu a0,zero,a5 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a4,42 +; eq a4,a0,a4##ty=i64 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a5, zero, 0x2a -; bne a0, a5, 0xc -; addi a5, zero, 1 +; addi a4, zero, 0x2a +; bne a0, a4, 0xc +; addi a4, zero, 1 ; j 8 -; mv a5, zero -; snez a0, a5 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; mv a4, zero +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i64, i64, i64) -> i64 { @@ -835,29 +809,27 @@ block0(v0: i64, v1: i64, v2: i64): ; VCode: ; block0: -; li a5,42 -; eq a5,a0,a5##ty=i64 -; sltu a0,zero,a5 -; sub a3,zero,a0 -; and a4,a1,a3 -; not a0,a3 -; and a2,a2,a0 -; or a0,a4,a2 +; li a4,42 +; eq a4,a0,a4##ty=i64 +; sub a0,zero,a4 +; and a3,a1,a0 +; not a4,a0 +; and a0,a2,a4 +; or a0,a3,a0 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi a5, zero, 0x2a -; bne a0, a5, 0xc -; addi a5, zero, 1 +; addi a4, zero, 0x2a +; bne a0, a4, 0xc +; addi a4, zero, 1 ; j 8 -; mv a5, zero -; snez a0, a5 -; neg a3, a0 -; and a4, a1, a3 -; not a0, a3 -; and a2, a2, a0 -; or a0, a4, a2 +; mv a4, zero +; neg a0, a4 +; and a3, a1, a0 +; not a4, a0 +; and a0, a2, a4 +; or a0, a3, a0 ; ret function %f(i64, i128, i128) -> i128 { @@ -873,23 +845,24 @@ block0(v0: i64, v1: i128, v2: i128): ; sd ra,8(sp) ; sd fp,0(sp) ; mv fp,sp -; sd s10,-8(sp) +; sd s11,-8(sp) ; add sp,-16 ; block0: +; mv s11,a1 ; li a5,42 ; eq a5,a0,a5##ty=i64 -; sltu a5,zero,a5 -; sub a5,zero,a5 -; and a0,a1,a5 -; and a2,a2,a5 -; not s10,a5 -; not a1,a5 -; and a3,a3,s10 -; and a4,a4,a1 -; or a0,a0,a3 -; or a1,a2,a4 +; sub a1,zero,a5 +; mv a5,s11 +; and a5,a5,a1 +; and a2,a2,a1 +; not a0,a1 +; not a1,a1 +; and a0,a3,a0 +; and a3,a4,a1 +; or a0,a5,a0 +; or a1,a2,a3 ; add sp,+16 -; ld s10,-8(sp) +; ld s11,-8(sp) ; ld ra,8(sp) ; ld fp,0(sp) ; add sp,+16 @@ -901,26 +874,27 @@ block0(v0: i64, v1: i128, v2: i128): ; sd ra, 8(sp) ; sd s0, 0(sp) ; mv s0, sp -; sd s10, -8(sp) +; sd s11, -8(sp) ; addi sp, sp, -0x10 ; block1: ; offset 0x18 +; mv s11, a1 ; addi a5, zero, 0x2a ; bne a0, a5, 0xc ; addi a5, zero, 1 ; j 8 ; mv a5, zero -; snez a5, a5 -; neg a5, a5 -; and a0, a1, a5 -; and a2, a2, a5 -; not s10, a5 -; not a1, a5 -; and a3, a3, s10 -; and a4, a4, a1 -; or a0, a0, a3 -; or a1, a2, a4 +; neg a1, a5 +; mv a5, s11 +; and a5, a5, a1 +; and a2, a2, a1 +; not a0, a1 +; not a1, a1 +; and a0, a3, a0 +; and a3, a4, a1 +; or a0, a5, a0 +; or a1, a2, a3 ; addi sp, sp, 0x10 -; ld s10, -8(sp) +; ld s11, -8(sp) ; ld ra, 8(sp) ; ld s0, 0(sp) ; addi sp, sp, 0x10 @@ -939,13 +913,12 @@ block0(v0: i128, v1: i8, v2: i8): ; block0: ; li a4,42 ; li a5,0 -; eq a1,[a0,a1],[a4,a5]##ty=i128 -; sltu a4,zero,a1 -; sub a4,zero,a4 -; and a0,a2,a4 -; not a2,a4 -; and a4,a3,a2 -; or a0,a0,a4 +; eq a0,[a0,a1],[a4,a5]##ty=i128 +; sub a5,zero,a0 +; and a4,a2,a5 +; not a0,a5 +; and a2,a3,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: @@ -954,15 +927,14 @@ block0(v0: i128, v1: i8, v2: i8): ; mv a5, zero ; bne a1, a5, 0x10 ; bne a0, a4, 0xc -; addi a1, zero, 1 +; addi a0, zero, 1 ; j 8 -; mv a1, zero -; snez a4, a1 -; neg a4, a4 -; and a0, a2, a4 -; not a2, a4 -; and a4, a3, a2 -; or a0, a0, a4 +; mv a0, zero +; neg a5, a0 +; and a4, a2, a5 +; not a0, a5 +; and a2, a3, a0 +; or a0, a4, a2 ; ret function %f(i128, i16, i16) -> i16 { @@ -978,13 +950,12 @@ block0(v0: i128, v1: i16, v2: i16): ; block0: ; li a4,42 ; li a5,0 -; eq a1,[a0,a1],[a4,a5]##ty=i128 -; sltu a4,zero,a1 -; sub a4,zero,a4 -; and a0,a2,a4 -; not a2,a4 -; and a4,a3,a2 -; or a0,a0,a4 +; eq a0,[a0,a1],[a4,a5]##ty=i128 +; sub a5,zero,a0 +; and a4,a2,a5 +; not a0,a5 +; and a2,a3,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: @@ -993,15 +964,14 @@ block0(v0: i128, v1: i16, v2: i16): ; mv a5, zero ; bne a1, a5, 0x10 ; bne a0, a4, 0xc -; addi a1, zero, 1 +; addi a0, zero, 1 ; j 8 -; mv a1, zero -; snez a4, a1 -; neg a4, a4 -; and a0, a2, a4 -; not a2, a4 -; and a4, a3, a2 -; or a0, a0, a4 +; mv a0, zero +; neg a5, a0 +; and a4, a2, a5 +; not a0, a5 +; and a2, a3, a0 +; or a0, a4, a2 ; ret function %f(i128, i32, i32) -> i32 { @@ -1017,13 +987,12 @@ block0(v0: i128, v1: i32, v2: i32): ; block0: ; li a4,42 ; li a5,0 -; eq a1,[a0,a1],[a4,a5]##ty=i128 -; sltu a4,zero,a1 -; sub a4,zero,a4 -; and a0,a2,a4 -; not a2,a4 -; and a4,a3,a2 -; or a0,a0,a4 +; eq a0,[a0,a1],[a4,a5]##ty=i128 +; sub a5,zero,a0 +; and a4,a2,a5 +; not a0,a5 +; and a2,a3,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: @@ -1032,15 +1001,14 @@ block0(v0: i128, v1: i32, v2: i32): ; mv a5, zero ; bne a1, a5, 0x10 ; bne a0, a4, 0xc -; addi a1, zero, 1 +; addi a0, zero, 1 ; j 8 -; mv a1, zero -; snez a4, a1 -; neg a4, a4 -; and a0, a2, a4 -; not a2, a4 -; and a4, a3, a2 -; or a0, a0, a4 +; mv a0, zero +; neg a5, a0 +; and a4, a2, a5 +; not a0, a5 +; and a2, a3, a0 +; or a0, a4, a2 ; ret function %f(i128, i64, i64) -> i64 { @@ -1056,13 +1024,12 @@ block0(v0: i128, v1: i64, v2: i64): ; block0: ; li a4,42 ; li a5,0 -; eq a1,[a0,a1],[a4,a5]##ty=i128 -; sltu a4,zero,a1 -; sub a4,zero,a4 -; and a0,a2,a4 -; not a2,a4 -; and a4,a3,a2 -; or a0,a0,a4 +; eq a0,[a0,a1],[a4,a5]##ty=i128 +; sub a5,zero,a0 +; and a4,a2,a5 +; not a0,a5 +; and a2,a3,a0 +; or a0,a4,a2 ; ret ; ; Disassembled: @@ -1071,15 +1038,14 @@ block0(v0: i128, v1: i64, v2: i64): ; mv a5, zero ; bne a1, a5, 0x10 ; bne a0, a4, 0xc -; addi a1, zero, 1 +; addi a0, zero, 1 ; j 8 -; mv a1, zero -; snez a4, a1 -; neg a4, a4 -; and a0, a2, a4 -; not a2, a4 -; and a4, a3, a2 -; or a0, a0, a4 +; mv a0, zero +; neg a5, a0 +; and a4, a2, a5 +; not a0, a5 +; and a2, a3, a0 +; or a0, a4, a2 ; ret function %f(i128, i128, i128) -> i128 { @@ -1093,39 +1059,37 @@ block0(v0: i128, v1: i128, v2: i128): ; VCode: ; block0: -; li t1,42 -; li t2,0 -; eq a1,[a0,a1],[t1,t2]##ty=i128 -; sltu a0,zero,a1 +; li t0,42 +; li t1,0 +; eq a0,[a0,a1],[t0,t1]##ty=i128 ; sub a1,zero,a0 -; and a2,a2,a1 -; and a3,a3,a1 -; not t0,a1 -; not a0,a1 -; and a4,a4,t0 -; and a1,a5,a0 -; or a0,a2,a4 -; or a1,a3,a1 +; and a0,a2,a1 +; and a2,a3,a1 +; not a3,a1 +; not a1,a1 +; and a3,a4,a3 +; and a4,a5,a1 +; or a0,a0,a3 +; or a1,a2,a4 ; ret ; ; Disassembled: ; block0: ; offset 0x0 -; addi t1, zero, 0x2a -; mv t2, zero -; bne a1, t2, 0x10 -; bne a0, t1, 0xc -; addi a1, zero, 1 +; addi t0, zero, 0x2a +; mv t1, zero +; bne a1, t1, 0x10 +; bne a0, t0, 0xc +; addi a0, zero, 1 ; j 8 -; mv a1, zero -; snez a0, a1 +; mv a0, zero ; neg a1, a0 -; and a2, a2, a1 -; and a3, a3, a1 -; not t0, a1 -; not a0, a1 -; and a4, a4, t0 -; and a1, a5, a0 -; or a0, a2, a4 -; or a1, a3, a1 +; and a0, a2, a1 +; and a2, a3, a1 +; not a3, a1 +; not a1, a1 +; and a3, a4, a3 +; and a4, a5, a1 +; or a0, a0, a3 +; or a1, a2, a4 ; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat index b5aa9400304d..9fd4382f23e8 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -41,42 +41,40 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a2) -;; addi a5,a5,-4 -;; ugt a3,a0,a5##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; li a0,0 -;; sltu a2,zero,a3 -;; sub a2,zero,a2 -;; and a4,a0,a2 -;; not a0,a2 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; ld a4,8(a2) +;; addi a4,a4,-4 +;; ugt a0,a5,a4##ty=i64 +;; ld a4,0(a2) +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a0 ;; and a2,a5,a0 -;; or a4,a4,a2 -;; sw a1,0(a4) +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; ld a5,8(a1) -;; addi a5,a5,-4 -;; ugt a2,a0,a5##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; li a0,0 -;; sltu a1,zero,a2 -;; sub a2,zero,a1 -;; and a4,a0,a2 -;; not a0,a2 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; ld a4,8(a1) +;; addi a4,a4,-4 +;; ugt a0,a5,a4##ty=i64 +;; ld a4,0(a1) +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a0 ;; and a2,a5,a0 -;; or a4,a4,a2 -;; lw a0,0(a4) +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; lw a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index 665af0e6def1..c69f808419b2 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -42,24 +42,23 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,8(a2) +;; srli a3,a3,32 +;; ld a4,8(a2) ;; lui a5,-1 ;; addi a5,a5,-4 -;; add a3,a3,a5 -;; ugt a3,a4,a3##ty=i64 -;; ld a5,0(a2) -;; add a4,a5,a4 -;; lui a5,1 ;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a3 -;; sub a0,zero,a0 -;; and a2,a5,a0 -;; not a5,a0 -;; and a0,a4,a5 -;; or a2,a2,a0 -;; sw a1,0(a2) +;; ugt a4,a3,a4##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a3 +;; lui a3,1 +;; add a2,a2,a3 +;; li a3,0 +;; sub a4,zero,a4 +;; and a0,a3,a4 +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; sw a1,0(a0) ;; j label1 ;; block1: ;; ret @@ -67,24 +66,23 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,8(a1) -;; lui a2,-1 -;; addi a5,a2,-4 -;; add a3,a3,a5 -;; ugt a3,a4,a3##ty=i64 -;; ld a5,0(a1) -;; add a4,a5,a4 -;; lui a5,1 -;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a3 -;; sub a0,zero,a0 -;; and a2,a5,a0 -;; not a5,a0 +;; srli a3,a2,32 +;; ld a2,8(a1) +;; lui a4,-1 +;; addi a4,a4,-4 +;; add a2,a2,a4 +;; ugt a2,a3,a2##ty=i64 +;; ld a4,0(a1) +;; add a3,a4,a3 +;; lui a4,1 +;; add a3,a3,a4 +;; li a4,0 +;; sub a5,zero,a2 ;; and a0,a4,a5 -;; or a2,a2,a0 -;; lw a0,0(a2) +;; not a2,a5 +;; and a4,a3,a2 +;; or a0,a0,a4 +;; lw a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 8e2d5c2cc84a..4518fae7edd7 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,56 +41,54 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 +;; slli a3,a0,32 +;; srli a5,a3,32 ;; lui a3,262140 -;; addi a5,a3,1 -;; slli a3,a5,2 -;; add a5,a0,a3 -;; trap_if heap_oob##(a5 ult a0) -;; ld a3,8(a2) -;; ugt a3,a5,a3##ty=i64 +;; addi a4,a3,1 +;; slli a0,a4,2 +;; add a4,a5,a0 +;; trap_if heap_oob##(a4 ult a5) +;; ld a0,8(a2) +;; ugt a0,a4,a0##ty=i64 ;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a5,65535 -;; slli a2,a5,4 -;; add a0,a0,a2 +;; add a5,a2,a5 +;; lui a4,65535 +;; slli a2,a4,4 +;; add a5,a5,a2 ;; li a2,0 -;; sltu a3,zero,a3 -;; sub a3,zero,a3 -;; and a5,a2,a3 -;; not a2,a3 -;; and a3,a0,a2 -;; or a5,a5,a3 -;; sw a1,0(a5) +;; sub a4,zero,a0 +;; and a3,a2,a4 +;; not a0,a4 +;; and a2,a5,a0 +;; or a3,a3,a2 +;; sw a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; lui a3,262140 -;; addi a5,a3,1 -;; slli a2,a5,2 -;; add a5,a0,a2 -;; trap_if heap_oob##(a5 ult a0) -;; ld a2,8(a1) -;; ugt a2,a5,a2##ty=i64 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; lui a2,262140 +;; addi a4,a2,1 +;; slli a0,a4,2 +;; add a4,a5,a0 +;; trap_if heap_oob##(a4 ult a5) +;; ld a0,8(a1) +;; ugt a0,a4,a0##ty=i64 ;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a5,65535 -;; slli a1,a5,4 -;; add a0,a0,a1 +;; add a5,a1,a5 +;; lui a4,65535 +;; slli a1,a4,4 +;; add a5,a5,a1 ;; li a1,0 -;; sltu a2,zero,a2 -;; sub a3,zero,a2 -;; and a5,a1,a3 -;; not a1,a3 -;; and a3,a0,a1 -;; or a5,a5,a3 -;; lw a0,0(a5) +;; sub a2,zero,a0 +;; and a3,a1,a2 +;; not a0,a2 +;; and a1,a5,a0 +;; or a3,a3,a1 +;; lw a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat index ec33de9fcad6..10d13ecfa1c7 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -42,39 +42,37 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a2) -;; uge a0,a5,a4##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a0 -;; sub a2,zero,a0 -;; and a3,a5,a2 -;; not a5,a2 +;; srli a4,a3,32 +;; ld a3,8(a2) +;; uge a5,a4,a3##ty=i64 +;; ld a3,0(a2) +;; add a3,a3,a4 +;; li a4,0 +;; sub a5,zero,a5 ;; and a2,a4,a5 -;; or a3,a3,a2 -;; sb a1,0(a3) +;; not a4,a5 +;; and a5,a3,a4 +;; or a2,a2,a5 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a1) -;; uge a0,a5,a4##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a0 -;; sub a1,zero,a0 -;; and a3,a5,a1 -;; not a5,a1 +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,8(a1) +;; uge a5,a4,a3##ty=i64 +;; ld a3,0(a1) +;; add a3,a3,a4 +;; li a4,0 +;; sub a5,zero,a5 ;; and a1,a4,a5 -;; or a3,a3,a1 -;; lbu a0,0(a3) +;; not a4,a5 +;; and a5,a3,a4 +;; or a1,a1,a5 +;; lbu a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index 19db59b51168..fccf05dea0bf 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -42,24 +42,23 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,8(a2) +;; srli a3,a3,32 +;; ld a4,8(a2) ;; lui a5,-1 ;; addi a5,a5,-1 -;; add a3,a3,a5 -;; ugt a3,a4,a3##ty=i64 -;; ld a5,0(a2) -;; add a4,a5,a4 -;; lui a5,1 ;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a3 -;; sub a0,zero,a0 -;; and a2,a5,a0 -;; not a5,a0 -;; and a0,a4,a5 -;; or a2,a2,a0 -;; sb a1,0(a2) +;; ugt a4,a3,a4##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a3 +;; lui a3,1 +;; add a2,a2,a3 +;; li a3,0 +;; sub a4,zero,a4 +;; and a0,a3,a4 +;; not a3,a4 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; sb a1,0(a0) ;; j label1 ;; block1: ;; ret @@ -67,24 +66,23 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,8(a1) -;; lui a2,-1 -;; addi a5,a2,-1 -;; add a3,a3,a5 -;; ugt a3,a4,a3##ty=i64 -;; ld a5,0(a1) -;; add a4,a5,a4 -;; lui a5,1 -;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a3 -;; sub a0,zero,a0 -;; and a2,a5,a0 -;; not a5,a0 +;; srli a3,a2,32 +;; ld a2,8(a1) +;; lui a4,-1 +;; addi a4,a4,-1 +;; add a2,a2,a4 +;; ugt a2,a3,a2##ty=i64 +;; ld a4,0(a1) +;; add a3,a4,a3 +;; lui a4,1 +;; add a3,a3,a4 +;; li a4,0 +;; sub a5,zero,a2 ;; and a0,a4,a5 -;; or a2,a2,a0 -;; lbu a0,0(a2) +;; not a2,a5 +;; and a4,a3,a2 +;; or a0,a0,a4 +;; lbu a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index bc39f65d29da..89a40e68f9f7 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -42,25 +42,24 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a4,a3,32 -;; ld a3,[const(0)] -;; add a3,a4,a3 -;; trap_if heap_oob##(a3 ult a4) +;; srli a3,a3,32 +;; ld a4,[const(0)] +;; add a4,a3,a4 +;; trap_if heap_oob##(a4 ult a3) ;; ld a5,8(a2) -;; ugt a5,a3,a5##ty=i64 -;; ld a0,0(a2) -;; add a4,a0,a4 -;; lui a3,65535 -;; slli a0,a3,4 -;; add a4,a4,a0 -;; li a0,0 -;; sltu a5,zero,a5 -;; sub a2,zero,a5 -;; and a3,a0,a2 -;; not a5,a2 -;; and a2,a4,a5 -;; or a3,a3,a2 -;; sb a1,0(a3) +;; ugt a4,a4,a5##ty=i64 +;; ld a5,0(a2) +;; add a3,a5,a3 +;; lui a2,65535 +;; slli a5,a2,4 +;; add a3,a3,a5 +;; li a5,0 +;; sub a0,zero,a4 +;; and a2,a5,a0 +;; not a4,a0 +;; and a5,a3,a4 +;; or a2,a2,a5 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret @@ -68,25 +67,24 @@ ;; function u0:1: ;; block0: ;; slli a2,a0,32 -;; srli a4,a2,32 -;; ld a3,[const(0)] -;; add a3,a4,a3 -;; trap_if heap_oob##(a3 ult a4) -;; ld a5,8(a1) -;; ugt a5,a3,a5##ty=i64 -;; ld a0,0(a1) -;; add a4,a0,a4 -;; lui a3,65535 -;; slli a0,a3,4 -;; add a4,a4,a0 -;; li a0,0 -;; sltu a5,zero,a5 -;; sub a1,zero,a5 -;; and a3,a0,a1 -;; not a5,a1 -;; and a1,a4,a5 -;; or a3,a3,a1 -;; lbu a0,0(a3) +;; srli a3,a2,32 +;; ld a2,[const(0)] +;; add a2,a3,a2 +;; trap_if heap_oob##(a2 ult a3) +;; ld a4,8(a1) +;; ugt a4,a2,a4##ty=i64 +;; ld a5,0(a1) +;; add a3,a5,a3 +;; lui a2,65535 +;; slli a5,a2,4 +;; add a3,a3,a5 +;; li a5,0 +;; sub a0,zero,a4 +;; and a1,a5,a0 +;; not a4,a0 +;; and a5,a3,a4 +;; or a1,a1,a5 +;; lbu a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index 3dd2f91baac1..3b519bbb3601 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -42,39 +42,37 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a2) -;; ugt a0,a5,a4##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a0 -;; sub a2,zero,a0 -;; and a3,a5,a2 -;; not a5,a2 +;; srli a4,a3,32 +;; ld a3,8(a2) +;; ugt a5,a4,a3##ty=i64 +;; ld a3,0(a2) +;; add a3,a3,a4 +;; li a4,0 +;; sub a5,zero,a5 ;; and a2,a4,a5 -;; or a3,a3,a2 -;; sw a1,0(a3) +;; not a4,a5 +;; and a5,a3,a4 +;; or a2,a2,a5 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a1) -;; ugt a0,a5,a4##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a0 -;; sub a1,zero,a0 -;; and a3,a5,a1 -;; not a5,a1 +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,8(a1) +;; ugt a5,a4,a3##ty=i64 +;; ld a3,0(a1) +;; add a3,a3,a4 +;; li a4,0 +;; sub a5,zero,a5 ;; and a1,a4,a5 -;; or a3,a3,a1 -;; lw a0,0(a3) +;; not a4,a5 +;; and a5,a3,a4 +;; or a1,a1,a5 +;; lw a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index f00d09c2bc70..01043e5f42d1 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -41,44 +41,42 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; ld a0,8(a2) -;; ugt a0,a3,a0##ty=i64 +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a2) +;; ugt a5,a0,a5##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a3 -;; lui a3,1 -;; add a2,a2,a3 -;; li a3,0 -;; sltu a4,zero,a0 -;; sub a4,zero,a4 -;; and a5,a3,a4 -;; not a3,a4 -;; and a3,a2,a3 -;; or a5,a5,a3 -;; sw a1,0(a5) +;; add a0,a2,a0 +;; lui a2,1 +;; add a0,a0,a2 +;; li a2,0 +;; sub a4,zero,a5 +;; and a3,a2,a4 +;; not a5,a4 +;; and a2,a0,a5 +;; or a3,a3,a2 +;; sw a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; ld a0,8(a1) -;; ugt a0,a2,a0##ty=i64 +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a1) +;; ugt a5,a0,a5##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a2 -;; lui a2,1 -;; add a1,a1,a2 -;; li a2,0 -;; sltu a3,zero,a0 -;; sub a3,zero,a3 -;; and a5,a2,a3 -;; not a2,a3 +;; add a0,a1,a0 +;; lui a1,1 +;; add a0,a0,a1 +;; li a1,0 +;; sub a2,zero,a5 ;; and a3,a1,a2 -;; or a5,a5,a3 -;; lw a0,0(a5) +;; not a5,a2 +;; and a1,a0,a5 +;; or a3,a3,a1 +;; lw a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 6ed2f33eb810..90d3740da58b 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,46 +41,44 @@ ;; function u0:0: ;; block0: -;; slli a0,a0,32 -;; srli a3,a0,32 -;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 +;; slli a5,a0,32 +;; srli a3,a5,32 +;; ld a0,8(a2) +;; ugt a0,a3,a0##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a3 -;; lui a0,65535 -;; slli a3,a0,4 -;; add a3,a2,a3 -;; li a2,0 -;; sltu a4,zero,a4 -;; sub a4,zero,a4 -;; and a0,a2,a4 -;; not a2,a4 -;; and a4,a3,a2 -;; or a0,a0,a4 -;; sw a1,0(a0) +;; lui a5,65535 +;; slli a3,a5,4 +;; add a2,a2,a3 +;; li a3,0 +;; sub a5,zero,a0 +;; and a4,a3,a5 +;; not a0,a5 +;; and a2,a2,a0 +;; or a4,a4,a2 +;; sw a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a0,a0,32 -;; srli a2,a0,32 -;; ld a3,8(a1) -;; ugt a3,a2,a3##ty=i64 +;; slli a5,a0,32 +;; srli a2,a5,32 +;; ld a0,8(a1) +;; ugt a0,a2,a0##ty=i64 ;; ld a1,0(a1) ;; add a1,a1,a2 -;; lui a0,65535 -;; slli a2,a0,4 -;; add a2,a1,a2 -;; li a1,0 -;; sltu a3,zero,a3 -;; sub a4,zero,a3 -;; and a0,a1,a4 -;; not a3,a4 +;; lui a5,65535 +;; slli a2,a5,4 +;; add a1,a1,a2 +;; li a2,0 +;; sub a3,zero,a0 ;; and a4,a2,a3 -;; or a0,a0,a4 -;; lw a0,0(a0) +;; not a0,a3 +;; and a2,a1,a0 +;; or a4,a4,a2 +;; lw a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index cf9be3f6d245..6f7cd8d86d6b 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -42,39 +42,37 @@ ;; function u0:0: ;; block0: ;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a2) -;; uge a0,a5,a4##ty=i64 -;; ld a4,0(a2) -;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a0 -;; sub a2,zero,a0 -;; and a3,a5,a2 -;; not a5,a2 +;; srli a4,a3,32 +;; ld a3,8(a2) +;; uge a5,a4,a3##ty=i64 +;; ld a3,0(a2) +;; add a3,a3,a4 +;; li a4,0 +;; sub a5,zero,a5 ;; and a2,a4,a5 -;; or a3,a3,a2 -;; sb a1,0(a3) +;; not a4,a5 +;; and a5,a3,a4 +;; or a2,a2,a5 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a3,a0,32 -;; srli a5,a3,32 -;; ld a4,8(a1) -;; uge a0,a5,a4##ty=i64 -;; ld a4,0(a1) -;; add a4,a4,a5 -;; li a5,0 -;; sltu a0,zero,a0 -;; sub a1,zero,a0 -;; and a3,a5,a1 -;; not a5,a1 +;; slli a2,a0,32 +;; srli a4,a2,32 +;; ld a3,8(a1) +;; uge a5,a4,a3##ty=i64 +;; ld a3,0(a1) +;; add a3,a3,a4 +;; li a4,0 +;; sub a5,zero,a5 ;; and a1,a4,a5 -;; or a3,a3,a1 -;; lbu a0,0(a3) +;; not a4,a5 +;; and a5,a3,a4 +;; or a1,a1,a5 +;; lbu a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index fac8d232e735..2821b9d170c7 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -41,44 +41,42 @@ ;; function u0:0: ;; block0: -;; slli a5,a0,32 -;; srli a3,a5,32 -;; ld a0,8(a2) -;; ugt a0,a3,a0##ty=i64 +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a2) +;; ugt a5,a0,a5##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a3 -;; lui a3,1 -;; add a2,a2,a3 -;; li a3,0 -;; sltu a4,zero,a0 -;; sub a4,zero,a4 -;; and a5,a3,a4 -;; not a3,a4 -;; and a3,a2,a3 -;; or a5,a5,a3 -;; sb a1,0(a5) +;; add a0,a2,a0 +;; lui a2,1 +;; add a0,a0,a2 +;; li a2,0 +;; sub a4,zero,a5 +;; and a3,a2,a4 +;; not a5,a4 +;; and a2,a0,a5 +;; or a3,a3,a2 +;; sb a1,0(a3) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a5,a0,32 -;; srli a2,a5,32 -;; ld a0,8(a1) -;; ugt a0,a2,a0##ty=i64 +;; slli a4,a0,32 +;; srli a0,a4,32 +;; ld a5,8(a1) +;; ugt a5,a0,a5##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a2 -;; lui a2,1 -;; add a1,a1,a2 -;; li a2,0 -;; sltu a3,zero,a0 -;; sub a3,zero,a3 -;; and a5,a2,a3 -;; not a2,a3 +;; add a0,a1,a0 +;; lui a1,1 +;; add a0,a0,a1 +;; li a1,0 +;; sub a2,zero,a5 ;; and a3,a1,a2 -;; or a5,a5,a3 -;; lbu a0,0(a5) +;; not a5,a2 +;; and a1,a0,a5 +;; or a3,a3,a1 +;; lbu a0,0(a3) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 579c3e1c7312..577b90de6e6f 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i32_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -41,46 +41,44 @@ ;; function u0:0: ;; block0: -;; slli a0,a0,32 -;; srli a3,a0,32 -;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 +;; slli a5,a0,32 +;; srli a3,a5,32 +;; ld a0,8(a2) +;; ugt a0,a3,a0##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a3 -;; lui a0,65535 -;; slli a3,a0,4 -;; add a3,a2,a3 -;; li a2,0 -;; sltu a4,zero,a4 -;; sub a4,zero,a4 -;; and a0,a2,a4 -;; not a2,a4 -;; and a4,a3,a2 -;; or a0,a0,a4 -;; sb a1,0(a0) +;; lui a5,65535 +;; slli a3,a5,4 +;; add a2,a2,a3 +;; li a3,0 +;; sub a5,zero,a0 +;; and a4,a3,a5 +;; not a0,a5 +;; and a2,a2,a0 +;; or a4,a4,a2 +;; sb a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a0,a0,32 -;; srli a2,a0,32 -;; ld a3,8(a1) -;; ugt a3,a2,a3##ty=i64 +;; slli a5,a0,32 +;; srli a2,a5,32 +;; ld a0,8(a1) +;; ugt a0,a2,a0##ty=i64 ;; ld a1,0(a1) ;; add a1,a1,a2 -;; lui a0,65535 -;; slli a2,a0,4 -;; add a2,a1,a2 -;; li a1,0 -;; sltu a3,zero,a3 -;; sub a4,zero,a3 -;; and a0,a1,a4 -;; not a3,a4 +;; lui a5,65535 +;; slli a2,a5,4 +;; add a1,a1,a2 +;; li a2,0 +;; sub a3,zero,a0 ;; and a4,a2,a3 -;; or a0,a0,a4 -;; lbu a0,0(a0) +;; not a0,a3 +;; and a2,a1,a0 +;; or a4,a4,a2 +;; lbu a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat index c52b742f2170..b7787b57f495 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -43,36 +43,34 @@ ;; block0: ;; ld a3,8(a2) ;; addi a3,a3,-4 -;; ugt a4,a0,a3##ty=i64 -;; ld a3,0(a2) -;; add a3,a3,a0 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; sw a1,0(a2) +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; li a4,0 +;; sub a5,zero,a3 +;; and a0,a4,a5 +;; not a3,a5 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; sw a1,0(a0) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a3,8(a1) -;; addi a3,a3,-4 -;; ugt a4,a0,a3##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; lw a0,0(a2) +;; ld a2,8(a1) +;; addi a2,a2,-4 +;; ugt a3,a0,a2##ty=i64 +;; ld a2,0(a1) +;; add a2,a2,a0 +;; li a4,0 +;; sub a5,zero,a3 +;; and a0,a4,a5 +;; not a3,a5 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lw a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index cf61dff531a1..34b82dfcf806 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -42,22 +42,21 @@ ;; function u0:0: ;; block0: ;; ld a3,8(a2) -;; lui a4,-1 -;; addi a4,a4,-4 +;; lui a5,-1 +;; addi a4,a5,-4 ;; add a3,a3,a4 -;; ugt a4,a0,a3##ty=i64 +;; ugt a3,a0,a3##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a0 -;; lui a3,1 -;; add a3,a2,a3 +;; add a0,a2,a0 +;; lui a2,1 +;; add a0,a0,a2 ;; li a2,0 -;; sltu a4,zero,a4 -;; sub a4,zero,a4 -;; and a0,a2,a4 -;; not a2,a4 -;; and a4,a3,a2 -;; or a0,a0,a4 -;; sw a1,0(a0) +;; sub a3,zero,a3 +;; and a4,a2,a3 +;; not a2,a3 +;; and a2,a0,a2 +;; or a4,a4,a2 +;; sw a1,0(a4) ;; j label1 ;; block1: ;; ret @@ -65,22 +64,21 @@ ;; function u0:1: ;; block0: ;; ld a2,8(a1) -;; lui a3,-1 -;; addi a3,a3,-4 +;; lui a5,-1 +;; addi a3,a5,-4 ;; add a2,a2,a3 -;; ugt a3,a0,a2##ty=i64 +;; ugt a2,a0,a2##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a0 -;; lui a2,1 -;; add a2,a1,a2 +;; add a0,a1,a0 +;; lui a1,1 +;; add a0,a0,a1 ;; li a1,0 -;; sltu a3,zero,a3 -;; sub a4,zero,a3 -;; and a0,a1,a4 -;; not a3,a4 -;; and a4,a2,a3 -;; or a0,a0,a4 -;; lw a0,0(a0) +;; sub a2,zero,a2 +;; and a4,a1,a2 +;; not a1,a2 +;; and a2,a0,a1 +;; or a4,a4,a2 +;; lw a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat index f49466b1499c..c547318ad104 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -43,24 +43,23 @@ ;; block0: ;; lui a3,262140 ;; addi a3,a3,1 -;; slli a5,a3,2 -;; add a3,a0,a5 +;; slli a4,a3,2 +;; add a3,a0,a4 ;; trap_if heap_oob##(a3 ult a0) ;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; lui a3,65535 -;; slli a0,a3,4 -;; add a5,a5,a0 -;; li a0,0 -;; sltu a2,zero,a4 -;; sub a2,zero,a2 -;; and a3,a0,a2 -;; not a0,a2 +;; ugt a3,a3,a4##ty=i64 +;; ld a4,0(a2) +;; add a4,a4,a0 +;; lui a2,65535 +;; slli a5,a2,4 +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a3 ;; and a2,a5,a0 -;; or a3,a3,a2 -;; sw a1,0(a3) +;; not a3,a0 +;; and a5,a4,a3 +;; or a2,a2,a5 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret @@ -68,25 +67,24 @@ ;; function u0:1: ;; block0: ;; lui a2,262140 -;; addi a3,a2,1 -;; slli a5,a3,2 -;; add a3,a0,a5 -;; trap_if heap_oob##(a3 ult a0) -;; ld a4,8(a1) -;; ugt a4,a3,a4##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lui a3,65535 -;; slli a0,a3,4 -;; add a5,a5,a0 -;; li a0,0 -;; sltu a1,zero,a4 -;; sub a1,zero,a1 -;; and a3,a0,a1 -;; not a0,a1 +;; addi a2,a2,1 +;; slli a4,a2,2 +;; add a2,a0,a4 +;; trap_if heap_oob##(a2 ult a0) +;; ld a3,8(a1) +;; ugt a3,a2,a3##ty=i64 +;; ld a4,0(a1) +;; add a4,a4,a0 +;; lui a2,65535 +;; slli a5,a2,4 +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a3 ;; and a1,a5,a0 -;; or a3,a3,a1 -;; lw a0,0(a3) +;; not a3,a0 +;; and a5,a4,a3 +;; or a1,a1,a5 +;; lw a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat index 8602214200a3..027b78d3dac8 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -46,13 +46,12 @@ ;; ld a2,0(a2) ;; add a2,a2,a0 ;; li a4,0 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a3,a4,a5 -;; not a4,a5 -;; and a5,a2,a4 -;; or a2,a3,a5 -;; sb a1,0(a2) +;; sub a3,zero,a3 +;; and a5,a4,a3 +;; not a3,a3 +;; and a3,a2,a3 +;; or a5,a5,a3 +;; sb a1,0(a5) ;; j label1 ;; block1: ;; ret @@ -60,17 +59,16 @@ ;; function u0:1: ;; block0: ;; ld a2,8(a1) -;; uge a3,a0,a2##ty=i64 -;; ld a2,0(a1) -;; add a2,a2,a0 -;; li a4,0 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a1,a4,a5 -;; not a3,a5 -;; and a5,a2,a3 -;; or a1,a1,a5 -;; lbu a0,0(a1) +;; uge a2,a0,a2##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a0 +;; li a3,0 +;; sub a4,zero,a2 +;; and a5,a3,a4 +;; not a2,a4 +;; and a3,a1,a2 +;; or a5,a5,a3 +;; lbu a0,0(a5) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index 6b6da4a049a2..08ab99c2de16 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -42,22 +42,21 @@ ;; function u0:0: ;; block0: ;; ld a3,8(a2) -;; lui a4,-1 -;; addi a4,a4,-1 +;; lui a5,-1 +;; addi a4,a5,-1 ;; add a3,a3,a4 -;; ugt a4,a0,a3##ty=i64 +;; ugt a3,a0,a3##ty=i64 ;; ld a2,0(a2) -;; add a2,a2,a0 -;; lui a3,1 -;; add a3,a2,a3 +;; add a0,a2,a0 +;; lui a2,1 +;; add a0,a0,a2 ;; li a2,0 -;; sltu a4,zero,a4 -;; sub a4,zero,a4 -;; and a0,a2,a4 -;; not a2,a4 -;; and a4,a3,a2 -;; or a0,a0,a4 -;; sb a1,0(a0) +;; sub a3,zero,a3 +;; and a4,a2,a3 +;; not a2,a3 +;; and a2,a0,a2 +;; or a4,a4,a2 +;; sb a1,0(a4) ;; j label1 ;; block1: ;; ret @@ -65,22 +64,21 @@ ;; function u0:1: ;; block0: ;; ld a2,8(a1) -;; lui a3,-1 -;; addi a3,a3,-1 +;; lui a5,-1 +;; addi a3,a5,-1 ;; add a2,a2,a3 -;; ugt a3,a0,a2##ty=i64 +;; ugt a2,a0,a2##ty=i64 ;; ld a1,0(a1) -;; add a1,a1,a0 -;; lui a2,1 -;; add a2,a1,a2 +;; add a0,a1,a0 +;; lui a1,1 +;; add a0,a0,a1 ;; li a1,0 -;; sltu a3,zero,a3 -;; sub a4,zero,a3 -;; and a0,a1,a4 -;; not a3,a4 -;; and a4,a2,a3 -;; or a0,a0,a4 -;; lbu a0,0(a0) +;; sub a2,zero,a2 +;; and a4,a1,a2 +;; not a1,a2 +;; and a2,a0,a1 +;; or a4,a4,a2 +;; lbu a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 26e0d05f0b4d..22a96a6ada14 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -45,20 +45,19 @@ ;; add a3,a0,a3 ;; trap_if heap_oob##(a3 ult a0) ;; ld a4,8(a2) -;; ugt a4,a3,a4##ty=i64 +;; ugt a3,a3,a4##ty=i64 ;; ld a2,0(a2) ;; add a2,a2,a0 -;; lui a3,65535 -;; slli a3,a3,4 -;; add a3,a2,a3 -;; li a2,0 -;; sltu a4,zero,a4 -;; sub a5,zero,a4 -;; and a2,a2,a5 -;; not a4,a5 -;; and a5,a3,a4 -;; or a2,a2,a5 -;; sb a1,0(a2) +;; lui a0,65535 +;; slli a4,a0,4 +;; add a2,a2,a4 +;; li a4,0 +;; sub a3,zero,a3 +;; and a5,a4,a3 +;; not a3,a3 +;; and a3,a2,a3 +;; or a5,a5,a3 +;; sb a1,0(a5) ;; j label1 ;; block1: ;; ret @@ -70,19 +69,18 @@ ;; trap_if heap_oob##(a2 ult a0) ;; ld a3,8(a1) ;; ugt a2,a2,a3##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; lui a1,65535 -;; slli a4,a1,4 -;; add a3,a3,a4 -;; li a4,0 -;; sltu a5,zero,a2 -;; sub a5,zero,a5 -;; and a1,a4,a5 -;; not a4,a5 +;; ld a1,0(a1) +;; add a1,a1,a0 +;; lui a0,65535 +;; slli a3,a0,4 +;; add a1,a1,a3 +;; li a3,0 +;; sub a4,zero,a2 ;; and a5,a3,a4 -;; or a1,a1,a5 -;; lbu a0,0(a1) +;; not a2,a4 +;; and a3,a1,a2 +;; or a5,a5,a3 +;; lbu a0,0(a5) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index 489b039647cb..8fedb3e30c55 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -46,13 +46,12 @@ ;; ld a2,0(a2) ;; add a2,a2,a0 ;; li a4,0 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a3,a4,a5 -;; not a4,a5 -;; and a5,a2,a4 -;; or a2,a3,a5 -;; sw a1,0(a2) +;; sub a3,zero,a3 +;; and a5,a4,a3 +;; not a3,a3 +;; and a3,a2,a3 +;; or a5,a5,a3 +;; sw a1,0(a5) ;; j label1 ;; block1: ;; ret @@ -60,17 +59,16 @@ ;; function u0:1: ;; block0: ;; ld a2,8(a1) -;; ugt a3,a0,a2##ty=i64 -;; ld a2,0(a1) -;; add a2,a2,a0 -;; li a4,0 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a1,a4,a5 -;; not a3,a5 -;; and a5,a2,a3 -;; or a1,a1,a5 -;; lw a0,0(a1) +;; ugt a2,a0,a2##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a0 +;; li a3,0 +;; sub a4,zero,a2 +;; and a5,a3,a4 +;; not a2,a4 +;; and a3,a1,a2 +;; or a5,a5,a3 +;; lw a0,0(a5) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index 3a0708c20d19..c28aad9f1383 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -41,40 +41,38 @@ ;; function u0:0: ;; block0: -;; ld a4,8(a2) -;; ugt a4,a0,a4##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; li a0,0 -;; sltu a2,zero,a4 -;; sub a2,zero,a2 -;; and a3,a0,a2 -;; not a0,a2 +;; ld a3,8(a2) +;; ugt a3,a0,a3##ty=i64 +;; ld a4,0(a2) +;; add a4,a4,a0 +;; lui a5,1 +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a3 ;; and a2,a5,a0 -;; or a3,a3,a2 -;; sw a1,0(a3) +;; not a3,a0 +;; and a5,a4,a3 +;; or a2,a2,a5 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a4,8(a1) -;; ugt a4,a0,a4##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; li a0,0 -;; sltu a1,zero,a4 -;; sub a1,zero,a1 -;; and a3,a0,a1 -;; not a0,a1 +;; ld a3,8(a1) +;; ugt a3,a0,a3##ty=i64 +;; ld a4,0(a1) +;; add a4,a4,a0 +;; lui a5,1 +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a3 ;; and a1,a5,a0 -;; or a3,a3,a1 -;; lw a0,0(a3) +;; not a3,a0 +;; and a5,a4,a3 +;; or a1,a1,a5 +;; lw a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat index 9efd0648bb0e..ae20340455bb 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0xffff0000_offset.wat @@ -41,42 +41,40 @@ ;; function u0:0: ;; block0: -;; ld a5,8(a2) -;; ugt a5,a0,a5##ty=i64 -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a4,65535 -;; slli a2,a4,4 -;; add a0,a0,a2 -;; li a2,0 -;; sltu a3,zero,a5 -;; sub a3,zero,a3 -;; and a4,a2,a3 -;; not a2,a3 -;; and a2,a0,a2 -;; or a4,a4,a2 -;; sw a1,0(a4) +;; ld a4,8(a2) +;; ugt a4,a0,a4##ty=i64 +;; ld a5,0(a2) +;; add a5,a5,a0 +;; lui a3,65535 +;; slli a0,a3,4 +;; add a5,a5,a0 +;; li a0,0 +;; sub a3,zero,a4 +;; and a2,a0,a3 +;; not a4,a3 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a5,8(a1) -;; ugt a5,a0,a5##ty=i64 -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a4,65535 -;; slli a1,a4,4 -;; add a0,a0,a1 -;; li a1,0 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a4,a1,a2 -;; not a1,a2 +;; ld a4,8(a1) +;; ugt a4,a0,a4##ty=i64 +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lui a3,65535 +;; slli a0,a3,4 +;; add a5,a5,a0 +;; li a0,0 +;; sub a1,zero,a4 ;; and a2,a0,a1 -;; or a4,a4,a2 -;; lw a0,0(a4) +;; not a4,a1 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; lw a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index fe719e0e93e2..83d06e5f4b3b 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -46,13 +46,12 @@ ;; ld a2,0(a2) ;; add a2,a2,a0 ;; li a4,0 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a3,a4,a5 -;; not a4,a5 -;; and a5,a2,a4 -;; or a2,a3,a5 -;; sb a1,0(a2) +;; sub a3,zero,a3 +;; and a5,a4,a3 +;; not a3,a3 +;; and a3,a2,a3 +;; or a5,a5,a3 +;; sb a1,0(a5) ;; j label1 ;; block1: ;; ret @@ -60,17 +59,16 @@ ;; function u0:1: ;; block0: ;; ld a2,8(a1) -;; uge a3,a0,a2##ty=i64 -;; ld a2,0(a1) -;; add a2,a2,a0 -;; li a4,0 -;; sltu a3,zero,a3 -;; sub a5,zero,a3 -;; and a1,a4,a5 -;; not a3,a5 -;; and a5,a2,a3 -;; or a1,a1,a5 -;; lbu a0,0(a1) +;; uge a2,a0,a2##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a0 +;; li a3,0 +;; sub a4,zero,a2 +;; and a5,a3,a4 +;; not a2,a4 +;; and a3,a1,a2 +;; or a5,a5,a3 +;; lbu a0,0(a5) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index c473fe49d832..01ef715b0945 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -41,40 +41,38 @@ ;; function u0:0: ;; block0: -;; ld a4,8(a2) -;; ugt a4,a0,a4##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; li a0,0 -;; sltu a2,zero,a4 -;; sub a2,zero,a2 -;; and a3,a0,a2 -;; not a0,a2 +;; ld a3,8(a2) +;; ugt a3,a0,a3##ty=i64 +;; ld a4,0(a2) +;; add a4,a4,a0 +;; lui a5,1 +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a3 ;; and a2,a5,a0 -;; or a3,a3,a2 -;; sb a1,0(a3) +;; not a3,a0 +;; and a5,a4,a3 +;; or a2,a2,a5 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a4,8(a1) -;; ugt a4,a0,a4##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; lui a0,1 -;; add a5,a5,a0 -;; li a0,0 -;; sltu a1,zero,a4 -;; sub a1,zero,a1 -;; and a3,a0,a1 -;; not a0,a1 +;; ld a3,8(a1) +;; ugt a3,a0,a3##ty=i64 +;; ld a4,0(a1) +;; add a4,a4,a0 +;; lui a5,1 +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a3 ;; and a1,a5,a0 -;; or a3,a3,a1 -;; lbu a0,0(a3) +;; not a3,a0 +;; and a5,a4,a3 +;; or a1,a1,a5 +;; lbu a0,0(a1) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat index 8543e04f2fa2..63f670506e91 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_dynamic_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0xffff0000_offset.wat @@ -41,42 +41,40 @@ ;; function u0:0: ;; block0: -;; ld a5,8(a2) -;; ugt a5,a0,a5##ty=i64 -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a4,65535 -;; slli a2,a4,4 -;; add a0,a0,a2 -;; li a2,0 -;; sltu a3,zero,a5 -;; sub a3,zero,a3 -;; and a4,a2,a3 -;; not a2,a3 -;; and a2,a0,a2 -;; or a4,a4,a2 -;; sb a1,0(a4) +;; ld a4,8(a2) +;; ugt a4,a0,a4##ty=i64 +;; ld a5,0(a2) +;; add a5,a5,a0 +;; lui a3,65535 +;; slli a0,a3,4 +;; add a5,a5,a0 +;; li a0,0 +;; sub a3,zero,a4 +;; and a2,a0,a3 +;; not a4,a3 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; ld a5,8(a1) -;; ugt a5,a0,a5##ty=i64 -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a4,65535 -;; slli a1,a4,4 -;; add a0,a0,a1 -;; li a1,0 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a4,a1,a2 -;; not a1,a2 +;; ld a4,8(a1) +;; ugt a4,a0,a4##ty=i64 +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lui a3,65535 +;; slli a0,a3,4 +;; add a5,a5,a0 +;; li a0,0 +;; sub a1,zero,a4 ;; and a2,a0,a1 -;; or a4,a4,a2 -;; lbu a0,0(a4) +;; not a4,a1 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; lbu a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat index 0a75a7b3d6da..2a05a2b2b972 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -39,42 +39,40 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; lui a4,65536 -;; addi a3,a4,-4 -;; ugt a3,a0,a3##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; li a0,0 -;; sltu a2,zero,a3 -;; sub a2,zero,a2 -;; and a4,a0,a2 -;; not a0,a2 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; lui a3,65536 +;; addi a0,a3,-4 +;; ugt a0,a5,a0##ty=i64 +;; ld a4,0(a2) +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a0 ;; and a2,a5,a0 -;; or a4,a4,a2 -;; sw a1,0(a4) +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; lui a4,65536 -;; addi a2,a4,-4 -;; ugt a2,a0,a2##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; li a0,0 -;; sltu a1,zero,a2 -;; sub a2,zero,a1 -;; and a4,a0,a2 -;; not a0,a2 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; lui a3,65536 +;; addi a0,a3,-4 +;; ugt a0,a5,a0##ty=i64 +;; ld a4,0(a1) +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a0 ;; and a2,a5,a0 -;; or a4,a4,a2 -;; lw a0,0(a4) +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; lw a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index 90d1bca843f1..7226d3aed744 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -39,49 +39,44 @@ ;; function u0:0: ;; block0: -;; mv a4,a2 -;; slli a0,a0,32 -;; srli a3,a0,32 -;; lui a0,65535 -;; addi a2,a0,-4 -;; ugt a2,a3,a2##ty=i64 -;; ld a4,0(a4) -;; add a3,a4,a3 -;; lui a4,1 -;; add a3,a3,a4 -;; li a4,0 -;; sltu a2,zero,a2 -;; sub a5,zero,a2 -;; and a0,a4,a5 -;; not a2,a5 -;; and a4,a3,a2 -;; or a0,a0,a4 -;; sw a1,0(a0) +;; slli a5,a0,32 +;; srli a3,a5,32 +;; lui a5,65535 +;; addi a4,a5,-4 +;; ugt a0,a3,a4##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a3 +;; lui a3,1 +;; add a2,a2,a3 +;; li a3,0 +;; sub a5,zero,a0 +;; and a4,a3,a5 +;; not a0,a5 +;; and a2,a2,a0 +;; or a4,a4,a2 +;; sw a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; mv a4,a1 -;; slli a0,a0,32 -;; srli a2,a0,32 -;; lui a0,65535 -;; addi a3,a0,-4 -;; ugt a1,a2,a3##ty=i64 -;; mv a3,a4 -;; ld a3,0(a3) -;; add a2,a3,a2 -;; lui a3,1 -;; add a2,a2,a3 -;; li a3,0 -;; sltu a4,zero,a1 -;; sub a4,zero,a4 -;; and a0,a3,a4 -;; not a3,a4 +;; slli a5,a0,32 +;; srli a2,a5,32 +;; lui a5,65535 +;; addi a3,a5,-4 +;; ugt a0,a2,a3##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lui a2,1 +;; add a1,a1,a2 +;; li a2,0 +;; sub a3,zero,a0 ;; and a4,a2,a3 -;; or a0,a0,a4 -;; lw a0,0(a0) +;; not a0,a3 +;; and a2,a1,a0 +;; or a4,a4,a2 +;; lw a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat index 168a14a89d3d..1bcdace5b9bb 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -39,42 +39,40 @@ ;; function u0:0: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; lui a4,65536 -;; addi a3,a4,-1 -;; ugt a3,a0,a3##ty=i64 -;; ld a5,0(a2) -;; add a5,a5,a0 -;; li a0,0 -;; sltu a2,zero,a3 -;; sub a2,zero,a2 -;; and a4,a0,a2 -;; not a0,a2 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; lui a3,65536 +;; addi a0,a3,-1 +;; ugt a0,a5,a0##ty=i64 +;; ld a4,0(a2) +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a0 ;; and a2,a5,a0 -;; or a4,a4,a2 -;; sb a1,0(a4) +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; slli a4,a0,32 -;; srli a0,a4,32 -;; lui a4,65536 -;; addi a2,a4,-1 -;; ugt a2,a0,a2##ty=i64 -;; ld a5,0(a1) -;; add a5,a5,a0 -;; li a0,0 -;; sltu a1,zero,a2 -;; sub a2,zero,a1 -;; and a4,a0,a2 -;; not a0,a2 +;; slli a3,a0,32 +;; srli a5,a3,32 +;; lui a3,65536 +;; addi a0,a3,-1 +;; ugt a0,a5,a0##ty=i64 +;; ld a4,0(a1) +;; add a4,a4,a5 +;; li a5,0 +;; sub a0,zero,a0 ;; and a2,a5,a0 -;; or a4,a4,a2 -;; lbu a0,0(a4) +;; not a5,a0 +;; and a0,a4,a5 +;; or a2,a2,a0 +;; lbu a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index 3b7117ad7c32..259243c2ac40 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i32_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -39,49 +39,44 @@ ;; function u0:0: ;; block0: -;; mv a4,a2 -;; slli a0,a0,32 -;; srli a3,a0,32 -;; lui a0,65535 -;; addi a2,a0,-1 -;; ugt a2,a3,a2##ty=i64 -;; ld a4,0(a4) -;; add a3,a4,a3 -;; lui a4,1 -;; add a3,a3,a4 -;; li a4,0 -;; sltu a2,zero,a2 -;; sub a5,zero,a2 -;; and a0,a4,a5 -;; not a2,a5 -;; and a4,a3,a2 -;; or a0,a0,a4 -;; sb a1,0(a0) +;; slli a5,a0,32 +;; srli a3,a5,32 +;; lui a5,65535 +;; addi a4,a5,-1 +;; ugt a0,a3,a4##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a3 +;; lui a3,1 +;; add a2,a2,a3 +;; li a3,0 +;; sub a5,zero,a0 +;; and a4,a3,a5 +;; not a0,a5 +;; and a2,a2,a0 +;; or a4,a4,a2 +;; sb a1,0(a4) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; mv a4,a1 -;; slli a0,a0,32 -;; srli a2,a0,32 -;; lui a0,65535 -;; addi a3,a0,-1 -;; ugt a1,a2,a3##ty=i64 -;; mv a3,a4 -;; ld a3,0(a3) -;; add a2,a3,a2 -;; lui a3,1 -;; add a2,a2,a3 -;; li a3,0 -;; sltu a4,zero,a1 -;; sub a4,zero,a4 -;; and a0,a3,a4 -;; not a3,a4 +;; slli a5,a0,32 +;; srli a2,a5,32 +;; lui a5,65535 +;; addi a3,a5,-1 +;; ugt a0,a2,a3##ty=i64 +;; ld a1,0(a1) +;; add a1,a1,a2 +;; lui a2,1 +;; add a1,a1,a2 +;; li a2,0 +;; sub a3,zero,a0 ;; and a4,a2,a3 -;; or a0,a0,a4 -;; lbu a0,0(a0) +;; not a0,a3 +;; and a2,a1,a0 +;; or a4,a4,a2 +;; lbu a0,0(a4) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat index 05335a7dfc54..e6c5a9493719 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0_offset.wat @@ -40,18 +40,17 @@ ;; function u0:0: ;; block0: ;; lui a3,65536 -;; addi a4,a3,-4 -;; ugt a4,a0,a4##ty=i64 -;; ld a3,0(a2) -;; add a3,a3,a0 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; sw a1,0(a2) +;; addi a3,a3,-4 +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; li a4,0 +;; sub a5,zero,a3 +;; and a0,a4,a5 +;; not a3,a5 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; sw a1,0(a0) ;; j label1 ;; block1: ;; ret @@ -59,18 +58,17 @@ ;; function u0:1: ;; block0: ;; lui a2,65536 -;; addi a4,a2,-4 -;; ugt a4,a0,a4##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; lw a0,0(a2) +;; addi a3,a2,-4 +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a1) +;; add a2,a2,a0 +;; li a4,0 +;; sub a5,zero,a3 +;; and a0,a4,a5 +;; not a3,a5 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lw a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat index 51822e1df936..a3cf5dc1e39c 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -39,42 +39,40 @@ ;; function u0:0: ;; block0: -;; lui a4,65535 -;; addi a3,a4,-4 -;; ugt a5,a0,a3##ty=i64 -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a2,1 -;; add a0,a0,a2 -;; li a2,0 -;; sltu a3,zero,a5 -;; sub a3,zero,a3 -;; and a4,a2,a3 -;; not a2,a3 -;; and a2,a0,a2 -;; or a4,a4,a2 -;; sw a1,0(a4) +;; lui a3,65535 +;; addi a5,a3,-4 +;; ugt a4,a0,a5##ty=i64 +;; ld a5,0(a2) +;; add a5,a5,a0 +;; lui a0,1 +;; add a5,a5,a0 +;; li a0,0 +;; sub a3,zero,a4 +;; and a2,a0,a3 +;; not a4,a3 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a4,65535 -;; addi a2,a4,-4 -;; ugt a5,a0,a2##ty=i64 -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a1,1 -;; add a0,a0,a1 -;; li a1,0 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a4,a1,a2 -;; not a1,a2 +;; lui a3,65535 +;; addi a5,a3,-4 +;; ugt a4,a0,a5##ty=i64 +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lui a0,1 +;; add a5,a5,a0 +;; li a0,0 +;; sub a1,zero,a4 ;; and a2,a0,a1 -;; or a4,a4,a2 -;; lw a0,0(a4) +;; not a4,a1 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; lw a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat index bb9a15f71417..35e0c379a35f 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0_offset.wat @@ -40,18 +40,17 @@ ;; function u0:0: ;; block0: ;; lui a3,65536 -;; addi a4,a3,-1 -;; ugt a4,a0,a4##ty=i64 -;; ld a3,0(a2) -;; add a3,a3,a0 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; sb a1,0(a2) +;; addi a3,a3,-1 +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; li a4,0 +;; sub a5,zero,a3 +;; and a0,a4,a5 +;; not a3,a5 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; sb a1,0(a0) ;; j label1 ;; block1: ;; ret @@ -59,18 +58,17 @@ ;; function u0:1: ;; block0: ;; lui a2,65536 -;; addi a4,a2,-1 -;; ugt a4,a0,a4##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; lbu a0,0(a2) +;; addi a3,a2,-1 +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a1) +;; add a2,a2,a0 +;; li a4,0 +;; sub a5,zero,a3 +;; and a0,a4,a5 +;; not a3,a5 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lbu a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat index e7c33e026da5..05cb5d9cbefa 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -39,42 +39,40 @@ ;; function u0:0: ;; block0: -;; lui a4,65535 -;; addi a3,a4,-1 -;; ugt a5,a0,a3##ty=i64 -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a2,1 -;; add a0,a0,a2 -;; li a2,0 -;; sltu a3,zero,a5 -;; sub a3,zero,a3 -;; and a4,a2,a3 -;; not a2,a3 -;; and a2,a0,a2 -;; or a4,a4,a2 -;; sb a1,0(a4) +;; lui a3,65535 +;; addi a5,a3,-1 +;; ugt a4,a0,a5##ty=i64 +;; ld a5,0(a2) +;; add a5,a5,a0 +;; lui a0,1 +;; add a5,a5,a0 +;; li a0,0 +;; sub a3,zero,a4 +;; and a2,a0,a3 +;; not a4,a3 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a4,65535 -;; addi a2,a4,-1 -;; ugt a5,a0,a2##ty=i64 -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a1,1 -;; add a0,a0,a1 -;; li a1,0 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a4,a1,a2 -;; not a1,a2 +;; lui a3,65535 +;; addi a5,a3,-1 +;; ugt a4,a0,a5##ty=i64 +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lui a0,1 +;; add a5,a5,a0 +;; li a0,0 +;; sub a1,zero,a4 ;; and a2,a0,a1 -;; or a4,a4,a2 -;; lbu a0,0(a4) +;; not a4,a1 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; lbu a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat index aa54e8732573..1300e0e5211e 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0_offset.wat @@ -40,18 +40,17 @@ ;; function u0:0: ;; block0: ;; lui a3,65536 -;; addi a4,a3,-4 -;; ugt a4,a0,a4##ty=i64 -;; ld a3,0(a2) -;; add a3,a3,a0 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; sw a1,0(a2) +;; addi a3,a3,-4 +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; li a4,0 +;; sub a5,zero,a3 +;; and a0,a4,a5 +;; not a3,a5 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; sw a1,0(a0) ;; j label1 ;; block1: ;; ret @@ -59,18 +58,17 @@ ;; function u0:1: ;; block0: ;; lui a2,65536 -;; addi a4,a2,-4 -;; ugt a4,a0,a4##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; lw a0,0(a2) +;; addi a3,a2,-4 +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a1) +;; add a2,a2,a0 +;; li a4,0 +;; sub a5,zero,a3 +;; and a0,a4,a5 +;; not a3,a5 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lw a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat index da0371942a94..6b7acc5cab47 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i32_access_0x1000_offset.wat @@ -39,42 +39,40 @@ ;; function u0:0: ;; block0: -;; lui a4,65535 -;; addi a3,a4,-4 -;; ugt a5,a0,a3##ty=i64 -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a2,1 -;; add a0,a0,a2 -;; li a2,0 -;; sltu a3,zero,a5 -;; sub a3,zero,a3 -;; and a4,a2,a3 -;; not a2,a3 -;; and a2,a0,a2 -;; or a4,a4,a2 -;; sw a1,0(a4) +;; lui a3,65535 +;; addi a5,a3,-4 +;; ugt a4,a0,a5##ty=i64 +;; ld a5,0(a2) +;; add a5,a5,a0 +;; lui a0,1 +;; add a5,a5,a0 +;; li a0,0 +;; sub a3,zero,a4 +;; and a2,a0,a3 +;; not a4,a3 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; sw a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a4,65535 -;; addi a2,a4,-4 -;; ugt a5,a0,a2##ty=i64 -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a1,1 -;; add a0,a0,a1 -;; li a1,0 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a4,a1,a2 -;; not a1,a2 +;; lui a3,65535 +;; addi a5,a3,-4 +;; ugt a4,a0,a5##ty=i64 +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lui a0,1 +;; add a5,a5,a0 +;; li a0,0 +;; sub a1,zero,a4 ;; and a2,a0,a1 -;; or a4,a4,a2 -;; lw a0,0(a4) +;; not a4,a1 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; lw a0,0(a2) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat index 32123e5ef387..d882575d696b 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0_offset.wat @@ -40,18 +40,17 @@ ;; function u0:0: ;; block0: ;; lui a3,65536 -;; addi a4,a3,-1 -;; ugt a4,a0,a4##ty=i64 -;; ld a3,0(a2) -;; add a3,a3,a0 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; sb a1,0(a2) +;; addi a3,a3,-1 +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a2) +;; add a2,a2,a0 +;; li a4,0 +;; sub a5,zero,a3 +;; and a0,a4,a5 +;; not a3,a5 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; sb a1,0(a0) ;; j label1 ;; block1: ;; ret @@ -59,18 +58,17 @@ ;; function u0:1: ;; block0: ;; lui a2,65536 -;; addi a4,a2,-1 -;; ugt a4,a0,a4##ty=i64 -;; ld a3,0(a1) -;; add a3,a3,a0 -;; li a5,0 -;; sltu a4,zero,a4 -;; sub a0,zero,a4 -;; and a2,a5,a0 -;; not a4,a0 -;; and a0,a3,a4 -;; or a2,a2,a0 -;; lbu a0,0(a2) +;; addi a3,a2,-1 +;; ugt a3,a0,a3##ty=i64 +;; ld a2,0(a1) +;; add a2,a2,a0 +;; li a4,0 +;; sub a5,zero,a3 +;; and a0,a4,a5 +;; not a3,a5 +;; and a4,a2,a3 +;; or a0,a0,a4 +;; lbu a0,0(a0) ;; j label1 ;; block1: ;; ret diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat index 5c5b2fa6d655..f10b7d2e8921 100644 --- a/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat +++ b/cranelift/filetests/filetests/isa/riscv64/wasm/load_store_static_kind_i64_index_0xffffffff_guard_yes_spectre_i8_access_0x1000_offset.wat @@ -39,42 +39,40 @@ ;; function u0:0: ;; block0: -;; lui a4,65535 -;; addi a3,a4,-1 -;; ugt a5,a0,a3##ty=i64 -;; ld a2,0(a2) -;; add a0,a2,a0 -;; lui a2,1 -;; add a0,a0,a2 -;; li a2,0 -;; sltu a3,zero,a5 -;; sub a3,zero,a3 -;; and a4,a2,a3 -;; not a2,a3 -;; and a2,a0,a2 -;; or a4,a4,a2 -;; sb a1,0(a4) +;; lui a3,65535 +;; addi a5,a3,-1 +;; ugt a4,a0,a5##ty=i64 +;; ld a5,0(a2) +;; add a5,a5,a0 +;; lui a0,1 +;; add a5,a5,a0 +;; li a0,0 +;; sub a3,zero,a4 +;; and a2,a0,a3 +;; not a4,a3 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; sb a1,0(a2) ;; j label1 ;; block1: ;; ret ;; ;; function u0:1: ;; block0: -;; lui a4,65535 -;; addi a2,a4,-1 -;; ugt a5,a0,a2##ty=i64 -;; ld a1,0(a1) -;; add a0,a1,a0 -;; lui a1,1 -;; add a0,a0,a1 -;; li a1,0 -;; sltu a2,zero,a5 -;; sub a2,zero,a2 -;; and a4,a1,a2 -;; not a1,a2 +;; lui a3,65535 +;; addi a5,a3,-1 +;; ugt a4,a0,a5##ty=i64 +;; ld a5,0(a1) +;; add a5,a5,a0 +;; lui a0,1 +;; add a5,a5,a0 +;; li a0,0 +;; sub a1,zero,a4 ;; and a2,a0,a1 -;; or a4,a4,a2 -;; lbu a0,0(a4) +;; not a4,a1 +;; and a0,a5,a4 +;; or a2,a2,a0 +;; lbu a0,0(a2) ;; j label1 ;; block1: ;; ret From f4be360648e080aae8d856967e6d2c40cccf57da Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 9 Oct 2023 14:22:42 -0500 Subject: [PATCH 091/199] Refactor the test-programs test suite (#7182) * Refactor the test-programs test suite This commit is a large refactoring that reorganizes `test-programs` and how we tests wasms in Wasmtime. Often writing tests requires complicated interactions with the guest which can't be done via hand-written `*.wat` syntax and requires a compiler to get engaged. For this purpose Wasmtime currently has the `crates/test-programs/*` test suite which builds files from source and then runs the tests. This has been somewhat cumbersome in the past though and it's not been easy to extend this over time, so this commit attempts to address this. The scheme implemented in this PR looks like: * All wasm test programs live in `crates/test-programs/src/bin/*.rs`. All of them, no exceptions. * Wasm tests have shared support located at `crates/test-programs/src/lib.rs` and its submodules, such as bindings generation for WASI. * Wasm tests are built by a new `crates/test-programs/artifacts` crate. This crate compiles modules and additionally creates components for all test programs. The crate itself only records the path to these outputs and a small amount of testing support, but otherwise doesn't interact with `wasmtime`-the-crate itself. * All tests in `crates/test-programs/tests/*.rs` have moved. For example wasi-http tests now live at `crates/wasi-http/tests/*.rs`. Legacy tests of wasi-common now live at `crates/wasi-common/tests/*.rs`. Modern tests for preview2 live at `crates/wasi/tests/*.rs`. * Wasm tests are bucketed based on their filename prefix. For example `preview1_*` is tested in wasi-common and wasmtime-wasi. The `preview2_*` prefix is only tested with wasmtime-wasi, however. * A new `cli_*` prefix is used to execute tests as part of `tests/all/main.rs`. This is a new submodule in `tests/all/cli_tests.rs` which executes these components on the command line. Many old "command" tests were migrated here. * Helper macros are generated to assert that a test suite is run in its entirety. This way if a `preview1_*` test is added it's asserted to get added to both wasi-common and wasmtime-wasi in the various modes they run tests. Overall this moved a number of tests around and refactored some edges of the tests, but this should not lose any tests (except one that wasn't actually testing anything). Additionally the hope is that it's much easier to add tests in the future. The process is to add a new file in `crates/test-programs/src/bin/*.rs` named appropriately. For example a preview2 executable is `preview2_*` and a CLI tests is `cli_*`. When building the test suite an error is generated in the appropriate module then of "please write a test here", and then a test is written in the same manner as the other tests in the module. * Remove no-longer-needed fetches prtest:full * I'm worried wasi is running low on semicolons * Add the WASI target in all CI actions * Add unknown-unknown target on all CI builders too * Fix building test artifacts under miri Need to avoid wrappers for these cross-compiled targets * Break circular dependency for packaging Don't use the workspace dep for `wasmtime-wasi` since it injects a version, instead use a `path = '..'` dependency to fool Cargo into dropping the dependency during the package phase. * Fix some merge conflicts with tests * Fix rebase for new tests * Remove stray comment * Fix some flaky tests * Fix network tests in synchronous mode This commit is an attempt to fix some networking tests in synchronous mode in our test suite. Currently networking tests don't actually run in synchronous mode on CI which is why no failures have been surfaced yet, but the refactoring in #7182 is going to start doing this. Currently the `udp_sample_application.rs` test blocks infinitely in synchronous mode for me locally, most of the time. This appears to be an interaction between how Tokio handles readiness and how we're entering the event loop. We're effectively entering the Tokio event loop with a future that's always ready which ends up starving Tokio of otherwise performing its background work such as updating flags for readiness of reading/writing. The fix here is to add a yield at the start of an `in_tokio` block which is used in synchronous mode. This is a kludge fix but the intention is to enable Tokio to have a chance to update readiness flags and process events from epoll/kqueue/etc. An additional fix to this issue is WebAssembly/wasi-sockets#64 where the test is waiting on `READABLE` or `WRITABLE`, but in this specific case it should only wait on `READABLE`. If it waited on just this then that would also fix this issue. Nevertheless having a `yield_now` is expected to have little-to-no overhead and otherwise fix this edge case of an always-ready future. * Fix passing empty arguments on the CLI * Add another blocking accept * Update crates/test-programs/src/bin/api_proxy.rs Co-authored-by: Trevor Elliott --------- Co-authored-by: Trevor Elliott --- .github/actions/install-rust/action.yml | 4 + .github/workflows/main.yml | 2 - Cargo.lock | 91 +--- Cargo.toml | 13 +- ci/run-tests.sh | 7 +- crates/test-programs/Cargo.toml | 41 +- crates/test-programs/README.md | 7 - crates/test-programs/artifacts/Cargo.toml | 12 + crates/test-programs/artifacts/build.rs | 174 ++++++ crates/test-programs/artifacts/src/lib.rs | 44 ++ crates/test-programs/build.rs | 282 ---------- crates/test-programs/command-tests/Cargo.toml | 18 - .../command-tests/src/bin/poll_stdin.rs | 14 - crates/test-programs/command-tests/src/lib.rs | 1 - crates/test-programs/reactor-tests/Cargo.toml | 11 - .../src/lib.rs => src/bin/api_proxy.rs} | 6 +- .../src/lib.rs => src/bin/api_reactor.rs} | 8 +- .../read_only.rs => src/bin/api_read_only.rs} | 0 .../src/bin/time.rs => src/bin/api_time.rs} | 0 .../src/bin/args.rs => src/bin/cli_args.rs} | 2 +- .../bin/cli_default_clocks.rs} | 0 .../bin/cli_directory_list.rs} | 0 .../src/bin/env.rs => src/bin/cli_env.rs} | 0 .../bin/cli_exit_default.rs} | 0 .../bin/cli_exit_failure.rs} | 0 .../bin/cli_exit_panic.rs} | 0 .../bin/cli_exit_success.rs} | 0 .../bin/cli_export_cabi_realloc.rs} | 0 .../bin/cli_file_append.rs} | 0 .../bin/cli_file_dir_sync.rs} | 0 .../file_read.rs => src/bin/cli_file_read.rs} | 0 .../bin/cli_hello_stdout.rs} | 0 .../src/bin/panic.rs => src/bin/cli_panic.rs} | 0 .../src/bin/stdin.rs => src/bin/cli_stdin.rs} | 0 .../bin/cli_stream_pollable_lifetimes.rs} | 9 +- .../bin/http_outbound_request_get.rs} | 4 +- .../http_outbound_request_invalid_dnsname.rs} | 4 +- .../http_outbound_request_invalid_port.rs} | 4 +- .../http_outbound_request_invalid_version.rs} | 5 +- .../bin/http_outbound_request_large_post.rs} | 4 +- .../bin/http_outbound_request_post.rs} | 4 +- .../bin/http_outbound_request_put.rs} | 9 +- .../http_outbound_request_response_build.rs} | 10 +- .../http_outbound_request_unknown_method.rs} | 4 +- ...tp_outbound_request_unsupported_scheme.rs} | 4 +- .../bin/preview1_big_random_buf.rs} | 0 .../bin/preview1_clock_time_get.rs} | 0 .../bin/preview1_close_preopen.rs} | 2 +- .../bin/preview1_dangling_fd.rs} | 4 +- .../bin/preview1_dangling_symlink.rs} | 4 +- .../bin/preview1_dir_fd_op_failures.rs} | 2 +- .../bin/preview1_directory_seek.rs} | 2 +- .../bin/preview1_fd_advise.rs} | 2 +- .../bin/preview1_fd_filestat_get.rs} | 0 .../bin/preview1_fd_filestat_set.rs} | 2 +- .../bin/preview1_fd_flags_set.rs} | 2 +- .../bin/preview1_fd_readdir.rs} | 2 +- .../bin/preview1_file_allocate.rs} | 2 +- .../bin/preview1_file_pread_pwrite.rs} | 2 +- .../bin/preview1_file_seek_tell.rs} | 2 +- .../bin/preview1_file_truncation.rs} | 2 +- .../bin/preview1_file_unbuffered_write.rs} | 2 +- .../bin/preview1_interesting_paths.rs} | 2 +- .../bin/preview1_nofollow_errors.rs} | 2 +- .../bin/preview1_overwrite_preopen.rs} | 2 +- .../bin/preview1_path_exists.rs} | 2 +- .../bin/preview1_path_filestat.rs} | 8 +- .../bin/preview1_path_link.rs} | 4 +- .../preview1_path_open_create_existing.rs} | 2 +- .../bin/preview1_path_open_dirfd_not_dir.rs} | 2 +- .../bin/preview1_path_open_missing.rs} | 2 +- .../bin/preview1_path_open_nonblock.rs} | 2 +- .../bin/preview1_path_open_preopen.rs} | 2 +- .../bin/preview1_path_open_read_write.rs} | 2 +- .../bin/preview1_path_rename.rs} | 6 +- ...view1_path_rename_dir_trailing_slashes.rs} | 2 +- ...iew1_path_rename_file_trailing_slashes.rs} | 2 +- ...preview1_path_symlink_trailing_slashes.rs} | 4 +- .../bin/preview1_poll_oneoff_files.rs} | 2 +- .../bin/preview1_poll_oneoff_stdio.rs} | 2 +- .../bin/preview1_readlink.rs} | 2 +- .../bin/preview1_regular_file_isatty.rs} | 2 +- ...iew1_remove_directory_trailing_slashes.rs} | 2 +- .../preview1_remove_nonempty_directory.rs} | 2 +- .../bin/preview1_renumber.rs} | 2 +- .../bin/preview1_sched_yield.rs} | 0 .../stdio.rs => src/bin/preview1_stdio.rs} | 2 +- .../bin/preview1_stdio_isatty.rs} | 0 .../bin/preview1_stdio_not_isatty.rs} | 0 .../bin/preview1_symlink_create.rs} | 2 +- .../bin/preview1_symlink_filestat.rs} | 2 +- .../bin/preview1_symlink_loop.rs} | 4 +- .../bin/preview1_unicode_output.rs} | 2 +- .../preview1_unlink_file_trailing_slashes.rs} | 2 +- .../bin/preview2_ip_name_lookup.rs} | 6 +- .../random.rs => src/bin/preview2_random.rs} | 0 .../sleep.rs => src/bin/preview2_sleep.rs} | 7 +- .../bin/preview2_tcp_bind.rs} | 7 +- .../bin/preview2_tcp_connect.rs} | 7 +- .../bin/preview2_tcp_sample_application.rs} | 9 +- .../bin/preview2_tcp_sockopts.rs} | 11 +- .../bin/preview2_tcp_states.rs} | 7 +- .../bin/preview2_udp_sample_application.rs} | 5 +- .../src/lib.rs => src/http.rs} | 15 +- crates/test-programs/src/lib.rs | 50 +- .../src/lib.rs => src/preview1.rs} | 82 ++- .../src/lib.rs => src/sockets.rs} | 20 +- crates/test-programs/tests/command.rs | 508 ------------------ crates/test-programs/tests/reactor.rs | 136 ----- .../test-programs/tests/wasi-cap-std-sync.rs | 297 ---------- .../tests/wasi-http-components-sync.rs | 168 ------ .../tests/wasi-http-components.rs | 169 ------ .../tests/wasi-preview1-host-in-preview2.rs | 336 ------------ .../tests/wasi-preview2-components-sync.rs | 316 ----------- .../tests/wasi-preview2-components.rs | 324 ----------- crates/test-programs/tests/wasi-sockets.rs | 102 ---- .../wasi-http-proxy-tests/Cargo.toml | 12 - .../test-programs/wasi-http-tests/Cargo.toml | 12 - .../wasi-sockets-tests/Cargo.toml | 10 - crates/test-programs/wasi-tests/Cargo.toml | 14 - crates/test-programs/wasi-tests/README.md | 3 - .../src/bin/read_empty_file.rs.disabled | 5 - crates/test-programs/wasi-tests/src/config.rs | 63 --- crates/wasi-common/Cargo.toml | 9 + .../tests/all/async_.rs} | 248 ++++----- crates/wasi-common/tests/all/main.rs | 21 + crates/wasi-common/tests/all/sync.rs | 275 ++++++++++ crates/wasi-http/Cargo.toml | 7 + crates/wasi-http/tests/all/async_.rs | 80 +++ .../tests/all}/http_server.rs | 0 .../tests/all/main.rs} | 123 +++-- crates/wasi-http/tests/all/sync.rs | 79 +++ crates/wasi-http/wit/test.wit | 34 +- crates/wasi/Cargo.toml | 3 + crates/wasi/src/preview2/mod.rs | 42 +- crates/wasi/tests/all/api.rs | 213 ++++++++ crates/wasi/tests/all/async_.rs | 330 ++++++++++++ crates/wasi/tests/all/main.rs | 113 ++++ crates/wasi/tests/all/preview1.rs | 247 +++++++++ crates/wasi/tests/all/sync.rs | 271 ++++++++++ crates/wasi/wit/test.wit | 34 +- src/commands/run.rs | 13 +- tests/all/cli_tests.rs | 292 +++++++++- 143 files changed, 2647 insertions(+), 3392 deletions(-) delete mode 100644 crates/test-programs/README.md create mode 100644 crates/test-programs/artifacts/Cargo.toml create mode 100644 crates/test-programs/artifacts/build.rs create mode 100644 crates/test-programs/artifacts/src/lib.rs delete mode 100644 crates/test-programs/build.rs delete mode 100644 crates/test-programs/command-tests/Cargo.toml delete mode 100644 crates/test-programs/command-tests/src/bin/poll_stdin.rs delete mode 100644 crates/test-programs/command-tests/src/lib.rs delete mode 100644 crates/test-programs/reactor-tests/Cargo.toml rename crates/test-programs/{wasi-http-proxy-tests/src/lib.rs => src/bin/api_proxy.rs} (83%) rename crates/test-programs/{reactor-tests/src/lib.rs => src/bin/api_reactor.rs} (89%) rename crates/test-programs/{command-tests/src/bin/read_only.rs => src/bin/api_read_only.rs} (100%) rename crates/test-programs/{command-tests/src/bin/time.rs => src/bin/api_time.rs} (100%) rename crates/test-programs/{command-tests/src/bin/args.rs => src/bin/cli_args.rs} (65%) rename crates/test-programs/{command-tests/src/bin/default_clocks.rs => src/bin/cli_default_clocks.rs} (100%) rename crates/test-programs/{command-tests/src/bin/directory_list.rs => src/bin/cli_directory_list.rs} (100%) rename crates/test-programs/{command-tests/src/bin/env.rs => src/bin/cli_env.rs} (100%) rename crates/test-programs/{command-tests/src/bin/exit_default.rs => src/bin/cli_exit_default.rs} (100%) rename crates/test-programs/{command-tests/src/bin/exit_failure.rs => src/bin/cli_exit_failure.rs} (100%) rename crates/test-programs/{command-tests/src/bin/exit_panic.rs => src/bin/cli_exit_panic.rs} (100%) rename crates/test-programs/{command-tests/src/bin/exit_success.rs => src/bin/cli_exit_success.rs} (100%) rename crates/test-programs/{command-tests/src/bin/export_cabi_realloc.rs => src/bin/cli_export_cabi_realloc.rs} (100%) rename crates/test-programs/{command-tests/src/bin/file_append.rs => src/bin/cli_file_append.rs} (100%) rename crates/test-programs/{command-tests/src/bin/file_dir_sync.rs => src/bin/cli_file_dir_sync.rs} (100%) rename crates/test-programs/{command-tests/src/bin/file_read.rs => src/bin/cli_file_read.rs} (100%) rename crates/test-programs/{command-tests/src/bin/hello_stdout.rs => src/bin/cli_hello_stdout.rs} (100%) rename crates/test-programs/{command-tests/src/bin/panic.rs => src/bin/cli_panic.rs} (100%) rename crates/test-programs/{command-tests/src/bin/stdin.rs => src/bin/cli_stdin.rs} (100%) rename crates/test-programs/{command-tests/src/bin/stream_pollable_lifetimes.rs => src/bin/cli_stream_pollable_lifetimes.rs} (82%) rename crates/test-programs/{wasi-http-tests/src/bin/outbound_request_get.rs => src/bin/http_outbound_request_get.rs} (85%) rename crates/test-programs/{wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs => src/bin/http_outbound_request_invalid_dnsname.rs} (72%) rename crates/test-programs/{wasi-http-tests/src/bin/outbound_request_invalid_port.rs => src/bin/http_outbound_request_invalid_port.rs} (71%) rename crates/test-programs/{wasi-http-tests/src/bin/outbound_request_invalid_version.rs => src/bin/http_outbound_request_invalid_version.rs} (71%) rename crates/test-programs/{wasi-http-tests/src/bin/outbound_request_large_post.rs => src/bin/http_outbound_request_large_post.rs} (86%) rename crates/test-programs/{wasi-http-tests/src/bin/outbound_request_post.rs => src/bin/http_outbound_request_post.rs} (82%) rename crates/test-programs/{wasi-http-tests/src/bin/outbound_request_put.rs => src/bin/http_outbound_request_put.rs} (58%) rename crates/test-programs/{wasi-http-tests/src/bin/outbound_request_response_build.rs => src/bin/http_outbound_request_response_build.rs} (81%) rename crates/test-programs/{wasi-http-tests/src/bin/outbound_request_unknown_method.rs => src/bin/http_outbound_request_unknown_method.rs} (73%) rename crates/test-programs/{wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs => src/bin/http_outbound_request_unsupported_scheme.rs} (72%) rename crates/test-programs/{wasi-tests/src/bin/big_random_buf.rs => src/bin/preview1_big_random_buf.rs} (100%) rename crates/test-programs/{wasi-tests/src/bin/clock_time_get.rs => src/bin/preview1_clock_time_get.rs} (100%) rename crates/test-programs/{wasi-tests/src/bin/close_preopen.rs => src/bin/preview1_close_preopen.rs} (94%) rename crates/test-programs/{wasi-tests/src/bin/dangling_fd.rs => src/bin/preview1_dangling_fd.rs} (94%) rename crates/test-programs/{wasi-tests/src/bin/dangling_symlink.rs => src/bin/preview1_dangling_symlink.rs} (91%) rename crates/test-programs/{wasi-tests/src/bin/dir_fd_op_failures.rs => src/bin/preview1_dir_fd_op_failures.rs} (98%) rename crates/test-programs/{wasi-tests/src/bin/directory_seek.rs => src/bin/preview1_directory_seek.rs} (95%) rename crates/test-programs/{wasi-tests/src/bin/fd_advise.rs => src/bin/preview1_fd_advise.rs} (97%) rename crates/test-programs/{wasi-tests/src/bin/fd_filestat_get.rs => src/bin/preview1_fd_filestat_get.rs} (100%) rename crates/test-programs/{wasi-tests/src/bin/fd_filestat_set.rs => src/bin/preview1_fd_filestat_set.rs} (97%) rename crates/test-programs/{wasi-tests/src/bin/fd_flags_set.rs => src/bin/preview1_fd_flags_set.rs} (98%) rename crates/test-programs/{wasi-tests/src/bin/fd_readdir.rs => src/bin/preview1_fd_readdir.rs} (99%) rename crates/test-programs/{wasi-tests/src/bin/file_allocate.rs => src/bin/preview1_file_allocate.rs} (96%) rename crates/test-programs/{wasi-tests/src/bin/file_pread_pwrite.rs => src/bin/preview1_file_pread_pwrite.rs} (98%) rename crates/test-programs/{wasi-tests/src/bin/file_seek_tell.rs => src/bin/preview1_file_seek_tell.rs} (97%) rename crates/test-programs/{wasi-tests/src/bin/file_truncation.rs => src/bin/preview1_file_truncation.rs} (97%) rename crates/test-programs/{wasi-tests/src/bin/file_unbuffered_write.rs => src/bin/preview1_file_unbuffered_write.rs} (97%) rename crates/test-programs/{wasi-tests/src/bin/interesting_paths.rs => src/bin/preview1_interesting_paths.rs} (97%) rename crates/test-programs/{wasi-tests/src/bin/nofollow_errors.rs => src/bin/preview1_nofollow_errors.rs} (98%) rename crates/test-programs/{wasi-tests/src/bin/overwrite_preopen.rs => src/bin/preview1_overwrite_preopen.rs} (95%) rename crates/test-programs/{wasi-tests/src/bin/path_exists.rs => src/bin/preview1_path_exists.rs} (97%) rename crates/test-programs/{wasi-tests/src/bin/path_filestat.rs => src/bin/preview1_path_filestat.rs} (94%) rename crates/test-programs/{wasi-tests/src/bin/path_link.rs => src/bin/preview1_path_link.rs} (98%) rename crates/test-programs/{wasi-tests/src/bin/path_open_create_existing.rs => src/bin/preview1_path_open_create_existing.rs} (92%) rename crates/test-programs/{wasi-tests/src/bin/path_open_dirfd_not_dir.rs => src/bin/preview1_path_open_dirfd_not_dir.rs} (93%) rename crates/test-programs/{wasi-tests/src/bin/path_open_missing.rs => src/bin/preview1_path_open_missing.rs} (92%) rename crates/test-programs/{wasi-tests/src/bin/path_open_nonblock.rs => src/bin/preview1_path_open_nonblock.rs} (93%) rename crates/test-programs/{wasi-tests/src/bin/path_open_preopen.rs => src/bin/preview1_path_open_preopen.rs} (98%) rename crates/test-programs/{wasi-tests/src/bin/path_open_read_write.rs => src/bin/preview1_path_open_read_write.rs} (98%) rename crates/test-programs/{wasi-tests/src/bin/path_rename.rs => src/bin/preview1_path_rename.rs} (97%) rename crates/test-programs/{wasi-tests/src/bin/path_rename_dir_trailing_slashes.rs => src/bin/preview1_path_rename_dir_trailing_slashes.rs} (96%) rename crates/test-programs/{wasi-tests/src/bin/path_rename_file_trailing_slashes.rs => src/bin/preview1_path_rename_file_trailing_slashes.rs} (95%) rename crates/test-programs/{wasi-tests/src/bin/path_symlink_trailing_slashes.rs => src/bin/preview1_path_symlink_trailing_slashes.rs} (95%) rename crates/test-programs/{wasi-tests/src/bin/poll_oneoff_files.rs => src/bin/preview1_poll_oneoff_files.rs} (99%) rename crates/test-programs/{wasi-tests/src/bin/poll_oneoff_stdio.rs => src/bin/preview1_poll_oneoff_stdio.rs} (98%) rename crates/test-programs/{wasi-tests/src/bin/readlink.rs => src/bin/preview1_readlink.rs} (97%) rename crates/test-programs/{wasi-tests/src/bin/regular_file_isatty.rs => src/bin/preview1_regular_file_isatty.rs} (95%) rename crates/test-programs/{wasi-tests/src/bin/remove_directory_trailing_slashes.rs => src/bin/preview1_remove_directory_trailing_slashes.rs} (95%) rename crates/test-programs/{wasi-tests/src/bin/remove_nonempty_directory.rs => src/bin/preview1_remove_nonempty_directory.rs} (95%) rename crates/test-programs/{wasi-tests/src/bin/renumber.rs => src/bin/preview1_renumber.rs} (97%) rename crates/test-programs/{wasi-tests/src/bin/sched_yield.rs => src/bin/preview1_sched_yield.rs} (100%) rename crates/test-programs/{wasi-tests/src/bin/stdio.rs => src/bin/preview1_stdio.rs} (82%) rename crates/test-programs/{wasi-tests/src/bin/stdio_isatty.rs => src/bin/preview1_stdio_isatty.rs} (100%) rename crates/test-programs/{wasi-tests/src/bin/stdio_not_isatty.rs => src/bin/preview1_stdio_not_isatty.rs} (100%) rename crates/test-programs/{wasi-tests/src/bin/symlink_create.rs => src/bin/preview1_symlink_create.rs} (98%) rename crates/test-programs/{wasi-tests/src/bin/symlink_filestat.rs => src/bin/preview1_symlink_filestat.rs} (98%) rename crates/test-programs/{wasi-tests/src/bin/symlink_loop.rs => src/bin/preview1_symlink_loop.rs} (89%) rename crates/test-programs/{wasi-tests/src/bin/unicode_output.rs => src/bin/preview1_unicode_output.rs} (90%) rename crates/test-programs/{wasi-tests/src/bin/unlink_file_trailing_slashes.rs => src/bin/preview1_unlink_file_trailing_slashes.rs} (95%) rename crates/test-programs/{wasi-sockets-tests/src/bin/ip_name_lookup.rs => src/bin/preview2_ip_name_lookup.rs} (91%) rename crates/test-programs/{command-tests/src/bin/random.rs => src/bin/preview2_random.rs} (100%) rename crates/test-programs/{wasi-tests/src/bin/sleep.rs => src/bin/preview2_sleep.rs} (79%) rename crates/test-programs/{wasi-sockets-tests/src/bin/tcp_bind.rs => src/bin/preview2_tcp_bind.rs} (96%) rename crates/test-programs/{wasi-sockets-tests/src/bin/tcp_connect.rs => src/bin/preview2_tcp_connect.rs} (95%) rename crates/test-programs/{wasi-sockets-tests/src/bin/tcp_sample_application.rs => src/bin/preview2_tcp_sample_application.rs} (89%) rename crates/test-programs/{wasi-sockets-tests/src/bin/tcp_sockopts.rs => src/bin/preview2_tcp_sockopts.rs} (96%) rename crates/test-programs/{wasi-sockets-tests/src/bin/tcp_states.rs => src/bin/preview2_tcp_states.rs} (98%) rename crates/test-programs/{wasi-sockets-tests/src/bin/udp_sample_application.rs => src/bin/preview2_udp_sample_application.rs} (96%) rename crates/test-programs/{wasi-http-tests/src/lib.rs => src/http.rs} (92%) rename crates/test-programs/{wasi-tests/src/lib.rs => src/preview1.rs} (59%) rename crates/test-programs/{wasi-sockets-tests/src/lib.rs => src/sockets.rs} (95%) delete mode 100644 crates/test-programs/tests/command.rs delete mode 100644 crates/test-programs/tests/reactor.rs delete mode 100644 crates/test-programs/tests/wasi-cap-std-sync.rs delete mode 100644 crates/test-programs/tests/wasi-http-components-sync.rs delete mode 100644 crates/test-programs/tests/wasi-http-components.rs delete mode 100644 crates/test-programs/tests/wasi-preview1-host-in-preview2.rs delete mode 100644 crates/test-programs/tests/wasi-preview2-components-sync.rs delete mode 100644 crates/test-programs/tests/wasi-preview2-components.rs delete mode 100644 crates/test-programs/tests/wasi-sockets.rs delete mode 100644 crates/test-programs/wasi-http-proxy-tests/Cargo.toml delete mode 100644 crates/test-programs/wasi-http-tests/Cargo.toml delete mode 100644 crates/test-programs/wasi-sockets-tests/Cargo.toml delete mode 100644 crates/test-programs/wasi-tests/Cargo.toml delete mode 100644 crates/test-programs/wasi-tests/README.md delete mode 100644 crates/test-programs/wasi-tests/src/bin/read_empty_file.rs.disabled delete mode 100644 crates/test-programs/wasi-tests/src/config.rs rename crates/{test-programs/tests/wasi-tokio.rs => wasi-common/tests/all/async_.rs} (50%) create mode 100644 crates/wasi-common/tests/all/main.rs create mode 100644 crates/wasi-common/tests/all/sync.rs create mode 100644 crates/wasi-http/tests/all/async_.rs rename crates/{test-programs/src => wasi-http/tests/all}/http_server.rs (100%) rename crates/{test-programs/tests/wasi-http-proxy.rs => wasi-http/tests/all/main.rs} (53%) create mode 100644 crates/wasi-http/tests/all/sync.rs create mode 100644 crates/wasi/tests/all/api.rs create mode 100644 crates/wasi/tests/all/async_.rs create mode 100644 crates/wasi/tests/all/main.rs create mode 100644 crates/wasi/tests/all/preview1.rs create mode 100644 crates/wasi/tests/all/sync.rs diff --git a/.github/actions/install-rust/action.yml b/.github/actions/install-rust/action.yml index a84a148f1aa5..b15c33c0a4cd 100644 --- a/.github/actions/install-rust/action.yml +++ b/.github/actions/install-rust/action.yml @@ -65,6 +65,10 @@ runs: shell: bash run: echo WIT_REQUIRE_SEMICOLONS=1 >> "$GITHUB_ENV" + - name: Install the WASI target + shell: bash + run: rustup target add wasm32-wasi wasm32-unknown-unknown + - name: Choose registry cache key shell: bash # Update the registry index cache at most once per day. actions/cache diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index adbad6b80985..80cfd9c119b3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -396,8 +396,6 @@ jobs: if: matrix.target == 'x86_64-pc-windows-gnu' - run: cargo fetch --locked - - run: cargo fetch --locked --manifest-path crates/test-programs/wasi-tests/Cargo.toml - - run: cargo fetch --locked --manifest-path crates/test-programs/wasi-http-tests/Cargo.toml - uses: actions/cache@v3 with: diff --git a/Cargo.lock b/Cargo.lock index 027c50eae36a..686bcac677a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -484,15 +484,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "command-tests" -version = "0.0.0" -dependencies = [ - "anyhow", - "getrandom", - "wit-bindgen", -] - [[package]] name = "component-fuzz-util" version = "0.0.0" @@ -2158,13 +2149,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "reactor-tests" -version = "0.1.0" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "redox_syscall" version = "0.2.13" @@ -2668,27 +2652,18 @@ name = "test-programs" version = "0.0.0" dependencies = [ "anyhow", - "bytes", - "cap-rand", - "cap-std", + "getrandom", + "libc", + "wasi", + "wit-bindgen", +] + +[[package]] +name = "test-programs-artifacts" +version = "0.0.0" +dependencies = [ "cargo_metadata", - "cfg-if", "heck", - "http", - "http-body", - "http-body-util", - "hyper", - "lazy_static", - "tempfile", - "test-log", - "tokio", - "tracing", - "tracing-subscriber", - "wasi-cap-std-sync", - "wasi-common", - "wasmtime", - "wasmtime-wasi", - "wasmtime-wasi-http", "wit-component", ] @@ -3044,28 +3019,19 @@ dependencies = [ "io-extras", "log", "rustix 0.38.8", + "tempfile", + "test-log", + "test-programs-artifacts", "thiserror", + "tokio", "tracing", + "tracing-subscriber", "wasmtime", + "wasmtime-wasi", "wiggle", "windows-sys", ] -[[package]] -name = "wasi-http-proxy-tests" -version = "0.0.0" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasi-http-tests" -version = "0.0.0" -dependencies = [ - "anyhow", - "wit-bindgen", -] - [[package]] name = "wasi-preview1-component-adapter" version = "15.0.0" @@ -3077,24 +3043,6 @@ dependencies = [ "wit-bindgen", ] -[[package]] -name = "wasi-sockets-tests" -version = "0.0.0" -dependencies = [ - "anyhow", - "wit-bindgen", -] - -[[package]] -name = "wasi-tests" -version = "0.0.0" -dependencies = [ - "libc", - "once_cell", - "wasi", - "wit-bindgen", -] - [[package]] name = "wasi-tokio" version = "15.0.0" @@ -3426,7 +3374,7 @@ dependencies = [ "serde_json", "target-lexicon", "tempfile", - "test-programs", + "test-programs-artifacts", "tokio", "walkdir", "wasm-encoder", @@ -3755,7 +3703,9 @@ dependencies = [ "once_cell", "rustix 0.38.8", "system-interface", + "tempfile", "test-log", + "test-programs-artifacts", "thiserror", "tokio", "tracing", @@ -3782,9 +3732,12 @@ dependencies = [ "http-body-util", "hyper", "rustls", + "test-log", + "test-programs-artifacts", "tokio", "tokio-rustls", "tracing", + "tracing-subscriber", "wasmtime", "wasmtime-wasi", "webpki-roots", diff --git a/Cargo.toml b/Cargo.toml index d60179bfbbe4..4b97cab4537f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,6 @@ env_logger = { workspace = true } log = { workspace = true } filecheck = { workspace = true } tempfile = { workspace = true } -test-programs = { path = "crates/test-programs" } wasmtime-runtime = { workspace = true } tokio = { workspace = true, features = ["rt", "time", "macros", "rt-multi-thread"] } wast = { workspace = true } @@ -83,6 +82,7 @@ libc = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } walkdir = { workspace = true } +test-programs-artifacts = { workspace = true } [target.'cfg(windows)'.dev-dependencies] windows-sys = { workspace = true, features = ["Win32_System_Memory"] } @@ -102,16 +102,8 @@ members = [ "cranelift/serde", "crates/bench-api", "crates/c-api", - "crates/cli-flags", "crates/environ/fuzz", - "crates/jit-icache-coherence", - "crates/test-programs/wasi-tests", - "crates/test-programs/wasi-http-tests", - "crates/test-programs/wasi-http-proxy-tests", - "crates/test-programs/wasi-sockets-tests", - "crates/test-programs/command-tests", - "crates/test-programs/reactor-tests", - "crates/wmemcheck", + "crates/test-programs", "crates/wasi-preview1-component-adapter", "crates/wasi-preview1-component-adapter/verify", "crates/winch", @@ -170,6 +162,7 @@ wasi-cap-std-sync = { path = "crates/wasi-common/cap-std-sync", version = "=15.0 wasmtime-fuzzing = { path = "crates/fuzzing" } wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=15.0.0" } wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=15.0.0" } +test-programs-artifacts = { path = 'crates/test-programs/artifacts' } cranelift-wasm = { path = "cranelift/wasm", version = "0.102.0" } cranelift-codegen = { path = "cranelift/codegen", version = "0.102.0" } diff --git a/ci/run-tests.sh b/ci/run-tests.sh index 568ff4405413..db338dc2409e 100755 --- a/ci/run-tests.sh +++ b/ci/run-tests.sh @@ -1,15 +1,10 @@ #!/bin/bash cargo test \ - --features "test-programs/test_programs" \ --features wasi-threads \ --features wasi-http \ --features component-model \ --features serve \ --workspace \ - --exclude 'wasmtime-wasi-*' \ - --exclude wasi-tests \ - --exclude wasi-http-tests \ - --exclude command-tests \ - --exclude reactor-tests \ + --exclude test-programs \ $@ diff --git a/crates/test-programs/Cargo.toml b/crates/test-programs/Cargo.toml index 9785dc38d738..422e851d34f8 100644 --- a/crates/test-programs/Cargo.toml +++ b/crates/test-programs/Cargo.toml @@ -2,46 +2,13 @@ name = "test-programs" version = "0.0.0" authors = ["The Wasmtime Project Developers"] -readme = "README.md" edition.workspace = true publish = false license = "Apache-2.0 WITH LLVM-exception" -[build-dependencies] -cfg-if = { workspace = true } -cargo_metadata = "0.15.3" -wit-component = { workspace = true } -heck = { workspace = true } - [dependencies] anyhow = { workspace = true } -bytes = { workspace = true } -http = { version = "0.2.9" } -http-body = "1.0.0-rc.2" -http-body-util = "0.1.0-rc.2" -hyper = { version = "1.0.0-rc.3", features = ["full"] } -tokio = { workspace = true, features = ["net", "rt-multi-thread", "macros"] } -tracing = { workspace = true } - -[dev-dependencies] -anyhow = { workspace = true } -tempfile = { workspace = true } -test-log = { workspace = true } -tracing = { workspace = true } -tracing-subscriber = { workspace = true } -lazy_static = "1" -wasmtime = { workspace = true, features = ['cranelift', 'component-model'] } - -wasi-common = { workspace = true } -wasi-cap-std-sync = { workspace = true } -wasmtime-wasi = { workspace = true, default-features = true, features = [ - "tokio", -] } -cap-std = { workspace = true } -cap-rand = { workspace = true } -tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } - -wasmtime-wasi-http = { workspace = true, features = ["sync"] } - -[features] -test_programs = [] +wasi = "0.11.0" +wit-bindgen = { workspace = true, features = ['default'] } +libc = { workspace = true } +getrandom = "0.2.9" diff --git a/crates/test-programs/README.md b/crates/test-programs/README.md deleted file mode 100644 index 31f3c1bfadb6..000000000000 --- a/crates/test-programs/README.md +++ /dev/null @@ -1,7 +0,0 @@ -This is the `test-programs` crate, which builds and runs whole programs -compiled to wasm32-wasi. - -To actually run these tests, the test-programs feature must be enabled, e.g.: -``` -cargo test --features test-programs/test_programs --package test-programs -``` diff --git a/crates/test-programs/artifacts/Cargo.toml b/crates/test-programs/artifacts/Cargo.toml new file mode 100644 index 000000000000..4c1e69d49128 --- /dev/null +++ b/crates/test-programs/artifacts/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "test-programs-artifacts" +version = "0.0.0" +authors = ["The Wasmtime Project Developers"] +edition.workspace = true +publish = false +license = "Apache-2.0 WITH LLVM-exception" + +[build-dependencies] +heck = { workspace = true } +wit-component = { workspace = true } +cargo_metadata = "0.15.3" diff --git a/crates/test-programs/artifacts/build.rs b/crates/test-programs/artifacts/build.rs new file mode 100644 index 000000000000..eb446fd0ba0d --- /dev/null +++ b/crates/test-programs/artifacts/build.rs @@ -0,0 +1,174 @@ +use heck::*; +use std::collections::BTreeMap; +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; +use wit_component::ComponentEncoder; + +fn main() { + build_and_generate_tests(); +} + +fn build_and_generate_tests() { + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + let reactor_adapter = build_adapter(&out_dir, "reactor", &[]); + let command_adapter = build_adapter( + &out_dir, + "command", + &["--no-default-features", "--features=command"], + ); + + println!("cargo:rerun-if-changed=../src"); + + // Build the test programs: + let mut cmd = cargo(); + cmd.arg("build") + .arg("--target=wasm32-wasi") + .arg("--package=test-programs") + .env("CARGO_TARGET_DIR", &out_dir) + .env("CARGO_PROFILE_DEV_DEBUG", "1") + .env("RUSTFLAGS", rustflags()) + .env_remove("CARGO_ENCODED_RUSTFLAGS"); + eprintln!("running: {cmd:?}"); + let status = cmd.status().unwrap(); + assert!(status.success()); + + let meta = cargo_metadata::MetadataCommand::new().exec().unwrap(); + let targets = meta + .packages + .iter() + .find(|p| p.name == "test-programs") + .unwrap() + .targets + .iter() + .filter(move |t| t.kind == &["bin"]) + .map(|t| &t.name) + .collect::>(); + + let mut generated_code = String::new(); + + let mut kinds = BTreeMap::new(); + + for target in targets { + let camel = target.to_shouty_snake_case(); + let wasm = out_dir + .join("wasm32-wasi") + .join("debug") + .join(format!("{target}.wasm")); + + generated_code += &format!("pub const {camel}: &'static str = {wasm:?};\n"); + + let adapter = match target.as_str() { + "reactor" => &reactor_adapter, + _ => &command_adapter, + }; + let path = compile_component(&wasm, adapter); + generated_code += &format!("pub const {camel}_COMPONENT: &'static str = {path:?};\n"); + + // Bucket, based on the name of the test, into a "kind" which generates + // a `foreach_*` macro below. + let kind = match target.as_str() { + s if s.starts_with("http_") => "http", + s if s.starts_with("preview1_") => "preview1", + s if s.starts_with("preview2_") => "preview2", + s if s.starts_with("cli_") => "cli", + s if s.starts_with("api_") => "api", + // If you're reading this because you hit this panic, either add it + // to a test suite above or add a new "suite". The purpose of the + // categorization above is to have a static assertion that tests + // added are actually run somewhere, so as long as you're also + // adding test code somewhere that's ok. + other => { + panic!("don't know how to classify test name `{other}` to a kind") + } + }; + if !kind.is_empty() { + kinds.entry(kind).or_insert(Vec::new()).push(target); + } + } + + for (kind, targets) in kinds { + generated_code += &format!("#[macro_export]"); + generated_code += &format!("macro_rules! foreach_{kind} {{\n"); + generated_code += &format!(" ($mac:ident) => {{\n"); + for target in targets { + generated_code += &format!("$mac!({target});\n") + } + generated_code += &format!(" }}\n"); + generated_code += &format!("}}\n"); + } + + std::fs::write(out_dir.join("gen.rs"), generated_code).unwrap(); +} + +// Build the WASI Preview 1 adapter, and get the binary: +fn build_adapter(out_dir: &PathBuf, name: &str, features: &[&str]) -> Vec { + println!("cargo:rerun-if-changed=../../wasi-preview1-component-adapter"); + let mut cmd = cargo(); + cmd.arg("build") + .arg("--release") + .arg("--package=wasi-preview1-component-adapter") + .arg("--target=wasm32-unknown-unknown") + .env("CARGO_TARGET_DIR", out_dir) + .env("RUSTFLAGS", rustflags()) + .env_remove("CARGO_ENCODED_RUSTFLAGS"); + for f in features { + cmd.arg(f); + } + eprintln!("running: {cmd:?}"); + let status = cmd.status().unwrap(); + assert!(status.success()); + + let adapter = out_dir.join(format!("wasi_snapshot_preview1.{name}.wasm")); + std::fs::copy( + out_dir + .join("wasm32-unknown-unknown") + .join("release") + .join("wasi_snapshot_preview1.wasm"), + &adapter, + ) + .unwrap(); + println!("wasi {name} adapter: {:?}", &adapter); + fs::read(&adapter).unwrap() +} + +fn rustflags() -> &'static str { + match option_env!("RUSTFLAGS") { + // If we're in CI which is denying warnings then deny warnings to code + // built here too to keep the tree warning-free. + Some(s) if s.contains("-D warnings") => "-D warnings", + _ => "", + } +} + +// Compile a component, return the path of the binary: +fn compile_component(wasm: &Path, adapter: &[u8]) -> PathBuf { + println!("creating a component from {wasm:?}"); + let module = fs::read(wasm).expect("read wasm module"); + let component = ComponentEncoder::default() + .module(module.as_slice()) + .unwrap() + .validate(true) + .adapter("wasi_snapshot_preview1", adapter) + .unwrap() + .encode() + .expect("module can be translated to a component"); + let out_dir = wasm.parent().unwrap(); + let stem = wasm.file_stem().unwrap().to_str().unwrap(); + let component_path = out_dir.join(format!("{stem}.component.wasm")); + fs::write(&component_path, component).expect("write component to disk"); + component_path +} + +fn cargo() -> Command { + // Miri configures its own sysroot which we don't want to use, so remove + // miri's own wrappers around rustc to ensure that we're using the real + // rustc to build these programs. + let mut cargo = Command::new("cargo"); + if std::env::var("CARGO_CFG_MIRI").is_ok() { + cargo.env_remove("RUSTC").env_remove("RUSTC_WRAPPER"); + } + cargo +} diff --git a/crates/test-programs/artifacts/src/lib.rs b/crates/test-programs/artifacts/src/lib.rs new file mode 100644 index 000000000000..e312491e9883 --- /dev/null +++ b/crates/test-programs/artifacts/src/lib.rs @@ -0,0 +1,44 @@ +include!(concat!(env!("OUT_DIR"), "/gen.rs")); + +use std::io::IsTerminal; + +/// The wasi-tests binaries use these environment variables to determine their +/// expected behavior. +/// Used by all of the tests/ which execute the wasi-tests binaries. +pub fn wasi_tests_environment() -> &'static [(&'static str, &'static str)] { + #[cfg(windows)] + { + &[ + ("ERRNO_MODE_WINDOWS", "1"), + // Windows does not support dangling links or symlinks in the filesystem. + ("NO_DANGLING_FILESYSTEM", "1"), + // Windows does not support renaming a directory to an empty directory - + // empty directory must be deleted. + ("NO_RENAME_DIR_TO_EMPTY_DIR", "1"), + // cap-std-sync does not support the sync family of fdflags + ("NO_FDFLAGS_SYNC_SUPPORT", "1"), + ] + } + #[cfg(all(unix, not(target_os = "macos")))] + { + &[ + ("ERRNO_MODE_UNIX", "1"), + // cap-std-sync does not support the sync family of fdflags + ("NO_FDFLAGS_SYNC_SUPPORT", "1"), + ] + } + #[cfg(target_os = "macos")] + { + &[ + ("ERRNO_MODE_MACOS", "1"), + // cap-std-sync does not support the sync family of fdflags + ("NO_FDFLAGS_SYNC_SUPPORT", "1"), + ] + } +} + +pub fn stdio_is_terminal() -> bool { + std::io::stdin().is_terminal() + && std::io::stdout().is_terminal() + && std::io::stderr().is_terminal() +} diff --git a/crates/test-programs/build.rs b/crates/test-programs/build.rs deleted file mode 100644 index efa0d20d63eb..000000000000 --- a/crates/test-programs/build.rs +++ /dev/null @@ -1,282 +0,0 @@ -#![cfg_attr(not(feature = "test_programs"), allow(dead_code))] - -use heck::ToSnakeCase; -use std::env; -use std::fs; -use std::path::PathBuf; -use std::process::Command; -use wit_component::ComponentEncoder; - -// NB: this is set to `false` when a breaking change to WIT is made since the -// wasi-http WIT is currently a submodule and can't be updated atomically with -// the rest of Wasmtime. -const BUILD_WASI_HTTP_TESTS: bool = true; - -fn main() { - #[cfg(feature = "test_programs")] - build_and_generate_tests(); -} - -fn build_and_generate_tests() { - let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - - let reactor_adapter = build_adapter(&out_dir, "reactor", &[]); - let command_adapter = build_adapter( - &out_dir, - "command", - &["--no-default-features", "--features=command"], - ); - - println!("cargo:rerun-if-changed=./wasi-tests"); - println!("cargo:rerun-if-changed=./command-tests"); - println!("cargo:rerun-if-changed=./reactor-tests"); - println!("cargo:rerun-if-changed=./wasi-sockets-tests"); - if BUILD_WASI_HTTP_TESTS { - println!("cargo:rerun-if-changed=./wasi-http-tests"); - println!("cargo:rerun-if-changed=./wasi-http-proxy-tests"); - } else { - println!("cargo:rustc-cfg=skip_wasi_http_tests"); - } - - // Build the test programs: - let mut cmd = Command::new("cargo"); - cmd.arg("build") - .arg("--target=wasm32-wasi") - .arg("--package=wasi-tests") - .arg("--package=command-tests") - .arg("--package=reactor-tests") - .arg("--package=wasi-sockets-tests") - .env("CARGO_TARGET_DIR", &out_dir) - .env("CARGO_PROFILE_DEV_DEBUG", "1") - .env("RUSTFLAGS", rustflags()) - .env_remove("CARGO_ENCODED_RUSTFLAGS"); - if BUILD_WASI_HTTP_TESTS { - cmd.arg("--package=wasi-http-tests"); - cmd.arg("--package=wasi-http-proxy-tests"); - } - let status = cmd.status().unwrap(); - assert!(status.success()); - - let meta = cargo_metadata::MetadataCommand::new().exec().unwrap(); - - modules_rs(&meta, "wasi-tests", "bin", &out_dir); - components_rs(&meta, "wasi-tests", "bin", &command_adapter, &out_dir); - - if BUILD_WASI_HTTP_TESTS { - components_rs(&meta, "wasi-http-tests", "bin", &command_adapter, &out_dir); - components_rs( - &meta, - "wasi-http-proxy-tests", - "cdylib", - &reactor_adapter, - &out_dir, - ); - } - - components_rs(&meta, "command-tests", "bin", &command_adapter, &out_dir); - components_rs(&meta, "reactor-tests", "cdylib", &reactor_adapter, &out_dir); - - components_rs( - &meta, - "wasi-sockets-tests", - "bin", - &command_adapter, - &out_dir, - ); -} - -// Creates an `${out_dir}/${package}_modules.rs` file that exposes a `get_module(&str) -> Module`, -// and contains a `use self::{module} as _;` for each module that ensures that the user defines -// a symbol (ideally a #[test]) corresponding to each module. -fn modules_rs(meta: &cargo_metadata::Metadata, package: &str, kind: &str, out_dir: &PathBuf) { - let modules = targets_in_package(meta, package, kind) - .into_iter() - .map(|stem| { - ( - stem.clone(), - out_dir - .join("wasm32-wasi") - .join("debug") - .join(format!("{stem}.wasm")) - .as_os_str() - .to_str() - .unwrap() - .to_string(), - ) - }) - .collect::>(); - - let mut decls = String::new(); - let mut cases = String::new(); - let mut uses = String::new(); - for (stem, file) in modules { - let global = format!("{}_MODULE", stem.to_uppercase()); - // Load the module from disk only once, in case it is used many times: - decls += &format!( - " - lazy_static::lazy_static!{{ - static ref {global}: wasmtime::Module = {{ - wasmtime::Module::from_file(&ENGINE, {file:?}).unwrap() - }}; - }} - " - ); - // Match the stem str literal to the module. Cloning is just a ref count incr. - cases += &format!("{stem:?} => {global}.clone(),\n"); - // Statically ensure that the user defines a function (ideally a #[test]) for each stem. - uses += &format!("#[allow(unused_imports)] use self::{stem} as _;\n"); - } - - std::fs::write( - out_dir.join(&format!("{}_modules.rs", package.to_snake_case())), - format!( - " - {decls} - pub fn get_module(s: &str) -> wasmtime::Module {{ - match s {{ - {cases} - _ => panic!(\"no such module: {{}}\", s), - }} - }} - {uses} - " - ), - ) - .unwrap(); -} - -// Build the WASI Preview 1 adapter, and get the binary: -fn build_adapter(out_dir: &PathBuf, name: &str, features: &[&str]) -> Vec { - println!("cargo:rerun-if-changed=../wasi-preview1-component-adapter"); - let mut cmd = Command::new("cargo"); - cmd.arg("build") - .arg("--release") - .arg("--package=wasi-preview1-component-adapter") - .arg("--target=wasm32-unknown-unknown") - .env("CARGO_TARGET_DIR", out_dir) - .env("RUSTFLAGS", rustflags()) - .env_remove("CARGO_ENCODED_RUSTFLAGS"); - for f in features { - cmd.arg(f); - } - let status = cmd.status().unwrap(); - assert!(status.success()); - - let adapter = out_dir.join(format!("wasi_snapshot_preview1.{name}.wasm")); - std::fs::copy( - out_dir - .join("wasm32-unknown-unknown") - .join("release") - .join("wasi_snapshot_preview1.wasm"), - &adapter, - ) - .unwrap(); - println!("wasi {name} adapter: {:?}", &adapter); - fs::read(&adapter).unwrap() -} - -fn rustflags() -> &'static str { - match option_env!("RUSTFLAGS") { - // If we're in CI which is denying warnings then deny warnings to code - // built here too to keep the tree warning-free. - Some(s) if s.contains("-D warnings") => "-D warnings", - _ => "", - } -} - -// Builds components out of modules, and creates an `${out_dir}/${package}_component.rs` file that -// exposes a `get_component(&str) -> Component` -// and a contains a `use self::{component} as _;` for each module that ensures that the user defines -// a symbol (ideally a #[test]) corresponding to each component. -fn components_rs( - meta: &cargo_metadata::Metadata, - package: &str, - kind: &str, - adapter: &[u8], - out_dir: &PathBuf, -) { - let mut decls = String::new(); - let mut cases = String::new(); - let mut uses = String::new(); - for target_name in targets_in_package(&meta, package, kind) { - let stem = target_name.to_snake_case(); - let file = compile_component(&stem, out_dir, adapter); - - let global = format!("{}_COMPONENT", stem.to_uppercase()); - decls += &format!( - " - lazy_static::lazy_static!{{ - static ref {global}: wasmtime::component::Component = {{ - wasmtime::component::Component::from_file(&ENGINE, {file:?}).unwrap() - }}; - }} - " - ); - cases += &format!("{stem:?} => {global}.clone(),\n"); - uses += &format!("#[allow(unused_imports)] use self::{stem} as _;\n"); - } - - std::fs::write( - out_dir.join(&format!("{}_components.rs", package.to_snake_case())), - format!( - " - {decls} - pub fn get_component(s: &str) -> wasmtime::component::Component {{ - match s {{ - {cases} - _ => panic!(\"no such component: {{}}\", s), - }} - }} - {uses} - " - ), - ) - .unwrap(); -} - -// Compile a component, return the path of the binary: -fn compile_component(stem: &str, out_dir: &PathBuf, adapter: &[u8]) -> PathBuf { - let file = out_dir - .join("wasm32-wasi") - .join("debug") - .join(format!("{stem}.wasm")); - let module = fs::read(&file).expect("read wasm module"); - let component = ComponentEncoder::default() - .module(module.as_slice()) - .unwrap() - .validate(true) - .adapter("wasi_snapshot_preview1", adapter) - .unwrap() - .encode() - .expect(&format!( - "module {:?} can be translated to a component", - file - )); - let component_path = out_dir.join(format!("{}.component.wasm", &stem)); - fs::write(&component_path, component).expect("write component to disk"); - component_path -} - -// Get all targets in a given package with a given kind -// kind is "bin" for test program crates that expose a `fn main`, and -// "cdylib" for crates that implement a reactor. -fn targets_in_package<'a>( - meta: &'a cargo_metadata::Metadata, - package: &'a str, - kind: &'a str, -) -> Vec { - let targets = meta - .packages - .iter() - .find(|p| p.name == package) - .unwrap() - .targets - .iter() - .filter(move |t| t.kind == &[kind]) - .map(|t| t.name.to_snake_case()) - .collect::>(); - if targets.is_empty() { - panic!("no targets for package {package:?} of kind {kind:?}") - } - targets -} diff --git a/crates/test-programs/command-tests/Cargo.toml b/crates/test-programs/command-tests/Cargo.toml deleted file mode 100644 index a93cc2f0d598..000000000000 --- a/crates/test-programs/command-tests/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "command-tests" -version = "0.0.0" -edition = "2021" -publish = false - -[dependencies] -anyhow = { workspace = true } -getrandom = "0.2.8" -wit-bindgen = { workspace = true, default-features = true } - -# FIXME: building cap-std and rustix currently requires a nightly rust, so we -# are not including it in the test suite. -# The poll_stdin and file_dir_sync tests contain commented-out uses of these -# crates. They should be re-enabled once these crates can build for -# wasm32-wasi on stable rust. -# rustix = { workspace = true } -# cap-std = { workspace = true } diff --git a/crates/test-programs/command-tests/src/bin/poll_stdin.rs b/crates/test-programs/command-tests/src/bin/poll_stdin.rs deleted file mode 100644 index 71315db750ea..000000000000 --- a/crates/test-programs/command-tests/src/bin/poll_stdin.rs +++ /dev/null @@ -1,14 +0,0 @@ -/* FIXME once rustix can build on stable rust, rewrite -use rustix::io::{PollFd, PollFlags}; - -fn main() { - let stdin = std::io::stdin(); - let mut pollfds = vec![PollFd::new(&stdin, PollFlags::IN)]; - let num = rustix::io::poll(&mut pollfds, -1).unwrap(); - assert_eq!(num, 1); - assert_eq!(pollfds[0].revents(), PollFlags::IN); -} -*/ -fn main() { - println!("FIXME: this test does nothing for now"); -} diff --git a/crates/test-programs/command-tests/src/lib.rs b/crates/test-programs/command-tests/src/lib.rs deleted file mode 100644 index 13a414db4af1..000000000000 --- a/crates/test-programs/command-tests/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -wit_bindgen::generate!("test-command" in "../../wasi/wit"); diff --git a/crates/test-programs/reactor-tests/Cargo.toml b/crates/test-programs/reactor-tests/Cargo.toml deleted file mode 100644 index 28a645a9401e..000000000000 --- a/crates/test-programs/reactor-tests/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "reactor-tests" -version = "0.1.0" -edition = "2021" -publish = false - -[lib] -crate-type=["cdylib"] - -[dependencies] -wit-bindgen = { workspace = true, features = ["macros", "realloc"] } diff --git a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs b/crates/test-programs/src/bin/api_proxy.rs similarity index 83% rename from crates/test-programs/wasi-http-proxy-tests/src/lib.rs rename to crates/test-programs/src/bin/api_proxy.rs index 5c25f34ddd3a..6798d4e30fe2 100644 --- a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs +++ b/crates/test-programs/src/bin/api_proxy.rs @@ -2,7 +2,7 @@ pub mod bindings { use super::T; wit_bindgen::generate!({ - path: "../../wasi-http/wit", + path: "../wasi-http/wit", world: "wasi:http/proxy", exports: { "wasi:http/incoming-handler": T, @@ -30,3 +30,7 @@ impl bindings::exports::wasi::http::incoming_handler::Guest for T { bindings::wasi::http::types::OutgoingBody::finish(body, None); } } + +// Technically this should not be here for a proxy, but given the current +// framework for tests it's required since this file is built as a `bin` +fn main() {} diff --git a/crates/test-programs/reactor-tests/src/lib.rs b/crates/test-programs/src/bin/api_reactor.rs similarity index 89% rename from crates/test-programs/reactor-tests/src/lib.rs rename to crates/test-programs/src/bin/api_reactor.rs index c5514d6a1505..5b700e9d2d1d 100644 --- a/crates/test-programs/reactor-tests/src/lib.rs +++ b/crates/test-programs/src/bin/api_reactor.rs @@ -1,13 +1,13 @@ wit_bindgen::generate!({ world: "test-reactor", - path: "../../wasi/wit", + path: "../wasi/wit", exports: { world: T, } }); struct T; -use wasi::io::poll; +use crate::wasi::io::poll; static mut STATE: Vec = Vec::new(); @@ -66,3 +66,7 @@ impl Guest for T { format!("{stat:?}") } } + +// Technically this should not be here for a reactor, but given the current +// framework for tests it's required since this file is built as a `bin` +fn main() {} diff --git a/crates/test-programs/command-tests/src/bin/read_only.rs b/crates/test-programs/src/bin/api_read_only.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/read_only.rs rename to crates/test-programs/src/bin/api_read_only.rs diff --git a/crates/test-programs/command-tests/src/bin/time.rs b/crates/test-programs/src/bin/api_time.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/time.rs rename to crates/test-programs/src/bin/api_time.rs diff --git a/crates/test-programs/command-tests/src/bin/args.rs b/crates/test-programs/src/bin/cli_args.rs similarity index 65% rename from crates/test-programs/command-tests/src/bin/args.rs rename to crates/test-programs/src/bin/cli_args.rs index 8403f7de5901..e152e452ee3f 100644 --- a/crates/test-programs/command-tests/src/bin/args.rs +++ b/crates/test-programs/src/bin/cli_args.rs @@ -1,5 +1,5 @@ fn main() { - let args = std::env::args().collect::>(); + let args = std::env::args().skip(1).collect::>(); assert_eq!( args, ["hello", "this", "", "is an argument", "with 🚩 emoji"] diff --git a/crates/test-programs/command-tests/src/bin/default_clocks.rs b/crates/test-programs/src/bin/cli_default_clocks.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/default_clocks.rs rename to crates/test-programs/src/bin/cli_default_clocks.rs diff --git a/crates/test-programs/command-tests/src/bin/directory_list.rs b/crates/test-programs/src/bin/cli_directory_list.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/directory_list.rs rename to crates/test-programs/src/bin/cli_directory_list.rs diff --git a/crates/test-programs/command-tests/src/bin/env.rs b/crates/test-programs/src/bin/cli_env.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/env.rs rename to crates/test-programs/src/bin/cli_env.rs diff --git a/crates/test-programs/command-tests/src/bin/exit_default.rs b/crates/test-programs/src/bin/cli_exit_default.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/exit_default.rs rename to crates/test-programs/src/bin/cli_exit_default.rs diff --git a/crates/test-programs/command-tests/src/bin/exit_failure.rs b/crates/test-programs/src/bin/cli_exit_failure.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/exit_failure.rs rename to crates/test-programs/src/bin/cli_exit_failure.rs diff --git a/crates/test-programs/command-tests/src/bin/exit_panic.rs b/crates/test-programs/src/bin/cli_exit_panic.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/exit_panic.rs rename to crates/test-programs/src/bin/cli_exit_panic.rs diff --git a/crates/test-programs/command-tests/src/bin/exit_success.rs b/crates/test-programs/src/bin/cli_exit_success.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/exit_success.rs rename to crates/test-programs/src/bin/cli_exit_success.rs diff --git a/crates/test-programs/command-tests/src/bin/export_cabi_realloc.rs b/crates/test-programs/src/bin/cli_export_cabi_realloc.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/export_cabi_realloc.rs rename to crates/test-programs/src/bin/cli_export_cabi_realloc.rs diff --git a/crates/test-programs/command-tests/src/bin/file_append.rs b/crates/test-programs/src/bin/cli_file_append.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/file_append.rs rename to crates/test-programs/src/bin/cli_file_append.rs diff --git a/crates/test-programs/command-tests/src/bin/file_dir_sync.rs b/crates/test-programs/src/bin/cli_file_dir_sync.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/file_dir_sync.rs rename to crates/test-programs/src/bin/cli_file_dir_sync.rs diff --git a/crates/test-programs/command-tests/src/bin/file_read.rs b/crates/test-programs/src/bin/cli_file_read.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/file_read.rs rename to crates/test-programs/src/bin/cli_file_read.rs diff --git a/crates/test-programs/command-tests/src/bin/hello_stdout.rs b/crates/test-programs/src/bin/cli_hello_stdout.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/hello_stdout.rs rename to crates/test-programs/src/bin/cli_hello_stdout.rs diff --git a/crates/test-programs/command-tests/src/bin/panic.rs b/crates/test-programs/src/bin/cli_panic.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/panic.rs rename to crates/test-programs/src/bin/cli_panic.rs diff --git a/crates/test-programs/command-tests/src/bin/stdin.rs b/crates/test-programs/src/bin/cli_stdin.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/stdin.rs rename to crates/test-programs/src/bin/cli_stdin.rs diff --git a/crates/test-programs/command-tests/src/bin/stream_pollable_lifetimes.rs b/crates/test-programs/src/bin/cli_stream_pollable_lifetimes.rs similarity index 82% rename from crates/test-programs/command-tests/src/bin/stream_pollable_lifetimes.rs rename to crates/test-programs/src/bin/cli_stream_pollable_lifetimes.rs index 8693e1f373d1..11fa0abc4928 100644 --- a/crates/test-programs/command-tests/src/bin/stream_pollable_lifetimes.rs +++ b/crates/test-programs/src/bin/cli_stream_pollable_lifetimes.rs @@ -1,10 +1,11 @@ -use command_tests::wasi::cli::environment; -use command_tests::wasi::cli::stdin; -use command_tests::wasi::io::poll; -use command_tests::wasi::io::streams; +use test_programs::wasi::cli::environment; +use test_programs::wasi::cli::stdin; +use test_programs::wasi::io::poll; +use test_programs::wasi::io::streams; fn main() { let args = environment::get_arguments(); + let args = &args[1..]; if args == &["correct"] { let stdin: streams::InputStream = stdin::get_stdin(); diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_get.rs b/crates/test-programs/src/bin/http_outbound_request_get.rs similarity index 85% rename from crates/test-programs/wasi-http-tests/src/bin/outbound_request_get.rs rename to crates/test-programs/src/bin/http_outbound_request_get.rs index 25d71c662ca7..49eb88d63779 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_get.rs +++ b/crates/test-programs/src/bin/http_outbound_request_get.rs @@ -1,9 +1,9 @@ use anyhow::Context; -use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; +use test_programs::wasi::http::types::{Method, Scheme}; fn main() { let addr = std::env::var("HTTP_SERVER").unwrap(); - let res = wasi_http_tests::request( + let res = test_programs::http::request( Method::Get, Scheme::Http, &addr, diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs b/crates/test-programs/src/bin/http_outbound_request_invalid_dnsname.rs similarity index 72% rename from crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs rename to crates/test-programs/src/bin/http_outbound_request_invalid_dnsname.rs index a36e5384336e..100f57d6d022 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_dnsname.rs +++ b/crates/test-programs/src/bin/http_outbound_request_invalid_dnsname.rs @@ -1,7 +1,7 @@ -use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; +use test_programs::wasi::http::types::{Method, Scheme}; fn main() { - let res = wasi_http_tests::request( + let res = test_programs::http::request( Method::Get, Scheme::Http, "some.invalid.dnsname:3000", diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_port.rs b/crates/test-programs/src/bin/http_outbound_request_invalid_port.rs similarity index 71% rename from crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_port.rs rename to crates/test-programs/src/bin/http_outbound_request_invalid_port.rs index fe0290a30d21..cb189eb931b4 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_port.rs +++ b/crates/test-programs/src/bin/http_outbound_request_invalid_port.rs @@ -1,7 +1,7 @@ -use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; +use test_programs::wasi::http::types::{Method, Scheme}; fn main() { - let res = wasi_http_tests::request( + let res = test_programs::http::request( Method::Get, Scheme::Http, "localhost:99999", diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs b/crates/test-programs/src/bin/http_outbound_request_invalid_version.rs similarity index 71% rename from crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs rename to crates/test-programs/src/bin/http_outbound_request_invalid_version.rs index c61bfeab1b32..59c652fc2b9a 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_invalid_version.rs +++ b/crates/test-programs/src/bin/http_outbound_request_invalid_version.rs @@ -1,8 +1,9 @@ -use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; +use test_programs::wasi::http::types::{Method, Scheme}; fn main() { let addr = std::env::var("HTTP_SERVER").unwrap(); - let res = wasi_http_tests::request(Method::Connect, Scheme::Http, &addr, "/", None, Some(&[])); + let res = + test_programs::http::request(Method::Connect, Scheme::Http, &addr, "/", None, Some(&[])); let error = res.unwrap_err().to_string(); if !error.starts_with("Error::ProtocolError(\"") { diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs b/crates/test-programs/src/bin/http_outbound_request_large_post.rs similarity index 86% rename from crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs rename to crates/test-programs/src/bin/http_outbound_request_large_post.rs index e5620910fd17..111cba139723 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_large_post.rs +++ b/crates/test-programs/src/bin/http_outbound_request_large_post.rs @@ -1,6 +1,6 @@ use anyhow::Context; use std::io::{self, Read}; -use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; +use test_programs::wasi::http::types::{Method, Scheme}; fn main() { // TODO: ensure more than 700 bytes is allowed without error @@ -8,7 +8,7 @@ fn main() { let mut buffer = [0; LEN]; let addr = std::env::var("HTTP_SERVER").unwrap(); io::repeat(0b001).read_exact(&mut buffer).unwrap(); - let res = wasi_http_tests::request( + let res = test_programs::http::request( Method::Post, Scheme::Http, &addr, diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs b/crates/test-programs/src/bin/http_outbound_request_post.rs similarity index 82% rename from crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs rename to crates/test-programs/src/bin/http_outbound_request_post.rs index 6fa0e9ad02df..3831f0a274a5 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_post.rs +++ b/crates/test-programs/src/bin/http_outbound_request_post.rs @@ -1,9 +1,9 @@ use anyhow::Context; -use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; +use test_programs::wasi::http::types::{Method, Scheme}; fn main() { let addr = std::env::var("HTTP_SERVER").unwrap(); - let res = wasi_http_tests::request( + let res = test_programs::http::request( Method::Post, Scheme::Http, &addr, diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_put.rs b/crates/test-programs/src/bin/http_outbound_request_put.rs similarity index 58% rename from crates/test-programs/wasi-http-tests/src/bin/outbound_request_put.rs rename to crates/test-programs/src/bin/http_outbound_request_put.rs index 68e34f6cb578..ae01967c1df5 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_put.rs +++ b/crates/test-programs/src/bin/http_outbound_request_put.rs @@ -1,11 +1,12 @@ use anyhow::Context; -use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; +use test_programs::wasi::http::types::{Method, Scheme}; fn main() { let addr = std::env::var("HTTP_SERVER").unwrap(); - let res = wasi_http_tests::request(Method::Put, Scheme::Http, &addr, "/put", Some(&[]), None) - .context("/put") - .unwrap(); + let res = + test_programs::http::request(Method::Put, Scheme::Http, &addr, "/put", Some(&[]), None) + .context("/put") + .unwrap(); println!("/put: {res:?}"); assert_eq!(res.status, 200); diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_response_build.rs b/crates/test-programs/src/bin/http_outbound_request_response_build.rs similarity index 81% rename from crates/test-programs/wasi-http-tests/src/bin/outbound_request_response_build.rs rename to crates/test-programs/src/bin/http_outbound_request_response_build.rs index 322992380d49..b377b4a47955 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_response_build.rs +++ b/crates/test-programs/src/bin/http_outbound_request_response_build.rs @@ -1,11 +1,7 @@ -use wasi_http_tests::bindings::wasi::{cli::stdout::get_stdout, http::types as http_types}; - -fn print(msg: &[u8]) { - let _ = get_stdout().blocking_write_and_flush(&msg); -} +use test_programs::wasi::http::types as http_types; fn main() { - print("Called _start\n".as_bytes()); + println!("Called _start"); { let headers = http_types::Headers::new(&[( "Content-Type".to_string(), @@ -36,5 +32,5 @@ fn main() { .blocking_write_and_flush("response-body".as_bytes()) .unwrap(); } - print("Done\n".as_bytes()); + println!("Done"); } diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs b/crates/test-programs/src/bin/http_outbound_request_unknown_method.rs similarity index 73% rename from crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs rename to crates/test-programs/src/bin/http_outbound_request_unknown_method.rs index cb7847e3b5c1..6211bf0c8b99 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unknown_method.rs +++ b/crates/test-programs/src/bin/http_outbound_request_unknown_method.rs @@ -1,7 +1,7 @@ -use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; +use test_programs::wasi::http::types::{Method, Scheme}; fn main() { - let res = wasi_http_tests::request( + let res = test_programs::http::request( Method::Other("OTHER".to_owned()), Scheme::Http, "localhost:3000", diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs b/crates/test-programs/src/bin/http_outbound_request_unsupported_scheme.rs similarity index 72% rename from crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs rename to crates/test-programs/src/bin/http_outbound_request_unsupported_scheme.rs index b9d198a44381..262cb11e62f0 100644 --- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request_unsupported_scheme.rs +++ b/crates/test-programs/src/bin/http_outbound_request_unsupported_scheme.rs @@ -1,7 +1,7 @@ -use wasi_http_tests::bindings::wasi::http::types::{Method, Scheme}; +use test_programs::wasi::http::types::{Method, Scheme}; fn main() { - let res = wasi_http_tests::request( + let res = test_programs::http::request( Method::Get, Scheme::Other("WS".to_owned()), "localhost:3000", diff --git a/crates/test-programs/wasi-tests/src/bin/big_random_buf.rs b/crates/test-programs/src/bin/preview1_big_random_buf.rs similarity index 100% rename from crates/test-programs/wasi-tests/src/bin/big_random_buf.rs rename to crates/test-programs/src/bin/preview1_big_random_buf.rs diff --git a/crates/test-programs/wasi-tests/src/bin/clock_time_get.rs b/crates/test-programs/src/bin/preview1_clock_time_get.rs similarity index 100% rename from crates/test-programs/wasi-tests/src/bin/clock_time_get.rs rename to crates/test-programs/src/bin/preview1_clock_time_get.rs diff --git a/crates/test-programs/wasi-tests/src/bin/close_preopen.rs b/crates/test-programs/src/bin/preview1_close_preopen.rs similarity index 94% rename from crates/test-programs/wasi-tests/src/bin/close_preopen.rs rename to crates/test-programs/src/bin/preview1_close_preopen.rs index 2d8f3a344fe3..9412d234bb6f 100644 --- a/crates/test-programs/wasi-tests/src/bin/close_preopen.rs +++ b/crates/test-programs/src/bin/preview1_close_preopen.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{assert_errno, open_scratch_directory}; unsafe fn test_close_preopen(dir_fd: wasi::Fd) { let pre_fd: wasi::Fd = (libc::STDERR_FILENO + 1) as wasi::Fd; diff --git a/crates/test-programs/wasi-tests/src/bin/dangling_fd.rs b/crates/test-programs/src/bin/preview1_dangling_fd.rs similarity index 94% rename from crates/test-programs/wasi-tests/src/bin/dangling_fd.rs rename to crates/test-programs/src/bin/preview1_dangling_fd.rs index 3d29c4fc7e5a..f47caef37dcd 100644 --- a/crates/test-programs/wasi-tests/src/bin/dangling_fd.rs +++ b/crates/test-programs/src/bin/preview1_dangling_fd.rs @@ -1,8 +1,8 @@ use std::{env, process}; -use wasi_tests::{open_scratch_directory, TESTCONFIG}; +use test_programs::preview1::{config, open_scratch_directory}; unsafe fn test_dangling_fd(dir_fd: wasi::Fd) { - if TESTCONFIG.support_dangling_filesystem() { + if config().support_dangling_filesystem() { // Create a file, open it, delete it without closing the handle, // and then try creating it again let fd = wasi::path_open(dir_fd, 0, "file", wasi::OFLAGS_CREAT, 0, 0, 0).unwrap(); diff --git a/crates/test-programs/wasi-tests/src/bin/dangling_symlink.rs b/crates/test-programs/src/bin/preview1_dangling_symlink.rs similarity index 91% rename from crates/test-programs/wasi-tests/src/bin/dangling_symlink.rs rename to crates/test-programs/src/bin/preview1_dangling_symlink.rs index f7538fc979f2..8b3fc9bc8a5c 100644 --- a/crates/test-programs/wasi-tests/src/bin/dangling_symlink.rs +++ b/crates/test-programs/src/bin/preview1_dangling_symlink.rs @@ -1,8 +1,8 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory, TESTCONFIG}; +use test_programs::preview1::{assert_errno, config, open_scratch_directory}; unsafe fn test_dangling_symlink(dir_fd: wasi::Fd) { - if TESTCONFIG.support_dangling_filesystem() { + if config().support_dangling_filesystem() { // First create a dangling symlink. wasi::path_symlink("target", dir_fd, "symlink").expect("creating a symlink"); diff --git a/crates/test-programs/wasi-tests/src/bin/dir_fd_op_failures.rs b/crates/test-programs/src/bin/preview1_dir_fd_op_failures.rs similarity index 98% rename from crates/test-programs/wasi-tests/src/bin/dir_fd_op_failures.rs rename to crates/test-programs/src/bin/preview1_dir_fd_op_failures.rs index bb081c6960f1..77a84f4104a3 100644 --- a/crates/test-programs/wasi-tests/src/bin/dir_fd_op_failures.rs +++ b/crates/test-programs/src/bin/preview1_dir_fd_op_failures.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_fd_dir_ops(dir_fd: wasi::Fd) { let stat = wasi::fd_filestat_get(dir_fd).expect("failed to fdstat"); diff --git a/crates/test-programs/wasi-tests/src/bin/directory_seek.rs b/crates/test-programs/src/bin/preview1_directory_seek.rs similarity index 95% rename from crates/test-programs/wasi-tests/src/bin/directory_seek.rs rename to crates/test-programs/src/bin/preview1_directory_seek.rs index dc4fd52fb52f..c7fe6ed9f44a 100644 --- a/crates/test-programs/wasi-tests/src/bin/directory_seek.rs +++ b/crates/test-programs/src/bin/preview1_directory_seek.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{assert_errno, open_scratch_directory}; unsafe fn test_directory_seek(dir_fd: wasi::Fd) { // Create a directory in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/fd_advise.rs b/crates/test-programs/src/bin/preview1_fd_advise.rs similarity index 97% rename from crates/test-programs/wasi-tests/src/bin/fd_advise.rs rename to crates/test-programs/src/bin/preview1_fd_advise.rs index 78a048db0f9e..89c045782487 100644 --- a/crates/test-programs/wasi-tests/src/bin/fd_advise.rs +++ b/crates/test-programs/src/bin/preview1_fd_advise.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_fd_advise(dir_fd: wasi::Fd) { // Create a file in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/fd_filestat_get.rs b/crates/test-programs/src/bin/preview1_fd_filestat_get.rs similarity index 100% rename from crates/test-programs/wasi-tests/src/bin/fd_filestat_get.rs rename to crates/test-programs/src/bin/preview1_fd_filestat_get.rs diff --git a/crates/test-programs/wasi-tests/src/bin/fd_filestat_set.rs b/crates/test-programs/src/bin/preview1_fd_filestat_set.rs similarity index 97% rename from crates/test-programs/wasi-tests/src/bin/fd_filestat_set.rs rename to crates/test-programs/src/bin/preview1_fd_filestat_set.rs index de358dfff23e..b72dec0afe67 100644 --- a/crates/test-programs/wasi-tests/src/bin/fd_filestat_set.rs +++ b/crates/test-programs/src/bin/preview1_fd_filestat_set.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_fd_filestat_set(dir_fd: wasi::Fd) { // Create a file in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/fd_flags_set.rs b/crates/test-programs/src/bin/preview1_fd_flags_set.rs similarity index 98% rename from crates/test-programs/wasi-tests/src/bin/fd_flags_set.rs rename to crates/test-programs/src/bin/preview1_fd_flags_set.rs index 9a21fc465f42..d465990bd6fc 100644 --- a/crates/test-programs/wasi-tests/src/bin/fd_flags_set.rs +++ b/crates/test-programs/src/bin/preview1_fd_flags_set.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_fd_fdstat_set_flags(dir_fd: wasi::Fd) { const FILE_NAME: &str = "file"; diff --git a/crates/test-programs/wasi-tests/src/bin/fd_readdir.rs b/crates/test-programs/src/bin/preview1_fd_readdir.rs similarity index 99% rename from crates/test-programs/wasi-tests/src/bin/fd_readdir.rs rename to crates/test-programs/src/bin/preview1_fd_readdir.rs index 3045f166b0c6..187610c73342 100644 --- a/crates/test-programs/wasi-tests/src/bin/fd_readdir.rs +++ b/crates/test-programs/src/bin/preview1_fd_readdir.rs @@ -1,5 +1,5 @@ use std::{env, mem, process, slice, str}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; const BUF_LEN: usize = 256; diff --git a/crates/test-programs/wasi-tests/src/bin/file_allocate.rs b/crates/test-programs/src/bin/preview1_file_allocate.rs similarity index 96% rename from crates/test-programs/wasi-tests/src/bin/file_allocate.rs rename to crates/test-programs/src/bin/preview1_file_allocate.rs index 356563015b30..a46f6ef46f72 100644 --- a/crates/test-programs/wasi-tests/src/bin/file_allocate.rs +++ b/crates/test-programs/src/bin/preview1_file_allocate.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_file_allocate(dir_fd: wasi::Fd) { // Create a file in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/file_pread_pwrite.rs b/crates/test-programs/src/bin/preview1_file_pread_pwrite.rs similarity index 98% rename from crates/test-programs/wasi-tests/src/bin/file_pread_pwrite.rs rename to crates/test-programs/src/bin/preview1_file_pread_pwrite.rs index 918fec842bf1..ef55362d0288 100644 --- a/crates/test-programs/wasi-tests/src/bin/file_pread_pwrite.rs +++ b/crates/test-programs/src/bin/preview1_file_pread_pwrite.rs @@ -1,6 +1,6 @@ use std::convert::TryInto; use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_file_pread_pwrite(dir_fd: wasi::Fd) { // Create a file in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/file_seek_tell.rs b/crates/test-programs/src/bin/preview1_file_seek_tell.rs similarity index 97% rename from crates/test-programs/wasi-tests/src/bin/file_seek_tell.rs rename to crates/test-programs/src/bin/preview1_file_seek_tell.rs index a54fe14ab05c..70bd6e9b9498 100644 --- a/crates/test-programs/wasi-tests/src/bin/file_seek_tell.rs +++ b/crates/test-programs/src/bin/preview1_file_seek_tell.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{assert_errno, open_scratch_directory}; unsafe fn test_file_seek_tell(dir_fd: wasi::Fd) { // Create a file in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/file_truncation.rs b/crates/test-programs/src/bin/preview1_file_truncation.rs similarity index 97% rename from crates/test-programs/wasi-tests/src/bin/file_truncation.rs rename to crates/test-programs/src/bin/preview1_file_truncation.rs index 35f511448ac7..73fa8c2e0b79 100644 --- a/crates/test-programs/wasi-tests/src/bin/file_truncation.rs +++ b/crates/test-programs/src/bin/preview1_file_truncation.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_file_truncation(dir_fd: wasi::Fd) { const FILENAME: &str = "test.txt"; diff --git a/crates/test-programs/wasi-tests/src/bin/file_unbuffered_write.rs b/crates/test-programs/src/bin/preview1_file_unbuffered_write.rs similarity index 97% rename from crates/test-programs/wasi-tests/src/bin/file_unbuffered_write.rs rename to crates/test-programs/src/bin/preview1_file_unbuffered_write.rs index 8056834fb347..289e755ee573 100644 --- a/crates/test-programs/wasi-tests/src/bin/file_unbuffered_write.rs +++ b/crates/test-programs/src/bin/preview1_file_unbuffered_write.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_file_unbuffered_write(dir_fd: wasi::Fd) { // Create and open file for reading diff --git a/crates/test-programs/wasi-tests/src/bin/interesting_paths.rs b/crates/test-programs/src/bin/preview1_interesting_paths.rs similarity index 97% rename from crates/test-programs/wasi-tests/src/bin/interesting_paths.rs rename to crates/test-programs/src/bin/preview1_interesting_paths.rs index 16954c92fd4f..264388233249 100644 --- a/crates/test-programs/wasi-tests/src/bin/interesting_paths.rs +++ b/crates/test-programs/src/bin/preview1_interesting_paths.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, create_file, open_scratch_directory}; +use test_programs::preview1::{assert_errno, create_file, open_scratch_directory}; unsafe fn test_interesting_paths(dir_fd: wasi::Fd, arg: &str) { // Create a directory in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/nofollow_errors.rs b/crates/test-programs/src/bin/preview1_nofollow_errors.rs similarity index 98% rename from crates/test-programs/wasi-tests/src/bin/nofollow_errors.rs rename to crates/test-programs/src/bin/preview1_nofollow_errors.rs index 86c6f385896b..b0d1b078c181 100644 --- a/crates/test-programs/wasi-tests/src/bin/nofollow_errors.rs +++ b/crates/test-programs/src/bin/preview1_nofollow_errors.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{assert_errno, open_scratch_directory}; unsafe fn test_nofollow_errors(dir_fd: wasi::Fd) { // Create a directory for the symlink to point to. diff --git a/crates/test-programs/wasi-tests/src/bin/overwrite_preopen.rs b/crates/test-programs/src/bin/preview1_overwrite_preopen.rs similarity index 95% rename from crates/test-programs/wasi-tests/src/bin/overwrite_preopen.rs rename to crates/test-programs/src/bin/preview1_overwrite_preopen.rs index 0721d18ab1bd..29da29b9eea3 100644 --- a/crates/test-programs/wasi-tests/src/bin/overwrite_preopen.rs +++ b/crates/test-programs/src/bin/preview1_overwrite_preopen.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{assert_errno, open_scratch_directory}; unsafe fn test_overwrite_preopen(dir_fd: wasi::Fd) { let pre_fd: wasi::Fd = (libc::STDERR_FILENO + 1) as wasi::Fd; diff --git a/crates/test-programs/wasi-tests/src/bin/path_exists.rs b/crates/test-programs/src/bin/preview1_path_exists.rs similarity index 97% rename from crates/test-programs/wasi-tests/src/bin/path_exists.rs rename to crates/test-programs/src/bin/preview1_path_exists.rs index c4d7a04667cf..2dda3236328b 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_exists.rs +++ b/crates/test-programs/src/bin/preview1_path_exists.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{create_file, open_scratch_directory}; +use test_programs::preview1::{create_file, open_scratch_directory}; unsafe fn test_path_exists(dir_fd: wasi::Fd) { // Create a temporary directory diff --git a/crates/test-programs/wasi-tests/src/bin/path_filestat.rs b/crates/test-programs/src/bin/preview1_path_filestat.rs similarity index 94% rename from crates/test-programs/wasi-tests/src/bin/path_filestat.rs rename to crates/test-programs/src/bin/preview1_path_filestat.rs index 2c4b63a93e8c..9901dba3546e 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_filestat.rs +++ b/crates/test-programs/src/bin/preview1_path_filestat.rs @@ -1,8 +1,8 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory, TESTCONFIG}; +use test_programs::preview1::{assert_errno, config, open_scratch_directory}; unsafe fn test_path_filestat(dir_fd: wasi::Fd) { - let fdflags = if TESTCONFIG.support_fdflags_sync() { + let fdflags = if config().support_fdflags_sync() { wasi::FDFLAGS_APPEND | wasi::FDFLAGS_SYNC } else { wasi::FDFLAGS_APPEND @@ -31,7 +31,7 @@ unsafe fn test_path_filestat(dir_fd: wasi::Fd) { wasi::FDFLAGS_APPEND, "file should have the APPEND fdflag used to create the file" ); - if TESTCONFIG.support_fdflags_sync() { + if config().support_fdflags_sync() { assert_eq!( fdstat.fs_flags & wasi::FDFLAGS_SYNC, wasi::FDFLAGS_SYNC, @@ -39,7 +39,7 @@ unsafe fn test_path_filestat(dir_fd: wasi::Fd) { ); } - if !TESTCONFIG.support_fdflags_sync() { + if !config().support_fdflags_sync() { assert_errno!( wasi::path_open( dir_fd, diff --git a/crates/test-programs/wasi-tests/src/bin/path_link.rs b/crates/test-programs/src/bin/preview1_path_link.rs similarity index 98% rename from crates/test-programs/wasi-tests/src/bin/path_link.rs rename to crates/test-programs/src/bin/preview1_path_link.rs index 161d865df568..b087e0740003 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_link.rs +++ b/crates/test-programs/src/bin/preview1_path_link.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, create_file, open_scratch_directory, TESTCONFIG}; +use test_programs::preview1::{assert_errno, config, create_file, open_scratch_directory}; unsafe fn create_or_open(dir_fd: wasi::Fd, name: &str, flags: wasi::Oflags) -> wasi::Fd { let file_fd = wasi::path_open(dir_fd, 0, name, flags, 0, 0, 0) @@ -136,7 +136,7 @@ unsafe fn test_path_link(dir_fd: wasi::Fd) { wasi::ERRNO_NOENT ); - if TESTCONFIG.support_dangling_filesystem() { + if config().support_dangling_filesystem() { // Create a link to a dangling symlink wasi::path_symlink("target", dir_fd, "symlink").expect("creating a dangling symlink"); diff --git a/crates/test-programs/wasi-tests/src/bin/path_open_create_existing.rs b/crates/test-programs/src/bin/preview1_path_open_create_existing.rs similarity index 92% rename from crates/test-programs/wasi-tests/src/bin/path_open_create_existing.rs rename to crates/test-programs/src/bin/preview1_path_open_create_existing.rs index 5c4cca201689..ed4f4f748c6b 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_open_create_existing.rs +++ b/crates/test-programs/src/bin/preview1_path_open_create_existing.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, create_file, open_scratch_directory}; +use test_programs::preview1::{assert_errno, create_file, open_scratch_directory}; unsafe fn test_path_open_create_existing(dir_fd: wasi::Fd) { create_file(dir_fd, "file"); diff --git a/crates/test-programs/wasi-tests/src/bin/path_open_dirfd_not_dir.rs b/crates/test-programs/src/bin/preview1_path_open_dirfd_not_dir.rs similarity index 93% rename from crates/test-programs/wasi-tests/src/bin/path_open_dirfd_not_dir.rs rename to crates/test-programs/src/bin/preview1_path_open_dirfd_not_dir.rs index 74a0d57d2580..b95404df5c0f 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_open_dirfd_not_dir.rs +++ b/crates/test-programs/src/bin/preview1_path_open_dirfd_not_dir.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{assert_errno, open_scratch_directory}; unsafe fn test_dirfd_not_dir(dir_fd: wasi::Fd) { // Open a file. diff --git a/crates/test-programs/wasi-tests/src/bin/path_open_missing.rs b/crates/test-programs/src/bin/preview1_path_open_missing.rs similarity index 92% rename from crates/test-programs/wasi-tests/src/bin/path_open_missing.rs rename to crates/test-programs/src/bin/preview1_path_open_missing.rs index 3f56f8d75dc4..221c71eb6b76 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_open_missing.rs +++ b/crates/test-programs/src/bin/preview1_path_open_missing.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{assert_errno, open_scratch_directory}; unsafe fn test_path_open_missing(dir_fd: wasi::Fd) { assert_errno!( diff --git a/crates/test-programs/wasi-tests/src/bin/path_open_nonblock.rs b/crates/test-programs/src/bin/preview1_path_open_nonblock.rs similarity index 93% rename from crates/test-programs/wasi-tests/src/bin/path_open_nonblock.rs rename to crates/test-programs/src/bin/preview1_path_open_nonblock.rs index 3ca99418117b..f3a14905b7fb 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_open_nonblock.rs +++ b/crates/test-programs/src/bin/preview1_path_open_nonblock.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn try_path_open(dir_fd: wasi::Fd) { let _fd = diff --git a/crates/test-programs/wasi-tests/src/bin/path_open_preopen.rs b/crates/test-programs/src/bin/preview1_path_open_preopen.rs similarity index 98% rename from crates/test-programs/wasi-tests/src/bin/path_open_preopen.rs rename to crates/test-programs/src/bin/preview1_path_open_preopen.rs index cb8e07490f6d..218446bd8a51 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_open_preopen.rs +++ b/crates/test-programs/src/bin/preview1_path_open_preopen.rs @@ -64,7 +64,7 @@ unsafe fn path_open_preopen() { ) .expect("open with O_DIRECTORY and read right"); - if !wasi_tests::TESTCONFIG.errno_expect_windows() { + if !test_programs::preview1::config().errno_expect_windows() { // Open OFLAGS_DIRECTORY and read/write rights should fail with isdir: let err = wasi::path_open( FIRST_PREOPEN, diff --git a/crates/test-programs/wasi-tests/src/bin/path_open_read_write.rs b/crates/test-programs/src/bin/preview1_path_open_read_write.rs similarity index 98% rename from crates/test-programs/wasi-tests/src/bin/path_open_read_write.rs rename to crates/test-programs/src/bin/preview1_path_open_read_write.rs index 0278fbf6d4f1..3d19daffcbd9 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_open_read_write.rs +++ b/crates/test-programs/src/bin/preview1_path_open_read_write.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, create_file, open_scratch_directory}; +use test_programs::preview1::{assert_errno, create_file, open_scratch_directory}; unsafe fn test_path_open_read_write(dir_fd: wasi::Fd) { create_file(dir_fd, "file"); diff --git a/crates/test-programs/wasi-tests/src/bin/path_rename.rs b/crates/test-programs/src/bin/preview1_path_rename.rs similarity index 97% rename from crates/test-programs/wasi-tests/src/bin/path_rename.rs rename to crates/test-programs/src/bin/preview1_path_rename.rs index 325a8a390cde..a7191bc2c486 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_rename.rs +++ b/crates/test-programs/src/bin/preview1_path_rename.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, create_file, open_scratch_directory, TESTCONFIG}; +use test_programs::preview1::{assert_errno, config, create_file, open_scratch_directory}; unsafe fn test_path_rename(dir_fd: wasi::Fd) { // First, try renaming a dir to nonexistent path @@ -31,7 +31,7 @@ unsafe fn test_path_rename(dir_fd: wasi::Fd) { // and its fairly important that it is atomic. // But, we haven't found a way to emulate it on windows. So, sometimes this // behavior is just hosed. Sorry. - if TESTCONFIG.support_rename_dir_to_empty_dir() { + if config().support_rename_dir_to_empty_dir() { // Now, try renaming renaming a dir to existing empty dir wasi::path_create_directory(dir_fd, "source").expect("creating a directory"); wasi::path_create_directory(dir_fd, "target").expect("creating a directory"); @@ -78,7 +78,7 @@ unsafe fn test_path_rename(dir_fd: wasi::Fd) { // This is technically a different property, but the root of these divergent behaviors is in // the semantics that windows gives us around renaming directories. So, it lives under the same // flag. - if TESTCONFIG.support_rename_dir_to_empty_dir() { + if config().support_rename_dir_to_empty_dir() { // Try renaming dir to a file assert_errno!( wasi::path_rename(dir_fd, "source", dir_fd, "target/file") diff --git a/crates/test-programs/wasi-tests/src/bin/path_rename_dir_trailing_slashes.rs b/crates/test-programs/src/bin/preview1_path_rename_dir_trailing_slashes.rs similarity index 96% rename from crates/test-programs/wasi-tests/src/bin/path_rename_dir_trailing_slashes.rs rename to crates/test-programs/src/bin/preview1_path_rename_dir_trailing_slashes.rs index 7c27d9cbb079..2e6a2316e7b3 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_rename_dir_trailing_slashes.rs +++ b/crates/test-programs/src/bin/preview1_path_rename_dir_trailing_slashes.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_path_rename_trailing_slashes(dir_fd: wasi::Fd) { // Test renaming a directory with a trailing slash in the name. diff --git a/crates/test-programs/wasi-tests/src/bin/path_rename_file_trailing_slashes.rs b/crates/test-programs/src/bin/preview1_path_rename_file_trailing_slashes.rs similarity index 95% rename from crates/test-programs/wasi-tests/src/bin/path_rename_file_trailing_slashes.rs rename to crates/test-programs/src/bin/preview1_path_rename_file_trailing_slashes.rs index 853b1d407176..2602195558d4 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_rename_file_trailing_slashes.rs +++ b/crates/test-programs/src/bin/preview1_path_rename_file_trailing_slashes.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, create_file, open_scratch_directory}; +use test_programs::preview1::{assert_errno, create_file, open_scratch_directory}; unsafe fn test_path_rename_trailing_slashes(dir_fd: wasi::Fd) { // Test renaming a file with a trailing slash in the name. diff --git a/crates/test-programs/wasi-tests/src/bin/path_symlink_trailing_slashes.rs b/crates/test-programs/src/bin/preview1_path_symlink_trailing_slashes.rs similarity index 95% rename from crates/test-programs/wasi-tests/src/bin/path_symlink_trailing_slashes.rs rename to crates/test-programs/src/bin/preview1_path_symlink_trailing_slashes.rs index f27ced633ede..fc66430f7573 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_symlink_trailing_slashes.rs +++ b/crates/test-programs/src/bin/preview1_path_symlink_trailing_slashes.rs @@ -1,8 +1,8 @@ use std::{env, process}; -use wasi_tests::{assert_errno, create_file, open_scratch_directory, TESTCONFIG}; +use test_programs::preview1::{assert_errno, config, create_file, open_scratch_directory}; unsafe fn test_path_symlink_trailing_slashes(dir_fd: wasi::Fd) { - if TESTCONFIG.support_dangling_filesystem() { + if config().support_dangling_filesystem() { // Dangling symlink: Link destination shouldn't end with a slash. assert_errno!( wasi::path_symlink("source", dir_fd, "target/") diff --git a/crates/test-programs/wasi-tests/src/bin/poll_oneoff_files.rs b/crates/test-programs/src/bin/preview1_poll_oneoff_files.rs similarity index 99% rename from crates/test-programs/wasi-tests/src/bin/poll_oneoff_files.rs rename to crates/test-programs/src/bin/preview1_poll_oneoff_files.rs index 7ca127865f6d..d1dfccaef7da 100644 --- a/crates/test-programs/wasi-tests/src/bin/poll_oneoff_files.rs +++ b/crates/test-programs/src/bin/preview1_poll_oneoff_files.rs @@ -1,5 +1,5 @@ use std::{env, mem::MaybeUninit, process}; -use wasi_tests::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{assert_errno, open_scratch_directory}; const CLOCK_ID: wasi::Userdata = 0x0123_45678; diff --git a/crates/test-programs/wasi-tests/src/bin/poll_oneoff_stdio.rs b/crates/test-programs/src/bin/preview1_poll_oneoff_stdio.rs similarity index 98% rename from crates/test-programs/wasi-tests/src/bin/poll_oneoff_stdio.rs rename to crates/test-programs/src/bin/preview1_poll_oneoff_stdio.rs index 2ddc00475281..bb3216b579a8 100644 --- a/crates/test-programs/wasi-tests/src/bin/poll_oneoff_stdio.rs +++ b/crates/test-programs/src/bin/preview1_poll_oneoff_stdio.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::mem::MaybeUninit; -use wasi_tests::{assert_errno, STDERR_FD, STDIN_FD, STDOUT_FD}; +use test_programs::preview1::{assert_errno, STDERR_FD, STDIN_FD, STDOUT_FD}; const TIMEOUT: u64 = 200_000_000u64; // 200 milliseconds, required to satisfy slow execution in CI const CLOCK_ID: wasi::Userdata = 0x0123_45678; diff --git a/crates/test-programs/wasi-tests/src/bin/readlink.rs b/crates/test-programs/src/bin/preview1_readlink.rs similarity index 97% rename from crates/test-programs/wasi-tests/src/bin/readlink.rs rename to crates/test-programs/src/bin/preview1_readlink.rs index f6bcc309587b..747f2e7ebb9b 100644 --- a/crates/test-programs/wasi-tests/src/bin/readlink.rs +++ b/crates/test-programs/src/bin/preview1_readlink.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{create_file, open_scratch_directory}; +use test_programs::preview1::{create_file, open_scratch_directory}; unsafe fn test_readlink(dir_fd: wasi::Fd) { // Create a file in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/regular_file_isatty.rs b/crates/test-programs/src/bin/preview1_regular_file_isatty.rs similarity index 95% rename from crates/test-programs/wasi-tests/src/bin/regular_file_isatty.rs rename to crates/test-programs/src/bin/preview1_regular_file_isatty.rs index 6a8c821becf7..c0098c8ed2fc 100644 --- a/crates/test-programs/wasi-tests/src/bin/regular_file_isatty.rs +++ b/crates/test-programs/src/bin/preview1_regular_file_isatty.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_file_isatty(dir_fd: wasi::Fd) { // Create a file in the scratch directory and test if it's a tty. diff --git a/crates/test-programs/wasi-tests/src/bin/remove_directory_trailing_slashes.rs b/crates/test-programs/src/bin/preview1_remove_directory_trailing_slashes.rs similarity index 95% rename from crates/test-programs/wasi-tests/src/bin/remove_directory_trailing_slashes.rs rename to crates/test-programs/src/bin/preview1_remove_directory_trailing_slashes.rs index 9628c7ad5c77..c9e73b930fbf 100644 --- a/crates/test-programs/wasi-tests/src/bin/remove_directory_trailing_slashes.rs +++ b/crates/test-programs/src/bin/preview1_remove_directory_trailing_slashes.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, create_file, open_scratch_directory}; +use test_programs::preview1::{assert_errno, create_file, open_scratch_directory}; unsafe fn test_remove_directory_trailing_slashes(dir_fd: wasi::Fd) { // Create a directory in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/remove_nonempty_directory.rs b/crates/test-programs/src/bin/preview1_remove_nonempty_directory.rs similarity index 95% rename from crates/test-programs/wasi-tests/src/bin/remove_nonempty_directory.rs rename to crates/test-programs/src/bin/preview1_remove_nonempty_directory.rs index 6732ba537e77..701ab5270012 100644 --- a/crates/test-programs/wasi-tests/src/bin/remove_nonempty_directory.rs +++ b/crates/test-programs/src/bin/preview1_remove_nonempty_directory.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{assert_errno, open_scratch_directory}; unsafe fn test_remove_nonempty_directory(dir_fd: wasi::Fd) { // Create a directory in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/renumber.rs b/crates/test-programs/src/bin/preview1_renumber.rs similarity index 97% rename from crates/test-programs/wasi-tests/src/bin/renumber.rs rename to crates/test-programs/src/bin/preview1_renumber.rs index 1a50f02ade12..0e90540f5c7d 100644 --- a/crates/test-programs/wasi-tests/src/bin/renumber.rs +++ b/crates/test-programs/src/bin/preview1_renumber.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{assert_errno, open_scratch_directory}; unsafe fn test_renumber(dir_fd: wasi::Fd) { let pre_fd: wasi::Fd = (libc::STDERR_FILENO + 1) as wasi::Fd; diff --git a/crates/test-programs/wasi-tests/src/bin/sched_yield.rs b/crates/test-programs/src/bin/preview1_sched_yield.rs similarity index 100% rename from crates/test-programs/wasi-tests/src/bin/sched_yield.rs rename to crates/test-programs/src/bin/preview1_sched_yield.rs diff --git a/crates/test-programs/wasi-tests/src/bin/stdio.rs b/crates/test-programs/src/bin/preview1_stdio.rs similarity index 82% rename from crates/test-programs/wasi-tests/src/bin/stdio.rs rename to crates/test-programs/src/bin/preview1_stdio.rs index 31b0a25b9b39..49d5d430c960 100644 --- a/crates/test-programs/wasi-tests/src/bin/stdio.rs +++ b/crates/test-programs/src/bin/preview1_stdio.rs @@ -1,4 +1,4 @@ -use wasi_tests::{STDERR_FD, STDIN_FD, STDOUT_FD}; +use test_programs::preview1::{STDERR_FD, STDIN_FD, STDOUT_FD}; unsafe fn test_stdio() { for fd in &[STDIN_FD, STDOUT_FD, STDERR_FD] { diff --git a/crates/test-programs/wasi-tests/src/bin/stdio_isatty.rs b/crates/test-programs/src/bin/preview1_stdio_isatty.rs similarity index 100% rename from crates/test-programs/wasi-tests/src/bin/stdio_isatty.rs rename to crates/test-programs/src/bin/preview1_stdio_isatty.rs diff --git a/crates/test-programs/wasi-tests/src/bin/stdio_not_isatty.rs b/crates/test-programs/src/bin/preview1_stdio_not_isatty.rs similarity index 100% rename from crates/test-programs/wasi-tests/src/bin/stdio_not_isatty.rs rename to crates/test-programs/src/bin/preview1_stdio_not_isatty.rs diff --git a/crates/test-programs/wasi-tests/src/bin/symlink_create.rs b/crates/test-programs/src/bin/preview1_symlink_create.rs similarity index 98% rename from crates/test-programs/wasi-tests/src/bin/symlink_create.rs rename to crates/test-programs/src/bin/preview1_symlink_create.rs index 2553f34fcd01..fcebfc9c9a65 100644 --- a/crates/test-programs/wasi-tests/src/bin/symlink_create.rs +++ b/crates/test-programs/src/bin/preview1_symlink_create.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn create_symlink_to_file(dir_fd: wasi::Fd) { // Create a directory for the symlink to point to. diff --git a/crates/test-programs/wasi-tests/src/bin/symlink_filestat.rs b/crates/test-programs/src/bin/preview1_symlink_filestat.rs similarity index 98% rename from crates/test-programs/wasi-tests/src/bin/symlink_filestat.rs rename to crates/test-programs/src/bin/preview1_symlink_filestat.rs index cad9dcd6c034..377c68e9398e 100644 --- a/crates/test-programs/wasi-tests/src/bin/symlink_filestat.rs +++ b/crates/test-programs/src/bin/preview1_symlink_filestat.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::open_scratch_directory; +use test_programs::preview1::open_scratch_directory; unsafe fn test_path_filestat(dir_fd: wasi::Fd) { // Create a file in the scratch directory. diff --git a/crates/test-programs/wasi-tests/src/bin/symlink_loop.rs b/crates/test-programs/src/bin/preview1_symlink_loop.rs similarity index 89% rename from crates/test-programs/wasi-tests/src/bin/symlink_loop.rs rename to crates/test-programs/src/bin/preview1_symlink_loop.rs index 1c3226adb775..1e21bbded227 100644 --- a/crates/test-programs/wasi-tests/src/bin/symlink_loop.rs +++ b/crates/test-programs/src/bin/preview1_symlink_loop.rs @@ -1,8 +1,8 @@ use std::{env, process}; -use wasi_tests::{assert_errno, open_scratch_directory, TESTCONFIG}; +use test_programs::preview1::{assert_errno, config, open_scratch_directory}; unsafe fn test_symlink_loop(dir_fd: wasi::Fd) { - if TESTCONFIG.support_dangling_filesystem() { + if config().support_dangling_filesystem() { // Create a self-referencing symlink. wasi::path_symlink("symlink", dir_fd, "symlink").expect("creating a symlink"); diff --git a/crates/test-programs/wasi-tests/src/bin/unicode_output.rs b/crates/test-programs/src/bin/preview1_unicode_output.rs similarity index 90% rename from crates/test-programs/wasi-tests/src/bin/unicode_output.rs rename to crates/test-programs/src/bin/preview1_unicode_output.rs index 211f7905fe06..7e88dc27c79a 100644 --- a/crates/test-programs/wasi-tests/src/bin/unicode_output.rs +++ b/crates/test-programs/src/bin/preview1_unicode_output.rs @@ -1,4 +1,4 @@ -use wasi_tests::STDOUT_FD; +use test_programs::preview1::STDOUT_FD; fn main() { let text = "مرحبا بكم\n"; diff --git a/crates/test-programs/wasi-tests/src/bin/unlink_file_trailing_slashes.rs b/crates/test-programs/src/bin/preview1_unlink_file_trailing_slashes.rs similarity index 95% rename from crates/test-programs/wasi-tests/src/bin/unlink_file_trailing_slashes.rs rename to crates/test-programs/src/bin/preview1_unlink_file_trailing_slashes.rs index 2417b5efa01b..59ca9bb831a0 100644 --- a/crates/test-programs/wasi-tests/src/bin/unlink_file_trailing_slashes.rs +++ b/crates/test-programs/src/bin/preview1_unlink_file_trailing_slashes.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi_tests::{assert_errno, create_file, open_scratch_directory}; +use test_programs::preview1::{assert_errno, create_file, open_scratch_directory}; unsafe fn test_unlink_file_trailing_slashes(dir_fd: wasi::Fd) { // Create a directory in the scratch directory. diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs b/crates/test-programs/src/bin/preview2_ip_name_lookup.rs similarity index 91% rename from crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs rename to crates/test-programs/src/bin/preview2_ip_name_lookup.rs index 8e8869109d1c..219ebba459f2 100644 --- a/crates/test-programs/wasi-sockets-tests/src/bin/ip_name_lookup.rs +++ b/crates/test-programs/src/bin/preview2_ip_name_lookup.rs @@ -1,6 +1,6 @@ -use wasi_sockets_tests::wasi::clocks::*; -use wasi_sockets_tests::wasi::io::*; -use wasi_sockets_tests::wasi::sockets::*; +use test_programs::wasi::clocks::*; +use test_programs::wasi::io::*; +use test_programs::wasi::sockets::*; fn main() { let network = instance_network::instance_network(); diff --git a/crates/test-programs/command-tests/src/bin/random.rs b/crates/test-programs/src/bin/preview2_random.rs similarity index 100% rename from crates/test-programs/command-tests/src/bin/random.rs rename to crates/test-programs/src/bin/preview2_random.rs diff --git a/crates/test-programs/wasi-tests/src/bin/sleep.rs b/crates/test-programs/src/bin/preview2_sleep.rs similarity index 79% rename from crates/test-programs/wasi-tests/src/bin/sleep.rs rename to crates/test-programs/src/bin/preview2_sleep.rs index d382a32e1559..be8a20cb2590 100644 --- a/crates/test-programs/wasi-tests/src/bin/sleep.rs +++ b/crates/test-programs/src/bin/preview2_sleep.rs @@ -1,9 +1,4 @@ -use crate::wasi::{clocks::monotonic_clock, io::poll}; - -wit_bindgen::generate!({ - path: "../../wasi/wit", - world: "wasmtime:wasi/command-extended", -}); +use test_programs::wasi::{clocks::monotonic_clock, io::poll}; fn main() { // Sleep ten milliseconds. Note that we call the relevant host functions directly rather than go through diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_bind.rs b/crates/test-programs/src/bin/preview2_tcp_bind.rs similarity index 96% rename from crates/test-programs/wasi-sockets-tests/src/bin/tcp_bind.rs rename to crates/test-programs/src/bin/preview2_tcp_bind.rs index 8c6ce3604d56..824b5978ee5e 100644 --- a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_bind.rs +++ b/crates/test-programs/src/bin/preview2_tcp_bind.rs @@ -1,6 +1,7 @@ -use wasi::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network}; -use wasi::sockets::tcp::TcpSocket; -use wasi_sockets_tests::*; +use test_programs::wasi::sockets::network::{ + ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network, +}; +use test_programs::wasi::sockets::tcp::TcpSocket; /// Bind a socket and let the system determine a port. fn test_tcp_bind_ephemeral_port(net: &Network, ip: IpAddress) { diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_connect.rs b/crates/test-programs/src/bin/preview2_tcp_connect.rs similarity index 95% rename from crates/test-programs/wasi-sockets-tests/src/bin/tcp_connect.rs rename to crates/test-programs/src/bin/preview2_tcp_connect.rs index 3629ad2bfca1..58d5a84c4d7c 100644 --- a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_connect.rs +++ b/crates/test-programs/src/bin/preview2_tcp_connect.rs @@ -1,6 +1,7 @@ -use wasi::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network}; -use wasi::sockets::tcp::TcpSocket; -use wasi_sockets_tests::*; +use test_programs::wasi::sockets::network::{ + ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network, +}; +use test_programs::wasi::sockets::tcp::TcpSocket; const SOME_PORT: u16 = 47; // If the tests pass, this will never actually be connected to. diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_sample_application.rs b/crates/test-programs/src/bin/preview2_tcp_sample_application.rs similarity index 89% rename from crates/test-programs/wasi-sockets-tests/src/bin/tcp_sample_application.rs rename to crates/test-programs/src/bin/preview2_tcp_sample_application.rs index 9f182f2c4660..a2b648106e5d 100644 --- a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_sample_application.rs +++ b/crates/test-programs/src/bin/preview2_tcp_sample_application.rs @@ -1,8 +1,7 @@ -use wasi::sockets::network::{ +use test_programs::wasi::sockets::network::{ IpAddressFamily, IpSocketAddress, Ipv4SocketAddress, Ipv6SocketAddress, Network, }; -use wasi::sockets::tcp::TcpSocket; -use wasi_sockets_tests::*; +use test_programs::wasi::sockets::tcp::TcpSocket; fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddress) { let first_message = b"Hello, world!"; @@ -26,7 +25,7 @@ fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddres } { - let (_accepted, input, _output) = listener.accept().unwrap(); + let (_accepted, input, _output) = listener.blocking_accept().unwrap(); let empty_data = input.read(0).unwrap(); assert!(empty_data.is_empty()); @@ -46,7 +45,7 @@ fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddres } { - let (_accepted, input, _output) = listener.accept().unwrap(); + let (_accepted, input, _output) = listener.blocking_accept().unwrap(); let data = input.blocking_read(second_message.len() as u64).unwrap(); // Check that we sent and recieved our message! diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_sockopts.rs b/crates/test-programs/src/bin/preview2_tcp_sockopts.rs similarity index 96% rename from crates/test-programs/wasi-sockets-tests/src/bin/tcp_sockopts.rs rename to crates/test-programs/src/bin/preview2_tcp_sockopts.rs index c1a5df8ea7a5..2170ed420b00 100644 --- a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_sockopts.rs +++ b/crates/test-programs/src/bin/preview2_tcp_sockopts.rs @@ -1,6 +1,7 @@ -use wasi::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network}; -use wasi::sockets::tcp::TcpSocket; -use wasi_sockets_tests::*; +use test_programs::wasi::sockets::network::{ + ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network, +}; +use test_programs::wasi::sockets::tcp::TcpSocket; fn test_tcp_sockopt_defaults(family: IpAddressFamily) { let sock = TcpSocket::new(family).unwrap(); @@ -103,7 +104,7 @@ fn test_tcp_sockopt_inheritance(net: &Network, family: IpAddressFamily) { let bound_addr = listener.local_address().unwrap(); let client = TcpSocket::new(family).unwrap(); client.blocking_connect(&net, bound_addr).unwrap(); - let (accepted_client, _, _) = listener.accept().unwrap(); + let (accepted_client, _, _) = listener.blocking_accept().unwrap(); // Verify options on accepted socket: { @@ -157,7 +158,7 @@ fn test_tcp_sockopt_after_listen(net: &Network, family: IpAddressFamily) { let client = TcpSocket::new(family).unwrap(); client.blocking_connect(&net, bound_addr).unwrap(); - let (accepted_client, _, _) = listener.accept().unwrap(); + let (accepted_client, _, _) = listener.blocking_accept().unwrap(); // Verify options on accepted socket: { diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_states.rs b/crates/test-programs/src/bin/preview2_tcp_states.rs similarity index 98% rename from crates/test-programs/wasi-sockets-tests/src/bin/tcp_states.rs rename to crates/test-programs/src/bin/preview2_tcp_states.rs index 8547749e5e68..6bf6cbdabbc5 100644 --- a/crates/test-programs/wasi-sockets-tests/src/bin/tcp_states.rs +++ b/crates/test-programs/src/bin/preview2_tcp_states.rs @@ -1,6 +1,7 @@ -use wasi::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network}; -use wasi::sockets::tcp::{ShutdownType, TcpSocket}; -use wasi_sockets_tests::*; +use test_programs::wasi::sockets::network::{ + ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network, +}; +use test_programs::wasi::sockets::tcp::{ShutdownType, TcpSocket}; fn test_tcp_unbound_state_invariants(family: IpAddressFamily) { let sock = TcpSocket::new(family).unwrap(); diff --git a/crates/test-programs/wasi-sockets-tests/src/bin/udp_sample_application.rs b/crates/test-programs/src/bin/preview2_udp_sample_application.rs similarity index 96% rename from crates/test-programs/wasi-sockets-tests/src/bin/udp_sample_application.rs rename to crates/test-programs/src/bin/preview2_udp_sample_application.rs index 7dc64fa586a6..de8a0a27ca46 100644 --- a/crates/test-programs/wasi-sockets-tests/src/bin/udp_sample_application.rs +++ b/crates/test-programs/src/bin/preview2_udp_sample_application.rs @@ -1,8 +1,7 @@ -use wasi::sockets::network::{ +use test_programs::wasi::sockets::network::{ IpAddressFamily, IpSocketAddress, Ipv4SocketAddress, Ipv6SocketAddress, Network, }; -use wasi::sockets::udp::{Datagram, UdpSocket}; -use wasi_sockets_tests::*; +use test_programs::wasi::sockets::udp::{Datagram, UdpSocket}; fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddress) { let first_message = &[]; diff --git a/crates/test-programs/wasi-http-tests/src/lib.rs b/crates/test-programs/src/http.rs similarity index 92% rename from crates/test-programs/wasi-http-tests/src/lib.rs rename to crates/test-programs/src/http.rs index fd30fda53c4f..e87347cb5405 100644 --- a/crates/test-programs/wasi-http-tests/src/lib.rs +++ b/crates/test-programs/src/http.rs @@ -1,16 +1,7 @@ -pub mod bindings { - wit_bindgen::generate!({ - path: "../../wasi-http/wit", - world: "wasmtime:wasi/command-extended", - // macro_call_prefix: "::wasi_http_tests::bindings::", - // macro_export, - }); -} - +use crate::wasi::http::{outgoing_handler, types as http_types}; +use crate::wasi::io::poll; +use crate::wasi::io::streams; use anyhow::{anyhow, Result}; -use bindings::wasi::http::{outgoing_handler, types as http_types}; -use bindings::wasi::io::poll; -use bindings::wasi::io::streams; use std::fmt; pub struct Response { diff --git a/crates/test-programs/src/lib.rs b/crates/test-programs/src/lib.rs index f793242b3048..25ed50efbba3 100644 --- a/crates/test-programs/src/lib.rs +++ b/crates/test-programs/src/lib.rs @@ -1,47 +1,5 @@ -///! This crate exists to build crates for wasm32-wasi in build.rs, and execute -///! these wasm programs in harnesses defined under tests/. -use std::io::IsTerminal; +pub mod http; +pub mod preview1; +pub mod sockets; -#[cfg(all(feature = "test_programs", not(skip_wasi_http_tests)))] -pub mod http_server; - -/// The wasi-tests binaries use these environment variables to determine their -/// expected behavior. -/// Used by all of the tests/ which execute the wasi-tests binaries. -pub fn wasi_tests_environment() -> &'static [(&'static str, &'static str)] { - #[cfg(windows)] - { - &[ - ("ERRNO_MODE_WINDOWS", "1"), - // Windows does not support dangling links or symlinks in the filesystem. - ("NO_DANGLING_FILESYSTEM", "1"), - // Windows does not support renaming a directory to an empty directory - - // empty directory must be deleted. - ("NO_RENAME_DIR_TO_EMPTY_DIR", "1"), - // cap-std-sync does not support the sync family of fdflags - ("NO_FDFLAGS_SYNC_SUPPORT", "1"), - ] - } - #[cfg(all(unix, not(target_os = "macos")))] - { - &[ - ("ERRNO_MODE_UNIX", "1"), - // cap-std-sync does not support the sync family of fdflags - ("NO_FDFLAGS_SYNC_SUPPORT", "1"), - ] - } - #[cfg(target_os = "macos")] - { - &[ - ("ERRNO_MODE_MACOS", "1"), - // cap-std-sync does not support the sync family of fdflags - ("NO_FDFLAGS_SYNC_SUPPORT", "1"), - ] - } -} - -pub fn stdio_is_terminal() -> bool { - std::io::stdin().is_terminal() - && std::io::stdout().is_terminal() - && std::io::stderr().is_terminal() -} +wit_bindgen::generate!("test-command" in "../wasi/wit"); diff --git a/crates/test-programs/wasi-tests/src/lib.rs b/crates/test-programs/src/preview1.rs similarity index 59% rename from crates/test-programs/wasi-tests/src/lib.rs rename to crates/test-programs/src/preview1.rs index 3a701ad86335..6f646bb4e047 100644 --- a/crates/test-programs/wasi-tests/src/lib.rs +++ b/crates/test-programs/src/preview1.rs @@ -1,7 +1,9 @@ -pub mod config; -use once_cell::sync::Lazy; +use std::sync::OnceLock; -pub static TESTCONFIG: Lazy = Lazy::new(config::TestConfig::from_env); +pub fn config() -> &'static TestConfig { + static TESTCONFIG: OnceLock = OnceLock::new(); + TESTCONFIG.get_or_init(TestConfig::from_env) +} // The `wasi` crate version 0.9.0 and beyond, doesn't // seem to define these constants, so we do it ourselves. @@ -43,11 +45,15 @@ pub unsafe fn create_file(dir_fd: wasi::Fd, filename: &str) { wasi::fd_close(file_fd).expect("closing a file"); } +// Small workaround to get the crate's `assert_errno`, through the +// `#[macro_export]` attribute below, also available from this module. +pub use crate::assert_errno; + #[macro_export] macro_rules! assert_errno { ($s:expr, windows => $i:expr, $( $rest:tt )+) => { let e = $s; - if $crate::TESTCONFIG.errno_expect_windows() { + if $crate::preview1::config().errno_expect_windows() { assert_errno!(e, $i); } else { assert_errno!(e, $($rest)+, $i); @@ -55,7 +61,7 @@ macro_rules! assert_errno { }; ($s:expr, macos => $i:expr, $( $rest:tt )+) => { let e = $s; - if $crate::TESTCONFIG.errno_expect_macos() { + if $crate::preview1::config().errno_expect_macos() { assert_errno!(e, $i); } else { assert_errno!(e, $($rest)+, $i); @@ -63,7 +69,7 @@ macro_rules! assert_errno { }; ($s:expr, unix => $i:expr, $( $rest:tt )+) => { let e = $s; - if $crate::TESTCONFIG.errno_expect_unix() { + if $crate::preview1::config().errno_expect_unix() { assert_errno!(e, $i); } else { assert_errno!(e, $($rest)+, $i); @@ -107,3 +113,67 @@ macro_rules! assert_errno { } }; } + +pub struct TestConfig { + errno_mode: ErrnoMode, + no_dangling_filesystem: bool, + no_rename_dir_to_empty_dir: bool, + no_fdflags_sync_support: bool, +} + +enum ErrnoMode { + Unix, + MacOS, + Windows, + Permissive, +} + +impl TestConfig { + pub fn from_env() -> Self { + let errno_mode = if std::env::var("ERRNO_MODE_UNIX").is_ok() { + ErrnoMode::Unix + } else if std::env::var("ERRNO_MODE_MACOS").is_ok() { + ErrnoMode::MacOS + } else if std::env::var("ERRNO_MODE_WINDOWS").is_ok() { + ErrnoMode::Windows + } else { + ErrnoMode::Permissive + }; + let no_dangling_filesystem = std::env::var("NO_DANGLING_FILESYSTEM").is_ok(); + let no_rename_dir_to_empty_dir = std::env::var("NO_RENAME_DIR_TO_EMPTY_DIR").is_ok(); + let no_fdflags_sync_support = std::env::var("NO_FDFLAGS_SYNC_SUPPORT").is_ok(); + TestConfig { + errno_mode, + no_dangling_filesystem, + no_rename_dir_to_empty_dir, + no_fdflags_sync_support, + } + } + pub fn errno_expect_unix(&self) -> bool { + match self.errno_mode { + ErrnoMode::Unix | ErrnoMode::MacOS => true, + _ => false, + } + } + pub fn errno_expect_macos(&self) -> bool { + match self.errno_mode { + ErrnoMode::MacOS => true, + _ => false, + } + } + pub fn errno_expect_windows(&self) -> bool { + match self.errno_mode { + ErrnoMode::Windows => true, + _ => false, + } + } + pub fn support_dangling_filesystem(&self) -> bool { + !self.no_dangling_filesystem + } + pub fn support_rename_dir_to_empty_dir(&self) -> bool { + !self.no_rename_dir_to_empty_dir + } + pub fn support_fdflags_sync(&self) -> bool { + !self.no_fdflags_sync_support + } +} diff --git a/crates/test-programs/wasi-sockets-tests/src/lib.rs b/crates/test-programs/src/sockets.rs similarity index 95% rename from crates/test-programs/wasi-sockets-tests/src/lib.rs rename to crates/test-programs/src/sockets.rs index 258099e5a05c..994e0e55e191 100644 --- a/crates/test-programs/wasi-sockets-tests/src/lib.rs +++ b/crates/test-programs/src/sockets.rs @@ -1,17 +1,15 @@ -wit_bindgen::generate!("test-command-with-sockets" in "../../wasi/wit"); - -use std::ops::Range; -use wasi::clocks::monotonic_clock; -use wasi::io::poll::{self, Pollable}; -use wasi::io::streams::{InputStream, OutputStream, StreamError}; -use wasi::sockets::instance_network; -use wasi::sockets::network::{ +use crate::wasi::clocks::monotonic_clock; +use crate::wasi::io::poll::{self, Pollable}; +use crate::wasi::io::streams::{InputStream, OutputStream, StreamError}; +use crate::wasi::sockets::instance_network; +use crate::wasi::sockets::network::{ ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Ipv4SocketAddress, Ipv6SocketAddress, Network, }; -use wasi::sockets::tcp::TcpSocket; -use wasi::sockets::udp::{Datagram, UdpSocket}; -use wasi::sockets::{tcp_create_socket, udp_create_socket}; +use crate::wasi::sockets::tcp::TcpSocket; +use crate::wasi::sockets::udp::{Datagram, UdpSocket}; +use crate::wasi::sockets::{tcp_create_socket, udp_create_socket}; +use std::ops::Range; const TIMEOUT_NS: u64 = 1_000_000_000; diff --git a/crates/test-programs/tests/command.rs b/crates/test-programs/tests/command.rs deleted file mode 100644 index bdba29ff3839..000000000000 --- a/crates/test-programs/tests/command.rs +++ /dev/null @@ -1,508 +0,0 @@ -use anyhow::Result; -use cap_std::{ambient_authority, fs::Dir, time::Duration}; -use std::{io::Write, sync::Mutex}; -use wasmtime::{ - component::{Component, Linker}, - Config, Engine, Store, -}; -use wasmtime_wasi::preview2::{ - command::{add_to_linker, Command}, - pipe::MemoryInputPipe, - DirPerms, FilePerms, HostMonotonicClock, HostWallClock, Table, WasiCtx, WasiCtxBuilder, - WasiView, -}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(true); - config.async_support(true); - - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_component(&str) -> Component -include!(concat!(env!("OUT_DIR"), "/command_tests_components.rs")); - -struct CommandCtx { - table: Table, - wasi: WasiCtx, -} - -impl WasiView for CommandCtx { - fn table(&self) -> &Table { - &self.table - } - fn table_mut(&mut self) -> &mut Table { - &mut self.table - } - fn ctx(&self) -> &WasiCtx { - &self.wasi - } - fn ctx_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } -} - -async fn instantiate( - component: Component, - ctx: CommandCtx, -) -> Result<(Store, Command)> { - let mut linker = Linker::new(&ENGINE); - add_to_linker(&mut linker)?; - - let mut store = Store::new(&ENGINE, ctx); - - let (command, _instance) = Command::instantiate_async(&mut store, &component, &linker).await?; - Ok((store, command)) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn hello_stdout() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .args(&["gussie", "sparky", "willa"]) - .build(); - let (mut store, command) = - instantiate(get_component("hello_stdout"), CommandCtx { table, wasi }).await?; - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn panic() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .args(&[ - "diesel", - "the", - "cat", - "scratched", - "me", - "real", - "good", - "yesterday", - ]) - .build(); - let (mut store, command) = - instantiate(get_component("panic"), CommandCtx { table, wasi }).await?; - let r = command.wasi_cli_run().call_run(&mut store).await; - assert!(r.is_err()); - println!("{:?}", r); - Ok(()) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn args() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .args(&["hello", "this", "", "is an argument", "with 🚩 emoji"]) - .build(); - let (mut store, command) = - instantiate(get_component("args"), CommandCtx { table, wasi }).await?; - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn random() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new().build(); - let (mut store, command) = - instantiate(get_component("random"), CommandCtx { table, wasi }).await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn time() -> Result<()> { - struct FakeWallClock; - - impl HostWallClock for FakeWallClock { - fn resolution(&self) -> Duration { - Duration::from_secs(1) - } - - fn now(&self) -> Duration { - Duration::new(1431648000, 100) - } - } - - struct FakeMonotonicClock { - now: Mutex, - } - - impl HostMonotonicClock for FakeMonotonicClock { - fn resolution(&self) -> u64 { - 1_000_000_000 - } - - fn now(&self) -> u64 { - let mut now = self.now.lock().unwrap(); - let then = *now; - *now += 42 * 1_000_000_000; - then - } - } - - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .monotonic_clock(FakeMonotonicClock { now: Mutex::new(0) }) - .wall_clock(FakeWallClock) - .build(); - - let (mut store, command) = - instantiate(get_component("time"), CommandCtx { table, wasi }).await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stdin() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .stdin(MemoryInputPipe::new( - "So rested he by the Tumtum tree".into(), - )) - .build(); - - let (mut store, command) = - instantiate(get_component("stdin"), CommandCtx { table, wasi }).await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn poll_stdin() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .stdin(MemoryInputPipe::new( - "So rested he by the Tumtum tree".into(), - )) - .build(); - - let (mut store, command) = - instantiate(get_component("poll_stdin"), CommandCtx { table, wasi }).await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn env() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .env("frabjous", "day") - .env("callooh", "callay") - .build(); - - let (mut store, command) = - instantiate(get_component("env"), CommandCtx { table, wasi }).await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_read() -> Result<()> { - let dir = tempfile::tempdir()?; - - std::fs::File::create(dir.path().join("bar.txt"))?.write_all(b"And stood awhile in thought")?; - - let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; - - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .preopened_dir(open_dir, DirPerms::all(), FilePerms::all(), "/") - .build(); - - let (mut store, command) = - instantiate(get_component("file_read"), CommandCtx { table, wasi }).await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_append() -> Result<()> { - let dir = tempfile::tempdir()?; - - std::fs::File::create(dir.path().join("bar.txt"))? - .write_all(b"'Twas brillig, and the slithy toves.\n")?; - - let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; - - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .preopened_dir(open_dir, DirPerms::all(), FilePerms::all(), "/") - .build(); - - let (mut store, command) = - instantiate(get_component("file_append"), CommandCtx { table, wasi }).await?; - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status"))?; - - let contents = std::fs::read(dir.path().join("bar.txt"))?; - assert_eq!( - std::str::from_utf8(&contents).unwrap(), - "'Twas brillig, and the slithy toves.\n\ - Did gyre and gimble in the wabe;\n\ - All mimsy were the borogoves,\n\ - And the mome raths outgrabe.\n" - ); - Ok(()) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_dir_sync() -> Result<()> { - let dir = tempfile::tempdir()?; - - std::fs::File::create(dir.path().join("bar.txt"))? - .write_all(b"'Twas brillig, and the slithy toves.\n")?; - - let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; - - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .preopened_dir(open_dir, DirPerms::all(), FilePerms::all(), "/") - .build(); - - let (mut store, command) = - instantiate(get_component("file_dir_sync"), CommandCtx { table, wasi }).await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn exit_success() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new().build(); - - let (mut store, command) = - instantiate(get_component("exit_success"), CommandCtx { table, wasi }).await?; - - let r = command.wasi_cli_run().call_run(&mut store).await; - let err = r.unwrap_err(); - let status = err - .downcast_ref::() - .unwrap(); - assert_eq!(status.0, 0); - Ok(()) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn exit_default() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new().build(); - - let (mut store, command) = - instantiate(get_component("exit_default"), CommandCtx { table, wasi }).await?; - - let r = command.wasi_cli_run().call_run(&mut store).await?; - assert!(r.is_ok()); - Ok(()) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn exit_failure() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new().build(); - - let (mut store, command) = - instantiate(get_component("exit_failure"), CommandCtx { table, wasi }).await?; - - let r = command.wasi_cli_run().call_run(&mut store).await; - let err = r.unwrap_err(); - let status = err - .downcast_ref::() - .unwrap(); - assert_eq!(status.0, 1); - Ok(()) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn exit_panic() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new().build(); - - let (mut store, command) = - instantiate(get_component("exit_panic"), CommandCtx { table, wasi }).await?; - - let r = command.wasi_cli_run().call_run(&mut store).await; - let err = r.unwrap_err(); - // The panic should trap. - assert!(err - .downcast_ref::() - .is_none()); - Ok(()) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn directory_list() -> Result<()> { - let dir = tempfile::tempdir()?; - - std::fs::File::create(dir.path().join("foo.txt"))?; - std::fs::File::create(dir.path().join("bar.txt"))?; - std::fs::File::create(dir.path().join("baz.txt"))?; - std::fs::create_dir(dir.path().join("sub"))?; - std::fs::File::create(dir.path().join("sub").join("wow.txt"))?; - std::fs::File::create(dir.path().join("sub").join("yay.txt"))?; - - let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; - - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .inherit_stdout() - .inherit_stderr() - .preopened_dir(open_dir, DirPerms::all(), FilePerms::all(), "/") - .build(); - - let (mut store, command) = - instantiate(get_component("directory_list"), CommandCtx { table, wasi }).await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn default_clocks() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new().build(); - - let (mut store, command) = - instantiate(get_component("default_clocks"), CommandCtx { table, wasi }).await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn export_cabi_realloc() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new().build(); - let (mut store, command) = instantiate( - get_component("export_cabi_realloc"), - CommandCtx { table, wasi }, - ) - .await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn read_only() -> Result<()> { - let dir = tempfile::tempdir()?; - - std::fs::File::create(dir.path().join("bar.txt"))?.write_all(b"And stood awhile in thought")?; - std::fs::create_dir(dir.path().join("sub"))?; - - let table = Table::new(); - let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; - let wasi = WasiCtxBuilder::new() - .preopened_dir(open_dir, DirPerms::READ, FilePerms::READ, "/") - .build(); - - let (mut store, command) = - instantiate(get_component("read_only"), CommandCtx { table, wasi }).await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stream_pollable_lifetimes() -> Result<()> { - // Test program has two modes, dispatching based on argument. - { - // Correct execution: should succeed - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .args(&["correct"]) - .stdin(MemoryInputPipe::new(" ".into())) - .build(); - - let (mut store, command) = instantiate( - get_component("stream_pollable_lifetimes"), - CommandCtx { table, wasi }, - ) - .await?; - - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status"))?; - } - { - // Incorrect execution: should trap with a TableError::HasChildren - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .args(&["trap"]) - .stdin(MemoryInputPipe::new(" ".into())) - .build(); - - let (mut store, command) = instantiate( - get_component("stream_pollable_lifetimes"), - CommandCtx { table, wasi }, - ) - .await?; - - let trap = command - .wasi_cli_run() - .call_run(&mut store) - .await - .err() - .expect("should trap"); - use wasmtime_wasi::preview2::TableError; - assert!(matches!(trap.downcast_ref(), Some(TableError::HasChildren))); - } - Ok(()) -} diff --git a/crates/test-programs/tests/reactor.rs b/crates/test-programs/tests/reactor.rs deleted file mode 100644 index 4b20ff97ce0a..000000000000 --- a/crates/test-programs/tests/reactor.rs +++ /dev/null @@ -1,136 +0,0 @@ -use anyhow::Result; -use wasmtime::{ - component::{Component, Linker}, - Config, Engine, Store, -}; -use wasmtime_wasi::preview2::bindings::clocks::wall_clock; -use wasmtime_wasi::preview2::bindings::filesystem::types as filesystem; -use wasmtime_wasi::preview2::{self, Table, WasiCtx, WasiCtxBuilder, WasiView}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(true); - config.async_support(true); - - let engine = Engine::new(&config).unwrap(); - engine - }; -} - -// uses ENGINE, creates a fn get_component(&str) -> Component -include!(concat!(env!("OUT_DIR"), "/reactor_tests_components.rs")); - -wasmtime::component::bindgen!({ - path: "../wasi/wit", - world: "test-reactor", - async: true, - with: { - "wasi:io/streams": preview2::bindings::io::streams, - "wasi:filesystem/types": preview2::bindings::filesystem::types, - "wasi:filesystem/preopens": preview2::bindings::filesystem::preopens, - "wasi:cli/environment": preview2::bindings::cli::environment, - "wasi:cli/exit": preview2::bindings::cli::exit, - "wasi:cli/stdin": preview2::bindings::cli::stdin, - "wasi:cli/stdout": preview2::bindings::cli::stdout, - "wasi:cli/stderr": preview2::bindings::cli::stderr, - "wasi:cli/terminal_input": preview2::bindings::cli::terminal_input, - "wasi:cli/terminal_output": preview2::bindings::cli::terminal_output, - "wasi:cli/terminal_stdin": preview2::bindings::cli::terminal_stdin, - "wasi:cli/terminal_stdout": preview2::bindings::cli::terminal_stdout, - "wasi:cli/terminal_stderr": preview2::bindings::cli::terminal_stderr, - }, - ownership: Borrowing { - duplicate_if_necessary: false - } -}); - -struct ReactorCtx { - table: Table, - wasi: WasiCtx, -} - -impl WasiView for ReactorCtx { - fn table(&self) -> &Table { - &self.table - } - fn table_mut(&mut self) -> &mut Table { - &mut self.table - } - fn ctx(&self) -> &WasiCtx { - &self.wasi - } - fn ctx_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } -} - -async fn instantiate( - component: Component, - wasi_ctx: ReactorCtx, -) -> Result<(Store, TestReactor)> { - let mut linker = Linker::new(&ENGINE); - preview2::command::add_to_linker(&mut linker)?; - - let mut store = Store::new(&ENGINE, wasi_ctx); - let (testreactor, _instance) = - TestReactor::instantiate_async(&mut store, &component, &linker).await?; - Ok((store, testreactor)) -} - -#[test_log::test(tokio::test)] -async fn reactor_tests() -> Result<()> { - let table = Table::new(); - let wasi = WasiCtxBuilder::new().env("GOOD_DOG", "gussie").build(); - - let (mut store, reactor) = - instantiate(get_component("reactor_tests"), ReactorCtx { table, wasi }).await?; - - // Show that integration with the WASI context is working - the guest will - // interpolate $GOOD_DOG to gussie here using the environment: - let r = reactor - .call_add_strings(&mut store, &["hello", "$GOOD_DOG"]) - .await?; - assert_eq!(r, 2); - - let contents = reactor.call_get_strings(&mut store).await?; - assert_eq!(contents, &["hello", "gussie"]); - - // Show that we can pass in a resource type whose impls are defined in the - // `host` and `wasi-common` crate. - // Note, this works because of the add_to_linker invocations using the - // `host` crate for `streams`, not because of `with` in the bindgen macro. - let writepipe = preview2::pipe::MemoryOutputPipe::new(4096); - let stream: preview2::OutputStream = Box::new(writepipe.clone()); - let table_ix = store.data_mut().table_mut().push_resource(stream)?; - let r = reactor.call_write_strings_to(&mut store, table_ix).await?; - assert_eq!(r, Ok(())); - - assert_eq!(writepipe.contents().as_ref(), b"hellogussie"); - - // Show that the `with` invocation in the macro means we get to re-use the - // type definitions from inside the `host` crate for these structures: - let ds = filesystem::DescriptorStat { - data_access_timestamp: Some(wall_clock::Datetime { - nanoseconds: 123, - seconds: 45, - }), - data_modification_timestamp: Some(wall_clock::Datetime { - nanoseconds: 789, - seconds: 10, - }), - link_count: 0, - size: 0, - status_change_timestamp: Some(wall_clock::Datetime { - nanoseconds: 0, - seconds: 1, - }), - type_: filesystem::DescriptorType::Unknown, - }; - let expected = format!("{ds:?}"); - let got = reactor.call_pass_an_imported_record(&mut store, ds).await?; - assert_eq!(expected, got); - - Ok(()) -} diff --git a/crates/test-programs/tests/wasi-cap-std-sync.rs b/crates/test-programs/tests/wasi-cap-std-sync.rs deleted file mode 100644 index 317e427a77ba..000000000000 --- a/crates/test-programs/tests/wasi-cap-std-sync.rs +++ /dev/null @@ -1,297 +0,0 @@ -#![cfg(feature = "test_programs")] -use anyhow::Result; -use tempfile::TempDir; -use wasi_common::pipe::WritePipe; -use wasmtime::{Config, Engine, Linker, Store}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(false); - config.async_support(false); - - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_module(&str) -> Module -include!(concat!(env!("OUT_DIR"), "/wasi_tests_modules.rs")); - -pub fn prepare_workspace(exe_name: &str) -> Result { - let prefix = format!("wasi_cap_std_sync_{}_", exe_name); - let tempdir = tempfile::Builder::new().prefix(&prefix).tempdir()?; - Ok(tempdir) -} - -use wasmtime_wasi::sync::{add_to_linker, WasiCtxBuilder}; -fn run(name: &str, inherit_stdio: bool) -> Result<()> { - let workspace = prepare_workspace(name)?; - let stdout = WritePipe::new_in_memory(); - let stderr = WritePipe::new_in_memory(); - let r = { - let mut linker = Linker::new(&ENGINE); - add_to_linker(&mut linker, |cx| cx)?; - - // Create our wasi context. - // Additionally register any preopened directories if we have them. - let mut builder = WasiCtxBuilder::new(); - - if inherit_stdio { - builder.inherit_stdio(); - } else { - builder - .stdout(Box::new(stdout.clone())) - .stderr(Box::new(stderr.clone())); - } - builder.arg(name)?.arg(".")?; - println!("preopen: {:?}", workspace); - let preopen_dir = - cap_std::fs::Dir::open_ambient_dir(workspace.path(), cap_std::ambient_authority())?; - builder.preopened_dir(preopen_dir, ".")?; - for (var, val) in test_programs::wasi_tests_environment() { - builder.env(var, val)?; - } - - let mut store = Store::new(&ENGINE, builder.build()); - let instance = linker.instantiate(&mut store, &get_module(name))?; - let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?; - start.call(&mut store, ())?; - Ok(()) - }; - - r.map_err(move |trap: anyhow::Error| { - let stdout = stdout - .try_into_inner() - .expect("sole ref to stdout") - .into_inner(); - if !stdout.is_empty() { - println!("guest stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); - } - let stderr = stderr - .try_into_inner() - .expect("sole ref to stderr") - .into_inner(); - if !stderr.is_empty() { - println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); - } - trap.context(format!( - "error while testing wasi-tests {} with cap-std-sync", - name - )) - })?; - Ok(()) -} - -// Below here is mechanical: there should be one test for every binary in -// wasi-tests. The only differences should be should_panic annotations for -// tests which fail. -#[test_log::test] -fn big_random_buf() { - run("big_random_buf", true).unwrap() -} -#[test_log::test] -fn clock_time_get() { - run("clock_time_get", true).unwrap() -} -#[test_log::test] -fn close_preopen() { - run("close_preopen", true).unwrap() -} -#[test_log::test] -fn dangling_fd() { - run("dangling_fd", true).unwrap() -} -#[test_log::test] -fn dangling_symlink() { - run("dangling_symlink", true).unwrap() -} -#[test_log::test] -fn directory_seek() { - run("directory_seek", true).unwrap() -} -#[test_log::test] -fn dir_fd_op_failures() { - run("dir_fd_op_failures", true).unwrap() -} -#[test_log::test] -fn fd_advise() { - run("fd_advise", true).unwrap() -} -#[test_log::test] -fn fd_filestat_get() { - run("fd_filestat_get", true).unwrap() -} -#[test_log::test] -fn fd_filestat_set() { - run("fd_filestat_set", true).unwrap() -} -#[test_log::test] -fn fd_flags_set() { - run("fd_flags_set", true).unwrap() -} -#[test_log::test] -fn fd_readdir() { - run("fd_readdir", true).unwrap() -} -#[test_log::test] -fn file_allocate() { - run("file_allocate", true).unwrap() -} -#[test_log::test] -fn file_pread_pwrite() { - run("file_pread_pwrite", true).unwrap() -} -#[test_log::test] -fn file_seek_tell() { - run("file_seek_tell", true).unwrap() -} -#[test_log::test] -fn file_truncation() { - run("file_truncation", true).unwrap() -} -#[test_log::test] -fn file_unbuffered_write() { - run("file_unbuffered_write", true).unwrap() -} -#[test_log::test] -#[cfg_attr(windows, should_panic)] -fn interesting_paths() { - run("interesting_paths", true).unwrap() -} -#[test_log::test] -fn regular_file_isatty() { - run("regular_file_isatty", true).unwrap() -} -#[test_log::test] -fn nofollow_errors() { - run("nofollow_errors", true).unwrap() -} -#[test_log::test] -fn overwrite_preopen() { - run("overwrite_preopen", true).unwrap() -} -#[test_log::test] -fn path_exists() { - run("path_exists", true).unwrap() -} -#[test_log::test] -fn path_filestat() { - run("path_filestat", true).unwrap() -} -#[test_log::test] -fn path_link() { - run("path_link", true).unwrap() -} -#[test_log::test] -fn path_open_create_existing() { - run("path_open_create_existing", true).unwrap() -} -#[test_log::test] -fn path_open_read_write() { - run("path_open_read_write", true).unwrap() -} -#[test_log::test] -fn path_open_dirfd_not_dir() { - run("path_open_dirfd_not_dir", true).unwrap() -} -#[test_log::test] -fn path_open_missing() { - run("path_open_missing", true).unwrap() -} -#[test_log::test] -fn path_open_nonblock() { - run("path_open_nonblock", true).unwrap() -} -#[test_log::test] -fn path_rename_dir_trailing_slashes() { - run("path_rename_dir_trailing_slashes", true).unwrap() -} -#[test_log::test] -#[should_panic] -fn path_rename_file_trailing_slashes() { - run("path_rename_file_trailing_slashes", false).unwrap() -} -#[test_log::test] -fn path_rename() { - run("path_rename", true).unwrap() -} -#[test_log::test] -fn path_symlink_trailing_slashes() { - run("path_symlink_trailing_slashes", true).unwrap() -} -#[test_log::test] -fn poll_oneoff_files() { - run("poll_oneoff_files", false).unwrap() -} -#[test_log::test] -fn poll_oneoff_stdio() { - run("poll_oneoff_stdio", true).unwrap() -} -#[test_log::test] -fn readlink() { - run("readlink", true).unwrap() -} -#[test_log::test] -#[should_panic] -fn remove_directory_trailing_slashes() { - run("remove_directory_trailing_slashes", false).unwrap() -} -#[test_log::test] -fn remove_nonempty_directory() { - run("remove_nonempty_directory", true).unwrap() -} -#[test_log::test] -fn renumber() { - run("renumber", true).unwrap() -} -#[test_log::test] -fn sched_yield() { - run("sched_yield", true).unwrap() -} -#[test_log::test] -#[should_panic] -fn sleep() { - run("sleep", true).unwrap() -} -#[test_log::test] -fn stdio() { - run("stdio", true).unwrap() -} -#[test_log::test] -fn stdio_isatty() { - if test_programs::stdio_is_terminal() { - // Inherit stdio, which is a terminal in the test runner's environment: - run("stdio_isatty", true).unwrap() - } -} -#[test_log::test] -fn stdio_not_isatty() { - // Don't inherit stdio, test asserts each is not tty: - run("stdio_not_isatty", false).unwrap() -} - -#[test_log::test] -fn symlink_create() { - run("symlink_create", true).unwrap() -} -#[test_log::test] -fn symlink_filestat() { - run("symlink_filestat", true).unwrap() -} -#[test_log::test] -fn symlink_loop() { - run("symlink_loop", true).unwrap() -} -#[test_log::test] -fn unlink_file_trailing_slashes() { - run("unlink_file_trailing_slashes", true).unwrap() -} -#[test_log::test] -fn path_open_preopen() { - run("path_open_preopen", true).unwrap() -} -#[test_log::test] -fn unicode_output() { - run("unicode_output", true).unwrap() -} diff --git a/crates/test-programs/tests/wasi-http-components-sync.rs b/crates/test-programs/tests/wasi-http-components-sync.rs deleted file mode 100644 index a05614595018..000000000000 --- a/crates/test-programs/tests/wasi-http-components-sync.rs +++ /dev/null @@ -1,168 +0,0 @@ -#![cfg(all(feature = "test_programs", not(skip_wasi_http_tests)))] - -use anyhow::Result; -use test_programs::http_server::Server; -use wasmtime::{ - component::{Component, Linker}, - Config, Engine, Store, -}; -use wasmtime_wasi::preview2::{ - command::sync::Command, pipe::MemoryOutputPipe, Table, WasiCtx, WasiCtxBuilder, WasiView, -}; -use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(true); - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_module(&str) -> Module -include!(concat!(env!("OUT_DIR"), "/wasi_http_tests_components.rs")); - -struct Ctx { - table: Table, - wasi: WasiCtx, - http: WasiHttpCtx, -} - -impl WasiView for Ctx { - fn table(&self) -> &Table { - &self.table - } - fn table_mut(&mut self) -> &mut Table { - &mut self.table - } - fn ctx(&self) -> &WasiCtx { - &self.wasi - } - fn ctx_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } -} - -impl WasiHttpView for Ctx { - fn ctx(&mut self) -> &mut WasiHttpCtx { - &mut self.http - } - - fn table(&mut self) -> &mut Table { - &mut self.table - } -} - -fn instantiate_component( - component: Component, - ctx: Ctx, -) -> Result<(Store, Command), anyhow::Error> { - let mut linker = Linker::new(&ENGINE); - wasmtime_wasi_http::proxy::sync::add_to_linker(&mut linker)?; - - let mut store = Store::new(&ENGINE, ctx); - - let (command, _instance) = Command::instantiate(&mut store, &component, &linker)?; - Ok((store, command)) -} - -fn run(name: &str, server: &Server) -> Result<()> { - let stdout = MemoryOutputPipe::new(4096); - let stderr = MemoryOutputPipe::new(4096); - let r = { - let table = Table::new(); - let component = get_component(name); - - // Create our wasi context. - let mut builder = WasiCtxBuilder::new(); - builder.stdout(stdout.clone()); - builder.stderr(stderr.clone()); - builder.arg(name); - for (var, val) in test_programs::wasi_tests_environment() { - builder.env(var, val); - } - builder.env("HTTP_SERVER", server.addr().to_string()); - let wasi = builder.build(); - let http = WasiHttpCtx {}; - - let (mut store, command) = instantiate_component(component, Ctx { table, wasi, http })?; - command.wasi_cli_run().call_run(&mut store) - }; - r.map_err(move |trap: anyhow::Error| { - let stdout = stdout.try_into_inner().expect("single ref to stdout"); - if !stdout.is_empty() { - println!("[guest] stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); - } - let stderr = stderr.try_into_inner().expect("single ref to stderr"); - if !stderr.is_empty() { - println!("[guest] stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); - } - trap.context(format!( - "error while testing wasi-tests {} with http-components-sync", - name - )) - })? - .map_err(|()| anyhow::anyhow!("run returned an error"))?; - Ok(()) -} - -#[test_log::test] -fn outbound_request_get() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_get", &server) -} - -#[test_log::test] -fn outbound_request_post() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_post", &server) -} - -#[test_log::test] -fn outbound_request_large_post() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_large_post", &server) -} - -#[test_log::test] -fn outbound_request_put() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_put", &server) -} - -#[test_log::test] -fn outbound_request_invalid_version() -> Result<()> { - let server = Server::http2()?; - run("outbound_request_invalid_version", &server) -} - -#[test_log::test] -fn outbound_request_unknown_method() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_unknown_method", &server) -} - -#[test_log::test] -fn outbound_request_unsupported_scheme() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_unsupported_scheme", &server) -} - -#[test_log::test] -fn outbound_request_invalid_port() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_invalid_port", &server) -} - -#[test_log::test] -fn outbound_request_invalid_dnsname() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_invalid_dnsname", &server) -} - -#[test_log::test] -fn outbound_request_response_build() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_response_build", &server) -} diff --git a/crates/test-programs/tests/wasi-http-components.rs b/crates/test-programs/tests/wasi-http-components.rs deleted file mode 100644 index 6bec2ab717f7..000000000000 --- a/crates/test-programs/tests/wasi-http-components.rs +++ /dev/null @@ -1,169 +0,0 @@ -#![cfg(all(feature = "test_programs", not(skip_wasi_http_tests)))] - -use anyhow::Result; -use test_programs::http_server::Server; -use wasmtime::{ - component::{Component, Linker}, - Config, Engine, Store, -}; -use wasmtime_wasi::preview2::{ - command::Command, pipe::MemoryOutputPipe, Table, WasiCtx, WasiCtxBuilder, WasiView, -}; -use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(true); - config.async_support(true); - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_module(&str) -> Module -include!(concat!(env!("OUT_DIR"), "/wasi_http_tests_components.rs")); - -struct Ctx { - table: Table, - wasi: WasiCtx, - http: WasiHttpCtx, -} - -impl WasiView for Ctx { - fn table(&self) -> &Table { - &self.table - } - fn table_mut(&mut self) -> &mut Table { - &mut self.table - } - fn ctx(&self) -> &WasiCtx { - &self.wasi - } - fn ctx_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } -} - -impl WasiHttpView for Ctx { - fn table(&mut self) -> &mut Table { - &mut self.table - } - fn ctx(&mut self) -> &mut WasiHttpCtx { - &mut self.http - } -} - -async fn instantiate_component( - component: Component, - ctx: Ctx, -) -> Result<(Store, Command), anyhow::Error> { - let mut linker = Linker::new(&ENGINE); - wasmtime_wasi_http::proxy::add_to_linker(&mut linker)?; - - let mut store = Store::new(&ENGINE, ctx); - - let (command, _instance) = Command::instantiate_async(&mut store, &component, &linker).await?; - Ok((store, command)) -} - -async fn run(name: &str, server: &Server) -> Result<()> { - let stdout = MemoryOutputPipe::new(4096); - let stderr = MemoryOutputPipe::new(4096); - let r = { - let table = Table::new(); - let component = get_component(name); - - // Create our wasi context. - let mut builder = WasiCtxBuilder::new(); - builder.stdout(stdout.clone()); - builder.stderr(stderr.clone()); - builder.arg(name); - for (var, val) in test_programs::wasi_tests_environment() { - builder.env(var, val); - } - builder.env("HTTP_SERVER", server.addr()); - let wasi = builder.build(); - let http = WasiHttpCtx; - - let (mut store, command) = - instantiate_component(component, Ctx { table, wasi, http }).await?; - command.wasi_cli_run().call_run(&mut store).await - }; - r.map_err(move |trap: anyhow::Error| { - let stdout = stdout.try_into_inner().expect("single ref to stdout"); - if !stdout.is_empty() { - println!("[guest] stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); - } - let stderr = stderr.try_into_inner().expect("single ref to stderr"); - if !stderr.is_empty() { - println!("[guest] stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); - } - trap.context(format!( - "error while testing wasi-tests {} with http-components", - name - )) - })? - .map_err(|()| anyhow::anyhow!("run returned an error"))?; - Ok(()) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_get() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_get", &server).await -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_post() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_post", &server).await -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_large_post() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_large_post", &server).await -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_put() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_put", &server).await -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_invalid_version() -> Result<()> { - let server = Server::http2()?; - run("outbound_request_invalid_version", &server).await -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_unknown_method() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_unknown_method", &server).await -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_unsupported_scheme() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_unsupported_scheme", &server).await -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_invalid_port() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_invalid_port", &server).await -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_invalid_dnsname() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_invalid_dnsname", &server).await -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn outbound_request_response_build() -> Result<()> { - let server = Server::http1()?; - run("outbound_request_response_build", &server).await -} diff --git a/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs b/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs deleted file mode 100644 index 4086694d57d2..000000000000 --- a/crates/test-programs/tests/wasi-preview1-host-in-preview2.rs +++ /dev/null @@ -1,336 +0,0 @@ -#![cfg(feature = "test_programs")] -use anyhow::Result; -use tempfile::TempDir; -use wasmtime::{Config, Engine, Linker, Store}; -use wasmtime_wasi::preview2::{ - pipe::MemoryOutputPipe, - preview1::{add_to_linker_async, WasiPreview1Adapter, WasiPreview1View}, - DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView, -}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(true); - config.async_support(true); - - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_module(&str) -> Module -include!(concat!(env!("OUT_DIR"), "/wasi_tests_modules.rs")); - -pub fn prepare_workspace(exe_name: &str) -> Result { - let prefix = format!("wasi_components_{}_", exe_name); - let tempdir = tempfile::Builder::new().prefix(&prefix).tempdir()?; - Ok(tempdir) -} - -async fn run(name: &str, inherit_stdio: bool) -> Result<()> { - let workspace = prepare_workspace(name)?; - let stdout = MemoryOutputPipe::new(4096); - let stderr = MemoryOutputPipe::new(4096); - let r = { - let mut linker = Linker::new(&ENGINE); - add_to_linker_async(&mut linker)?; - - // Create our wasi context. - // Additionally register any preopened directories if we have them. - let mut builder = WasiCtxBuilder::new(); - - if inherit_stdio { - builder.inherit_stdio(); - } else { - builder.stdout(stdout.clone()).stderr(stderr.clone()); - } - builder.args(&[name, "."]); - println!("preopen: {:?}", workspace); - let preopen_dir = - cap_std::fs::Dir::open_ambient_dir(workspace.path(), cap_std::ambient_authority())?; - builder.preopened_dir(preopen_dir, DirPerms::all(), FilePerms::all(), "."); - for (var, val) in test_programs::wasi_tests_environment() { - builder.env(var, val); - } - - let table = Table::new(); - let wasi = builder.build(); - struct Ctx { - wasi: WasiCtx, - table: Table, - adapter: WasiPreview1Adapter, - } - impl WasiView for Ctx { - fn ctx(&self) -> &WasiCtx { - &self.wasi - } - fn ctx_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } - fn table(&self) -> &Table { - &self.table - } - fn table_mut(&mut self) -> &mut Table { - &mut self.table - } - } - impl WasiPreview1View for Ctx { - fn adapter(&self) -> &WasiPreview1Adapter { - &self.adapter - } - fn adapter_mut(&mut self) -> &mut WasiPreview1Adapter { - &mut self.adapter - } - } - - let adapter = WasiPreview1Adapter::new(); - let ctx = Ctx { - wasi, - table, - adapter, - }; - let mut store = Store::new(&ENGINE, ctx); - let instance = linker - .instantiate_async(&mut store, &get_module(name)) - .await?; - let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?; - start.call_async(&mut store, ()).await?; - Ok(()) - }; - - r.map_err(move |trap: anyhow::Error| { - let stdout = stdout.try_into_inner().expect("sole ref to stdout"); - if !stdout.is_empty() { - println!("guest stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); - } - let stderr = stderr.try_into_inner().expect("sole ref to stderr"); - if !stderr.is_empty() { - println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); - } - trap.context(format!( - "error while testing wasi-tests {} with cap-std-sync", - name - )) - })?; - Ok(()) -} - -// Below here is mechanical: there should be one test for every binary in -// wasi-tests. The only differences should be should_panic annotations for -// tests which fail. -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn big_random_buf() { - run("big_random_buf", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn clock_time_get() { - run("clock_time_get", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn close_preopen() { - run("close_preopen", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn dangling_fd() { - run("dangling_fd", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn dangling_symlink() { - run("dangling_symlink", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn directory_seek() { - run("directory_seek", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn dir_fd_op_failures() { - run("dir_fd_op_failures", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_advise() { - run("fd_advise", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_filestat_get() { - run("fd_filestat_get", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_filestat_set() { - run("fd_filestat_set", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_flags_set() { - run("fd_flags_set", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_readdir() { - run("fd_readdir", true).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_allocate() { - run("file_allocate", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_pread_pwrite() { - run("file_pread_pwrite", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_seek_tell() { - run("file_seek_tell", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_truncation() { - run("file_truncation", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_unbuffered_write() { - run("file_unbuffered_write", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr(windows, should_panic)] -async fn interesting_paths() { - run("interesting_paths", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn regular_file_isatty() { - run("regular_file_isatty", true).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn nofollow_errors() { - run("nofollow_errors", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn overwrite_preopen() { - run("overwrite_preopen", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_exists() { - run("path_exists", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_filestat() { - run("path_filestat", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_link() { - run("path_link", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_create_existing() { - run("path_open_create_existing", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_read_write() { - run("path_open_read_write", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_dirfd_not_dir() { - run("path_open_dirfd_not_dir", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_missing() { - run("path_open_missing", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_nonblock() { - run("path_open_nonblock", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_rename_dir_trailing_slashes() { - run("path_rename_dir_trailing_slashes", false) - .await - .unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[should_panic] -async fn path_rename_file_trailing_slashes() { - run("path_rename_file_trailing_slashes", false) - .await - .unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_rename() { - run("path_rename", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_symlink_trailing_slashes() { - run("path_symlink_trailing_slashes", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn poll_oneoff_files() { - run("poll_oneoff_files", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn poll_oneoff_stdio() { - run("poll_oneoff_stdio", true).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn readlink() { - run("readlink", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[should_panic] -async fn remove_directory_trailing_slashes() { - run("remove_directory_trailing_slashes", false) - .await - .unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn remove_nonempty_directory() { - run("remove_nonempty_directory", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn renumber() { - run("renumber", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn sched_yield() { - run("sched_yield", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[should_panic] -async fn sleep() { - run("sleep", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stdio() { - run("stdio", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stdio_isatty() { - // If the test process is setup such that stdio is a terminal: - if test_programs::stdio_is_terminal() { - // Inherit stdio, test asserts each is a tty: - run("stdio_isatty", true).await.unwrap() - } -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stdio_not_isatty() { - // Don't inherit stdio, test asserts each is not tty: - run("stdio_not_isatty", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn symlink_create() { - run("symlink_create", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn symlink_filestat() { - run("symlink_filestat", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn symlink_loop() { - run("symlink_loop", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn unlink_file_trailing_slashes() { - run("unlink_file_trailing_slashes", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_preopen() { - run("path_open_preopen", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn unicode_output() { - run("unicode_output", true).await.unwrap() -} diff --git a/crates/test-programs/tests/wasi-preview2-components-sync.rs b/crates/test-programs/tests/wasi-preview2-components-sync.rs deleted file mode 100644 index 9772f9c8473f..000000000000 --- a/crates/test-programs/tests/wasi-preview2-components-sync.rs +++ /dev/null @@ -1,316 +0,0 @@ -#![cfg(feature = "test_programs")] -use anyhow::Result; -use tempfile::TempDir; -use wasmtime::{component::Linker, Config, Engine, Store}; -use wasmtime_wasi::preview2::{ - command::sync::{add_to_linker, Command}, - pipe::MemoryOutputPipe, - DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView, -}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(true); - config.async_support(false); - - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_component(&str) -> Component -include!(concat!(env!("OUT_DIR"), "/wasi_tests_components.rs")); - -pub fn prepare_workspace(exe_name: &str) -> Result { - let prefix = format!("wasi_components_{}_", exe_name); - let tempdir = tempfile::Builder::new().prefix(&prefix).tempdir()?; - Ok(tempdir) -} - -fn run(name: &str, inherit_stdio: bool) -> Result<()> { - let workspace = prepare_workspace(name)?; - let stdout = MemoryOutputPipe::new(4096); - let stderr = MemoryOutputPipe::new(4096); - let r = { - let mut linker = Linker::new(&ENGINE); - add_to_linker(&mut linker)?; - - // Create our wasi context. - // Additionally register any preopened directories if we have them. - let mut builder = WasiCtxBuilder::new(); - - if inherit_stdio { - builder.inherit_stdio(); - } else { - builder.stdout(stdout.clone()).stderr(stderr.clone()); - } - builder.args(&[name, "."]); - println!("preopen: {:?}", workspace); - let preopen_dir = - cap_std::fs::Dir::open_ambient_dir(workspace.path(), cap_std::ambient_authority())?; - builder.preopened_dir(preopen_dir, DirPerms::all(), FilePerms::all(), "."); - for (var, val) in test_programs::wasi_tests_environment() { - builder.env(var, val); - } - - let table = Table::new(); - let wasi = builder.build(); - struct Ctx { - wasi: WasiCtx, - table: Table, - } - impl WasiView for Ctx { - fn ctx(&self) -> &WasiCtx { - &self.wasi - } - fn ctx_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } - fn table(&self) -> &Table { - &self.table - } - fn table_mut(&mut self) -> &mut Table { - &mut self.table - } - } - - let ctx = Ctx { wasi, table }; - let mut store = Store::new(&ENGINE, ctx); - let (command, _instance) = Command::instantiate(&mut store, &get_component(name), &linker)?; - command - .wasi_cli_run() - .call_run(&mut store)? - .map_err(|()| anyhow::anyhow!("run returned a failure"))?; - Ok(()) - }; - - r.map_err(move |trap: anyhow::Error| { - let stdout = stdout.try_into_inner().expect("single ref to stdout"); - if !stdout.is_empty() { - println!("guest stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); - } - let stderr = stderr.try_into_inner().expect("single ref to stderr"); - if !stderr.is_empty() { - println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); - } - trap.context(format!( - "error while testing wasi-tests {} with cap-std-sync", - name - )) - })?; - Ok(()) -} - -// Below here is mechanical: there should be one test for every binary in -// wasi-tests. The only differences should be should_panic annotations for -// tests which fail. -#[test_log::test] -fn big_random_buf() { - run("big_random_buf", false).unwrap() -} -#[test_log::test] -fn clock_time_get() { - run("clock_time_get", false).unwrap() -} -#[test_log::test] -fn close_preopen() { - run("close_preopen", false).unwrap() -} -#[test_log::test] -fn dangling_fd() { - run("dangling_fd", false).unwrap() -} -#[test_log::test] -fn dangling_symlink() { - run("dangling_symlink", false).unwrap() -} -#[test_log::test] -fn directory_seek() { - run("directory_seek", false).unwrap() -} -#[test_log::test] -fn dir_fd_op_failures() { - run("dir_fd_op_failures", false).unwrap() -} -#[test_log::test] -fn fd_advise() { - run("fd_advise", false).unwrap() -} -#[test_log::test] -fn fd_filestat_get() { - run("fd_filestat_get", false).unwrap() -} -#[test_log::test] -fn fd_filestat_set() { - run("fd_filestat_set", false).unwrap() -} -#[test_log::test] -fn fd_flags_set() { - run("fd_flags_set", false).unwrap() -} -#[test_log::test] -fn fd_readdir() { - run("fd_readdir", false).unwrap() -} -#[test_log::test] -fn file_allocate() { - run("file_allocate", false).unwrap() -} -#[test_log::test] -fn file_pread_pwrite() { - run("file_pread_pwrite", false).unwrap() -} -#[test_log::test] -fn file_seek_tell() { - run("file_seek_tell", false).unwrap() -} -#[test_log::test] -fn file_truncation() { - run("file_truncation", false).unwrap() -} -#[test_log::test] -fn file_unbuffered_write() { - run("file_unbuffered_write", false).unwrap() -} -#[test_log::test] -#[cfg_attr(windows, should_panic)] -fn interesting_paths() { - run("interesting_paths", false).unwrap() -} -#[test_log::test] -fn regular_file_isatty() { - run("regular_file_isatty", false).unwrap() -} -#[test_log::test] -fn nofollow_errors() { - run("nofollow_errors", false).unwrap() -} -#[test_log::test] -fn overwrite_preopen() { - run("overwrite_preopen", false).unwrap() -} -#[test_log::test] -fn path_exists() { - run("path_exists", false).unwrap() -} -#[test_log::test] -fn path_filestat() { - run("path_filestat", false).unwrap() -} -#[test_log::test] -fn path_link() { - run("path_link", false).unwrap() -} -#[test_log::test] -fn path_open_create_existing() { - run("path_open_create_existing", false).unwrap() -} -#[test_log::test] -fn path_open_read_write() { - run("path_open_read_write", false).unwrap() -} -#[test_log::test] -fn path_open_dirfd_not_dir() { - run("path_open_dirfd_not_dir", false).unwrap() -} -#[test_log::test] -fn path_open_missing() { - run("path_open_missing", false).unwrap() -} -#[test_log::test] -fn path_open_nonblock() { - run("path_open_nonblock", false).unwrap() -} -#[test_log::test] -fn path_rename_dir_trailing_slashes() { - run("path_rename_dir_trailing_slashes", false).unwrap() -} -#[test_log::test] -#[should_panic] -fn path_rename_file_trailing_slashes() { - run("path_rename_file_trailing_slashes", false).unwrap() -} -#[test_log::test] -fn path_rename() { - run("path_rename", false).unwrap() -} -#[test_log::test] -fn path_symlink_trailing_slashes() { - run("path_symlink_trailing_slashes", false).unwrap() -} -#[test_log::test] -fn poll_oneoff_files() { - run("poll_oneoff_files", false).unwrap() -} - -#[test_log::test] -fn poll_oneoff_stdio() { - run("poll_oneoff_stdio", true).unwrap() -} -#[test_log::test] -fn readlink() { - run("readlink", false).unwrap() -} -#[test_log::test] -#[should_panic] -fn remove_directory_trailing_slashes() { - run("remove_directory_trailing_slashes", false).unwrap() -} -#[test_log::test] -fn remove_nonempty_directory() { - run("remove_nonempty_directory", false).unwrap() -} -#[test_log::test] -fn renumber() { - run("renumber", false).unwrap() -} -#[test_log::test] -fn sched_yield() { - run("sched_yield", false).unwrap() -} -#[test_log::test] -fn sleep() { - run("sleep", false).unwrap() -} -#[test_log::test] -fn stdio() { - run("stdio", false).unwrap() -} -#[test_log::test] -fn stdio_isatty() { - // If the test process is setup such that stdio is a terminal: - if test_programs::stdio_is_terminal() { - // Inherit stdio, test asserts each is a tty: - run("stdio_isatty", true).unwrap() - } -} -#[test_log::test] -fn stdio_not_isatty() { - // Don't inherit stdio, test asserts not isatty: - run("stdio_not_isatty", false).unwrap() -} -#[test_log::test] -fn symlink_create() { - run("symlink_create", false).unwrap() -} -#[test_log::test] -fn symlink_filestat() { - run("symlink_filestat", false).unwrap() -} -#[test_log::test] -fn symlink_loop() { - run("symlink_loop", false).unwrap() -} -#[test_log::test] -fn unlink_file_trailing_slashes() { - run("unlink_file_trailing_slashes", false).unwrap() -} -#[test_log::test] -fn path_open_preopen() { - run("path_open_preopen", false).unwrap() -} -#[test_log::test] -fn unicode_output() { - run("unicode_output", true).unwrap() -} diff --git a/crates/test-programs/tests/wasi-preview2-components.rs b/crates/test-programs/tests/wasi-preview2-components.rs deleted file mode 100644 index a2b30ed0d346..000000000000 --- a/crates/test-programs/tests/wasi-preview2-components.rs +++ /dev/null @@ -1,324 +0,0 @@ -#![cfg(feature = "test_programs")] -use anyhow::Result; -use tempfile::TempDir; -use wasmtime::{component::Linker, Config, Engine, Store}; -use wasmtime_wasi::preview2::{ - command::{add_to_linker, Command}, - pipe::MemoryOutputPipe, - DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView, -}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(true); - config.async_support(true); - - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_component(&str) -> Component -include!(concat!(env!("OUT_DIR"), "/wasi_tests_components.rs")); - -pub fn prepare_workspace(exe_name: &str) -> Result { - let prefix = format!("wasi_components_{}_", exe_name); - let tempdir = tempfile::Builder::new().prefix(&prefix).tempdir()?; - Ok(tempdir) -} - -async fn run(name: &str, inherit_stdio: bool) -> Result<()> { - let workspace = prepare_workspace(name)?; - let stdout = MemoryOutputPipe::new(4096); - let stderr = MemoryOutputPipe::new(4096); - let r = { - let mut linker = Linker::new(&ENGINE); - add_to_linker(&mut linker)?; - - // Create our wasi context. - // Additionally register any preopened directories if we have them. - let mut builder = WasiCtxBuilder::new(); - - if inherit_stdio { - builder.inherit_stdio(); - } else { - builder.stdout(stdout.clone()).stderr(stderr.clone()); - } - builder.args(&[name, "."]); - println!("preopen: {:?}", workspace); - let preopen_dir = - cap_std::fs::Dir::open_ambient_dir(workspace.path(), cap_std::ambient_authority())?; - builder.preopened_dir(preopen_dir, DirPerms::all(), FilePerms::all(), "."); - for (var, val) in test_programs::wasi_tests_environment() { - builder.env(var, val); - } - - let table = Table::new(); - let wasi = builder.build(); - struct Ctx { - wasi: WasiCtx, - table: Table, - } - impl WasiView for Ctx { - fn ctx(&self) -> &WasiCtx { - &self.wasi - } - fn ctx_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } - fn table(&self) -> &Table { - &self.table - } - fn table_mut(&mut self) -> &mut Table { - &mut self.table - } - } - - let ctx = Ctx { wasi, table }; - let mut store = Store::new(&ENGINE, ctx); - let (command, _instance) = - Command::instantiate_async(&mut store, &get_component(name), &linker).await?; - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("run returned a failure"))?; - Ok(()) - }; - - r.map_err(move |trap: anyhow::Error| { - let stdout = stdout.try_into_inner().expect("single ref to stdout"); - if !stdout.is_empty() { - println!("guest stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); - } - let stderr = stderr.try_into_inner().expect("single ref to stderr"); - if !stderr.is_empty() { - println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); - } - trap.context(format!( - "error while testing wasi-tests {} with cap-std-sync", - name - )) - })?; - Ok(()) -} - -// Below here is mechanical: there should be one test for every binary in -// wasi-tests. The only differences should be should_panic annotations for -// tests which fail. -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn big_random_buf() { - run("big_random_buf", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn clock_time_get() { - run("clock_time_get", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn close_preopen() { - run("close_preopen", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn dangling_fd() { - run("dangling_fd", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn dangling_symlink() { - run("dangling_symlink", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn directory_seek() { - run("directory_seek", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn dir_fd_op_failures() { - run("dir_fd_op_failures", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_advise() { - run("fd_advise", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_filestat_get() { - run("fd_filestat_get", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_filestat_set() { - run("fd_filestat_set", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_flags_set() { - run("fd_flags_set", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_readdir() { - run("fd_readdir", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_allocate() { - run("file_allocate", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_pread_pwrite() { - run("file_pread_pwrite", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_seek_tell() { - run("file_seek_tell", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_truncation() { - run("file_truncation", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_unbuffered_write() { - run("file_unbuffered_write", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[cfg_attr(windows, should_panic)] -async fn interesting_paths() { - run("interesting_paths", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn regular_file_isatty() { - run("regular_file_isatty", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn nofollow_errors() { - run("nofollow_errors", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn overwrite_preopen() { - run("overwrite_preopen", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_exists() { - run("path_exists", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_filestat() { - run("path_filestat", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_link() { - run("path_link", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_create_existing() { - run("path_open_create_existing", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_read_write() { - run("path_open_read_write", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_dirfd_not_dir() { - run("path_open_dirfd_not_dir", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_missing() { - run("path_open_missing", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_nonblock() { - run("path_open_nonblock", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_rename_dir_trailing_slashes() { - run("path_rename_dir_trailing_slashes", false) - .await - .unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[should_panic] -async fn path_rename_file_trailing_slashes() { - run("path_rename_file_trailing_slashes", false) - .await - .unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_rename() { - run("path_rename", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_symlink_trailing_slashes() { - run("path_symlink_trailing_slashes", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn poll_oneoff_files() { - run("poll_oneoff_files", false).await.unwrap() -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn poll_oneoff_stdio() { - run("poll_oneoff_stdio", true).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn readlink() { - run("readlink", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -#[should_panic] -async fn remove_directory_trailing_slashes() { - run("remove_directory_trailing_slashes", false) - .await - .unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn remove_nonempty_directory() { - run("remove_nonempty_directory", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn renumber() { - run("renumber", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn sched_yield() { - run("sched_yield", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn sleep() { - run("sleep", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stdio() { - run("stdio", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stdio_isatty() { - // If the test process is setup such that stdio is a terminal: - if test_programs::stdio_is_terminal() { - // Inherit stdio, test asserts each is not tty: - run("stdio_isatty", true).await.unwrap() - } -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stdio_not_isatty() { - // Don't inherit stdio, test asserts each is not tty: - run("stdio_not_isatty", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn symlink_create() { - run("symlink_create", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn symlink_filestat() { - run("symlink_filestat", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn symlink_loop() { - run("symlink_loop", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn unlink_file_trailing_slashes() { - run("unlink_file_trailing_slashes", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_preopen() { - run("path_open_preopen", false).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn unicode_output() { - run("unicode_output", true).await.unwrap() -} diff --git a/crates/test-programs/tests/wasi-sockets.rs b/crates/test-programs/tests/wasi-sockets.rs deleted file mode 100644 index 806d21c6c5db..000000000000 --- a/crates/test-programs/tests/wasi-sockets.rs +++ /dev/null @@ -1,102 +0,0 @@ -#![cfg(all(feature = "test_programs", not(skip_wasi_sockets_tests)))] -use cap_std::ambient_authority; -use wasmtime::component::Linker; -use wasmtime::{Config, Engine, Store}; -use wasmtime_wasi::preview2::{self, command::Command, Table, WasiCtx, WasiCtxBuilder, WasiView}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(true); - config.async_support(true); - - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_component(&str) -> Component -include!(concat!( - env!("OUT_DIR"), - "/wasi_sockets_tests_components.rs" -)); - -struct SocketsCtx { - table: Table, - wasi: WasiCtx, -} - -impl WasiView for SocketsCtx { - fn table(&self) -> &Table { - &self.table - } - fn table_mut(&mut self) -> &mut Table { - &mut self.table - } - fn ctx(&self) -> &WasiCtx { - &self.wasi - } - fn ctx_mut(&mut self) -> &mut WasiCtx { - &mut self.wasi - } -} - -async fn run(name: &str) -> anyhow::Result<()> { - let component = get_component(name); - let mut linker = Linker::new(&ENGINE); - - preview2::command::add_to_linker(&mut linker)?; - - // Create our wasi context. - let table = Table::new(); - let wasi = WasiCtxBuilder::new() - .inherit_stdio() - .inherit_network(ambient_authority()) - .allow_ip_name_lookup(true) - .arg(name) - .build(); - - let mut store = Store::new(&ENGINE, SocketsCtx { table, wasi }); - - let (command, _instance) = Command::instantiate_async(&mut store, &component, &linker).await?; - command - .wasi_cli_run() - .call_run(&mut store) - .await? - .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn tcp_sample_application() { - run("tcp_sample_application").await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn tcp_bind() { - run("tcp_bind").await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn tcp_connect() { - run("tcp_connect").await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn tcp_states() { - run("tcp_states").await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn tcp_sockopts() { - run("tcp_sockopts").await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn udp_sample_application() { - run("udp_sample_application").await.unwrap(); -} - -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn ip_name_lookup() { - run("ip_name_lookup").await.unwrap(); -} diff --git a/crates/test-programs/wasi-http-proxy-tests/Cargo.toml b/crates/test-programs/wasi-http-proxy-tests/Cargo.toml deleted file mode 100644 index fc87dc1bce7d..000000000000 --- a/crates/test-programs/wasi-http-proxy-tests/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "wasi-http-proxy-tests" -version = "0.0.0" -readme = "README.md" -edition = "2021" -publish = false - -[lib] -crate-type=["cdylib"] - -[dependencies] -wit-bindgen = { workspace = true, features = ["macros", "realloc"] } diff --git a/crates/test-programs/wasi-http-tests/Cargo.toml b/crates/test-programs/wasi-http-tests/Cargo.toml deleted file mode 100644 index 14206c3bbaa4..000000000000 --- a/crates/test-programs/wasi-http-tests/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "wasi-http-tests" -version = "0.0.0" -readme = "README.md" -edition = "2021" -publish = false - -[dependencies] -anyhow = { workspace = true } -wit-bindgen = { workspace = true, default-features = false, features = [ - "macros", -] } diff --git a/crates/test-programs/wasi-sockets-tests/Cargo.toml b/crates/test-programs/wasi-sockets-tests/Cargo.toml deleted file mode 100644 index 3dcc85370173..000000000000 --- a/crates/test-programs/wasi-sockets-tests/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "wasi-sockets-tests" -version = "0.0.0" -readme = "README.md" -edition = "2021" -publish = false - -[dependencies] -anyhow = { workspace = true } -wit-bindgen = { workspace = true, default-features = false, features = ["macros"] } diff --git a/crates/test-programs/wasi-tests/Cargo.toml b/crates/test-programs/wasi-tests/Cargo.toml deleted file mode 100644 index bf72d58ea313..000000000000 --- a/crates/test-programs/wasi-tests/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "wasi-tests" -version = "0.0.0" -readme = "README.md" -edition = "2021" -publish = false - -[dependencies] -libc = "0.2.65" -wasi = "0.11.0" -once_cell = "1.12" -wit-bindgen = { workspace = true, default-features = false, features = [ - "macros", -] } diff --git a/crates/test-programs/wasi-tests/README.md b/crates/test-programs/wasi-tests/README.md deleted file mode 100644 index f281fa1a8edc..000000000000 --- a/crates/test-programs/wasi-tests/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This is the `wasi-tests` crate, which contains source code for the system-level WASI tests. - -Building these tests requires that the `wasm32-wasi` target be installed. diff --git a/crates/test-programs/wasi-tests/src/bin/read_empty_file.rs.disabled b/crates/test-programs/wasi-tests/src/bin/read_empty_file.rs.disabled deleted file mode 100644 index 510466eaefe0..000000000000 --- a/crates/test-programs/wasi-tests/src/bin/read_empty_file.rs.disabled +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - let mut file = std::fs::File::open("").unwrap(); - let mut buffer = Vec::new(); - std::io::Read::read_to_end(&mut file, &mut buffer).unwrap(); -} diff --git a/crates/test-programs/wasi-tests/src/config.rs b/crates/test-programs/wasi-tests/src/config.rs deleted file mode 100644 index 2c8e0c0f6832..000000000000 --- a/crates/test-programs/wasi-tests/src/config.rs +++ /dev/null @@ -1,63 +0,0 @@ -pub struct TestConfig { - errno_mode: ErrnoMode, - no_dangling_filesystem: bool, - no_rename_dir_to_empty_dir: bool, - no_fdflags_sync_support: bool, -} - -enum ErrnoMode { - Unix, - MacOS, - Windows, - Permissive, -} - -impl TestConfig { - pub fn from_env() -> Self { - let errno_mode = if std::env::var("ERRNO_MODE_UNIX").is_ok() { - ErrnoMode::Unix - } else if std::env::var("ERRNO_MODE_MACOS").is_ok() { - ErrnoMode::MacOS - } else if std::env::var("ERRNO_MODE_WINDOWS").is_ok() { - ErrnoMode::Windows - } else { - ErrnoMode::Permissive - }; - let no_dangling_filesystem = std::env::var("NO_DANGLING_FILESYSTEM").is_ok(); - let no_rename_dir_to_empty_dir = std::env::var("NO_RENAME_DIR_TO_EMPTY_DIR").is_ok(); - let no_fdflags_sync_support = std::env::var("NO_FDFLAGS_SYNC_SUPPORT").is_ok(); - TestConfig { - errno_mode, - no_dangling_filesystem, - no_rename_dir_to_empty_dir, - no_fdflags_sync_support, - } - } - pub fn errno_expect_unix(&self) -> bool { - match self.errno_mode { - ErrnoMode::Unix | ErrnoMode::MacOS => true, - _ => false, - } - } - pub fn errno_expect_macos(&self) -> bool { - match self.errno_mode { - ErrnoMode::MacOS => true, - _ => false, - } - } - pub fn errno_expect_windows(&self) -> bool { - match self.errno_mode { - ErrnoMode::Windows => true, - _ => false, - } - } - pub fn support_dangling_filesystem(&self) -> bool { - !self.no_dangling_filesystem - } - pub fn support_rename_dir_to_empty_dir(&self) -> bool { - !self.no_rename_dir_to_empty_dir - } - pub fn support_fdflags_sync(&self) -> bool { - !self.no_fdflags_sync_support - } -} diff --git a/crates/wasi-common/Cargo.toml b/crates/wasi-common/Cargo.toml index 3382912e63f2..dbce4a957906 100644 --- a/crates/wasi-common/Cargo.toml +++ b/crates/wasi-common/Cargo.toml @@ -41,6 +41,15 @@ features = [ "Win32_Networking_WinSock", ] +[dev-dependencies] +tempfile = { workspace = true } +test-log = { workspace = true } +tracing-subscriber = { workspace = true } +wasmtime = { workspace = true, features = ['cranelift', 'async'] } +wasmtime-wasi = { path = '../wasi', features = ['sync', 'tokio'] } +test-programs-artifacts = { workspace = true } +tokio = { workspace = true, features = ['macros', 'rt-multi-thread'] } + [features] default = ["trace_log"] # This feature enables the `tracing` logs in the calls to target the `log` diff --git a/crates/test-programs/tests/wasi-tokio.rs b/crates/wasi-common/tests/all/async_.rs similarity index 50% rename from crates/test-programs/tests/wasi-tokio.rs rename to crates/wasi-common/tests/all/async_.rs index 49280d2abefc..54c63d143067 100644 --- a/crates/test-programs/tests/wasi-tokio.rs +++ b/crates/wasi-common/tests/all/async_.rs @@ -1,23 +1,8 @@ -#![cfg(feature = "test_programs")] -use anyhow::Result; -use tempfile::TempDir; -use wasi_common::pipe::WritePipe; -use wasmtime::{Config, Engine, Linker, Store}; +use super::*; +use test_programs_artifacts::*; use wasmtime_wasi::tokio::{add_to_linker, WasiCtxBuilder}; -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(false); - config.async_support(true); - - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_module(&str) -> Module -include!(concat!(env!("OUT_DIR"), "/wasi_tests_modules.rs")); +foreach_preview1!(assert_test_exists); pub fn prepare_workspace(exe_name: &str) -> Result { let prefix = format!("wasi_tokio_{}_", exe_name); @@ -25,12 +10,17 @@ pub fn prepare_workspace(exe_name: &str) -> Result { Ok(tempdir) } -async fn run(name: &str, inherit_stdio: bool) -> Result<()> { +async fn run(path: &str, inherit_stdio: bool) -> Result<()> { + let path = Path::new(path); + let name = path.file_stem().unwrap().to_str().unwrap(); let workspace = prepare_workspace(name)?; let stdout = WritePipe::new_in_memory(); let stderr = WritePipe::new_in_memory(); let r = { - let mut linker = Linker::new(&ENGINE); + let mut config = Config::new(); + config.async_support(true); + let engine = Engine::new(&config)?; + let mut linker = Linker::new(&engine); add_to_linker(&mut linker, |cx| cx)?; // Create our wasi context. @@ -49,14 +39,13 @@ async fn run(name: &str, inherit_stdio: bool) -> Result<()> { let preopen_dir = cap_std::fs::Dir::open_ambient_dir(workspace.path(), cap_std::ambient_authority())?; builder.preopened_dir(preopen_dir, ".")?; - for (var, val) in test_programs::wasi_tests_environment() { + for (var, val) in test_programs_artifacts::wasi_tests_environment() { builder.env(var, val)?; } - let mut store = Store::new(&ENGINE, builder.build()); - let instance = linker - .instantiate_async(&mut store, &get_module(name)) - .await?; + let mut store = Store::new(&engine, builder.build()); + let module = Module::from_file(&engine, path)?; + let instance = linker.instantiate_async(&mut store, &module).await?; let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?; start.call_async(&mut store, ()).await?; Ok(()) @@ -89,215 +78,216 @@ async fn run(name: &str, inherit_stdio: bool) -> Result<()> { // wasi-tests. The only differences should be should_panic annotations for // tests which fail. #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn big_random_buf() { - run("big_random_buf", true).await.unwrap() +async fn preview1_big_random_buf() { + run(PREVIEW1_BIG_RANDOM_BUF, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn clock_time_get() { - run("clock_time_get", true).await.unwrap() +async fn preview1_clock_time_get() { + run(PREVIEW1_CLOCK_TIME_GET, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn close_preopen() { - run("close_preopen", true).await.unwrap() +async fn preview1_close_preopen() { + run(PREVIEW1_CLOSE_PREOPEN, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn dangling_fd() { - run("dangling_fd", true).await.unwrap() +async fn preview1_dangling_fd() { + run(PREVIEW1_DANGLING_FD, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn dangling_symlink() { - run("dangling_symlink", true).await.unwrap() +async fn preview1_dangling_symlink() { + run(PREVIEW1_DANGLING_SYMLINK, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn directory_seek() { - run("directory_seek", true).await.unwrap() +async fn preview1_directory_seek() { + run(PREVIEW1_DIRECTORY_SEEK, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn dir_fd_op_failures() { - run("dir_fd_op_failures", true).await.unwrap() +async fn preview1_dir_fd_op_failures() { + run(PREVIEW1_DIR_FD_OP_FAILURES, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_advise() { - run("fd_advise", true).await.unwrap() +async fn preview1_fd_advise() { + run(PREVIEW1_FD_ADVISE, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_filestat_get() { - run("fd_filestat_get", true).await.unwrap() +async fn preview1_fd_filestat_get() { + run(PREVIEW1_FD_FILESTAT_GET, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_filestat_set() { - run("fd_filestat_set", true).await.unwrap() +async fn preview1_fd_filestat_set() { + run(PREVIEW1_FD_FILESTAT_SET, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_flags_set() { - run("fd_flags_set", true).await.unwrap() +async fn preview1_fd_flags_set() { + run(PREVIEW1_FD_FLAGS_SET, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn fd_readdir() { - run("fd_readdir", true).await.unwrap() +async fn preview1_fd_readdir() { + run(PREVIEW1_FD_READDIR, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_allocate() { - run("file_allocate", true).await.unwrap() +async fn preview1_file_allocate() { + run(PREVIEW1_FILE_ALLOCATE, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_pread_pwrite() { - run("file_pread_pwrite", true).await.unwrap() +async fn preview1_file_pread_pwrite() { + run(PREVIEW1_FILE_PREAD_PWRITE, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_seek_tell() { - run("file_seek_tell", true).await.unwrap() +async fn preview1_file_seek_tell() { + run(PREVIEW1_FILE_SEEK_TELL, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_truncation() { - run("file_truncation", true).await.unwrap() +async fn preview1_file_truncation() { + run(PREVIEW1_FILE_TRUNCATION, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn file_unbuffered_write() { - run("file_unbuffered_write", true).await.unwrap() +async fn preview1_file_unbuffered_write() { + run(PREVIEW1_FILE_UNBUFFERED_WRITE, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] #[cfg_attr(windows, should_panic)] -async fn interesting_paths() { - run("interesting_paths", true).await.unwrap() +async fn preview1_interesting_paths() { + run(PREVIEW1_INTERESTING_PATHS, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn regular_file_isatty() { - run("regular_file_isatty", false).await.unwrap() +async fn preview1_regular_file_isatty() { + run(PREVIEW1_REGULAR_FILE_ISATTY, false).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn nofollow_errors() { - run("nofollow_errors", true).await.unwrap() +async fn preview1_nofollow_errors() { + run(PREVIEW1_NOFOLLOW_ERRORS, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn overwrite_preopen() { - run("overwrite_preopen", true).await.unwrap() +async fn preview1_overwrite_preopen() { + run(PREVIEW1_OVERWRITE_PREOPEN, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_exists() { - run("path_exists", true).await.unwrap() +async fn preview1_path_exists() { + run(PREVIEW1_PATH_EXISTS, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_filestat() { - run("path_filestat", true).await.unwrap() +async fn preview1_path_filestat() { + run(PREVIEW1_PATH_FILESTAT, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_link() { - run("path_link", true).await.unwrap() +async fn preview1_path_link() { + run(PREVIEW1_PATH_LINK, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_create_existing() { - run("path_open_create_existing", true).await.unwrap() +async fn preview1_path_open_create_existing() { + run(PREVIEW1_PATH_OPEN_CREATE_EXISTING, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_read_write() { - run("path_open_read_write", true).await.unwrap() +async fn preview1_path_open_read_write() { + run(PREVIEW1_PATH_OPEN_READ_WRITE, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_dirfd_not_dir() { - run("path_open_dirfd_not_dir", true).await.unwrap() +async fn preview1_path_open_dirfd_not_dir() { + run(PREVIEW1_PATH_OPEN_DIRFD_NOT_DIR, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_missing() { - run("path_open_missing", true).await.unwrap() +async fn preview1_path_open_missing() { + run(PREVIEW1_PATH_OPEN_MISSING, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_nonblock() { - run("path_open_nonblock", true).await.unwrap() +async fn preview1_path_open_nonblock() { + run(PREVIEW1_PATH_OPEN_NONBLOCK, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_rename_dir_trailing_slashes() { - run("path_rename_dir_trailing_slashes", true).await.unwrap() +async fn preview1_path_rename_dir_trailing_slashes() { + run(PREVIEW1_PATH_RENAME_DIR_TRAILING_SLASHES, true) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] #[should_panic] -async fn path_rename_file_trailing_slashes() { - run("path_rename_file_trailing_slashes", false) +async fn preview1_path_rename_file_trailing_slashes() { + run(PREVIEW1_PATH_RENAME_FILE_TRAILING_SLASHES, false) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_rename() { - run("path_rename", true).await.unwrap() +async fn preview1_path_rename() { + run(PREVIEW1_PATH_RENAME, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_symlink_trailing_slashes() { - run("path_symlink_trailing_slashes", true).await.unwrap() +async fn preview1_path_symlink_trailing_slashes() { + run(PREVIEW1_PATH_SYMLINK_TRAILING_SLASHES, true) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn poll_oneoff_files() { - run("poll_oneoff_files", false).await.unwrap() +async fn preview1_poll_oneoff_files() { + run(PREVIEW1_POLL_ONEOFF_FILES, false).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn poll_oneoff_stdio() { - run("poll_oneoff_stdio", true).await.unwrap() +async fn preview1_poll_oneoff_stdio() { + run(PREVIEW1_POLL_ONEOFF_STDIO, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn readlink() { - run("readlink", true).await.unwrap() +async fn preview1_readlink() { + run(PREVIEW1_READLINK, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] #[should_panic] -async fn remove_directory_trailing_slashes() { - run("remove_directory_trailing_slashes", false) +async fn preview1_remove_directory_trailing_slashes() { + run(PREVIEW1_REMOVE_DIRECTORY_TRAILING_SLASHES, false) .await .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn remove_nonempty_directory() { - run("remove_nonempty_directory", true).await.unwrap() -} -#[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn renumber() { - run("renumber", true).await.unwrap() +async fn preview1_remove_nonempty_directory() { + run(PREVIEW1_REMOVE_NONEMPTY_DIRECTORY, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn sched_yield() { - run("sched_yield", true).await.unwrap() +async fn preview1_renumber() { + run(PREVIEW1_RENUMBER, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -#[should_panic] -async fn sleep() { - run("sleep", true).await.unwrap() +async fn preview1_sched_yield() { + run(PREVIEW1_SCHED_YIELD, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stdio() { - run("stdio", true).await.unwrap() +async fn preview1_stdio() { + run(PREVIEW1_STDIO, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stdio_isatty() { +async fn preview1_stdio_isatty() { // Only a valid test if the host executable's stdio is a terminal: - if test_programs::stdio_is_terminal() { + if test_programs_artifacts::stdio_is_terminal() { // Inherit stdio, test asserts it is a tty: - run("stdio_isatty", true).await.unwrap() + run(PREVIEW1_STDIO_ISATTY, true).await.unwrap() } } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn stdio_not_isatty() { +async fn preview1_stdio_not_isatty() { // Don't inherit stdio, test asserts each is not tty: - run("stdio_not_isatty", false).await.unwrap() + run(PREVIEW1_STDIO_NOT_ISATTY, false).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn symlink_create() { - run("symlink_create", true).await.unwrap() +async fn preview1_symlink_create() { + run(PREVIEW1_SYMLINK_CREATE, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn symlink_filestat() { - run("symlink_filestat", true).await.unwrap() +async fn preview1_symlink_filestat() { + run(PREVIEW1_SYMLINK_FILESTAT, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn symlink_loop() { - run("symlink_loop", true).await.unwrap() +async fn preview1_symlink_loop() { + run(PREVIEW1_SYMLINK_LOOP, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn unlink_file_trailing_slashes() { - run("unlink_file_trailing_slashes", true).await.unwrap() +async fn preview1_unlink_file_trailing_slashes() { + run(PREVIEW1_UNLINK_FILE_TRAILING_SLASHES, true) + .await + .unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn path_open_preopen() { - run("path_open_preopen", true).await.unwrap() +async fn preview1_path_open_preopen() { + run(PREVIEW1_PATH_OPEN_PREOPEN, true).await.unwrap() } #[test_log::test(tokio::test(flavor = "multi_thread"))] -async fn unicode_output() { - run("unicode_output", true).await.unwrap() +async fn preview1_unicode_output() { + run(PREVIEW1_UNICODE_OUTPUT, true).await.unwrap() } diff --git a/crates/wasi-common/tests/all/main.rs b/crates/wasi-common/tests/all/main.rs new file mode 100644 index 000000000000..5324e8656e7d --- /dev/null +++ b/crates/wasi-common/tests/all/main.rs @@ -0,0 +1,21 @@ +use anyhow::Result; +use std::path::Path; +use tempfile::TempDir; +use wasi_common::pipe::WritePipe; +use wasmtime::{Config, Engine, Linker, Module, Store}; + +pub fn prepare_workspace(exe_name: &str) -> Result { + let prefix = format!("wasi_cap_std_sync_{}_", exe_name); + let tempdir = tempfile::Builder::new().prefix(&prefix).tempdir()?; + Ok(tempdir) +} + +macro_rules! assert_test_exists { + ($name:ident) => { + #[allow(unused_imports)] + use self::$name as _; + }; +} + +mod async_; +mod sync; diff --git a/crates/wasi-common/tests/all/sync.rs b/crates/wasi-common/tests/all/sync.rs new file mode 100644 index 000000000000..42e0b8459530 --- /dev/null +++ b/crates/wasi-common/tests/all/sync.rs @@ -0,0 +1,275 @@ +use super::*; +use test_programs_artifacts::*; +use wasmtime_wasi::sync::{add_to_linker, WasiCtxBuilder}; + +foreach_preview1!(assert_test_exists); + +fn run(path: &str, inherit_stdio: bool) -> Result<()> { + let path = Path::new(path); + let name = path.file_stem().unwrap().to_str().unwrap(); + let workspace = prepare_workspace(name)?; + let stdout = WritePipe::new_in_memory(); + let stderr = WritePipe::new_in_memory(); + let r = { + let engine = Engine::default(); + let mut linker = Linker::new(&engine); + add_to_linker(&mut linker, |cx| cx)?; + + // Create our wasi context. + // Additionally register any preopened directories if we have them. + let mut builder = WasiCtxBuilder::new(); + + if inherit_stdio { + builder.inherit_stdio(); + } else { + builder + .stdout(Box::new(stdout.clone())) + .stderr(Box::new(stderr.clone())); + } + builder.arg(name)?.arg(".")?; + println!("preopen: {:?}", workspace); + let preopen_dir = + cap_std::fs::Dir::open_ambient_dir(workspace.path(), cap_std::ambient_authority())?; + builder.preopened_dir(preopen_dir, ".")?; + for (var, val) in test_programs_artifacts::wasi_tests_environment() { + builder.env(var, val)?; + } + + let mut store = Store::new(&engine, builder.build()); + let module = Module::from_file(&engine, path)?; + let instance = linker.instantiate(&mut store, &module)?; + let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?; + start.call(&mut store, ())?; + Ok(()) + }; + + r.map_err(move |trap: anyhow::Error| { + let stdout = stdout + .try_into_inner() + .expect("sole ref to stdout") + .into_inner(); + if !stdout.is_empty() { + println!("guest stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); + } + let stderr = stderr + .try_into_inner() + .expect("sole ref to stderr") + .into_inner(); + if !stderr.is_empty() { + println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); + } + trap.context(format!( + "error while testing wasi-tests {} with cap-std-sync", + name + )) + })?; + Ok(()) +} + +// Below here is mechanical: there should be one test for every binary in +// wasi-tests. The only differences should be should_panic annotations for +// tests which fail. +#[test_log::test] +fn preview1_big_random_buf() { + run(PREVIEW1_BIG_RANDOM_BUF, true).unwrap() +} +#[test_log::test] +fn preview1_clock_time_get() { + run(PREVIEW1_CLOCK_TIME_GET, true).unwrap() +} +#[test_log::test] +fn preview1_close_preopen() { + run(PREVIEW1_CLOSE_PREOPEN, true).unwrap() +} +#[test_log::test] +fn preview1_dangling_fd() { + run(PREVIEW1_DANGLING_FD, true).unwrap() +} +#[test_log::test] +fn preview1_dangling_symlink() { + run(PREVIEW1_DANGLING_SYMLINK, true).unwrap() +} +#[test_log::test] +fn preview1_directory_seek() { + run(PREVIEW1_DIRECTORY_SEEK, true).unwrap() +} +#[test_log::test] +fn preview1_dir_fd_op_failures() { + run(PREVIEW1_DIR_FD_OP_FAILURES, true).unwrap() +} +#[test_log::test] +fn preview1_fd_advise() { + run(PREVIEW1_FD_ADVISE, true).unwrap() +} +#[test_log::test] +fn preview1_fd_filestat_get() { + run(PREVIEW1_FD_FILESTAT_GET, true).unwrap() +} +#[test_log::test] +fn preview1_fd_filestat_set() { + run(PREVIEW1_FD_FILESTAT_SET, true).unwrap() +} +#[test_log::test] +fn preview1_fd_flags_set() { + run(PREVIEW1_FD_FLAGS_SET, true).unwrap() +} +#[test_log::test] +fn preview1_fd_readdir() { + run(PREVIEW1_FD_READDIR, true).unwrap() +} +#[test_log::test] +fn preview1_file_allocate() { + run(PREVIEW1_FILE_ALLOCATE, true).unwrap() +} +#[test_log::test] +fn preview1_file_pread_pwrite() { + run(PREVIEW1_FILE_PREAD_PWRITE, true).unwrap() +} +#[test_log::test] +fn preview1_file_seek_tell() { + run(PREVIEW1_FILE_SEEK_TELL, true).unwrap() +} +#[test_log::test] +fn preview1_file_truncation() { + run(PREVIEW1_FILE_TRUNCATION, true).unwrap() +} +#[test_log::test] +fn preview1_file_unbuffered_write() { + run(PREVIEW1_FILE_UNBUFFERED_WRITE, true).unwrap() +} +#[test_log::test] +#[cfg_attr(windows, should_panic)] +fn preview1_interesting_paths() { + run(PREVIEW1_INTERESTING_PATHS, true).unwrap() +} +#[test_log::test] +fn preview1_regular_file_isatty() { + run(PREVIEW1_REGULAR_FILE_ISATTY, true).unwrap() +} +#[test_log::test] +fn preview1_nofollow_errors() { + run(PREVIEW1_NOFOLLOW_ERRORS, true).unwrap() +} +#[test_log::test] +fn preview1_overwrite_preopen() { + run(PREVIEW1_OVERWRITE_PREOPEN, true).unwrap() +} +#[test_log::test] +fn preview1_path_exists() { + run(PREVIEW1_PATH_EXISTS, true).unwrap() +} +#[test_log::test] +fn preview1_path_filestat() { + run(PREVIEW1_PATH_FILESTAT, true).unwrap() +} +#[test_log::test] +fn preview1_path_link() { + run(PREVIEW1_PATH_LINK, true).unwrap() +} +#[test_log::test] +fn preview1_path_open_create_existing() { + run(PREVIEW1_PATH_OPEN_CREATE_EXISTING, true).unwrap() +} +#[test_log::test] +fn preview1_path_open_read_write() { + run(PREVIEW1_PATH_OPEN_READ_WRITE, true).unwrap() +} +#[test_log::test] +fn preview1_path_open_dirfd_not_dir() { + run(PREVIEW1_PATH_OPEN_DIRFD_NOT_DIR, true).unwrap() +} +#[test_log::test] +fn preview1_path_open_missing() { + run(PREVIEW1_PATH_OPEN_MISSING, true).unwrap() +} +#[test_log::test] +fn preview1_path_open_nonblock() { + run(PREVIEW1_PATH_OPEN_NONBLOCK, true).unwrap() +} +#[test_log::test] +fn preview1_path_rename_dir_trailing_slashes() { + run(PREVIEW1_PATH_RENAME_DIR_TRAILING_SLASHES, true).unwrap() +} +#[test_log::test] +#[should_panic] +fn preview1_path_rename_file_trailing_slashes() { + run(PREVIEW1_PATH_RENAME_FILE_TRAILING_SLASHES, false).unwrap() +} +#[test_log::test] +fn preview1_path_rename() { + run(PREVIEW1_PATH_RENAME, true).unwrap() +} +#[test_log::test] +fn preview1_path_symlink_trailing_slashes() { + run(PREVIEW1_PATH_SYMLINK_TRAILING_SLASHES, true).unwrap() +} +#[test_log::test] +fn preview1_poll_oneoff_files() { + run(PREVIEW1_POLL_ONEOFF_FILES, false).unwrap() +} +#[test_log::test] +fn preview1_poll_oneoff_stdio() { + run(PREVIEW1_POLL_ONEOFF_STDIO, true).unwrap() +} +#[test_log::test] +fn preview1_readlink() { + run(PREVIEW1_READLINK, true).unwrap() +} +#[test_log::test] +#[should_panic] +fn preview1_remove_directory_trailing_slashes() { + run(PREVIEW1_REMOVE_DIRECTORY_TRAILING_SLASHES, false).unwrap() +} +#[test_log::test] +fn preview1_remove_nonempty_directory() { + run(PREVIEW1_REMOVE_NONEMPTY_DIRECTORY, true).unwrap() +} +#[test_log::test] +fn preview1_renumber() { + run(PREVIEW1_RENUMBER, true).unwrap() +} +#[test_log::test] +fn preview1_sched_yield() { + run(PREVIEW1_SCHED_YIELD, true).unwrap() +} +#[test_log::test] +fn preview1_stdio() { + run(PREVIEW1_STDIO, true).unwrap() +} +#[test_log::test] +fn preview1_stdio_isatty() { + if test_programs_artifacts::stdio_is_terminal() { + // Inherit stdio, which is a terminal in the test runner's environment: + run(PREVIEW1_STDIO_ISATTY, true).unwrap() + } +} +#[test_log::test] +fn preview1_stdio_not_isatty() { + // Don't inherit stdio, test asserts each is not tty: + run(PREVIEW1_STDIO_NOT_ISATTY, false).unwrap() +} + +#[test_log::test] +fn preview1_symlink_create() { + run(PREVIEW1_SYMLINK_CREATE, true).unwrap() +} +#[test_log::test] +fn preview1_symlink_filestat() { + run(PREVIEW1_SYMLINK_FILESTAT, true).unwrap() +} +#[test_log::test] +fn preview1_symlink_loop() { + run(PREVIEW1_SYMLINK_LOOP, true).unwrap() +} +#[test_log::test] +fn preview1_unlink_file_trailing_slashes() { + run(PREVIEW1_UNLINK_FILE_TRAILING_SLASHES, true).unwrap() +} +#[test_log::test] +fn preview1_path_open_preopen() { + run(PREVIEW1_PATH_OPEN_PREOPEN, true).unwrap() +} +#[test_log::test] +fn preview1_unicode_output() { + run(PREVIEW1_UNICODE_OUTPUT, true).unwrap() +} diff --git a/crates/wasi-http/Cargo.toml b/crates/wasi-http/Cargo.toml index 354d00624525..9210d50384e7 100644 --- a/crates/wasi-http/Cargo.toml +++ b/crates/wasi-http/Cargo.toml @@ -33,6 +33,13 @@ tokio-rustls = { version = "0.24.0" } rustls = { version = "0.21.6" } webpki-roots = { version = "0.25.2" } +[dev-dependencies] +test-programs-artifacts = { workspace = true } +test-log = { workspace = true } +tracing-subscriber = { workspace = true } +wasmtime = { workspace = true, features = ['cranelift'] } +tokio = { workspace = true, features = ['macros'] } + [features] default = ["sync"] sync = ["wasmtime-wasi/sync"] diff --git a/crates/wasi-http/tests/all/async_.rs b/crates/wasi-http/tests/all/async_.rs new file mode 100644 index 000000000000..128c1f25fce9 --- /dev/null +++ b/crates/wasi-http/tests/all/async_.rs @@ -0,0 +1,80 @@ +use super::*; +use test_programs_artifacts::*; +use wasmtime_wasi::preview2::command::Command; + +foreach_http!(assert_test_exists); + +async fn run(path: &str, server: &Server) -> Result<()> { + let mut config = Config::new(); + config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); + config.wasm_component_model(true); + config.async_support(true); + let engine = Engine::new(&config)?; + let component = Component::from_file(&engine, path)?; + let mut store = store(&engine, server); + let mut linker = Linker::new(&engine); + wasmtime_wasi_http::proxy::add_to_linker(&mut linker)?; + let (command, _instance) = Command::instantiate_async(&mut store, &component, &linker).await?; + let result = command.wasi_cli_run().call_run(&mut store).await?; + result.map_err(|()| anyhow::anyhow!("run returned an error")) +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn http_outbound_request_get() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_GET_COMPONENT, &server).await +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn http_outbound_request_post() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_POST_COMPONENT, &server).await +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn http_outbound_request_large_post() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_LARGE_POST_COMPONENT, &server).await +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn http_outbound_request_put() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_PUT_COMPONENT, &server).await +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn http_outbound_request_invalid_version() -> Result<()> { + let server = Server::http2()?; + run(HTTP_OUTBOUND_REQUEST_INVALID_VERSION_COMPONENT, &server).await +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn http_outbound_request_unknown_method() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_UNKNOWN_METHOD_COMPONENT, &server).await +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn http_outbound_request_unsupported_scheme() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_UNSUPPORTED_SCHEME_COMPONENT, &server).await +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn http_outbound_request_invalid_port() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_INVALID_PORT_COMPONENT, &server).await +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn http_outbound_request_invalid_dnsname() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_INVALID_DNSNAME_COMPONENT, &server).await +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn http_outbound_request_response_build() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_RESPONSE_BUILD_COMPONENT, &server).await +} diff --git a/crates/test-programs/src/http_server.rs b/crates/wasi-http/tests/all/http_server.rs similarity index 100% rename from crates/test-programs/src/http_server.rs rename to crates/wasi-http/tests/all/http_server.rs diff --git a/crates/test-programs/tests/wasi-http-proxy.rs b/crates/wasi-http/tests/all/main.rs similarity index 53% rename from crates/test-programs/tests/wasi-http-proxy.rs rename to crates/wasi-http/tests/all/main.rs index 65c07872de52..9df4e557c4c0 100644 --- a/crates/test-programs/tests/wasi-http-proxy.rs +++ b/crates/wasi-http/tests/all/main.rs @@ -1,34 +1,20 @@ -#![cfg(all(feature = "test_programs", not(skip_wasi_http_tests)))] -use anyhow::Context; +use crate::http_server::Server; +use anyhow::{Context, Result}; use wasmtime::{ component::{Component, Linker}, Config, Engine, Store, }; -use wasmtime_wasi::preview2::{ - self, pipe::MemoryOutputPipe, Table, WasiCtx, WasiCtxBuilder, WasiView, -}; -use wasmtime_wasi_http::{proxy::Proxy, WasiHttpCtx, WasiHttpView}; - -lazy_static::lazy_static! { - static ref ENGINE: Engine = { - let mut config = Config::new(); - config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); - config.wasm_component_model(true); - config.async_support(true); - let engine = Engine::new(&config).unwrap(); - engine - }; -} -// uses ENGINE, creates a fn get_module(&str) -> Module -include!(concat!( - env!("OUT_DIR"), - "/wasi_http_proxy_tests_components.rs" -)); +use wasmtime_wasi::preview2::{pipe::MemoryOutputPipe, Table, WasiCtx, WasiCtxBuilder, WasiView}; +use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; + +mod http_server; struct Ctx { table: Table, wasi: WasiCtx, http: WasiHttpCtx, + stdout: MemoryOutputPipe, + stderr: MemoryOutputPipe, } impl WasiView for Ctx { @@ -47,45 +33,93 @@ impl WasiView for Ctx { } impl WasiHttpView for Ctx { - fn table(&mut self) -> &mut Table { - &mut self.table - } fn ctx(&mut self) -> &mut WasiHttpCtx { &mut self.http } + + fn table(&mut self) -> &mut Table { + &mut self.table + } } -async fn instantiate(component: Component, ctx: Ctx) -> Result<(Store, Proxy), anyhow::Error> { - let mut linker = Linker::new(&ENGINE); - wasmtime_wasi_http::proxy::add_to_linker(&mut linker)?; +fn store(engine: &Engine, server: &Server) -> Store { + let stdout = MemoryOutputPipe::new(4096); + let stderr = MemoryOutputPipe::new(4096); + + // Create our wasi context. + let mut builder = WasiCtxBuilder::new(); + builder.stdout(stdout.clone()); + builder.stderr(stderr.clone()); + builder.env("HTTP_SERVER", server.addr().to_string()); + let ctx = Ctx { + table: Table::new(), + wasi: builder.build(), + http: WasiHttpCtx {}, + stderr, + stdout, + }; + + Store::new(&engine, ctx) +} - let mut store = Store::new(&ENGINE, ctx); +impl Drop for Ctx { + fn drop(&mut self) { + let stdout = self.stdout.contents(); + if !stdout.is_empty() { + println!("[guest] stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); + } + let stderr = self.stderr.contents(); + if !stderr.is_empty() { + println!("[guest] stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); + } + } +} - let (proxy, _instance) = Proxy::instantiate_async(&mut store, &component, &linker).await?; - Ok((store, proxy)) +// Assert that each of `sync` and `async` below are testing everything through +// assertion of the existence of the test function itself. +macro_rules! assert_test_exists { + ($name:ident) => { + #[allow(unused_imports)] + use self::$name as _; + }; } +mod async_; +mod sync; + #[test_log::test(tokio::test)] async fn wasi_http_proxy_tests() -> anyhow::Result<()> { let stdout = MemoryOutputPipe::new(4096); let stderr = MemoryOutputPipe::new(4096); - let table = Table::new(); - let component = get_component("wasi_http_proxy_tests"); + + let mut config = Config::new(); + config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); + config.wasm_component_model(true); + config.async_support(true); + let engine = Engine::new(&config)?; + let component = Component::from_file(&engine, test_programs_artifacts::API_PROXY_COMPONENT)?; // Create our wasi context. let mut builder = WasiCtxBuilder::new(); builder.stdout(stdout.clone()); builder.stderr(stderr.clone()); - for (var, val) in test_programs::wasi_tests_environment() { - builder.env(var, val); - } let wasi = builder.build(); let http = WasiHttpCtx; + let ctx = Ctx { + table, + wasi, + http, + stderr, + stdout, + }; + let mut store = Store::new(&engine, ctx); - let ctx = Ctx { table, wasi, http }; - - let (mut store, proxy) = instantiate(component, ctx).await?; + let mut linker = Linker::new(&engine); + wasmtime_wasi_http::proxy::add_to_linker(&mut linker)?; + let (proxy, _) = + wasmtime_wasi_http::proxy::Proxy::instantiate_async(&mut store, &component, &linker) + .await?; let req = { use http_body_util::{BodyExt, Empty}; @@ -101,7 +135,7 @@ async fn wasi_http_proxy_tests() -> anyhow::Result<()> { let (sender, receiver) = tokio::sync::oneshot::channel(); let out = store.data_mut().new_response_outparam(sender)?; - let handle = preview2::spawn(async move { + let handle = wasmtime_wasi::preview2::spawn(async move { proxy .wasi_http_incoming_handler() .call_handle(&mut store, req, out) @@ -128,15 +162,6 @@ async fn wasi_http_proxy_tests() -> anyhow::Result<()> { // deadlocking. handle.await.context("Component execution")?; - let stdout = stdout.contents(); - if !stdout.is_empty() { - println!("[guest] stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); - } - let stderr = stderr.contents(); - if !stderr.is_empty() { - println!("[guest] stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); - } - match resp { Ok(resp) => println!("response: {resp:?}"), Err(e) => panic!("Error given in response: {e:?}"), diff --git a/crates/wasi-http/tests/all/sync.rs b/crates/wasi-http/tests/all/sync.rs new file mode 100644 index 000000000000..729b1e427a38 --- /dev/null +++ b/crates/wasi-http/tests/all/sync.rs @@ -0,0 +1,79 @@ +use super::*; +use test_programs_artifacts::*; +use wasmtime_wasi::preview2::command::sync::Command; + +foreach_http!(assert_test_exists); + +fn run(path: &str, server: &Server) -> Result<()> { + let mut config = Config::new(); + config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); + config.wasm_component_model(true); + let engine = Engine::new(&config)?; + let component = Component::from_file(&engine, path)?; + let mut store = store(&engine, server); + let mut linker = Linker::new(&engine); + wasmtime_wasi_http::proxy::sync::add_to_linker(&mut linker)?; + let (command, _instance) = Command::instantiate(&mut store, &component, &linker)?; + let result = command.wasi_cli_run().call_run(&mut store)?; + result.map_err(|()| anyhow::anyhow!("run returned an error")) +} + +#[test_log::test] +fn http_outbound_request_get() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_GET_COMPONENT, &server) +} + +#[test_log::test] +fn http_outbound_request_post() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_POST_COMPONENT, &server) +} + +#[test_log::test] +fn http_outbound_request_large_post() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_LARGE_POST_COMPONENT, &server) +} + +#[test_log::test] +fn http_outbound_request_put() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_PUT_COMPONENT, &server) +} + +#[test_log::test] +fn http_outbound_request_invalid_version() -> Result<()> { + let server = Server::http2()?; + run(HTTP_OUTBOUND_REQUEST_INVALID_VERSION_COMPONENT, &server) +} + +#[test_log::test] +fn http_outbound_request_unknown_method() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_UNKNOWN_METHOD_COMPONENT, &server) +} + +#[test_log::test] +fn http_outbound_request_unsupported_scheme() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_UNSUPPORTED_SCHEME_COMPONENT, &server) +} + +#[test_log::test] +fn http_outbound_request_invalid_port() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_INVALID_PORT_COMPONENT, &server) +} + +#[test_log::test] +fn http_outbound_request_invalid_dnsname() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_INVALID_DNSNAME_COMPONENT, &server) +} + +#[test_log::test] +fn http_outbound_request_response_build() -> Result<()> { + let server = Server::http1()?; + run(HTTP_OUTBOUND_REQUEST_RESPONSE_BUILD_COMPONENT, &server) +} diff --git a/crates/wasi-http/wit/test.wit b/crates/wasi-http/wit/test.wit index a0d1d07a6c64..cc09b2422471 100644 --- a/crates/wasi-http/wit/test.wit +++ b/crates/wasi-http/wit/test.wit @@ -1,12 +1,6 @@ // only used as part of `test-programs` world test-reactor { - - import wasi:cli/environment; - import wasi:io/poll; - import wasi:io/streams; - import wasi:filesystem/types; - import wasi:filesystem/preopens; - import wasi:cli/exit; + include wasi:cli/reactor; export add-strings: func(s: list) -> u32; export get-strings: func() -> list; @@ -20,27 +14,7 @@ world test-reactor { } world test-command { - import wasi:io/poll; - import wasi:io/streams; - import wasi:cli/environment; - import wasi:cli/stdin; - import wasi:cli/stdout; - import wasi:cli/stderr; -} - -world test-command-with-sockets { - import wasi:io/poll; - import wasi:io/streams; - import wasi:cli/environment; - import wasi:cli/stdin; - import wasi:cli/stdout; - import wasi:cli/stderr; - import wasi:sockets/tcp; - import wasi:sockets/tcp-create-socket; - import wasi:sockets/udp; - import wasi:sockets/udp-create-socket; - import wasi:sockets/network; - import wasi:sockets/instance-network; - import wasi:sockets/ip-name-lookup; - import wasi:clocks/monotonic-clock; + include wasi:cli/reactor; + import wasi:http/types; + import wasi:http/outgoing-handler; } diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 1582ddfc18b1..9657139a2490 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -44,6 +44,9 @@ futures = { workspace = true, optional = true } tokio = { workspace = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net", "macros"] } test-log = { workspace = true } tracing-subscriber = { workspace = true } +test-programs-artifacts = { workspace = true } +tempfile = { workspace = true } +wasmtime = { workspace = true, features = ['cranelift'] } [target.'cfg(unix)'.dependencies] rustix = { workspace = true, features = ["event", "fs", "net"], optional = true } diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index a64efa7708a4..2d37ad035b56 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -241,9 +241,49 @@ pub fn in_tokio(f: F) -> F::Output { let _enter = h.enter(); h.block_on(f) } + // The `yield_now` here is non-obvious and if you're reading this + // you're likely curious about why it's here. This is currently required + // to get some features of "sync mode" working correctly, such as with + // the CLI. To illustrate why this is required, consider a program + // organized as: + // + // * A program has a `pollable` that it's waiting on. + // * This `pollable` is always ready . + // * Actually making the corresponding operation ready, however, + // requires some background work on Tokio's part. + // * The program is looping on "wait for readiness" coupled with + // performing the operation. + // + // In this situation this program ends up infinitely looping in waiting + // for pollables. The reason appears to be that when we enter the tokio + // runtime here it doesn't necessary yield to background work because + // the provided future `f` is ready immediately. The future `f` will run + // through the list of pollables and determine one of them is ready. + // + // Historically this happened with UDP sockets. A test send a datagram + // from one socket to another and the other socket infinitely didn't + // receive the data. This appeared to be because the server socket was + // waiting on `READABLE | WRITABLE` (which is itself a bug but ignore + // that) and the socket was currently in the "writable" state but never + // ended up receiving a notification for the "readable" state. Moving + // the socket to "readable" would require Tokio to perform some + // background work via epoll/kqueue/handle events but if the future + // provided here is always ready, then that never happened. + // + // Thus the `yield_now()` is an attempt to force Tokio to go do some + // background work eventually and look at new interest masks for + // example. This is a bit of a kludge but everything's already a bit + // wonky in synchronous mode anyway. Note that this is hypothesized to + // not be an issue in async mode because async mode typically has the + // Tokio runtime in a separate thread or otherwise participating in a + // larger application, it's only here in synchronous mode where we + // effectively own the runtime that we need some special care. Err(_) => { let _enter = RUNTIME.enter(); - RUNTIME.block_on(f) + RUNTIME.block_on(async move { + tokio::task::yield_now().await; + f.await + }) } } } diff --git a/crates/wasi/tests/all/api.rs b/crates/wasi/tests/all/api.rs new file mode 100644 index 000000000000..c1b0deaacaf0 --- /dev/null +++ b/crates/wasi/tests/all/api.rs @@ -0,0 +1,213 @@ +use anyhow::Result; +use cap_std::ambient_authority; +use cap_std::fs::Dir; +use std::io::Write; +use std::sync::Mutex; +use std::time::Duration; +use wasmtime::component::{Component, Linker}; +use wasmtime::{Config, Engine, Store}; +use wasmtime_wasi::preview2::bindings::wasi::clocks::wall_clock; +use wasmtime_wasi::preview2::bindings::wasi::filesystem::types as filesystem; +use wasmtime_wasi::preview2::command::{add_to_linker, Command}; +use wasmtime_wasi::preview2::{ + self, DirPerms, FilePerms, HostMonotonicClock, HostWallClock, Table, WasiCtx, WasiCtxBuilder, + WasiView, +}; + +struct CommandCtx { + table: Table, + wasi: WasiCtx, +} + +impl WasiView for CommandCtx { + fn table(&self) -> &Table { + &self.table + } + fn table_mut(&mut self) -> &mut Table { + &mut self.table + } + fn ctx(&self) -> &WasiCtx { + &self.wasi + } + fn ctx_mut(&mut self) -> &mut WasiCtx { + &mut self.wasi + } +} + +use test_programs_artifacts::*; + +foreach_api!(assert_test_exists); + +async fn instantiate(path: &str, ctx: CommandCtx) -> Result<(Store, Command)> { + let mut config = Config::new(); + config.async_support(true).wasm_component_model(true); + let engine = Engine::new(&config)?; + let mut linker = Linker::new(&engine); + add_to_linker(&mut linker)?; + + let mut store = Store::new(&engine, ctx); + let component = Component::from_file(&engine, path)?; + let (command, _instance) = Command::instantiate_async(&mut store, &component, &linker).await?; + Ok((store, command)) +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn api_time() -> Result<()> { + struct FakeWallClock; + + impl HostWallClock for FakeWallClock { + fn resolution(&self) -> Duration { + Duration::from_secs(1) + } + + fn now(&self) -> Duration { + Duration::new(1431648000, 100) + } + } + + struct FakeMonotonicClock { + now: Mutex, + } + + impl HostMonotonicClock for FakeMonotonicClock { + fn resolution(&self) -> u64 { + 1_000_000_000 + } + + fn now(&self) -> u64 { + let mut now = self.now.lock().unwrap(); + let then = *now; + *now += 42 * 1_000_000_000; + then + } + } + + let table = Table::new(); + let wasi = WasiCtxBuilder::new() + .monotonic_clock(FakeMonotonicClock { now: Mutex::new(0) }) + .wall_clock(FakeWallClock) + .build(); + + let (mut store, command) = instantiate(API_TIME_COMPONENT, CommandCtx { table, wasi }).await?; + + command + .wasi_cli_run() + .call_run(&mut store) + .await? + .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn api_read_only() -> Result<()> { + let dir = tempfile::tempdir()?; + + std::fs::File::create(dir.path().join("bar.txt"))?.write_all(b"And stood awhile in thought")?; + std::fs::create_dir(dir.path().join("sub"))?; + + let table = Table::new(); + let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?; + let wasi = WasiCtxBuilder::new() + .preopened_dir(open_dir, DirPerms::READ, FilePerms::READ, "/") + .build(); + + let (mut store, command) = + instantiate(API_READ_ONLY_COMPONENT, CommandCtx { table, wasi }).await?; + + command + .wasi_cli_run() + .call_run(&mut store) + .await? + .map_err(|()| anyhow::anyhow!("command returned with failing exit status")) +} + +// This is tested in the wasi-http crate, but need to satisfy the `foreach_api!` +// macro above. +#[allow(dead_code)] +fn api_proxy() {} + +wasmtime::component::bindgen!({ + world: "test-reactor", + async: true, + with: { + "wasi:io/streams": preview2::bindings::io::streams, + "wasi:filesystem/types": preview2::bindings::filesystem::types, + "wasi:filesystem/preopens": preview2::bindings::filesystem::preopens, + "wasi:cli/environment": preview2::bindings::cli::environment, + "wasi:cli/exit": preview2::bindings::cli::exit, + "wasi:cli/stdin": preview2::bindings::cli::stdin, + "wasi:cli/stdout": preview2::bindings::cli::stdout, + "wasi:cli/stderr": preview2::bindings::cli::stderr, + "wasi:cli/terminal_input": preview2::bindings::cli::terminal_input, + "wasi:cli/terminal_output": preview2::bindings::cli::terminal_output, + "wasi:cli/terminal_stdin": preview2::bindings::cli::terminal_stdin, + "wasi:cli/terminal_stdout": preview2::bindings::cli::terminal_stdout, + "wasi:cli/terminal_stderr": preview2::bindings::cli::terminal_stderr, + }, + ownership: Borrowing { + duplicate_if_necessary: false + } +}); + +#[test_log::test(tokio::test)] +async fn api_reactor() -> Result<()> { + let table = Table::new(); + let wasi = WasiCtxBuilder::new().env("GOOD_DOG", "gussie").build(); + + let mut config = Config::new(); + config.async_support(true).wasm_component_model(true); + let engine = Engine::new(&config)?; + let mut linker = Linker::new(&engine); + add_to_linker(&mut linker)?; + + let mut store = Store::new(&engine, CommandCtx { table, wasi }); + let component = Component::from_file(&engine, API_REACTOR_COMPONENT)?; + let (reactor, _instance) = + TestReactor::instantiate_async(&mut store, &component, &linker).await?; + + // Show that integration with the WASI context is working - the guest will + // interpolate $GOOD_DOG to gussie here using the environment: + let r = reactor + .call_add_strings(&mut store, &["hello", "$GOOD_DOG"]) + .await?; + assert_eq!(r, 2); + + let contents = reactor.call_get_strings(&mut store).await?; + assert_eq!(contents, &["hello", "gussie"]); + + // Show that we can pass in a resource type whose impls are defined in the + // `host` and `wasi-common` crate. + // Note, this works because of the add_to_linker invocations using the + // `host` crate for `streams`, not because of `with` in the bindgen macro. + let writepipe = preview2::pipe::MemoryOutputPipe::new(4096); + let stream: preview2::OutputStream = Box::new(writepipe.clone()); + let table_ix = store.data_mut().table_mut().push_resource(stream)?; + let r = reactor.call_write_strings_to(&mut store, table_ix).await?; + assert_eq!(r, Ok(())); + + assert_eq!(writepipe.contents().as_ref(), b"hellogussie"); + + // Show that the `with` invocation in the macro means we get to re-use the + // type definitions from inside the `host` crate for these structures: + let ds = filesystem::DescriptorStat { + data_access_timestamp: Some(wall_clock::Datetime { + nanoseconds: 123, + seconds: 45, + }), + data_modification_timestamp: Some(wall_clock::Datetime { + nanoseconds: 789, + seconds: 10, + }), + link_count: 0, + size: 0, + status_change_timestamp: Some(wall_clock::Datetime { + nanoseconds: 0, + seconds: 1, + }), + type_: filesystem::DescriptorType::Unknown, + }; + let expected = format!("{ds:?}"); + let got = reactor.call_pass_an_imported_record(&mut store, ds).await?; + assert_eq!(expected, got); + + Ok(()) +} diff --git a/crates/wasi/tests/all/async_.rs b/crates/wasi/tests/all/async_.rs new file mode 100644 index 000000000000..c6e60d1e677d --- /dev/null +++ b/crates/wasi/tests/all/async_.rs @@ -0,0 +1,330 @@ +use super::*; +use std::path::Path; +use test_programs_artifacts::*; +use wasmtime_wasi::preview2::command::{add_to_linker, Command}; + +async fn run(path: &str, inherit_stdio: bool) -> Result<()> { + let path = Path::new(path); + let name = path.file_stem().unwrap().to_str().unwrap(); + let mut config = Config::new(); + config.async_support(true).wasm_component_model(true); + let engine = Engine::new(&config)?; + let mut linker = Linker::new(&engine); + add_to_linker(&mut linker)?; + + let (mut store, _td) = store(&engine, name, inherit_stdio)?; + let component = Component::from_file(&engine, path)?; + let (command, _instance) = Command::instantiate_async(&mut store, &component, &linker).await?; + command + .wasi_cli_run() + .call_run(&mut store) + .await? + .map_err(|()| anyhow::anyhow!("run returned a failure")) +} + +foreach_preview1!(assert_test_exists); +foreach_preview2!(assert_test_exists); + +// Below here is mechanical: there should be one test for every binary in +// wasi-tests. The only differences should be should_panic annotations for +// tests which fail. +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_big_random_buf() { + run(PREVIEW1_BIG_RANDOM_BUF_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_clock_time_get() { + run(PREVIEW1_CLOCK_TIME_GET_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_close_preopen() { + run(PREVIEW1_CLOSE_PREOPEN_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_dangling_fd() { + run(PREVIEW1_DANGLING_FD_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_dangling_symlink() { + run(PREVIEW1_DANGLING_SYMLINK_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_directory_seek() { + run(PREVIEW1_DIRECTORY_SEEK_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_dir_fd_op_failures() { + run(PREVIEW1_DIR_FD_OP_FAILURES_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_fd_advise() { + run(PREVIEW1_FD_ADVISE_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_fd_filestat_get() { + run(PREVIEW1_FD_FILESTAT_GET_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_fd_filestat_set() { + run(PREVIEW1_FD_FILESTAT_SET_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_fd_flags_set() { + run(PREVIEW1_FD_FLAGS_SET_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_fd_readdir() { + run(PREVIEW1_FD_READDIR_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_file_allocate() { + run(PREVIEW1_FILE_ALLOCATE_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_file_pread_pwrite() { + run(PREVIEW1_FILE_PREAD_PWRITE_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_file_seek_tell() { + run(PREVIEW1_FILE_SEEK_TELL_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_file_truncation() { + run(PREVIEW1_FILE_TRUNCATION_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_file_unbuffered_write() { + run(PREVIEW1_FILE_UNBUFFERED_WRITE_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +#[cfg_attr(windows, should_panic)] +async fn preview1_interesting_paths() { + run(PREVIEW1_INTERESTING_PATHS_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_regular_file_isatty() { + run(PREVIEW1_REGULAR_FILE_ISATTY_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_nofollow_errors() { + run(PREVIEW1_NOFOLLOW_ERRORS_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_overwrite_preopen() { + run(PREVIEW1_OVERWRITE_PREOPEN_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_exists() { + run(PREVIEW1_PATH_EXISTS_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_filestat() { + run(PREVIEW1_PATH_FILESTAT_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_link() { + run(PREVIEW1_PATH_LINK_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_create_existing() { + run(PREVIEW1_PATH_OPEN_CREATE_EXISTING_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_read_write() { + run(PREVIEW1_PATH_OPEN_READ_WRITE_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_dirfd_not_dir() { + run(PREVIEW1_PATH_OPEN_DIRFD_NOT_DIR_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_missing() { + run(PREVIEW1_PATH_OPEN_MISSING_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_nonblock() { + run(PREVIEW1_PATH_OPEN_NONBLOCK_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_rename_dir_trailing_slashes() { + run(PREVIEW1_PATH_RENAME_DIR_TRAILING_SLASHES_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +#[should_panic] +async fn preview1_path_rename_file_trailing_slashes() { + run(PREVIEW1_PATH_RENAME_FILE_TRAILING_SLASHES_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_rename() { + run(PREVIEW1_PATH_RENAME_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_symlink_trailing_slashes() { + run(PREVIEW1_PATH_SYMLINK_TRAILING_SLASHES_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_poll_oneoff_files() { + run(PREVIEW1_POLL_ONEOFF_FILES_COMPONENT, false) + .await + .unwrap() +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_poll_oneoff_stdio() { + run(PREVIEW1_POLL_ONEOFF_STDIO_COMPONENT, true) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_readlink() { + run(PREVIEW1_READLINK_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +#[should_panic] +async fn preview1_remove_directory_trailing_slashes() { + run(PREVIEW1_REMOVE_DIRECTORY_TRAILING_SLASHES_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_remove_nonempty_directory() { + run(PREVIEW1_REMOVE_NONEMPTY_DIRECTORY_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_renumber() { + run(PREVIEW1_RENUMBER_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_sched_yield() { + run(PREVIEW1_SCHED_YIELD_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_stdio() { + run(PREVIEW1_STDIO_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_stdio_isatty() { + // If the test process is setup such that stdio is a terminal: + if test_programs_artifacts::stdio_is_terminal() { + // Inherit stdio, test asserts each is not tty: + run(PREVIEW1_STDIO_ISATTY_COMPONENT, true).await.unwrap() + } +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_stdio_not_isatty() { + // Don't inherit stdio, test asserts each is not tty: + run(PREVIEW1_STDIO_NOT_ISATTY_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_symlink_create() { + run(PREVIEW1_SYMLINK_CREATE_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_symlink_filestat() { + run(PREVIEW1_SYMLINK_FILESTAT_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_symlink_loop() { + run(PREVIEW1_SYMLINK_LOOP_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_unlink_file_trailing_slashes() { + run(PREVIEW1_UNLINK_FILE_TRAILING_SLASHES_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_preopen() { + run(PREVIEW1_PATH_OPEN_PREOPEN_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_unicode_output() { + run(PREVIEW1_UNICODE_OUTPUT_COMPONENT, true).await.unwrap() +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview2_sleep() { + run(PREVIEW2_SLEEP_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview2_random() { + run(PREVIEW2_RANDOM_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview2_ip_name_lookup() { + run(PREVIEW2_IP_NAME_LOOKUP_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview2_tcp_sockopts() { + run(PREVIEW2_TCP_SOCKOPTS_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview2_tcp_sample_application() { + run(PREVIEW2_TCP_SAMPLE_APPLICATION_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview2_tcp_states() { + run(PREVIEW2_TCP_STATES_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview2_tcp_bind() { + run(PREVIEW2_TCP_BIND_COMPONENT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview2_udp_sample_application() { + run(PREVIEW2_UDP_SAMPLE_APPLICATION_COMPONENT, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview2_tcp_connect() { + run(PREVIEW2_TCP_CONNECT_COMPONENT, false).await.unwrap() +} diff --git a/crates/wasi/tests/all/main.rs b/crates/wasi/tests/all/main.rs new file mode 100644 index 000000000000..1af5503f029d --- /dev/null +++ b/crates/wasi/tests/all/main.rs @@ -0,0 +1,113 @@ +use anyhow::Result; +use cap_std::ambient_authority; +use tempfile::TempDir; +use wasmtime::{ + component::{Component, Linker}, + Config, Engine, Store, +}; +use wasmtime_wasi::preview2::{ + pipe::MemoryOutputPipe, + preview1::{WasiPreview1Adapter, WasiPreview1View}, + DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView, +}; + +struct Ctx { + table: Table, + wasi: WasiCtx, + stdout: MemoryOutputPipe, + stderr: MemoryOutputPipe, + adapter: WasiPreview1Adapter, +} + +impl WasiView for Ctx { + fn table(&self) -> &Table { + &self.table + } + fn table_mut(&mut self) -> &mut Table { + &mut self.table + } + fn ctx(&self) -> &WasiCtx { + &self.wasi + } + fn ctx_mut(&mut self) -> &mut WasiCtx { + &mut self.wasi + } +} + +impl WasiPreview1View for Ctx { + fn adapter(&self) -> &WasiPreview1Adapter { + &self.adapter + } + fn adapter_mut(&mut self) -> &mut WasiPreview1Adapter { + &mut self.adapter + } +} + +fn prepare_workspace(exe_name: &str) -> Result { + let prefix = format!("wasi_components_{}_", exe_name); + let tempdir = tempfile::Builder::new().prefix(&prefix).tempdir()?; + Ok(tempdir) +} + +fn store(engine: &Engine, name: &str, inherit_stdio: bool) -> Result<(Store, TempDir)> { + let stdout = MemoryOutputPipe::new(4096); + let stderr = MemoryOutputPipe::new(4096); + let workspace = prepare_workspace(name)?; + + // Create our wasi context. + let mut builder = WasiCtxBuilder::new(); + if inherit_stdio { + builder.inherit_stdio(); + } else { + builder.stdout(stdout.clone()).stderr(stderr.clone()); + } + + builder + .args(&[name, "."]) + .inherit_network(ambient_authority()) + .allow_ip_name_lookup(true); + println!("preopen: {:?}", workspace); + let preopen_dir = + cap_std::fs::Dir::open_ambient_dir(workspace.path(), cap_std::ambient_authority())?; + builder.preopened_dir(preopen_dir, DirPerms::all(), FilePerms::all(), "."); + for (var, val) in test_programs_artifacts::wasi_tests_environment() { + builder.env(var, val); + } + + let ctx = Ctx { + table: Table::new(), + wasi: builder.build(), + stderr, + stdout, + adapter: WasiPreview1Adapter::new(), + }; + + Ok((Store::new(&engine, ctx), workspace)) +} + +impl Drop for Ctx { + fn drop(&mut self) { + let stdout = self.stdout.contents(); + if !stdout.is_empty() { + println!("[guest] stdout:\n{}\n===", String::from_utf8_lossy(&stdout)); + } + let stderr = self.stderr.contents(); + if !stderr.is_empty() { + println!("[guest] stderr:\n{}\n===", String::from_utf8_lossy(&stderr)); + } + } +} + +// Assert that each of `sync` and `async` below are testing everything through +// assertion of the existence of the test function itself. +macro_rules! assert_test_exists { + ($name:ident) => { + #[allow(unused_imports)] + use self::$name as _; + }; +} + +mod api; +mod async_; +mod preview1; +mod sync; diff --git a/crates/wasi/tests/all/preview1.rs b/crates/wasi/tests/all/preview1.rs new file mode 100644 index 000000000000..692b4fa55e68 --- /dev/null +++ b/crates/wasi/tests/all/preview1.rs @@ -0,0 +1,247 @@ +use super::*; +use std::path::Path; +use test_programs_artifacts::*; +use wasmtime::{Linker, Module}; +use wasmtime_wasi::preview2::preview1::add_to_linker_async; + +async fn run(path: &str, inherit_stdio: bool) -> Result<()> { + let path = Path::new(path); + let name = path.file_stem().unwrap().to_str().unwrap(); + let mut config = Config::new(); + config.async_support(true).wasm_component_model(true); + let engine = Engine::new(&config)?; + let mut linker = Linker::new(&engine); + add_to_linker_async(&mut linker)?; + + let module = Module::from_file(&engine, path)?; + let (mut store, _td) = store(&engine, name, inherit_stdio)?; + let instance = linker.instantiate_async(&mut store, &module).await?; + let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?; + start.call_async(&mut store, ()).await?; + Ok(()) +} + +foreach_preview1!(assert_test_exists); + +// Below here is mechanical: there should be one test for every binary in +// wasi-tests. The only differences should be should_panic annotations for +// tests which fail. +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_big_random_buf() { + run(PREVIEW1_BIG_RANDOM_BUF, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_clock_time_get() { + run(PREVIEW1_CLOCK_TIME_GET, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_close_preopen() { + run(PREVIEW1_CLOSE_PREOPEN, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_dangling_fd() { + run(PREVIEW1_DANGLING_FD, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_dangling_symlink() { + run(PREVIEW1_DANGLING_SYMLINK, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_directory_seek() { + run(PREVIEW1_DIRECTORY_SEEK, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_dir_fd_op_failures() { + run(PREVIEW1_DIR_FD_OP_FAILURES, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_fd_advise() { + run(PREVIEW1_FD_ADVISE, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_fd_filestat_get() { + run(PREVIEW1_FD_FILESTAT_GET, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_fd_filestat_set() { + run(PREVIEW1_FD_FILESTAT_SET, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_fd_flags_set() { + run(PREVIEW1_FD_FLAGS_SET, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_fd_readdir() { + run(PREVIEW1_FD_READDIR, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_file_allocate() { + run(PREVIEW1_FILE_ALLOCATE, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_file_pread_pwrite() { + run(PREVIEW1_FILE_PREAD_PWRITE, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_file_seek_tell() { + run(PREVIEW1_FILE_SEEK_TELL, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_file_truncation() { + run(PREVIEW1_FILE_TRUNCATION, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_file_unbuffered_write() { + run(PREVIEW1_FILE_UNBUFFERED_WRITE, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +#[cfg_attr(windows, should_panic)] +async fn preview1_interesting_paths() { + run(PREVIEW1_INTERESTING_PATHS, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_regular_file_isatty() { + run(PREVIEW1_REGULAR_FILE_ISATTY, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_nofollow_errors() { + run(PREVIEW1_NOFOLLOW_ERRORS, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_overwrite_preopen() { + run(PREVIEW1_OVERWRITE_PREOPEN, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_exists() { + run(PREVIEW1_PATH_EXISTS, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_filestat() { + run(PREVIEW1_PATH_FILESTAT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_link() { + run(PREVIEW1_PATH_LINK, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_create_existing() { + run(PREVIEW1_PATH_OPEN_CREATE_EXISTING, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_read_write() { + run(PREVIEW1_PATH_OPEN_READ_WRITE, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_dirfd_not_dir() { + run(PREVIEW1_PATH_OPEN_DIRFD_NOT_DIR, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_missing() { + run(PREVIEW1_PATH_OPEN_MISSING, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_nonblock() { + run(PREVIEW1_PATH_OPEN_NONBLOCK, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_rename_dir_trailing_slashes() { + run(PREVIEW1_PATH_RENAME_DIR_TRAILING_SLASHES, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +#[should_panic] +async fn preview1_path_rename_file_trailing_slashes() { + run(PREVIEW1_PATH_RENAME_FILE_TRAILING_SLASHES, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_rename() { + run(PREVIEW1_PATH_RENAME, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_symlink_trailing_slashes() { + run(PREVIEW1_PATH_SYMLINK_TRAILING_SLASHES, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_poll_oneoff_files() { + run(PREVIEW1_POLL_ONEOFF_FILES, false).await.unwrap() +} + +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_poll_oneoff_stdio() { + run(PREVIEW1_POLL_ONEOFF_STDIO, true).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_readlink() { + run(PREVIEW1_READLINK, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +#[should_panic] +async fn preview1_remove_directory_trailing_slashes() { + run(PREVIEW1_REMOVE_DIRECTORY_TRAILING_SLASHES, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_remove_nonempty_directory() { + run(PREVIEW1_REMOVE_NONEMPTY_DIRECTORY, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_renumber() { + run(PREVIEW1_RENUMBER, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_sched_yield() { + run(PREVIEW1_SCHED_YIELD, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_stdio() { + run(PREVIEW1_STDIO, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_stdio_isatty() { + // If the test process is setup such that stdio is a terminal: + if test_programs_artifacts::stdio_is_terminal() { + // Inherit stdio, test asserts each is not tty: + run(PREVIEW1_STDIO_ISATTY, true).await.unwrap() + } +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_stdio_not_isatty() { + // Don't inherit stdio, test asserts each is not tty: + run(PREVIEW1_STDIO_NOT_ISATTY, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_symlink_create() { + run(PREVIEW1_SYMLINK_CREATE, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_symlink_filestat() { + run(PREVIEW1_SYMLINK_FILESTAT, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_symlink_loop() { + run(PREVIEW1_SYMLINK_LOOP, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_unlink_file_trailing_slashes() { + run(PREVIEW1_UNLINK_FILE_TRAILING_SLASHES, false) + .await + .unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_path_open_preopen() { + run(PREVIEW1_PATH_OPEN_PREOPEN, false).await.unwrap() +} +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview1_unicode_output() { + run(PREVIEW1_UNICODE_OUTPUT, true).await.unwrap() +} diff --git a/crates/wasi/tests/all/sync.rs b/crates/wasi/tests/all/sync.rs new file mode 100644 index 000000000000..c7e9da1d72e2 --- /dev/null +++ b/crates/wasi/tests/all/sync.rs @@ -0,0 +1,271 @@ +use super::*; +use std::path::Path; +use test_programs_artifacts::*; +use wasmtime_wasi::preview2::command::sync::{add_to_linker, Command}; + +fn run(path: &str, inherit_stdio: bool) -> Result<()> { + let path = Path::new(path); + let name = path.file_stem().unwrap().to_str().unwrap(); + let mut config = Config::new(); + config.wasm_component_model(true); + let engine = Engine::new(&config)?; + let mut linker = Linker::new(&engine); + add_to_linker(&mut linker)?; + + let (mut store, _td) = store(&engine, name, inherit_stdio)?; + let component = Component::from_file(&engine, path)?; + let (command, _instance) = Command::instantiate(&mut store, &component, &linker)?; + command + .wasi_cli_run() + .call_run(&mut store)? + .map_err(|()| anyhow::anyhow!("run returned a failure")) +} + +foreach_preview1!(assert_test_exists); +foreach_preview2!(assert_test_exists); + +// Below here is mechanical: there should be one test for every binary in +// wasi-tests. The only differences should be should_panic annotations for +// tests which fail. +#[test_log::test] +fn preview1_big_random_buf() { + run(PREVIEW1_BIG_RANDOM_BUF_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_clock_time_get() { + run(PREVIEW1_CLOCK_TIME_GET_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_close_preopen() { + run(PREVIEW1_CLOSE_PREOPEN_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_dangling_fd() { + run(PREVIEW1_DANGLING_FD_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_dangling_symlink() { + run(PREVIEW1_DANGLING_SYMLINK_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_directory_seek() { + run(PREVIEW1_DIRECTORY_SEEK_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_dir_fd_op_failures() { + run(PREVIEW1_DIR_FD_OP_FAILURES_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_fd_advise() { + run(PREVIEW1_FD_ADVISE_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_fd_filestat_get() { + run(PREVIEW1_FD_FILESTAT_GET_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_fd_filestat_set() { + run(PREVIEW1_FD_FILESTAT_SET_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_fd_flags_set() { + run(PREVIEW1_FD_FLAGS_SET_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_fd_readdir() { + run(PREVIEW1_FD_READDIR_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_file_allocate() { + run(PREVIEW1_FILE_ALLOCATE_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_file_pread_pwrite() { + run(PREVIEW1_FILE_PREAD_PWRITE_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_file_seek_tell() { + run(PREVIEW1_FILE_SEEK_TELL_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_file_truncation() { + run(PREVIEW1_FILE_TRUNCATION_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_file_unbuffered_write() { + run(PREVIEW1_FILE_UNBUFFERED_WRITE_COMPONENT, false).unwrap() +} +#[test_log::test] +#[cfg_attr(windows, should_panic)] +fn preview1_interesting_paths() { + run(PREVIEW1_INTERESTING_PATHS_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_regular_file_isatty() { + run(PREVIEW1_REGULAR_FILE_ISATTY_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_nofollow_errors() { + run(PREVIEW1_NOFOLLOW_ERRORS_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_overwrite_preopen() { + run(PREVIEW1_OVERWRITE_PREOPEN_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_exists() { + run(PREVIEW1_PATH_EXISTS_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_filestat() { + run(PREVIEW1_PATH_FILESTAT_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_link() { + run(PREVIEW1_PATH_LINK_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_open_create_existing() { + run(PREVIEW1_PATH_OPEN_CREATE_EXISTING_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_open_read_write() { + run(PREVIEW1_PATH_OPEN_READ_WRITE_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_open_dirfd_not_dir() { + run(PREVIEW1_PATH_OPEN_DIRFD_NOT_DIR_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_open_missing() { + run(PREVIEW1_PATH_OPEN_MISSING_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_open_nonblock() { + run(PREVIEW1_PATH_OPEN_NONBLOCK_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_rename_dir_trailing_slashes() { + run(PREVIEW1_PATH_RENAME_DIR_TRAILING_SLASHES_COMPONENT, false).unwrap() +} +#[test_log::test] +#[should_panic] +fn preview1_path_rename_file_trailing_slashes() { + run(PREVIEW1_PATH_RENAME_FILE_TRAILING_SLASHES_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_rename() { + run(PREVIEW1_PATH_RENAME_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_symlink_trailing_slashes() { + run(PREVIEW1_PATH_SYMLINK_TRAILING_SLASHES_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_poll_oneoff_files() { + run(PREVIEW1_POLL_ONEOFF_FILES_COMPONENT, false).unwrap() +} + +#[test_log::test] +fn preview1_poll_oneoff_stdio() { + run(PREVIEW1_POLL_ONEOFF_STDIO_COMPONENT, true).unwrap() +} +#[test_log::test] +fn preview1_readlink() { + run(PREVIEW1_READLINK_COMPONENT, false).unwrap() +} +#[test_log::test] +#[should_panic] +fn preview1_remove_directory_trailing_slashes() { + run(PREVIEW1_REMOVE_DIRECTORY_TRAILING_SLASHES_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_remove_nonempty_directory() { + run(PREVIEW1_REMOVE_NONEMPTY_DIRECTORY_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_renumber() { + run(PREVIEW1_RENUMBER_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_sched_yield() { + run(PREVIEW1_SCHED_YIELD_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_stdio() { + run(PREVIEW1_STDIO_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_stdio_isatty() { + // If the test process is setup such that stdio is a terminal: + if test_programs_artifacts::stdio_is_terminal() { + // Inherit stdio, test asserts each is not tty: + run(PREVIEW1_STDIO_ISATTY_COMPONENT, true).unwrap() + } +} +#[test_log::test] +fn preview1_stdio_not_isatty() { + // Don't inherit stdio, test asserts each is not tty: + run(PREVIEW1_STDIO_NOT_ISATTY_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_symlink_create() { + run(PREVIEW1_SYMLINK_CREATE_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_symlink_filestat() { + run(PREVIEW1_SYMLINK_FILESTAT_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_symlink_loop() { + run(PREVIEW1_SYMLINK_LOOP_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_unlink_file_trailing_slashes() { + run(PREVIEW1_UNLINK_FILE_TRAILING_SLASHES_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_path_open_preopen() { + run(PREVIEW1_PATH_OPEN_PREOPEN_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview1_unicode_output() { + run(PREVIEW1_UNICODE_OUTPUT_COMPONENT, true).unwrap() +} + +#[test_log::test] +fn preview2_sleep() { + run(PREVIEW2_SLEEP_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview2_random() { + run(PREVIEW2_RANDOM_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview2_ip_name_lookup() { + run(PREVIEW2_IP_NAME_LOOKUP_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview2_tcp_sockopts() { + run(PREVIEW2_TCP_SOCKOPTS_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview2_tcp_sample_application() { + run(PREVIEW2_TCP_SAMPLE_APPLICATION_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview2_tcp_states() { + run(PREVIEW2_TCP_STATES_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview2_tcp_bind() { + run(PREVIEW2_TCP_BIND_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview2_udp_sample_application() { + run(PREVIEW2_UDP_SAMPLE_APPLICATION_COMPONENT, false).unwrap() +} +#[test_log::test] +fn preview2_tcp_connect() { + run(PREVIEW2_TCP_CONNECT_COMPONENT, false).unwrap() +} diff --git a/crates/wasi/wit/test.wit b/crates/wasi/wit/test.wit index a0d1d07a6c64..cc09b2422471 100644 --- a/crates/wasi/wit/test.wit +++ b/crates/wasi/wit/test.wit @@ -1,12 +1,6 @@ // only used as part of `test-programs` world test-reactor { - - import wasi:cli/environment; - import wasi:io/poll; - import wasi:io/streams; - import wasi:filesystem/types; - import wasi:filesystem/preopens; - import wasi:cli/exit; + include wasi:cli/reactor; export add-strings: func(s: list) -> u32; export get-strings: func() -> list; @@ -20,27 +14,7 @@ world test-reactor { } world test-command { - import wasi:io/poll; - import wasi:io/streams; - import wasi:cli/environment; - import wasi:cli/stdin; - import wasi:cli/stdout; - import wasi:cli/stderr; -} - -world test-command-with-sockets { - import wasi:io/poll; - import wasi:io/streams; - import wasi:cli/environment; - import wasi:cli/stdin; - import wasi:cli/stdout; - import wasi:cli/stderr; - import wasi:sockets/tcp; - import wasi:sockets/tcp-create-socket; - import wasi:sockets/udp; - import wasi:sockets/udp-create-socket; - import wasi:sockets/network; - import wasi:sockets/instance-network; - import wasi:sockets/ip-name-lookup; - import wasi:clocks/monotonic-clock; + include wasi:cli/reactor; + import wasi:http/types; + import wasi:http/outgoing-handler; } diff --git a/src/commands/run.rs b/src/commands/run.rs index 4bf6da4c4974..77af3882791d 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -9,9 +9,10 @@ use crate::common::{Profile, RunCommon, RunTarget}; use anyhow::{anyhow, bail, Context as _, Error, Result}; use clap::Parser; +use std::ffi::OsString; use std::fs::File; use std::io::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use std::thread; use wasmtime::{ @@ -102,7 +103,7 @@ pub struct RunCommand { /// arguments unless the `--invoke` CLI argument is passed in which case /// arguments will be interpreted as arguments to the function specified. #[clap(value_name = "WASM", trailing_var_arg = true, required = true)] - module_and_args: Vec, + module_and_args: Vec, } enum CliLinker { @@ -135,7 +136,9 @@ impl RunCommand { let engine = Engine::new(&config)?; // Read the wasm module binary either as `*.wat` or a raw binary. - let main = self.run.load_module(&engine, &self.module_and_args[0])?; + let main = self + .run + .load_module(&engine, self.module_and_args[0].as_ref())?; // Validate coredump-on-trap argument if let Some(path) = &self.run.common.debug.coredump { @@ -212,7 +215,7 @@ impl RunCommand { .with_context(|| { format!( "failed to run main module `{}`", - self.module_and_args[0].display() + self.module_and_args[0].to_string_lossy() ) }) { Ok(()) => (), @@ -262,7 +265,7 @@ impl RunCommand { // For argv[0], which is the program name. Only include the base // name of the main wasm module, to avoid leaking path information. let arg = if i == 0 { - arg.components().next_back().unwrap().as_os_str() + Path::new(arg).components().next_back().unwrap().as_os_str() } else { arg.as_ref() }; diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs index f1b39c078b77..93e19b64c488 100644 --- a/tests/all/cli_tests.rs +++ b/tests/all/cli_tests.rs @@ -790,28 +790,6 @@ fn run_basic_component() -> Result<()> { Ok(()) } -#[test] -// #[cfg_attr(not(feature = "wasi-http"), ignore)] -#[ignore = "re-enable when we can reference tests from test-programs"] -fn run_wasi_http_component() -> Result<()> { - let output = run_wasmtime_for_output( - &[ - "-Ccache=no", - "-Wcomponent-model", - "-Scommon,http,preview2", - "tests/all/cli_tests/wasi-http-component.wat", - ], - None, - )?; - println!("{}", String::from_utf8_lossy(&output.stderr)); - let stdout = String::from_utf8_lossy(&output.stdout); - println!("{}", stdout); - assert!(stdout.starts_with("Called _start\n")); - assert!(stdout.ends_with("Done\n")); - assert!(output.status.success()); - Ok(()) -} - #[test] #[cfg_attr(not(feature = "component-model"), ignore)] fn run_precompiled_component() -> Result<()> { @@ -1019,3 +997,273 @@ fn preview2_stdin() -> Result<()> { } Ok(()) } + +mod test_programs { + use super::{get_wasmtime_command, run_wasmtime}; + use anyhow::Result; + use std::io::Write; + use std::process::Stdio; + use test_programs_artifacts::*; + + macro_rules! assert_test_exists { + ($name:ident) => { + #[allow(unused_imports)] + use self::$name as _; + }; + } + foreach_cli!(assert_test_exists); + + #[test] + fn cli_hello_stdout() -> Result<()> { + run_wasmtime(&[ + "run", + "-Wcomponent-model", + CLI_HELLO_STDOUT_COMPONENT, + "gussie", + "sparky", + "willa", + ])?; + Ok(()) + } + + #[test] + fn cli_panic() -> Result<()> { + let output = get_wasmtime_command()? + .args(&[ + "run", + "-Wcomponent-model", + CLI_PANIC_COMPONENT, + "diesel", + "the", + "cat", + "scratched", + "me", + "real", + "good", + "yesterday", + ]) + .output()?; + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!(stderr.contains("idk!!!")); + Ok(()) + } + + #[test] + fn cli_args() -> Result<()> { + run_wasmtime(&[ + "run", + "-Wcomponent-model", + CLI_ARGS_COMPONENT, + "hello", + "this", + "", + "is an argument", + "with 🚩 emoji", + ])?; + Ok(()) + } + + #[test] + fn cli_stdin() -> Result<()> { + let mut child = get_wasmtime_command()? + .args(&["run", "-Wcomponent-model", CLI_STDIN_COMPONENT]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .stdin(Stdio::piped()) + .spawn()?; + child + .stdin + .take() + .unwrap() + .write_all(b"So rested he by the Tumtum tree") + .unwrap(); + let output = child.wait_with_output()?; + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + assert!(output.status.success()); + Ok(()) + } + + #[test] + fn cli_env() -> Result<()> { + run_wasmtime(&[ + "run", + "-Wcomponent-model", + "--env=frabjous=day", + "--env=callooh=callay", + CLI_ENV_COMPONENT, + ])?; + Ok(()) + } + + #[test] + fn cli_file_read() -> Result<()> { + let dir = tempfile::tempdir()?; + + std::fs::write(dir.path().join("bar.txt"), b"And stood awhile in thought")?; + + run_wasmtime(&[ + "run", + "-Wcomponent-model", + &format!("--dir=/::{}", dir.path().to_str().unwrap()), + CLI_FILE_READ_COMPONENT, + ])?; + Ok(()) + } + + #[test] + fn cli_file_append() -> Result<()> { + let dir = tempfile::tempdir()?; + + std::fs::File::create(dir.path().join("bar.txt"))? + .write_all(b"'Twas brillig, and the slithy toves.\n")?; + + run_wasmtime(&[ + "run", + "-Wcomponent-model", + &format!("--dir=/::{}", dir.path().to_str().unwrap()), + CLI_FILE_APPEND_COMPONENT, + ])?; + + let contents = std::fs::read(dir.path().join("bar.txt"))?; + assert_eq!( + std::str::from_utf8(&contents).unwrap(), + "'Twas brillig, and the slithy toves.\n\ + Did gyre and gimble in the wabe;\n\ + All mimsy were the borogoves,\n\ + And the mome raths outgrabe.\n" + ); + Ok(()) + } + + #[test] + fn cli_file_dir_sync() -> Result<()> { + let dir = tempfile::tempdir()?; + + std::fs::File::create(dir.path().join("bar.txt"))? + .write_all(b"'Twas brillig, and the slithy toves.\n")?; + + run_wasmtime(&[ + "run", + "-Wcomponent-model", + &format!("--dir=/::{}", dir.path().to_str().unwrap()), + CLI_FILE_DIR_SYNC_COMPONENT, + ])?; + + Ok(()) + } + + #[test] + fn cli_exit_success() -> Result<()> { + run_wasmtime(&["run", "-Wcomponent-model", CLI_EXIT_SUCCESS_COMPONENT])?; + Ok(()) + } + + #[test] + fn cli_exit_default() -> Result<()> { + run_wasmtime(&["run", "-Wcomponent-model", CLI_EXIT_DEFAULT_COMPONENT])?; + Ok(()) + } + + #[test] + fn cli_exit_failure() -> Result<()> { + let output = get_wasmtime_command()? + .args(&["run", "-Wcomponent-model", CLI_EXIT_FAILURE_COMPONENT]) + .output()?; + assert!(!output.status.success()); + assert_eq!(output.status.code(), Some(1)); + Ok(()) + } + + #[test] + fn cli_exit_panic() -> Result<()> { + let output = get_wasmtime_command()? + .args(&["run", "-Wcomponent-model", CLI_EXIT_PANIC_COMPONENT]) + .output()?; + assert!(!output.status.success()); + Ok(()) + } + + #[test] + fn cli_directory_list() -> Result<()> { + let dir = tempfile::tempdir()?; + + std::fs::File::create(dir.path().join("foo.txt"))?; + std::fs::File::create(dir.path().join("bar.txt"))?; + std::fs::File::create(dir.path().join("baz.txt"))?; + std::fs::create_dir(dir.path().join("sub"))?; + std::fs::File::create(dir.path().join("sub").join("wow.txt"))?; + std::fs::File::create(dir.path().join("sub").join("yay.txt"))?; + + run_wasmtime(&[ + "run", + "-Wcomponent-model", + &format!("--dir=/::{}", dir.path().to_str().unwrap()), + CLI_DIRECTORY_LIST_COMPONENT, + ])?; + Ok(()) + } + + #[test] + fn cli_default_clocks() -> Result<()> { + run_wasmtime(&["run", "-Wcomponent-model", CLI_DEFAULT_CLOCKS_COMPONENT])?; + Ok(()) + } + + #[test] + fn cli_export_cabi_realloc() -> Result<()> { + run_wasmtime(&[ + "run", + "-Wcomponent-model", + CLI_EXPORT_CABI_REALLOC_COMPONENT, + ])?; + Ok(()) + } + + #[test] + fn cli_stream_pollable_lifetimes() -> Result<()> { + // Test program has two modes, dispatching based on argument. + run_wasmtime(&[ + "run", + "-Wcomponent-model", + CLI_STREAM_POLLABLE_LIFETIMES_COMPONENT, + "correct", + ])?; + let output = get_wasmtime_command()? + .args(&[ + "run", + "-Wcomponent-model", + CLI_STREAM_POLLABLE_LIFETIMES_COMPONENT, + "trap", + ]) + .output()?; + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("entry still has children"), + "bad stderr: {stderr}" + ); + Ok(()) + } + + #[test] + fn run_wasi_http_component() -> Result<()> { + let output = super::run_wasmtime_for_output( + &[ + "-Ccache=no", + "-Wcomponent-model", + "-Scommon,http,preview2", + HTTP_OUTBOUND_REQUEST_RESPONSE_BUILD_COMPONENT, + ], + None, + )?; + println!("{}", String::from_utf8_lossy(&output.stderr)); + let stdout = String::from_utf8_lossy(&output.stdout); + println!("{}", stdout); + assert!(stdout.starts_with("Called _start\n")); + assert!(stdout.ends_with("Done\n")); + assert!(output.status.success()); + Ok(()) + } +} From ccaf2bfa9fcb2fb0a1ca07be7122e4fb9e9672f6 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 10 Oct 2023 11:30:05 +0100 Subject: [PATCH 092/199] aarch64: Implement TLSDESC for TLS GD accesses (#7201) * aarch64: Implement TLSDESC for TLS GD accesses * aarch64: Box ExternalName to avoid growing the `Inst` enum --- cranelift/codegen/src/binemit/mod.rs | 28 +++--- cranelift/codegen/src/isa/aarch64/inst.isle | 8 +- .../codegen/src/isa/aarch64/inst/emit.rs | 87 +++++++++++++++---- cranelift/codegen/src/isa/aarch64/inst/mod.rs | 21 +++-- .../filetests/isa/aarch64/tls-elf-gd.clif | 42 +++------ cranelift/object/src/backend.rs | 37 ++++++-- 6 files changed, 149 insertions(+), 74 deletions(-) diff --git a/cranelift/codegen/src/binemit/mod.rs b/cranelift/codegen/src/binemit/mod.rs index d8d804bbdc28..9eed2905c4cd 100644 --- a/cranelift/codegen/src/binemit/mod.rs +++ b/cranelift/codegen/src/binemit/mod.rs @@ -64,15 +64,21 @@ pub enum Reloc { /// Offset within page of TLVP slot. MachOAarch64TlsAdrPageOff12, - /// Aarch64 TLS GD - /// Set an ADRP immediate field to the top 21 bits of the final address. Checks for overflow. - /// This is equivalent to `R_AARCH64_TLSGD_ADR_PAGE21` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#relocations-for-thread-local-storage) - Aarch64TlsGdAdrPage21, + /// Aarch64 TLSDESC Adr Page21 + /// This is equivalent to `R_AARCH64_TLSDESC_ADR_PAGE21` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors) + Aarch64TlsDescAdrPage21, - /// Aarch64 TLS GD - /// Set the add immediate field to the low 12 bits of the final address. Does not check for overflow. - /// This is equivalent to `R_AARCH64_TLSGD_ADD_LO12_NC` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#relocations-for-thread-local-storage) - Aarch64TlsGdAddLo12Nc, + /// Aarch64 TLSDESC Ld64 Lo12 + /// This is equivalent to `R_AARCH64_TLSDESC_LD64_LO12` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors) + Aarch64TlsDescLd64Lo12, + + /// Aarch64 TLSDESC Add Lo12 + /// This is equivalent to `R_AARCH64_TLSGD_ADD_LO12` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors) + Aarch64TlsDescAddLo12, + + /// Aarch64 TLSDESC Call + /// This is equivalent to `R_AARCH64_TLSDESC_CALL` in the [aaelf64](https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aaelf64/aaelf64.rst#57105thread-local-storage-descriptors) + Aarch64TlsDescCall, /// AArch64 GOT Page /// Set the immediate value of an ADRP to bits 32:12 of X; check that –232 <= X < 232 @@ -141,8 +147,10 @@ impl fmt::Display for Reloc { Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"), Self::MachOAarch64TlsAdrPage21 => write!(f, "MachOAarch64TlsAdrPage21"), Self::MachOAarch64TlsAdrPageOff12 => write!(f, "MachOAarch64TlsAdrPageOff12"), - Self::Aarch64TlsGdAdrPage21 => write!(f, "Aarch64TlsGdAdrPage21"), - Self::Aarch64TlsGdAddLo12Nc => write!(f, "Aarch64TlsGdAddLo12Nc"), + Self::Aarch64TlsDescAdrPage21 => write!(f, "Aarch64TlsDescAdrPage21"), + Self::Aarch64TlsDescLd64Lo12 => write!(f, "Aarch64TlsDescLd64Lo12"), + Self::Aarch64TlsDescAddLo12 => write!(f, "Aarch64TlsDescAddLo12"), + Self::Aarch64TlsDescCall => write!(f, "Aarch64TlsDescCall"), Self::Aarch64AdrGotPage21 => write!(f, "Aarch64AdrGotPage21"), Self::Aarch64Ld64GotLo12Nc => write!(f, "Aarch64AdrGotLo12Nc"), Self::S390xTlsGd64 => write!(f, "TlsGd64"), diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle index d9c064f6c6cb..90c0911185c5 100644 --- a/cranelift/codegen/src/isa/aarch64/inst.isle +++ b/cranelift/codegen/src/isa/aarch64/inst.isle @@ -947,8 +947,9 @@ ;; A call to the `ElfTlsGetAddr` libcall. Returns address of TLS symbol in x0. (ElfTlsGetAddr - (symbol ExternalName) - (rd WritableReg)) + (symbol BoxExternalName) + (rd WritableReg) + (tmp WritableReg)) (MachOTlsGetAddr (symbol ExternalName) @@ -3764,7 +3765,8 @@ (decl elf_tls_get_addr (ExternalName) Reg) (rule (elf_tls_get_addr name) (let ((dst WritableReg (temp_writable_reg $I64)) - (_ Unit (emit (MInst.ElfTlsGetAddr name dst)))) + (tmp WritableReg (temp_writable_reg $I64)) + (_ Unit (emit (MInst.ElfTlsGetAddr (box_external_name name) dst tmp)))) dst)) (decl macho_tls_get_addr (ExternalName) Reg) diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index 2742134d1f0f..39a49dfdd275 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -4,7 +4,7 @@ use cranelift_control::ControlPlane; use regalloc2::Allocation; use crate::binemit::{Reloc, StackMap}; -use crate::ir::{self, types::*, LibCall, MemFlags, RelSourceLoc, TrapCode}; +use crate::ir::{self, types::*, MemFlags, RelSourceLoc, TrapCode}; use crate::isa::aarch64::inst::*; use crate::machinst::{ty_bits, Reg, RegClass, Writable}; use crate::trace; @@ -3537,32 +3537,81 @@ impl MachInstEmit for Inst { } } - &Inst::ElfTlsGetAddr { ref symbol, rd } => { + &Inst::ElfTlsGetAddr { + ref symbol, + rd, + tmp, + } => { let rd = allocs.next_writable(rd); + let tmp = allocs.next_writable(tmp); assert_eq!(xreg(0), rd.to_reg()); + // See the original proposal for TLSDESC. + // http://www.fsfla.org/~lxoliva/writeups/TLS/paper-lk2006.pdf + // + // Implement the TLSDESC instruction sequence: + // adrp x0, :tlsdesc:tlsvar + // ldr tmp, [x0, :tlsdesc_lo12:tlsvar] + // add x0, x0, :tlsdesc_lo12:tlsvar + // blr tmp + // mrs tmp, tpidr_el0 + // add x0, x0, tmp + // // This is the instruction sequence that GCC emits for ELF GD TLS Relocations in aarch64 - // See: https://gcc.godbolt.org/z/KhMh5Gvra + // See: https://gcc.godbolt.org/z/e4j7MdErh - // adrp x0,

, call_conv: CallingConvention) -> Self { + let size = vmoffsets.ptr.size(); Self { ptr_size: size, call_conv, - base, + base: vmoffsets.vmctx_builtin_functions(), ptr_type: ptr_type_from_ptr_size(size), + ceil_f32: None, + ceil_f64: None, + floor_f32: None, + floor_f64: None, + trunc_f32: None, + trunc_f64: None, + nearest_f32: None, + nearest_f64: None, $( $name: None, )* @@ -68,6 +133,14 @@ macro_rules! declare_function_sig { WasmType::I32 } + fn f32(&self) -> WasmType { + WasmType::F32 + } + + fn f64(&self) -> WasmType { + WasmType::F64 + } + fn i64(&self) -> WasmType { WasmType::I64 } @@ -76,21 +149,116 @@ macro_rules! declare_function_sig { self.pointer() } + fn over_f64(&self) -> ABISig { + A::sig_from(&[self.f64()], &[self.f64()], &self.call_conv) + } + + fn over_f32(&self) -> ABISig { + A::sig_from(&[self.f64()], &[self.f64()], &self.call_conv) + } + + pub(crate) fn ceil_f32(&mut self) -> BuiltinFunction { + if self.ceil_f32.is_none() { + let sig = self.over_f32::(); + let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::CeilF32) }); + self.ceil_f32 = Some(BuiltinFunction { + inner, + }); + } + self.ceil_f32.as_ref().unwrap().clone() + } + + pub(crate) fn ceil_f64(&mut self) -> BuiltinFunction { + if self.ceil_f64.is_none() { + let sig = self.over_f64::(); + let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::CeilF64) }); + self.ceil_f64 = Some(BuiltinFunction { + inner, + }); + } + self.ceil_f64.as_ref().unwrap().clone() + } + + pub(crate) fn floor_f32(&mut self) -> BuiltinFunction { + if self.floor_f32.is_none() { + let sig = self.over_f32::(); + let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::FloorF32) }); + self.floor_f32 = Some(BuiltinFunction { + inner, + }); + } + self.floor_f32.as_ref().unwrap().clone() + } + + pub(crate) fn floor_f64(&mut self) -> BuiltinFunction { + if self.floor_f64.is_none() { + let sig = self.over_f64::(); + let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::FloorF64) }); + self.floor_f64 = Some(BuiltinFunction { + inner, + }); + } + self.floor_f64.as_ref().unwrap().clone() + } + + pub(crate) fn trunc_f32(&mut self) -> BuiltinFunction { + if self.trunc_f32.is_none() { + let sig = self.over_f32::(); + let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::TruncF32) }); + self.trunc_f32 = Some(BuiltinFunction { + inner, + }); + } + self.trunc_f32.as_ref().unwrap().clone() + } + + pub(crate) fn trunc_f64(&mut self) -> BuiltinFunction { + if self.trunc_f64.is_none() { + let sig = self.over_f64::(); + let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::TruncF64) }); + self.trunc_f64 = Some(BuiltinFunction { + inner, + }); + } + self.trunc_f64.as_ref().unwrap().clone() + } + + pub(crate) fn nearest_f32(&mut self) -> BuiltinFunction { + if self.nearest_f32.is_none() { + let sig = self.over_f32::(); + let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::NearestF32) }); + self.nearest_f32 = Some(BuiltinFunction { + inner, + }); + } + self.nearest_f32.as_ref().unwrap().clone() + } + + pub(crate) fn nearest_f64(&mut self) -> BuiltinFunction { + if self.nearest_f64.is_none() { + let sig = self.over_f64::(); + let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::known(LibCall::NearestF64) }); + self.nearest_f64 = Some(BuiltinFunction { + inner, + }); + } + self.nearest_f64.as_ref().unwrap().clone() + } + $( - pub(crate) fn $name(&mut self) -> &BuiltinFunction { + pub(crate) fn $name(&mut self) -> BuiltinFunction { if self.$name.is_none() { let params = vec![ $(self.$param() ),* ]; let result = vec![ $(self.$result() )?]; let sig = A::sig_from(¶ms, &result, &self.call_conv); let index = BuiltinFunctionIndex::$name(); + let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::dynamic(index.index() * (self.ptr_size as u32), self.base) }); self.$name = Some(BuiltinFunction { - sig, - offset: index.index() * (self.ptr_size as u32), - base: self.base, + inner, }); } - self.$name.as_ref().unwrap() + self.$name.as_ref().unwrap().clone() } )* } diff --git a/winch/codegen/src/codegen/call.rs b/winch/codegen/src/codegen/call.rs index 3f9bbba93c4d..977b4757e901 100644 --- a/winch/codegen/src/codegen/call.rs +++ b/winch/codegen/src/codegen/call.rs @@ -1,330 +1,262 @@ //! Function call emission. For more details around the ABI and //! calling convention, see [ABI]. +//! +//! This module exposes a single function [`FnCall::emit`], which is responsible +//! of orchestrating the emission of calls. In general such orchestration +//! takes place in 4 steps: +//! +//! 1. [`Callee`] resolution. +//! 2. Mapping of the [`Callee`] to the [`CalleeKind`]. +//! 3. Calculation of the stack space consumed by the call. +//! 4. Emission. +//! +//! The stack space consumed by the function call; that is, +//! the sum of: +//! +//! 1. The amount of stack space created by saving any live +//! registers at the callsite. +//! 2. The amount of space used by any memory entries in the value +//! stack present at the callsite, that will be used as +//! arguments for the function call. Any memory values in the +//! value stack that are needed as part of the function +//! arguments, will be consumed by the function call (either by +//! assigning those values to a register or by storing those +//! values to a memory location if the callee argument is on +//! the stack), so we track that stack space to reclaim it once +//! the function call has ended. This could also be done in +//! when assigning arguments everytime a memory entry needs to be assigned +//! to a particular location, but doing so, will incur in more +//! instructions (e.g. a pop per argument that needs to be +//! assigned); it's more efficient to track the space needed by +//! those memory values and reclaim it at once. +//! +//! The machine stack throghout the function call is as follows: +//! ┌──────────────────────────────────────────────────┐ +//! │ │ +//! │ 1 │ +//! │ Stack space created by any previous spills │ +//! │ from the value stack; and which memory values │ +//! │ are used as function arguments. │ +//! │ │ +//! ├──────────────────────────────────────────────────┤ ---> The Wasm value stack at this point in time would look like: +//! │ │ [ Reg | Reg | Mem(offset) | Mem(offset) ] +//! │ 2 │ +//! │ Stack space created by saving │ +//! │ any live registers at the callsite. │ +//! │ │ +//! │ │ +//! ├─────────────────────────────────────────────────┬┤ ---> The Wasm value stack at this point in time would look like: +//! │ │ [ Mem(offset) | Mem(offset) | Mem(offset) | Mem(offset) ] +//! │ │ Assuming that the callee takes 4 arguments, we calculate +//! │ │ 2 spilled registers + 2 memory values; all of which will be used +//! │ Stack space allocated for │ as arguments to the call via `assign_args`, thus the memory they represent is +//! │ the callee function arguments in the stack; │ is considered to be consumed by the call. +//! │ represented by `arg_stack_space` │ +//! │ │ +//! │ │ +//! │ │ +//! └──────────────────────────────────────────────────┘ ------> Stack pointer when emitting the call + use crate::{ - abi::{ABIArg, ABIResult, ABISig, ABI}, - codegen::{BuiltinFunction, CodeGenContext}, + abi::{ABIArg, ABISig, ABI}, + codegen::{ + ptr_type_from_ptr_size, BuiltinFunction, BuiltinType, Callee, CalleeInfo, CodeGenContext, + TypedReg, + }, masm::{CalleeKind, MacroAssembler, OperandSize}, reg::Reg, + CallingConvention, }; -use wasmtime_environ::FuncIndex; +use smallvec::SmallVec; +use std::borrow::Cow; +use wasmtime_environ::{PtrSize, VMOffsets, WasmType}; /// All the information needed to emit a function call. #[derive(Copy, Clone)] -pub(crate) struct FnCall<'a> { - /// The stack space consumed by the function call; that is, - /// the sum of: - /// - /// 1. The amount of stack space created by saving any live - /// registers at the callsite. - /// 2. The amount of space used by any memory entries in the value - /// stack present at the callsite, that will be used as - /// arguments for the function call. Any memory values in the - /// value stack that are needed as part of the function - /// arguments, will be consumed by the function call (either by - /// assigning those values to a register or by storing those - /// values to a memory location if the callee argument is on - /// the stack), so we track that stack space to reclaim it once - /// the function call has ended. This could also be done in - /// `assign_args` everytime a memory entry needs to be assigned - /// to a particular location, but doing so, will incur in more - /// instructions (e.g. a pop per argument that needs to be - /// assigned); it's more efficient to track the space needed by - /// those memory values and reclaim it at once. - /// - /// The machine stack throghout the function call is as follows: - /// ┌──────────────────────────────────────────────────┐ - /// │ │ - /// │ 1 │ - /// │ Stack space created by any previous spills │ - /// │ from the value stack; and which memory values │ - /// │ are used as function arguments. │ - /// │ │ - /// ├──────────────────────────────────────────────────┤ ---> The Wasm value stack at this point in time would look like: - /// │ │ [ Reg | Reg | Mem(offset) | Mem(offset) ] - /// │ 2 │ - /// │ Stack space created by saving │ - /// │ any live registers at the callsite. │ - /// │ │ - /// │ │ - /// ├─────────────────────────────────────────────────┬┤ ---> The Wasm value stack at this point in time would look like: - /// │ │ [ Mem(offset) | Mem(offset) | Mem(offset) | Mem(offset) ] - /// │ │ Assuming that the callee takes 4 arguments, we calculate - /// │ │ 2 spilled registers + 2 memory values; all of which will be used - /// │ Stack space allocated for │ as arguments to the call via `assign_args`, thus the memory they represent is - /// │ the callee function arguments in the stack; │ is considered to be consumed by the call. - /// │ represented by `arg_stack_space` │ - /// │ │ - /// │ │ - /// │ │ - /// └──────────────────────────────────────────────────┘ ------> Stack pointer when emitting the call - /// - call_stack_space: Option, - /// The total stack space needed for the callee arguments on the - /// stack, including any adjustments to the function's frame and - /// aligned to to the required ABI alignment. - arg_stack_space: u32, - /// The ABI-specific signature of the callee. - pub abi_sig: &'a ABISig, - /// Whether this a built-in function call. - lib: bool, -} +pub(crate) struct FnCall {} + +impl FnCall { + /// Orchestrates the emission of a function call: + /// 1. Resolves the [`Callee`] through the given callback. + /// 2. Maps the resolved [`Callee`] to the [`CalleeKind`]. + /// 3. Saves any live registers and calculates the stack space consumed + /// by the function call. + /// 4. Emits the call. + pub fn emit( + masm: &mut M, + context: &mut CodeGenContext, + mut resolve: R, + ) where + R: FnMut(&mut CodeGenContext) -> Callee, + { + let callee = resolve(context); + let ptr_type = ptr_type_from_ptr_size(context.vmoffsets.ptr.size()); + let sig = Self::get_sig::(&callee, ptr_type); + let sig = sig.as_ref(); + + let arg_stack_space = sig.stack_bytes; + let kind = Self::map(&context.vmoffsets, &callee, sig, context, masm); + let call_stack_space = Self::save(context, masm, &sig); -impl<'a> FnCall<'a> { - /// Creates a new [`FnCall`] from the callee's [`ABISig`]. - pub fn new(callee_sig: &'a ABISig) -> Self { - Self { - abi_sig: &callee_sig, - arg_stack_space: callee_sig.stack_bytes, - call_stack_space: None, - lib: false, + let reserved_stack = masm.call(arg_stack_space, |masm| { + let scratch = ::scratch_reg(); + Self::assign(sig, context, masm, scratch); + kind + }); + + match kind { + CalleeKind::Indirect(r) => context.free_reg(r), + _ => {} } + Self::cleanup( + sig, + call_stack_space.checked_add(reserved_stack).unwrap(), + masm, + context, + ); } - /// Saves any live registers and records the stack space that will be - /// consumed by the function call. The stack space consumed by the call must - /// be known before emitting the call via any of the emission variants: - /// [`FnCall::direct`], [`FnCall::indirect`] or [`FnCall::addr`], which - /// means that the call stack space must be calculated either by invoking - /// [`FnCall::save_live_registers`] or - /// [`FnCall::calculate_call_stack_space`] before invoking any of - /// the emission variants. - pub fn save_live_registers( - &mut self, - context: &mut CodeGenContext, - masm: &mut M, - ) -> &mut Self { - // Invariant: ensure that `call_stack_space` is only set once: either by - // [`FnCall::save_live_registers`] or - // [`FnCall::calculate_call_stack_space`] - debug_assert!(self.call_stack_space.is_none()); - let callee_params = &self.abi_sig.params; - let stack = &context.stack; - let call_stack_space = match callee_params.len() { - 0 => { - let _ = context.save_live_registers_and_calculate_sizeof(masm, ..); - 0u32 + /// Derive the [`ABISig`] for a particulare [`Callee]. + fn get_sig(callee: &Callee, ptr_type: WasmType) -> Cow<'_, ABISig> { + match callee { + Callee::Builtin(info) => Cow::Borrowed(info.sig()), + Callee::Import(info) => { + let mut params: SmallVec<[WasmType; 6]> = + SmallVec::with_capacity(info.ty.params().len() + 2); + params.extend_from_slice(&[ptr_type, ptr_type]); + params.extend_from_slice(info.ty.params()); + Cow::Owned(::sig_from( + ¶ms, + info.ty.returns(), + &CallingConvention::Default, + )) } - _ => { - // Here we perform a "spill" of the register entries - // in the Wasm value stack, we also count any memory - // values that will be used used as part of the callee - // arguments. Saving the live registers is done by - // emitting push operations for every `Reg` entry in - // the Wasm value stack. We do this to be compliant - // with Winch's internal ABI, in which all registers - // are treated as caller-saved. For more details, see - // [ABI]. - // - // The next few lines, partition the value stack into - // two sections: - // +------------------+--+--- (Stack top) - // | | | - // | | | 1. The top `n` elements, which are used for - // | | | function arguments; for which we save any - // | | | live registers, keeping track of the amount of registers - // +------------------+ | saved plus the amount of memory values consumed by the function call; - // | | | with this information we can later reclaim the space used by the function call. - // | | | - // +------------------+--+--- - // | | | 2. The rest of the items in the stack, for which - // | | | we only save any live registers. - // | | | - // +------------------+ | - assert!(stack.len() >= callee_params.len()); - let partition = stack.len() - callee_params.len(); - let _ = context.save_live_registers_and_calculate_sizeof(masm, 0..partition); - context.save_live_registers_and_calculate_sizeof(masm, partition..) + Callee::Local(info) => { + Cow::Owned(::sig(&info.ty, &CallingConvention::Default)) } - }; - - self.call_stack_space = Some(call_stack_space); - self + Callee::FuncRef(ty) => { + Cow::Owned(::sig(&ty, &CallingConvention::Default)) + } + } } - /// Records the stack space that will be needeed by the function call by - /// scanning the value stack and returning the size of the all the memory - /// entries present in callee's argument length range. The stack space - /// consumed by the call must be known before emitting the call via any of - /// the emission variants: [`FnCall::direct`], [`FnCall::indirect`] or - /// [`FnCall::addr`], which means that the call stack space must be - /// calculated either by invoking [`FnCall::save_live_registers`] or - /// [`FnCall::calculate_call_stack_space`] before invoking any of - /// the emission variants. - /// This function is particularly useful when there's no need to save any - /// live registers before emitting the function call. This could happen when - /// emitting calls to libcalls: [`FnCall::with_lib`] will eagerly save all - /// the live registers when invoked and will also ensure that any registers - /// allocated after are non argument registers, in which case if any of - /// those registers need to go on the value stack to be used as function - /// arguments, they don't need to be saved. - pub fn calculate_call_stack_space(&mut self, context: &mut CodeGenContext) -> &mut Self { - // Invariant: ensure that `call_stack_space` is only set once: either by - // [`FnCall::save_live_registers`] or - // [`FnCall::calculate_call_stack_space`] - debug_assert!(self.call_stack_space.is_none()); - let params_len = self.abi_sig.params.len(); - assert!(context.stack.len() >= params_len); - - let stack_len = context.stack.len(); - let call_stack_space = if params_len == 0 { - 0 - } else { - context.stack.sizeof((stack_len - params_len)..) - }; - self.call_stack_space = Some(call_stack_space); - self + /// Maps the given [`Callee`] to a [`CalleeKind`]. + fn map( + vmoffsets: &VMOffsets