summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cranelift-wasm
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/cranelift-wasm
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/cranelift-wasm')
-rw-r--r--third_party/rust/cranelift-wasm/.cargo-checksum.json1
-rw-r--r--third_party/rust/cranelift-wasm/Cargo.toml39
-rw-r--r--third_party/rust/cranelift-wasm/LICENSE220
-rw-r--r--third_party/rust/cranelift-wasm/README.md8
-rw-r--r--third_party/rust/cranelift-wasm/src/code_translator.rs2754
-rw-r--r--third_party/rust/cranelift-wasm/src/environ/dummy.rs783
-rw-r--r--third_party/rust/cranelift-wasm/src/environ/mod.rs11
-rw-r--r--third_party/rust/cranelift-wasm/src/environ/spec.rs915
-rw-r--r--third_party/rust/cranelift-wasm/src/func_translator.rs418
-rw-r--r--third_party/rust/cranelift-wasm/src/lib.rs76
-rw-r--r--third_party/rust/cranelift-wasm/src/module_translator.rs159
-rw-r--r--third_party/rust/cranelift-wasm/src/sections_translator.rs454
-rw-r--r--third_party/rust/cranelift-wasm/src/state/func_state.rs543
-rw-r--r--third_party/rust/cranelift-wasm/src/state/mod.rs14
-rw-r--r--third_party/rust/cranelift-wasm/src/state/module_state.rs70
-rw-r--r--third_party/rust/cranelift-wasm/src/translation_utils.rs349
-rw-r--r--third_party/rust/cranelift-wasm/tests/wasm_testsuite.rs96
17 files changed, 6910 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-wasm/.cargo-checksum.json b/third_party/rust/cranelift-wasm/.cargo-checksum.json
new file mode 100644
index 0000000000..c0782b2c4b
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"f2978cc72e6013dfc336294b7e97d2b9966848f1c140ae0edfeba513255edcce","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"c82c252fbeeaa101a0eef042b9a925eb1fa3d2b51d19481b9c22e593e6a8d772","src/code_translator.rs":"2bfc9607bfd350c130182614c2e05207f15ef8185f98e7d381140a07083c6589","src/environ/dummy.rs":"e22052b6d966744658a189a6d77595d2b92e9d0bc8b6fbc1be2d68f2dbfccb28","src/environ/mod.rs":"692f35d75f125f9c071f7166252f427e4bac29401356f73307c6c36e23c667fb","src/environ/spec.rs":"f38c2f2c69d60ebd665b7991def3d06340fe5593e44b2f441d212adc756ac2d4","src/func_translator.rs":"ebc7e7f872d03fc05e9c013e0eb575b0ae2828322b7194c60b8e764f2816d12e","src/lib.rs":"fbbbe573ec5b4bd0f667c20500b2836a302d378933db56141df445e9aa8fba42","src/module_translator.rs":"9c24c44bcf866ac46ca90e22db5321080c1b507dca40fef73c4cdb0918391be6","src/sections_translator.rs":"8ce41a58daacc706f2a73c1cdb52c8838faa230413bb29a50826cf5a2c550608","src/state/func_state.rs":"0a6bcb31db482bdccf90c9260f9ea05a19e7439a24f81fd46173ed6c810cd1a7","src/state/mod.rs":"20014cb93615467b4d20321b52f67f66040417efcaa739a4804093bb559eed19","src/state/module_state.rs":"7ca3cb06b4481bc3ae74697fbcd437aea1d851eaa3cfe18cc013a4af43728957","src/translation_utils.rs":"4a70e54ce4e9040a05f4d49ca3595da4a213e865f2baa12965bef4236780681a","tests/wasm_testsuite.rs":"5e9f8441acdafe1552b4ae79c8c27603dad2b047791b035d28354d9f29b4d4e7"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/cranelift-wasm/Cargo.toml b/third_party/rust/cranelift-wasm/Cargo.toml
new file mode 100644
index 0000000000..d8bd9d2c44
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/Cargo.toml
@@ -0,0 +1,39 @@
+[package]
+name = "cranelift-wasm"
+version = "0.68.0"
+authors = ["The Cranelift Project Developers"]
+description = "Translator from WebAssembly to Cranelift IR"
+documentation = "https://docs.rs/cranelift-wasm"
+repository = "https://github.com/bytecodealliance/wasmtime"
+license = "Apache-2.0 WITH LLVM-exception"
+categories = ["no-std", "wasm"]
+readme = "README.md"
+keywords = ["webassembly", "wasm"]
+edition = "2018"
+
+[dependencies]
+wasmparser = { git = "https://github.com/mozilla-spidermonkey/wasm-tools", rev = "1b7763faa484e62752538b78e7a69883f4faceee", default-features = false }
+cranelift-codegen = { path = "../codegen", version = "0.68.0", default-features = false }
+cranelift-entity = { path = "../entity", version = "0.68.0" }
+cranelift-frontend = { path = "../frontend", version = "0.68.0", default-features = false }
+hashbrown = { version = "0.9.1", optional = true }
+itertools = "0.9.0"
+log = { version = "0.4.6", default-features = false }
+serde = { version = "1.0.94", features = ["derive"], optional = true }
+smallvec = "1.0.0"
+thiserror = "1.0.4"
+
+[dev-dependencies]
+wat = "1.0.23"
+target-lexicon = "0.11"
+# Enable the riscv feature for cranelift-codegen, as some tests require it
+cranelift-codegen = { path = "../codegen", version = "0.68.0", default-features = false, features = ["riscv"] }
+
+[features]
+default = ["std"]
+std = ["cranelift-codegen/std", "cranelift-frontend/std"]
+core = ["hashbrown", "cranelift-codegen/core", "cranelift-frontend/core"]
+enable-serde = ["serde"]
+
+[badges]
+maintenance = { status = "experimental" }
diff --git a/third_party/rust/cranelift-wasm/LICENSE b/third_party/rust/cranelift-wasm/LICENSE
new file mode 100644
index 0000000000..f9d81955f4
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/LICENSE
@@ -0,0 +1,220 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+--- LLVM Exceptions to the Apache 2.0 License ----
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into an Object form of such source code, you
+may redistribute such embedded portions in such Object form without complying
+with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
+
+In addition, if you combine or link compiled forms of this Software with
+software that is licensed under the GPLv2 ("Combined Software") and if a
+court of competent jurisdiction determines that the patent provision (Section
+3), the indemnity provision (Section 9) or other Section of the License
+conflicts with the conditions of the GPLv2, you may retroactively and
+prospectively choose to deem waived or otherwise exclude such Section(s) of
+the License, but only in their entirety and only with respect to the Combined
+Software.
+
diff --git a/third_party/rust/cranelift-wasm/README.md b/third_party/rust/cranelift-wasm/README.md
new file mode 100644
index 0000000000..556b4b9ede
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/README.md
@@ -0,0 +1,8 @@
+This crate performs the translation from a wasm module in binary format to the
+in-memory form of the [Cranelift IR].
+
+If you're looking for a complete WebAssembly implementation that uses this
+library, see [Wasmtime].
+
+[Wasmtime]: https://github.com/bytecodealliance/wasmtime
+[Cranelift IR]: https://github.com/bytecodealliance/wasmtime/blob/main/cranelift/docs/ir.md
diff --git a/third_party/rust/cranelift-wasm/src/code_translator.rs b/third_party/rust/cranelift-wasm/src/code_translator.rs
new file mode 100644
index 0000000000..4a8beaec3b
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/code_translator.rs
@@ -0,0 +1,2754 @@
+//! This module contains the bulk of the interesting code performing the translation between
+//! WebAssembly and Cranelift IR.
+//!
+//! The translation is done in one pass, opcode by opcode. Two main data structures are used during
+//! code translations: the value stack and the control stack. The value stack mimics the execution
+//! of the WebAssembly stack machine: each instruction result is pushed onto the stack and
+//! instruction arguments are popped off the stack. Similarly, when encountering a control flow
+//! block, it is pushed onto the control stack and popped off when encountering the corresponding
+//! `End`.
+//!
+//! Another data structure, the translation state, records information concerning unreachable code
+//! status and about if inserting a return at the end of the function is necessary.
+//!
+//! Some of the WebAssembly instructions need information about the environment for which they
+//! are being translated:
+//!
+//! - the loads and stores need the memory base address;
+//! - the `get_global` and `set_global` instructions depend on how the globals are implemented;
+//! - `memory.size` and `memory.grow` are runtime functions;
+//! - `call_indirect` has to translate the function index into the address of where this
+//! is;
+//!
+//! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as
+//! argument.
+//!
+//! There is extra complexity associated with translation of 128-bit SIMD instructions.
+//! Wasm only considers there to be a single 128-bit vector type. But CLIF's type system
+//! distinguishes different lane configurations, so considers 8X16, 16X8, 32X4 and 64X2 to be
+//! different types. The result is that, in wasm, it's perfectly OK to take the output of (eg)
+//! an `add.16x8` and use that as an operand of a `sub.32x4`, without using any cast. But when
+//! translated into CLIF, that will cause a verifier error due to the apparent type mismatch.
+//!
+//! This file works around that problem by liberally inserting `bitcast` instructions in many
+//! places -- mostly, before the use of vector values, either as arguments to CLIF instructions
+//! or as block actual parameters. These are no-op casts which nevertheless have different
+//! input and output types, and are used (mostly) to "convert" 16X8, 32X4 and 64X2-typed vectors
+//! to the "canonical" type, 8X16. Hence the functions `optionally_bitcast_vector`,
+//! `bitcast_arguments`, `pop*_with_bitcast`, `canonicalise_then_jump`,
+//! `canonicalise_then_br{z,nz}`, `is_non_canonical_v128` and `canonicalise_v128_values`.
+//! Note that the `bitcast*` functions are occasionally used to convert to some type other than
+//! 8X16, but the `canonicalise*` functions always convert to type 8X16.
+//!
+//! Be careful when adding support for new vector instructions. And when adding new jumps, even
+//! if they are apparently don't have any connection to vectors. Never generate any kind of
+//! (inter-block) jump directly. Instead use `canonicalise_then_jump` and
+//! `canonicalise_then_br{z,nz}`.
+//!
+//! The use of bitcasts is ugly and inefficient, but currently unavoidable:
+//!
+//! * they make the logic in this file fragile: miss out a bitcast for any reason, and there is
+//! the risk of the system failing in the verifier. At least for debug builds.
+//!
+//! * in the new backends, they potentially interfere with pattern matching on CLIF -- the
+//! patterns need to take into account the presence of bitcast nodes.
+//!
+//! * in the new backends, they get translated into machine-level vector-register-copy
+//! instructions, none of which are actually necessary. We then depend on the register
+//! allocator to coalesce them all out.
+//!
+//! * they increase the total number of CLIF nodes that have to be processed, hence slowing down
+//! the compilation pipeline. Also, the extra coalescing work generates a slowdown.
+//!
+//! A better solution which would avoid all four problems would be to remove the 8X16, 16X8,
+//! 32X4 and 64X2 types from CLIF and instead have a single V128 type.
+//!
+//! For further background see also:
+//! https://github.com/bytecodealliance/wasmtime/issues/1147
+//! ("Too many raw_bitcasts in SIMD code")
+//! https://github.com/bytecodealliance/cranelift/pull/1251
+//! ("Add X128 type to represent WebAssembly's V128 type")
+//! https://github.com/bytecodealliance/cranelift/pull/1236
+//! ("Relax verification to allow I8X16 to act as a default vector type")
+
+use super::{hash_map, HashMap};
+use crate::environ::{FuncEnvironment, GlobalVariable, ReturnMode, WasmResult};
+use crate::state::{ControlStackFrame, ElseData, FuncTranslationState};
+use crate::translation_utils::{
+ block_with_params, blocktype_params_results, f32_translation, f64_translation,
+};
+use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TypeIndex};
+use crate::wasm_unsupported;
+use core::convert::TryInto;
+use core::{i32, u32};
+use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
+use cranelift_codegen::ir::immediates::Offset32;
+use cranelift_codegen::ir::types::*;
+use cranelift_codegen::ir::{
+ self, AtomicRmwOp, ConstantData, InstBuilder, JumpTableData, MemFlags, Value, ValueLabel,
+};
+use cranelift_codegen::packed_option::ReservedValue;
+use cranelift_frontend::{FunctionBuilder, Variable};
+use smallvec::SmallVec;
+use std::cmp;
+use std::convert::TryFrom;
+use std::vec::Vec;
+use wasmparser::{FuncValidator, MemoryImmediate, Operator, WasmModuleResources};
+
+// Clippy warns about "align: _" but its important to document that the flags field is ignored
+#[cfg_attr(
+ feature = "cargo-clippy",
+ allow(clippy::unneeded_field_pattern, clippy::cognitive_complexity)
+)]
+/// Translates wasm operators into Cranelift IR instructions. Returns `true` if it inserted
+/// a return.
+pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
+ validator: &mut FuncValidator<impl WasmModuleResources>,
+ op: &Operator,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<()> {
+ if !state.reachable {
+ translate_unreachable_operator(validator, &op, builder, state, environ)?;
+ return Ok(());
+ }
+
+ // This big match treats all Wasm code operators.
+ match op {
+ /********************************** Locals ****************************************
+ * `get_local` and `set_local` are treated as non-SSA variables and will completely
+ * disappear in the Cranelift Code
+ ***********************************************************************************/
+ Operator::LocalGet { local_index } => {
+ let val = builder.use_var(Variable::with_u32(*local_index));
+ state.push1(val);
+ let label = ValueLabel::from_u32(*local_index);
+ builder.set_val_label(val, label);
+ }
+ Operator::LocalSet { local_index } => {
+ let mut val = state.pop1();
+
+ // Ensure SIMD values are cast to their default Cranelift type, I8x16.
+ let ty = builder.func.dfg.value_type(val);
+ if ty.is_vector() {
+ val = optionally_bitcast_vector(val, I8X16, builder);
+ }
+
+ builder.def_var(Variable::with_u32(*local_index), val);
+ let label = ValueLabel::from_u32(*local_index);
+ builder.set_val_label(val, label);
+ }
+ Operator::LocalTee { local_index } => {
+ let mut val = state.peek1();
+
+ // Ensure SIMD values are cast to their default Cranelift type, I8x16.
+ let ty = builder.func.dfg.value_type(val);
+ if ty.is_vector() {
+ val = optionally_bitcast_vector(val, I8X16, builder);
+ }
+
+ builder.def_var(Variable::with_u32(*local_index), val);
+ let label = ValueLabel::from_u32(*local_index);
+ builder.set_val_label(val, label);
+ }
+ /********************************** Globals ****************************************
+ * `get_global` and `set_global` are handled by the environment.
+ ***********************************************************************************/
+ Operator::GlobalGet { global_index } => {
+ let val = match state.get_global(builder.func, *global_index, environ)? {
+ GlobalVariable::Const(val) => val,
+ GlobalVariable::Memory { gv, offset, ty } => {
+ let addr = builder.ins().global_value(environ.pointer_type(), gv);
+ let flags = ir::MemFlags::trusted();
+ builder.ins().load(ty, flags, addr, offset)
+ }
+ GlobalVariable::Custom => environ.translate_custom_global_get(
+ builder.cursor(),
+ GlobalIndex::from_u32(*global_index),
+ )?,
+ };
+ state.push1(val);
+ }
+ Operator::GlobalSet { global_index } => {
+ match state.get_global(builder.func, *global_index, environ)? {
+ GlobalVariable::Const(_) => panic!("global #{} is a constant", *global_index),
+ GlobalVariable::Memory { gv, offset, ty } => {
+ let addr = builder.ins().global_value(environ.pointer_type(), gv);
+ let flags = ir::MemFlags::trusted();
+ let mut val = state.pop1();
+ // Ensure SIMD values are cast to their default Cranelift type, I8x16.
+ if ty.is_vector() {
+ val = optionally_bitcast_vector(val, I8X16, builder);
+ }
+ debug_assert_eq!(ty, builder.func.dfg.value_type(val));
+ builder.ins().store(flags, val, addr, offset);
+ }
+ GlobalVariable::Custom => {
+ let val = state.pop1();
+ environ.translate_custom_global_set(
+ builder.cursor(),
+ GlobalIndex::from_u32(*global_index),
+ val,
+ )?;
+ }
+ }
+ }
+ /********************************* Stack misc ***************************************
+ * `drop`, `nop`, `unreachable` and `select`.
+ ***********************************************************************************/
+ Operator::Drop => {
+ state.pop1();
+ }
+ Operator::Select => {
+ let (arg1, arg2, cond) = state.pop3();
+ state.push1(builder.ins().select(cond, arg1, arg2));
+ }
+ Operator::TypedSelect { ty: _ } => {
+ // We ignore the explicit type parameter as it is only needed for
+ // validation, which we require to have been performed before
+ // translation.
+ let (arg1, arg2, cond) = state.pop3();
+ state.push1(builder.ins().select(cond, arg1, arg2));
+ }
+ Operator::Nop => {
+ // We do nothing
+ }
+ Operator::Unreachable => {
+ builder.ins().trap(ir::TrapCode::UnreachableCodeReached);
+ state.reachable = false;
+ }
+ /***************************** Control flow blocks **********************************
+ * When starting a control flow block, we create a new `Block` that will hold the code
+ * after the block, and we push a frame on the control stack. Depending on the type
+ * of block, we create a new `Block` for the body of the block with an associated
+ * jump instruction.
+ *
+ * The `End` instruction pops the last control frame from the control stack, seals
+ * the destination block (since `br` instructions targeting it only appear inside the
+ * block and have already been translated) and modify the value stack to use the
+ * possible `Block`'s arguments values.
+ ***********************************************************************************/
+ Operator::Block { ty } => {
+ let (params, results) = blocktype_params_results(validator, *ty)?;
+ let next = block_with_params(builder, results.clone(), environ)?;
+ state.push_block(next, params.len(), results.len());
+ }
+ Operator::Loop { ty } => {
+ let (params, results) = blocktype_params_results(validator, *ty)?;
+ let loop_body = block_with_params(builder, params.clone(), environ)?;
+ let next = block_with_params(builder, results.clone(), environ)?;
+ canonicalise_then_jump(builder, loop_body, state.peekn(params.len()));
+ state.push_loop(loop_body, next, params.len(), results.len());
+
+ // Pop the initial `Block` actuals and replace them with the `Block`'s
+ // params since control flow joins at the top of the loop.
+ state.popn(params.len());
+ state
+ .stack
+ .extend_from_slice(builder.block_params(loop_body));
+
+ builder.switch_to_block(loop_body);
+ environ.translate_loop_header(builder.cursor())?;
+ }
+ Operator::If { ty } => {
+ let val = state.pop1();
+
+ let (params, results) = blocktype_params_results(validator, *ty)?;
+ let (destination, else_data) = if params.clone().eq(results.clone()) {
+ // It is possible there is no `else` block, so we will only
+ // allocate a block for it if/when we find the `else`. For now,
+ // we if the condition isn't true, then we jump directly to the
+ // destination block following the whole `if...end`. If we do end
+ // up discovering an `else`, then we will allocate a block for it
+ // and go back and patch the jump.
+ let destination = block_with_params(builder, results.clone(), environ)?;
+ let branch_inst =
+ canonicalise_then_brz(builder, val, destination, state.peekn(params.len()));
+ (destination, ElseData::NoElse { branch_inst })
+ } else {
+ // The `if` type signature is not valid without an `else` block,
+ // so we eagerly allocate the `else` block here.
+ let destination = block_with_params(builder, results.clone(), environ)?;
+ let else_block = block_with_params(builder, params.clone(), environ)?;
+ canonicalise_then_brz(builder, val, else_block, state.peekn(params.len()));
+ builder.seal_block(else_block);
+ (destination, ElseData::WithElse { else_block })
+ };
+
+ let next_block = builder.create_block();
+ canonicalise_then_jump(builder, next_block, &[]);
+ builder.seal_block(next_block); // Only predecessor is the current block.
+ builder.switch_to_block(next_block);
+
+ // Here we append an argument to a Block targeted by an argumentless jump instruction
+ // But in fact there are two cases:
+ // - either the If does not have a Else clause, in that case ty = EmptyBlock
+ // and we add nothing;
+ // - either the If have an Else clause, in that case the destination of this jump
+ // instruction will be changed later when we translate the Else operator.
+ state.push_if(destination, else_data, params.len(), results.len(), *ty);
+ }
+ Operator::Else => {
+ let i = state.control_stack.len() - 1;
+ match state.control_stack[i] {
+ ControlStackFrame::If {
+ ref else_data,
+ head_is_reachable,
+ ref mut consequent_ends_reachable,
+ num_return_values,
+ blocktype,
+ destination,
+ ..
+ } => {
+ // We finished the consequent, so record its final
+ // reachability state.
+ debug_assert!(consequent_ends_reachable.is_none());
+ *consequent_ends_reachable = Some(state.reachable);
+
+ if head_is_reachable {
+ // We have a branch from the head of the `if` to the `else`.
+ state.reachable = true;
+
+ // Ensure we have a block for the `else` block (it may have
+ // already been pre-allocated, see `ElseData` for details).
+ let else_block = match *else_data {
+ ElseData::NoElse { branch_inst } => {
+ let (params, _results) =
+ blocktype_params_results(validator, blocktype)?;
+ debug_assert_eq!(params.len(), num_return_values);
+ let else_block =
+ block_with_params(builder, params.clone(), environ)?;
+ canonicalise_then_jump(
+ builder,
+ destination,
+ state.peekn(params.len()),
+ );
+ state.popn(params.len());
+
+ builder.change_jump_destination(branch_inst, else_block);
+ builder.seal_block(else_block);
+ else_block
+ }
+ ElseData::WithElse { else_block } => {
+ canonicalise_then_jump(
+ builder,
+ destination,
+ state.peekn(num_return_values),
+ );
+ state.popn(num_return_values);
+ else_block
+ }
+ };
+
+ // You might be expecting that we push the parameters for this
+ // `else` block here, something like this:
+ //
+ // state.pushn(&control_stack_frame.params);
+ //
+ // We don't do that because they are already on the top of the stack
+ // for us: we pushed the parameters twice when we saw the initial
+ // `if` so that we wouldn't have to save the parameters in the
+ // `ControlStackFrame` as another `Vec` allocation.
+
+ builder.switch_to_block(else_block);
+
+ // We don't bother updating the control frame's `ElseData`
+ // to `WithElse` because nothing else will read it.
+ }
+ }
+ _ => unreachable!(),
+ }
+ }
+ Operator::End => {
+ let frame = state.control_stack.pop().unwrap();
+ let next_block = frame.following_code();
+
+ if !builder.is_unreachable() || !builder.is_pristine() {
+ let return_count = frame.num_return_values();
+ let return_args = state.peekn_mut(return_count);
+ canonicalise_then_jump(builder, frame.following_code(), return_args);
+ // You might expect that if we just finished an `if` block that
+ // didn't have a corresponding `else` block, then we would clean
+ // up our duplicate set of parameters that we pushed earlier
+ // right here. However, we don't have to explicitly do that,
+ // since we truncate the stack back to the original height
+ // below.
+ }
+
+ builder.switch_to_block(next_block);
+ builder.seal_block(next_block);
+
+ // If it is a loop we also have to seal the body loop block
+ if let ControlStackFrame::Loop { header, .. } = frame {
+ builder.seal_block(header)
+ }
+
+ frame.truncate_value_stack_to_original_size(&mut state.stack);
+ state
+ .stack
+ .extend_from_slice(builder.block_params(next_block));
+ }
+ /**************************** Branch instructions *********************************
+ * The branch instructions all have as arguments a target nesting level, which
+ * corresponds to how many control stack frames do we have to pop to get the
+ * destination `Block`.
+ *
+ * Once the destination `Block` is found, we sometimes have to declare a certain depth
+ * of the stack unreachable, because some branch instructions are terminator.
+ *
+ * The `br_table` case is much more complicated because Cranelift's `br_table` instruction
+ * does not support jump arguments like all the other branch instructions. That is why, in
+ * the case where we would use jump arguments for every other branch instruction, we
+ * need to split the critical edges leaving the `br_tables` by creating one `Block` per
+ * table destination; the `br_table` will point to these newly created `Blocks` and these
+ * `Block`s contain only a jump instruction pointing to the final destination, this time with
+ * jump arguments.
+ *
+ * This system is also implemented in Cranelift's SSA construction algorithm, because
+ * `use_var` located in a destination `Block` of a `br_table` might trigger the addition
+ * of jump arguments in each predecessor branch instruction, one of which might be a
+ * `br_table`.
+ ***********************************************************************************/
+ Operator::Br { relative_depth } => {
+ let i = state.control_stack.len() - 1 - (*relative_depth as usize);
+ let (return_count, br_destination) = {
+ let frame = &mut state.control_stack[i];
+ // We signal that all the code that follows until the next End is unreachable
+ frame.set_branched_to_exit();
+ let return_count = if frame.is_loop() {
+ frame.num_param_values()
+ } else {
+ frame.num_return_values()
+ };
+ (return_count, frame.br_destination())
+ };
+ let destination_args = state.peekn_mut(return_count);
+ canonicalise_then_jump(builder, br_destination, destination_args);
+ state.popn(return_count);
+ state.reachable = false;
+ }
+ Operator::BrIf { relative_depth } => translate_br_if(*relative_depth, builder, state),
+ Operator::BrTable { table } => {
+ let mut depths = table.targets().collect::<Result<Vec<_>, _>>()?;
+ let default = depths.pop().unwrap().0;
+ let mut min_depth = default;
+ for (depth, _) in depths.iter() {
+ if *depth < min_depth {
+ min_depth = *depth;
+ }
+ }
+ let jump_args_count = {
+ let i = state.control_stack.len() - 1 - (min_depth as usize);
+ let min_depth_frame = &state.control_stack[i];
+ if min_depth_frame.is_loop() {
+ min_depth_frame.num_param_values()
+ } else {
+ min_depth_frame.num_return_values()
+ }
+ };
+ let val = state.pop1();
+ let mut data = JumpTableData::with_capacity(depths.len());
+ if jump_args_count == 0 {
+ // No jump arguments
+ for (depth, _) in depths.iter() {
+ let block = {
+ let i = state.control_stack.len() - 1 - (*depth as usize);
+ let frame = &mut state.control_stack[i];
+ frame.set_branched_to_exit();
+ frame.br_destination()
+ };
+ data.push_entry(block);
+ }
+ let jt = builder.create_jump_table(data);
+ let block = {
+ let i = state.control_stack.len() - 1 - (default as usize);
+ let frame = &mut state.control_stack[i];
+ frame.set_branched_to_exit();
+ frame.br_destination()
+ };
+ builder.ins().br_table(val, block, jt);
+ } else {
+ // Here we have jump arguments, but Cranelift's br_table doesn't support them
+ // We then proceed to split the edges going out of the br_table
+ let return_count = jump_args_count;
+ let mut dest_block_sequence = vec![];
+ let mut dest_block_map = HashMap::new();
+ for (depth, _) in depths.iter() {
+ let branch_block = match dest_block_map.entry(*depth as usize) {
+ hash_map::Entry::Occupied(entry) => *entry.get(),
+ hash_map::Entry::Vacant(entry) => {
+ let block = builder.create_block();
+ dest_block_sequence.push((*depth as usize, block));
+ *entry.insert(block)
+ }
+ };
+ data.push_entry(branch_block);
+ }
+ let default_branch_block = match dest_block_map.entry(default as usize) {
+ hash_map::Entry::Occupied(entry) => *entry.get(),
+ hash_map::Entry::Vacant(entry) => {
+ let block = builder.create_block();
+ dest_block_sequence.push((default as usize, block));
+ *entry.insert(block)
+ }
+ };
+ let jt = builder.create_jump_table(data);
+ builder.ins().br_table(val, default_branch_block, jt);
+ for (depth, dest_block) in dest_block_sequence {
+ builder.switch_to_block(dest_block);
+ builder.seal_block(dest_block);
+ let real_dest_block = {
+ let i = state.control_stack.len() - 1 - depth;
+ let frame = &mut state.control_stack[i];
+ frame.set_branched_to_exit();
+ frame.br_destination()
+ };
+ let destination_args = state.peekn_mut(return_count);
+ canonicalise_then_jump(builder, real_dest_block, destination_args);
+ }
+ state.popn(return_count);
+ }
+ state.reachable = false;
+ }
+ Operator::Return => {
+ let (return_count, br_destination) = {
+ let frame = &mut state.control_stack[0];
+ if environ.return_mode() == ReturnMode::FallthroughReturn {
+ frame.set_branched_to_exit();
+ }
+ let return_count = frame.num_return_values();
+ (return_count, frame.br_destination())
+ };
+ {
+ let return_args = state.peekn_mut(return_count);
+ let return_types = wasm_param_types(&builder.func.signature.returns, |i| {
+ environ.is_wasm_return(&builder.func.signature, i)
+ });
+ bitcast_arguments(return_args, &return_types, builder);
+ match environ.return_mode() {
+ ReturnMode::NormalReturns => builder.ins().return_(return_args),
+ ReturnMode::FallthroughReturn => {
+ canonicalise_then_jump(builder, br_destination, return_args)
+ }
+ };
+ }
+ state.popn(return_count);
+ state.reachable = false;
+ }
+ /************************************ Calls ****************************************
+ * The call instructions pop off their arguments from the stack and append their
+ * return values to it. `call_indirect` needs environment support because there is an
+ * argument referring to an index in the external functions table of the module.
+ ************************************************************************************/
+ Operator::Call { function_index } => {
+ let (fref, num_args) = state.get_direct_func(builder.func, *function_index, environ)?;
+
+ // Bitcast any vector arguments to their default type, I8X16, before calling.
+ let callee_signature =
+ &builder.func.dfg.signatures[builder.func.dfg.ext_funcs[fref].signature];
+ let args = state.peekn_mut(num_args);
+ let types = wasm_param_types(&callee_signature.params, |i| {
+ environ.is_wasm_parameter(&callee_signature, i)
+ });
+ bitcast_arguments(args, &types, builder);
+
+ let call = environ.translate_call(
+ builder.cursor(),
+ FuncIndex::from_u32(*function_index),
+ fref,
+ args,
+ )?;
+ let inst_results = builder.inst_results(call);
+ debug_assert_eq!(
+ inst_results.len(),
+ builder.func.dfg.signatures[builder.func.dfg.ext_funcs[fref].signature]
+ .returns
+ .len(),
+ "translate_call results should match the call signature"
+ );
+ state.popn(num_args);
+ state.pushn(inst_results);
+ }
+ Operator::CallIndirect { index, table_index } => {
+ // `index` is the index of the function's signature and `table_index` is the index of
+ // the table to search the function in.
+ let (sigref, num_args) = state.get_indirect_sig(builder.func, *index, environ)?;
+ let table = state.get_or_create_table(builder.func, *table_index, environ)?;
+ let callee = state.pop1();
+
+ // Bitcast any vector arguments to their default type, I8X16, before calling.
+ let callee_signature = &builder.func.dfg.signatures[sigref];
+ let args = state.peekn_mut(num_args);
+ let types = wasm_param_types(&callee_signature.params, |i| {
+ environ.is_wasm_parameter(&callee_signature, i)
+ });
+ bitcast_arguments(args, &types, builder);
+
+ let call = environ.translate_call_indirect(
+ builder.cursor(),
+ TableIndex::from_u32(*table_index),
+ table,
+ TypeIndex::from_u32(*index),
+ sigref,
+ callee,
+ state.peekn(num_args),
+ )?;
+ let inst_results = builder.inst_results(call);
+ debug_assert_eq!(
+ inst_results.len(),
+ builder.func.dfg.signatures[sigref].returns.len(),
+ "translate_call_indirect results should match the call signature"
+ );
+ state.popn(num_args);
+ state.pushn(inst_results);
+ }
+ /******************************* Memory management ***********************************
+ * Memory management is handled by environment. It is usually translated into calls to
+ * special functions.
+ ************************************************************************************/
+ Operator::MemoryGrow { mem, mem_byte: _ } => {
+ // The WebAssembly MVP only supports one linear memory, but we expect the reserved
+ // argument to be a memory index.
+ let heap_index = MemoryIndex::from_u32(*mem);
+ let heap = state.get_heap(builder.func, *mem, environ)?;
+ let val = state.pop1();
+ state.push1(environ.translate_memory_grow(builder.cursor(), heap_index, heap, val)?)
+ }
+ Operator::MemorySize { mem, mem_byte: _ } => {
+ let heap_index = MemoryIndex::from_u32(*mem);
+ let heap = state.get_heap(builder.func, *mem, environ)?;
+ state.push1(environ.translate_memory_size(builder.cursor(), heap_index, heap)?);
+ }
+ /******************************* Load instructions ***********************************
+ * Wasm specifies an integer alignment flag but we drop it in Cranelift.
+ * The memory base address is provided by the environment.
+ ************************************************************************************/
+ Operator::I32Load8U { memarg } => {
+ translate_load(memarg, ir::Opcode::Uload8, I32, builder, state, environ)?;
+ }
+ Operator::I32Load16U { memarg } => {
+ translate_load(memarg, ir::Opcode::Uload16, I32, builder, state, environ)?;
+ }
+ Operator::I32Load8S { memarg } => {
+ translate_load(memarg, ir::Opcode::Sload8, I32, builder, state, environ)?;
+ }
+ Operator::I32Load16S { memarg } => {
+ translate_load(memarg, ir::Opcode::Sload16, I32, builder, state, environ)?;
+ }
+ Operator::I64Load8U { memarg } => {
+ translate_load(memarg, ir::Opcode::Uload8, I64, builder, state, environ)?;
+ }
+ Operator::I64Load16U { memarg } => {
+ translate_load(memarg, ir::Opcode::Uload16, I64, builder, state, environ)?;
+ }
+ Operator::I64Load8S { memarg } => {
+ translate_load(memarg, ir::Opcode::Sload8, I64, builder, state, environ)?;
+ }
+ Operator::I64Load16S { memarg } => {
+ translate_load(memarg, ir::Opcode::Sload16, I64, builder, state, environ)?;
+ }
+ Operator::I64Load32S { memarg } => {
+ translate_load(memarg, ir::Opcode::Sload32, I64, builder, state, environ)?;
+ }
+ Operator::I64Load32U { memarg } => {
+ translate_load(memarg, ir::Opcode::Uload32, I64, builder, state, environ)?;
+ }
+ Operator::I32Load { memarg } => {
+ translate_load(memarg, ir::Opcode::Load, I32, builder, state, environ)?;
+ }
+ Operator::F32Load { memarg } => {
+ translate_load(memarg, ir::Opcode::Load, F32, builder, state, environ)?;
+ }
+ Operator::I64Load { memarg } => {
+ translate_load(memarg, ir::Opcode::Load, I64, builder, state, environ)?;
+ }
+ Operator::F64Load { memarg } => {
+ translate_load(memarg, ir::Opcode::Load, F64, builder, state, environ)?;
+ }
+ Operator::V128Load { memarg } => {
+ translate_load(memarg, ir::Opcode::Load, I8X16, builder, state, environ)?;
+ }
+ Operator::V128Load8x8S { memarg } => {
+ let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?;
+ let loaded = builder.ins().sload8x8(flags, base, offset);
+ state.push1(loaded);
+ }
+ Operator::V128Load8x8U { memarg } => {
+ let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?;
+ let loaded = builder.ins().uload8x8(flags, base, offset);
+ state.push1(loaded);
+ }
+ Operator::V128Load16x4S { memarg } => {
+ let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?;
+ let loaded = builder.ins().sload16x4(flags, base, offset);
+ state.push1(loaded);
+ }
+ Operator::V128Load16x4U { memarg } => {
+ let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?;
+ let loaded = builder.ins().uload16x4(flags, base, offset);
+ state.push1(loaded);
+ }
+ Operator::V128Load32x2S { memarg } => {
+ let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?;
+ let loaded = builder.ins().sload32x2(flags, base, offset);
+ state.push1(loaded);
+ }
+ Operator::V128Load32x2U { memarg } => {
+ let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?;
+ let loaded = builder.ins().uload32x2(flags, base, offset);
+ state.push1(loaded);
+ }
+ /****************************** Store instructions ***********************************
+ * Wasm specifies an integer alignment flag but we drop it in Cranelift.
+ * The memory base address is provided by the environment.
+ ************************************************************************************/
+ Operator::I32Store { memarg }
+ | Operator::I64Store { memarg }
+ | Operator::F32Store { memarg }
+ | Operator::F64Store { memarg } => {
+ translate_store(memarg, ir::Opcode::Store, builder, state, environ)?;
+ }
+ Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => {
+ translate_store(memarg, ir::Opcode::Istore8, builder, state, environ)?;
+ }
+ Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => {
+ translate_store(memarg, ir::Opcode::Istore16, builder, state, environ)?;
+ }
+ Operator::I64Store32 { memarg } => {
+ translate_store(memarg, ir::Opcode::Istore32, builder, state, environ)?;
+ }
+ Operator::V128Store { memarg } => {
+ translate_store(memarg, ir::Opcode::Store, builder, state, environ)?;
+ }
+ /****************************** Nullary Operators ************************************/
+ Operator::I32Const { value } => state.push1(builder.ins().iconst(I32, i64::from(*value))),
+ Operator::I64Const { value } => state.push1(builder.ins().iconst(I64, *value)),
+ Operator::F32Const { value } => {
+ state.push1(builder.ins().f32const(f32_translation(*value)));
+ }
+ Operator::F64Const { value } => {
+ state.push1(builder.ins().f64const(f64_translation(*value)));
+ }
+ /******************************* Unary Operators *************************************/
+ Operator::I32Clz | Operator::I64Clz => {
+ let arg = state.pop1();
+ state.push1(builder.ins().clz(arg));
+ }
+ Operator::I32Ctz | Operator::I64Ctz => {
+ let arg = state.pop1();
+ state.push1(builder.ins().ctz(arg));
+ }
+ Operator::I32Popcnt | Operator::I64Popcnt => {
+ let arg = state.pop1();
+ state.push1(builder.ins().popcnt(arg));
+ }
+ Operator::I64ExtendI32S => {
+ let val = state.pop1();
+ state.push1(builder.ins().sextend(I64, val));
+ }
+ Operator::I64ExtendI32U => {
+ let val = state.pop1();
+ state.push1(builder.ins().uextend(I64, val));
+ }
+ Operator::I32WrapI64 => {
+ let val = state.pop1();
+ state.push1(builder.ins().ireduce(I32, val));
+ }
+ Operator::F32Sqrt | Operator::F64Sqrt => {
+ let arg = state.pop1();
+ state.push1(builder.ins().sqrt(arg));
+ }
+ Operator::F32Ceil | Operator::F64Ceil => {
+ let arg = state.pop1();
+ state.push1(builder.ins().ceil(arg));
+ }
+ Operator::F32Floor | Operator::F64Floor => {
+ let arg = state.pop1();
+ state.push1(builder.ins().floor(arg));
+ }
+ Operator::F32Trunc | Operator::F64Trunc => {
+ let arg = state.pop1();
+ state.push1(builder.ins().trunc(arg));
+ }
+ Operator::F32Nearest | Operator::F64Nearest => {
+ let arg = state.pop1();
+ state.push1(builder.ins().nearest(arg));
+ }
+ Operator::F32Abs | Operator::F64Abs => {
+ let val = state.pop1();
+ state.push1(builder.ins().fabs(val));
+ }
+ Operator::F32Neg | Operator::F64Neg => {
+ let arg = state.pop1();
+ state.push1(builder.ins().fneg(arg));
+ }
+ Operator::F64ConvertI64U | Operator::F64ConvertI32U => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_from_uint(F64, val));
+ }
+ Operator::F64ConvertI64S | Operator::F64ConvertI32S => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_from_sint(F64, val));
+ }
+ Operator::F32ConvertI64S | Operator::F32ConvertI32S => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_from_sint(F32, val));
+ }
+ Operator::F32ConvertI64U | Operator::F32ConvertI32U => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_from_uint(F32, val));
+ }
+ Operator::F64PromoteF32 => {
+ let val = state.pop1();
+ state.push1(builder.ins().fpromote(F64, val));
+ }
+ Operator::F32DemoteF64 => {
+ let val = state.pop1();
+ state.push1(builder.ins().fdemote(F32, val));
+ }
+ Operator::I64TruncF64S | Operator::I64TruncF32S => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_to_sint(I64, val));
+ }
+ Operator::I32TruncF64S | Operator::I32TruncF32S => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_to_sint(I32, val));
+ }
+ Operator::I64TruncF64U | Operator::I64TruncF32U => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_to_uint(I64, val));
+ }
+ Operator::I32TruncF64U | Operator::I32TruncF32U => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_to_uint(I32, val));
+ }
+ Operator::I64TruncSatF64S | Operator::I64TruncSatF32S => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_to_sint_sat(I64, val));
+ }
+ Operator::I32TruncSatF64S | Operator::I32TruncSatF32S => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_to_sint_sat(I32, val));
+ }
+ Operator::I64TruncSatF64U | Operator::I64TruncSatF32U => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_to_uint_sat(I64, val));
+ }
+ Operator::I32TruncSatF64U | Operator::I32TruncSatF32U => {
+ let val = state.pop1();
+ state.push1(builder.ins().fcvt_to_uint_sat(I32, val));
+ }
+ Operator::F32ReinterpretI32 => {
+ let val = state.pop1();
+ state.push1(builder.ins().bitcast(F32, val));
+ }
+ Operator::F64ReinterpretI64 => {
+ let val = state.pop1();
+ state.push1(builder.ins().bitcast(F64, val));
+ }
+ Operator::I32ReinterpretF32 => {
+ let val = state.pop1();
+ state.push1(builder.ins().bitcast(I32, val));
+ }
+ Operator::I64ReinterpretF64 => {
+ let val = state.pop1();
+ state.push1(builder.ins().bitcast(I64, val));
+ }
+ Operator::I32Extend8S => {
+ let val = state.pop1();
+ state.push1(builder.ins().ireduce(I8, val));
+ let val = state.pop1();
+ state.push1(builder.ins().sextend(I32, val));
+ }
+ Operator::I32Extend16S => {
+ let val = state.pop1();
+ state.push1(builder.ins().ireduce(I16, val));
+ let val = state.pop1();
+ state.push1(builder.ins().sextend(I32, val));
+ }
+ Operator::I64Extend8S => {
+ let val = state.pop1();
+ state.push1(builder.ins().ireduce(I8, val));
+ let val = state.pop1();
+ state.push1(builder.ins().sextend(I64, val));
+ }
+ Operator::I64Extend16S => {
+ let val = state.pop1();
+ state.push1(builder.ins().ireduce(I16, val));
+ let val = state.pop1();
+ state.push1(builder.ins().sextend(I64, val));
+ }
+ Operator::I64Extend32S => {
+ let val = state.pop1();
+ state.push1(builder.ins().ireduce(I32, val));
+ let val = state.pop1();
+ state.push1(builder.ins().sextend(I64, val));
+ }
+ /****************************** Binary Operators ************************************/
+ Operator::I32Add | Operator::I64Add => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().iadd(arg1, arg2));
+ }
+ Operator::I32And | Operator::I64And => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().band(arg1, arg2));
+ }
+ Operator::I32Or | Operator::I64Or => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().bor(arg1, arg2));
+ }
+ Operator::I32Xor | Operator::I64Xor => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().bxor(arg1, arg2));
+ }
+ Operator::I32Shl | Operator::I64Shl => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().ishl(arg1, arg2));
+ }
+ Operator::I32ShrS | Operator::I64ShrS => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().sshr(arg1, arg2));
+ }
+ Operator::I32ShrU | Operator::I64ShrU => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().ushr(arg1, arg2));
+ }
+ Operator::I32Rotl | Operator::I64Rotl => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().rotl(arg1, arg2));
+ }
+ Operator::I32Rotr | Operator::I64Rotr => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().rotr(arg1, arg2));
+ }
+ Operator::F32Add | Operator::F64Add => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().fadd(arg1, arg2));
+ }
+ Operator::I32Sub | Operator::I64Sub => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().isub(arg1, arg2));
+ }
+ Operator::F32Sub | Operator::F64Sub => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().fsub(arg1, arg2));
+ }
+ Operator::I32Mul | Operator::I64Mul => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().imul(arg1, arg2));
+ }
+ Operator::F32Mul | Operator::F64Mul => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().fmul(arg1, arg2));
+ }
+ Operator::F32Div | Operator::F64Div => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().fdiv(arg1, arg2));
+ }
+ Operator::I32DivS | Operator::I64DivS => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().sdiv(arg1, arg2));
+ }
+ Operator::I32DivU | Operator::I64DivU => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().udiv(arg1, arg2));
+ }
+ Operator::I32RemS | Operator::I64RemS => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().srem(arg1, arg2));
+ }
+ Operator::I32RemU | Operator::I64RemU => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().urem(arg1, arg2));
+ }
+ Operator::F32Min | Operator::F64Min => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().fmin(arg1, arg2));
+ }
+ Operator::F32Max | Operator::F64Max => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().fmax(arg1, arg2));
+ }
+ Operator::F32Copysign | Operator::F64Copysign => {
+ let (arg1, arg2) = state.pop2();
+ state.push1(builder.ins().fcopysign(arg1, arg2));
+ }
+ /**************************** Comparison Operators **********************************/
+ Operator::I32LtS | Operator::I64LtS => {
+ translate_icmp(IntCC::SignedLessThan, builder, state)
+ }
+ Operator::I32LtU | Operator::I64LtU => {
+ translate_icmp(IntCC::UnsignedLessThan, builder, state)
+ }
+ Operator::I32LeS | Operator::I64LeS => {
+ translate_icmp(IntCC::SignedLessThanOrEqual, builder, state)
+ }
+ Operator::I32LeU | Operator::I64LeU => {
+ translate_icmp(IntCC::UnsignedLessThanOrEqual, builder, state)
+ }
+ Operator::I32GtS | Operator::I64GtS => {
+ translate_icmp(IntCC::SignedGreaterThan, builder, state)
+ }
+ Operator::I32GtU | Operator::I64GtU => {
+ translate_icmp(IntCC::UnsignedGreaterThan, builder, state)
+ }
+ Operator::I32GeS | Operator::I64GeS => {
+ translate_icmp(IntCC::SignedGreaterThanOrEqual, builder, state)
+ }
+ Operator::I32GeU | Operator::I64GeU => {
+ translate_icmp(IntCC::UnsignedGreaterThanOrEqual, builder, state)
+ }
+ Operator::I32Eqz | Operator::I64Eqz => {
+ let arg = state.pop1();
+ let val = builder.ins().icmp_imm(IntCC::Equal, arg, 0);
+ state.push1(builder.ins().bint(I32, val));
+ }
+ Operator::I32Eq | Operator::I64Eq => translate_icmp(IntCC::Equal, builder, state),
+ Operator::F32Eq | Operator::F64Eq => translate_fcmp(FloatCC::Equal, builder, state),
+ Operator::I32Ne | Operator::I64Ne => translate_icmp(IntCC::NotEqual, builder, state),
+ Operator::F32Ne | Operator::F64Ne => translate_fcmp(FloatCC::NotEqual, builder, state),
+ Operator::F32Gt | Operator::F64Gt => translate_fcmp(FloatCC::GreaterThan, builder, state),
+ Operator::F32Ge | Operator::F64Ge => {
+ translate_fcmp(FloatCC::GreaterThanOrEqual, builder, state)
+ }
+ Operator::F32Lt | Operator::F64Lt => translate_fcmp(FloatCC::LessThan, builder, state),
+ Operator::F32Le | Operator::F64Le => {
+ translate_fcmp(FloatCC::LessThanOrEqual, builder, state)
+ }
+ Operator::RefNull { ty } => {
+ state.push1(environ.translate_ref_null(builder.cursor(), (*ty).try_into()?)?)
+ }
+ Operator::RefIsNull => {
+ let value = state.pop1();
+ state.push1(environ.translate_ref_is_null(builder.cursor(), value)?);
+ }
+ Operator::RefFunc { function_index } => {
+ let index = FuncIndex::from_u32(*function_index);
+ state.push1(environ.translate_ref_func(builder.cursor(), index)?);
+ }
+ Operator::MemoryAtomicWait32 { memarg } | Operator::MemoryAtomicWait64 { memarg } => {
+ // The WebAssembly MVP only supports one linear memory and
+ // wasmparser will ensure that the memory indices specified are
+ // zero.
+ let implied_ty = match op {
+ Operator::MemoryAtomicWait64 { .. } => I64,
+ Operator::MemoryAtomicWait32 { .. } => I32,
+ _ => unreachable!(),
+ };
+ let heap_index = MemoryIndex::from_u32(memarg.memory);
+ let heap = state.get_heap(builder.func, memarg.memory, environ)?;
+ let timeout = state.pop1(); // 64 (fixed)
+ let expected = state.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`)
+ let addr = state.pop1(); // 32 (fixed)
+ assert!(builder.func.dfg.value_type(expected) == implied_ty);
+ // `fn translate_atomic_wait` can inspect the type of `expected` to figure out what
+ // code it needs to generate, if it wants.
+ let res = environ.translate_atomic_wait(
+ builder.cursor(),
+ heap_index,
+ heap,
+ addr,
+ expected,
+ timeout,
+ )?;
+ state.push1(res);
+ }
+ Operator::MemoryAtomicNotify { memarg } => {
+ let heap_index = MemoryIndex::from_u32(memarg.memory);
+ let heap = state.get_heap(builder.func, memarg.memory, environ)?;
+ let count = state.pop1(); // 32 (fixed)
+ let addr = state.pop1(); // 32 (fixed)
+ let res =
+ environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count)?;
+ state.push1(res);
+ }
+ Operator::I32AtomicLoad { memarg } => {
+ translate_atomic_load(I32, I32, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicLoad { memarg } => {
+ translate_atomic_load(I64, I64, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicLoad8U { memarg } => {
+ translate_atomic_load(I32, I8, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicLoad16U { memarg } => {
+ translate_atomic_load(I32, I16, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicLoad8U { memarg } => {
+ translate_atomic_load(I64, I8, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicLoad16U { memarg } => {
+ translate_atomic_load(I64, I16, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicLoad32U { memarg } => {
+ translate_atomic_load(I64, I32, memarg, builder, state, environ)?
+ }
+
+ Operator::I32AtomicStore { memarg } => {
+ translate_atomic_store(I32, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicStore { memarg } => {
+ translate_atomic_store(I64, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicStore8 { memarg } => {
+ translate_atomic_store(I8, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicStore16 { memarg } => {
+ translate_atomic_store(I16, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicStore8 { memarg } => {
+ translate_atomic_store(I8, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicStore16 { memarg } => {
+ translate_atomic_store(I16, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicStore32 { memarg } => {
+ translate_atomic_store(I32, memarg, builder, state, environ)?
+ }
+
+ Operator::I32AtomicRmwAdd { memarg } => {
+ translate_atomic_rmw(I32, I32, AtomicRmwOp::Add, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmwAdd { memarg } => {
+ translate_atomic_rmw(I64, I64, AtomicRmwOp::Add, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw8AddU { memarg } => {
+ translate_atomic_rmw(I32, I8, AtomicRmwOp::Add, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw16AddU { memarg } => {
+ translate_atomic_rmw(I32, I16, AtomicRmwOp::Add, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw8AddU { memarg } => {
+ translate_atomic_rmw(I64, I8, AtomicRmwOp::Add, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw16AddU { memarg } => {
+ translate_atomic_rmw(I64, I16, AtomicRmwOp::Add, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw32AddU { memarg } => {
+ translate_atomic_rmw(I64, I32, AtomicRmwOp::Add, memarg, builder, state, environ)?
+ }
+
+ Operator::I32AtomicRmwSub { memarg } => {
+ translate_atomic_rmw(I32, I32, AtomicRmwOp::Sub, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmwSub { memarg } => {
+ translate_atomic_rmw(I64, I64, AtomicRmwOp::Sub, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw8SubU { memarg } => {
+ translate_atomic_rmw(I32, I8, AtomicRmwOp::Sub, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw16SubU { memarg } => {
+ translate_atomic_rmw(I32, I16, AtomicRmwOp::Sub, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw8SubU { memarg } => {
+ translate_atomic_rmw(I64, I8, AtomicRmwOp::Sub, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw16SubU { memarg } => {
+ translate_atomic_rmw(I64, I16, AtomicRmwOp::Sub, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw32SubU { memarg } => {
+ translate_atomic_rmw(I64, I32, AtomicRmwOp::Sub, memarg, builder, state, environ)?
+ }
+
+ Operator::I32AtomicRmwAnd { memarg } => {
+ translate_atomic_rmw(I32, I32, AtomicRmwOp::And, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmwAnd { memarg } => {
+ translate_atomic_rmw(I64, I64, AtomicRmwOp::And, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw8AndU { memarg } => {
+ translate_atomic_rmw(I32, I8, AtomicRmwOp::And, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw16AndU { memarg } => {
+ translate_atomic_rmw(I32, I16, AtomicRmwOp::And, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw8AndU { memarg } => {
+ translate_atomic_rmw(I64, I8, AtomicRmwOp::And, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw16AndU { memarg } => {
+ translate_atomic_rmw(I64, I16, AtomicRmwOp::And, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw32AndU { memarg } => {
+ translate_atomic_rmw(I64, I32, AtomicRmwOp::And, memarg, builder, state, environ)?
+ }
+
+ Operator::I32AtomicRmwOr { memarg } => {
+ translate_atomic_rmw(I32, I32, AtomicRmwOp::Or, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmwOr { memarg } => {
+ translate_atomic_rmw(I64, I64, AtomicRmwOp::Or, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw8OrU { memarg } => {
+ translate_atomic_rmw(I32, I8, AtomicRmwOp::Or, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw16OrU { memarg } => {
+ translate_atomic_rmw(I32, I16, AtomicRmwOp::Or, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw8OrU { memarg } => {
+ translate_atomic_rmw(I64, I8, AtomicRmwOp::Or, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw16OrU { memarg } => {
+ translate_atomic_rmw(I64, I16, AtomicRmwOp::Or, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw32OrU { memarg } => {
+ translate_atomic_rmw(I64, I32, AtomicRmwOp::Or, memarg, builder, state, environ)?
+ }
+
+ Operator::I32AtomicRmwXor { memarg } => {
+ translate_atomic_rmw(I32, I32, AtomicRmwOp::Xor, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmwXor { memarg } => {
+ translate_atomic_rmw(I64, I64, AtomicRmwOp::Xor, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw8XorU { memarg } => {
+ translate_atomic_rmw(I32, I8, AtomicRmwOp::Xor, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw16XorU { memarg } => {
+ translate_atomic_rmw(I32, I16, AtomicRmwOp::Xor, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw8XorU { memarg } => {
+ translate_atomic_rmw(I64, I8, AtomicRmwOp::Xor, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw16XorU { memarg } => {
+ translate_atomic_rmw(I64, I16, AtomicRmwOp::Xor, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw32XorU { memarg } => {
+ translate_atomic_rmw(I64, I32, AtomicRmwOp::Xor, memarg, builder, state, environ)?
+ }
+
+ Operator::I32AtomicRmwXchg { memarg } => {
+ translate_atomic_rmw(I32, I32, AtomicRmwOp::Xchg, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmwXchg { memarg } => {
+ translate_atomic_rmw(I64, I64, AtomicRmwOp::Xchg, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw8XchgU { memarg } => {
+ translate_atomic_rmw(I32, I8, AtomicRmwOp::Xchg, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw16XchgU { memarg } => {
+ translate_atomic_rmw(I32, I16, AtomicRmwOp::Xchg, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw8XchgU { memarg } => {
+ translate_atomic_rmw(I64, I8, AtomicRmwOp::Xchg, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw16XchgU { memarg } => {
+ translate_atomic_rmw(I64, I16, AtomicRmwOp::Xchg, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw32XchgU { memarg } => {
+ translate_atomic_rmw(I64, I32, AtomicRmwOp::Xchg, memarg, builder, state, environ)?
+ }
+
+ Operator::I32AtomicRmwCmpxchg { memarg } => {
+ translate_atomic_cas(I32, I32, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmwCmpxchg { memarg } => {
+ translate_atomic_cas(I64, I64, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw8CmpxchgU { memarg } => {
+ translate_atomic_cas(I32, I8, memarg, builder, state, environ)?
+ }
+ Operator::I32AtomicRmw16CmpxchgU { memarg } => {
+ translate_atomic_cas(I32, I16, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw8CmpxchgU { memarg } => {
+ translate_atomic_cas(I64, I8, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw16CmpxchgU { memarg } => {
+ translate_atomic_cas(I64, I16, memarg, builder, state, environ)?
+ }
+ Operator::I64AtomicRmw32CmpxchgU { memarg } => {
+ translate_atomic_cas(I64, I32, memarg, builder, state, environ)?
+ }
+
+ Operator::AtomicFence { .. } => {
+ builder.ins().fence();
+ }
+ Operator::MemoryCopy { src, dst } => {
+ let src_index = MemoryIndex::from_u32(*src);
+ let dst_index = MemoryIndex::from_u32(*dst);
+ let src_heap = state.get_heap(builder.func, *src, environ)?;
+ let dst_heap = state.get_heap(builder.func, *dst, environ)?;
+ let len = state.pop1();
+ let src_pos = state.pop1();
+ let dst_pos = state.pop1();
+ environ.translate_memory_copy(
+ builder.cursor(),
+ src_index,
+ src_heap,
+ dst_index,
+ dst_heap,
+ dst_pos,
+ src_pos,
+ len,
+ )?;
+ }
+ Operator::MemoryFill { mem } => {
+ let heap_index = MemoryIndex::from_u32(*mem);
+ let heap = state.get_heap(builder.func, *mem, environ)?;
+ let len = state.pop1();
+ let val = state.pop1();
+ let dest = state.pop1();
+ environ.translate_memory_fill(builder.cursor(), heap_index, heap, dest, val, len)?;
+ }
+ Operator::MemoryInit { segment, mem } => {
+ let heap_index = MemoryIndex::from_u32(*mem);
+ let heap = state.get_heap(builder.func, *mem, environ)?;
+ let len = state.pop1();
+ let src = state.pop1();
+ let dest = state.pop1();
+ environ.translate_memory_init(
+ builder.cursor(),
+ heap_index,
+ heap,
+ *segment,
+ dest,
+ src,
+ len,
+ )?;
+ }
+ Operator::DataDrop { segment } => {
+ environ.translate_data_drop(builder.cursor(), *segment)?;
+ }
+ Operator::TableSize { table: index } => {
+ let table = state.get_or_create_table(builder.func, *index, environ)?;
+ state.push1(environ.translate_table_size(
+ builder.cursor(),
+ TableIndex::from_u32(*index),
+ table,
+ )?);
+ }
+ Operator::TableGrow { table: index } => {
+ let table_index = TableIndex::from_u32(*index);
+ let table = state.get_or_create_table(builder.func, *index, environ)?;
+ let delta = state.pop1();
+ let init_value = state.pop1();
+ state.push1(environ.translate_table_grow(
+ builder.cursor(),
+ table_index,
+ table,
+ delta,
+ init_value,
+ )?);
+ }
+ Operator::TableGet { table: index } => {
+ let table_index = TableIndex::from_u32(*index);
+ let table = state.get_or_create_table(builder.func, *index, environ)?;
+ let index = state.pop1();
+ state.push1(environ.translate_table_get(builder, table_index, table, index)?);
+ }
+ Operator::TableSet { table: index } => {
+ let table_index = TableIndex::from_u32(*index);
+ let table = state.get_or_create_table(builder.func, *index, environ)?;
+ let value = state.pop1();
+ let index = state.pop1();
+ environ.translate_table_set(builder, table_index, table, value, index)?;
+ }
+ Operator::TableCopy {
+ dst_table: dst_table_index,
+ src_table: src_table_index,
+ } => {
+ let dst_table = state.get_or_create_table(builder.func, *dst_table_index, environ)?;
+ let src_table = state.get_or_create_table(builder.func, *src_table_index, environ)?;
+ let len = state.pop1();
+ let src = state.pop1();
+ let dest = state.pop1();
+ environ.translate_table_copy(
+ builder.cursor(),
+ TableIndex::from_u32(*dst_table_index),
+ dst_table,
+ TableIndex::from_u32(*src_table_index),
+ src_table,
+ dest,
+ src,
+ len,
+ )?;
+ }
+ Operator::TableFill { table } => {
+ let table_index = TableIndex::from_u32(*table);
+ let len = state.pop1();
+ let val = state.pop1();
+ let dest = state.pop1();
+ environ.translate_table_fill(builder.cursor(), table_index, dest, val, len)?;
+ }
+ Operator::TableInit {
+ segment,
+ table: table_index,
+ } => {
+ let table = state.get_or_create_table(builder.func, *table_index, environ)?;
+ let len = state.pop1();
+ let src = state.pop1();
+ let dest = state.pop1();
+ environ.translate_table_init(
+ builder.cursor(),
+ *segment,
+ TableIndex::from_u32(*table_index),
+ table,
+ dest,
+ src,
+ len,
+ )?;
+ }
+ Operator::ElemDrop { segment } => {
+ environ.translate_elem_drop(builder.cursor(), *segment)?;
+ }
+ Operator::V128Const { value } => {
+ let data = value.bytes().to_vec().into();
+ let handle = builder.func.dfg.constants.insert(data);
+ let value = builder.ins().vconst(I8X16, handle);
+ // the v128.const is typed in CLIF as a I8x16 but raw_bitcast to a different type
+ // before use
+ state.push1(value)
+ }
+ Operator::I8x16Splat | Operator::I16x8Splat => {
+ let reduced = builder.ins().ireduce(type_of(op).lane_type(), state.pop1());
+ let splatted = builder.ins().splat(type_of(op), reduced);
+ state.push1(splatted)
+ }
+ Operator::I32x4Splat
+ | Operator::I64x2Splat
+ | Operator::F32x4Splat
+ | Operator::F64x2Splat => {
+ let splatted = builder.ins().splat(type_of(op), state.pop1());
+ state.push1(splatted)
+ }
+ Operator::V128Load8Splat { memarg }
+ | Operator::V128Load16Splat { memarg }
+ | Operator::V128Load32Splat { memarg }
+ | Operator::V128Load64Splat { memarg } => {
+ let opcode = ir::Opcode::LoadSplat;
+ let result_ty = type_of(op);
+ let (flags, base, offset) = prepare_load(
+ memarg,
+ mem_op_size(opcode, result_ty.lane_type()),
+ builder,
+ state,
+ environ,
+ )?;
+ let (load, dfg) = builder.ins().Load(opcode, result_ty, flags, offset, base);
+ state.push1(dfg.first_result(load))
+ }
+ Operator::V128Load32Zero { memarg } | Operator::V128Load64Zero { memarg } => {
+ translate_load(
+ memarg,
+ ir::Opcode::Load,
+ type_of(op).lane_type(),
+ builder,
+ state,
+ environ,
+ )?;
+ let as_vector = builder.ins().scalar_to_vector(type_of(op), state.pop1());
+ state.push1(as_vector)
+ }
+ Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => {
+ let vector = pop1_with_bitcast(state, type_of(op), builder);
+ let extracted = builder.ins().extractlane(vector, lane.clone());
+ state.push1(builder.ins().sextend(I32, extracted))
+ }
+ Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => {
+ let vector = pop1_with_bitcast(state, type_of(op), builder);
+ let extracted = builder.ins().extractlane(vector, lane.clone());
+ state.push1(builder.ins().uextend(I32, extracted));
+ // On x86, PEXTRB zeroes the upper bits of the destination register of extractlane so
+ // uextend could be elided; for now, uextend is needed for Cranelift's type checks to
+ // work.
+ }
+ Operator::I32x4ExtractLane { lane }
+ | Operator::I64x2ExtractLane { lane }
+ | Operator::F32x4ExtractLane { lane }
+ | Operator::F64x2ExtractLane { lane } => {
+ let vector = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().extractlane(vector, lane.clone()))
+ }
+ Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => {
+ let (vector, replacement) = state.pop2();
+ let ty = type_of(op);
+ let reduced = builder.ins().ireduce(ty.lane_type(), replacement);
+ let vector = optionally_bitcast_vector(vector, ty, builder);
+ state.push1(builder.ins().insertlane(vector, reduced, *lane))
+ }
+ Operator::I32x4ReplaceLane { lane }
+ | Operator::I64x2ReplaceLane { lane }
+ | Operator::F32x4ReplaceLane { lane }
+ | Operator::F64x2ReplaceLane { lane } => {
+ let (vector, replacement) = state.pop2();
+ let vector = optionally_bitcast_vector(vector, type_of(op), builder);
+ state.push1(builder.ins().insertlane(vector, replacement, *lane))
+ }
+ Operator::I8x16Shuffle { lanes, .. } => {
+ let (a, b) = pop2_with_bitcast(state, I8X16, builder);
+ let lanes = ConstantData::from(lanes.as_ref());
+ let mask = builder.func.dfg.immediates.push(lanes);
+ let shuffled = builder.ins().shuffle(a, b, mask);
+ state.push1(shuffled)
+ // At this point the original types of a and b are lost; users of this value (i.e. this
+ // WASM-to-CLIF translator) may need to raw_bitcast for type-correctness. This is due
+ // to WASM using the less specific v128 type for certain operations and more specific
+ // types (e.g. i8x16) for others.
+ }
+ Operator::I8x16Swizzle => {
+ let (a, b) = pop2_with_bitcast(state, I8X16, builder);
+ state.push1(builder.ins().swizzle(I8X16, a, b))
+ }
+ Operator::I8x16Add | Operator::I16x8Add | Operator::I32x4Add | Operator::I64x2Add => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().iadd(a, b))
+ }
+ Operator::I8x16AddSatS | Operator::I16x8AddSatS => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().sadd_sat(a, b))
+ }
+ Operator::I8x16AddSatU | Operator::I16x8AddSatU => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().uadd_sat(a, b))
+ }
+ Operator::I8x16Sub | Operator::I16x8Sub | Operator::I32x4Sub | Operator::I64x2Sub => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().isub(a, b))
+ }
+ Operator::I8x16SubSatS | Operator::I16x8SubSatS => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().ssub_sat(a, b))
+ }
+ Operator::I8x16SubSatU | Operator::I16x8SubSatU => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().usub_sat(a, b))
+ }
+ Operator::I8x16MinS | Operator::I16x8MinS | Operator::I32x4MinS => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().imin(a, b))
+ }
+ Operator::I8x16MinU | Operator::I16x8MinU | Operator::I32x4MinU => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().umin(a, b))
+ }
+ Operator::I8x16MaxS | Operator::I16x8MaxS | Operator::I32x4MaxS => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().imax(a, b))
+ }
+ Operator::I8x16MaxU | Operator::I16x8MaxU | Operator::I32x4MaxU => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().umax(a, b))
+ }
+ Operator::I8x16RoundingAverageU | Operator::I16x8RoundingAverageU => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().avg_round(a, b))
+ }
+ Operator::I8x16Neg | Operator::I16x8Neg | Operator::I32x4Neg | Operator::I64x2Neg => {
+ let a = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().ineg(a))
+ }
+ Operator::I8x16Abs | Operator::I16x8Abs | Operator::I32x4Abs => {
+ let a = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().iabs(a))
+ }
+ Operator::I16x8Mul | Operator::I32x4Mul | Operator::I64x2Mul => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().imul(a, b))
+ }
+ Operator::V128Or => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().bor(a, b))
+ }
+ Operator::V128Xor => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().bxor(a, b))
+ }
+ Operator::V128And => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().band(a, b))
+ }
+ Operator::V128AndNot => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().band_not(a, b))
+ }
+ Operator::V128Not => {
+ let a = state.pop1();
+ state.push1(builder.ins().bnot(a));
+ }
+ Operator::I8x16Shl | Operator::I16x8Shl | Operator::I32x4Shl | Operator::I64x2Shl => {
+ let (a, b) = state.pop2();
+ let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);
+ let bitwidth = i64::from(type_of(op).lane_bits());
+ // The spec expects to shift with `b mod lanewidth`; so, e.g., for 16 bit lane-width
+ // we do `b AND 15`; this means fewer instructions than `iconst + urem`.
+ let b_mod_bitwidth = builder.ins().band_imm(b, bitwidth - 1);
+ state.push1(builder.ins().ishl(bitcast_a, b_mod_bitwidth))
+ }
+ Operator::I8x16ShrU | Operator::I16x8ShrU | Operator::I32x4ShrU | Operator::I64x2ShrU => {
+ let (a, b) = state.pop2();
+ let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);
+ let bitwidth = i64::from(type_of(op).lane_bits());
+ // The spec expects to shift with `b mod lanewidth`; so, e.g., for 16 bit lane-width
+ // we do `b AND 15`; this means fewer instructions than `iconst + urem`.
+ let b_mod_bitwidth = builder.ins().band_imm(b, bitwidth - 1);
+ state.push1(builder.ins().ushr(bitcast_a, b_mod_bitwidth))
+ }
+ Operator::I8x16ShrS | Operator::I16x8ShrS | Operator::I32x4ShrS | Operator::I64x2ShrS => {
+ let (a, b) = state.pop2();
+ let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);
+ let bitwidth = i64::from(type_of(op).lane_bits());
+ // The spec expects to shift with `b mod lanewidth`; so, e.g., for 16 bit lane-width
+ // we do `b AND 15`; this means fewer instructions than `iconst + urem`.
+ let b_mod_bitwidth = builder.ins().band_imm(b, bitwidth - 1);
+ state.push1(builder.ins().sshr(bitcast_a, b_mod_bitwidth))
+ }
+ Operator::V128Bitselect => {
+ let (a, b, c) = state.pop3();
+ let bitcast_a = optionally_bitcast_vector(a, I8X16, builder);
+ let bitcast_b = optionally_bitcast_vector(b, I8X16, builder);
+ let bitcast_c = optionally_bitcast_vector(c, I8X16, builder);
+ // The CLIF operand ordering is slightly different and the types of all three
+ // operands must match (hence the bitcast).
+ state.push1(builder.ins().bitselect(bitcast_c, bitcast_a, bitcast_b))
+ }
+ Operator::I8x16AnyTrue | Operator::I16x8AnyTrue | Operator::I32x4AnyTrue => {
+ let a = pop1_with_bitcast(state, type_of(op), builder);
+ let bool_result = builder.ins().vany_true(a);
+ state.push1(builder.ins().bint(I32, bool_result))
+ }
+ Operator::I8x16AllTrue | Operator::I16x8AllTrue | Operator::I32x4AllTrue => {
+ let a = pop1_with_bitcast(state, type_of(op), builder);
+ let bool_result = builder.ins().vall_true(a);
+ state.push1(builder.ins().bint(I32, bool_result))
+ }
+ Operator::I8x16Bitmask | Operator::I16x8Bitmask | Operator::I32x4Bitmask => {
+ let a = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().vhigh_bits(I32, a));
+ }
+ Operator::I8x16Eq | Operator::I16x8Eq | Operator::I32x4Eq => {
+ translate_vector_icmp(IntCC::Equal, type_of(op), builder, state)
+ }
+ Operator::I8x16Ne | Operator::I16x8Ne | Operator::I32x4Ne => {
+ translate_vector_icmp(IntCC::NotEqual, type_of(op), builder, state)
+ }
+ Operator::I8x16GtS | Operator::I16x8GtS | Operator::I32x4GtS => {
+ translate_vector_icmp(IntCC::SignedGreaterThan, type_of(op), builder, state)
+ }
+ Operator::I8x16LtS | Operator::I16x8LtS | Operator::I32x4LtS => {
+ translate_vector_icmp(IntCC::SignedLessThan, type_of(op), builder, state)
+ }
+ Operator::I8x16GtU | Operator::I16x8GtU | Operator::I32x4GtU => {
+ translate_vector_icmp(IntCC::UnsignedGreaterThan, type_of(op), builder, state)
+ }
+ Operator::I8x16LtU | Operator::I16x8LtU | Operator::I32x4LtU => {
+ translate_vector_icmp(IntCC::UnsignedLessThan, type_of(op), builder, state)
+ }
+ Operator::I8x16GeS | Operator::I16x8GeS | Operator::I32x4GeS => {
+ translate_vector_icmp(IntCC::SignedGreaterThanOrEqual, type_of(op), builder, state)
+ }
+ Operator::I8x16LeS | Operator::I16x8LeS | Operator::I32x4LeS => {
+ translate_vector_icmp(IntCC::SignedLessThanOrEqual, type_of(op), builder, state)
+ }
+ Operator::I8x16GeU | Operator::I16x8GeU | Operator::I32x4GeU => translate_vector_icmp(
+ IntCC::UnsignedGreaterThanOrEqual,
+ type_of(op),
+ builder,
+ state,
+ ),
+ Operator::I8x16LeU | Operator::I16x8LeU | Operator::I32x4LeU => {
+ translate_vector_icmp(IntCC::UnsignedLessThanOrEqual, type_of(op), builder, state)
+ }
+ Operator::F32x4Eq | Operator::F64x2Eq => {
+ translate_vector_fcmp(FloatCC::Equal, type_of(op), builder, state)
+ }
+ Operator::F32x4Ne | Operator::F64x2Ne => {
+ translate_vector_fcmp(FloatCC::NotEqual, type_of(op), builder, state)
+ }
+ Operator::F32x4Lt | Operator::F64x2Lt => {
+ translate_vector_fcmp(FloatCC::LessThan, type_of(op), builder, state)
+ }
+ Operator::F32x4Gt | Operator::F64x2Gt => {
+ translate_vector_fcmp(FloatCC::GreaterThan, type_of(op), builder, state)
+ }
+ Operator::F32x4Le | Operator::F64x2Le => {
+ translate_vector_fcmp(FloatCC::LessThanOrEqual, type_of(op), builder, state)
+ }
+ Operator::F32x4Ge | Operator::F64x2Ge => {
+ translate_vector_fcmp(FloatCC::GreaterThanOrEqual, type_of(op), builder, state)
+ }
+ Operator::F32x4Add | Operator::F64x2Add => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().fadd(a, b))
+ }
+ Operator::F32x4Sub | Operator::F64x2Sub => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().fsub(a, b))
+ }
+ Operator::F32x4Mul | Operator::F64x2Mul => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().fmul(a, b))
+ }
+ Operator::F32x4Div | Operator::F64x2Div => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().fdiv(a, b))
+ }
+ Operator::F32x4Max | Operator::F64x2Max => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().fmax(a, b))
+ }
+ Operator::F32x4Min | Operator::F64x2Min => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().fmin(a, b))
+ }
+ Operator::F32x4PMax | Operator::F64x2PMax => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().fmax_pseudo(a, b))
+ }
+ Operator::F32x4PMin | Operator::F64x2PMin => {
+ let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().fmin_pseudo(a, b))
+ }
+ Operator::F32x4Sqrt | Operator::F64x2Sqrt => {
+ let a = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().sqrt(a))
+ }
+ Operator::F32x4Neg | Operator::F64x2Neg => {
+ let a = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().fneg(a))
+ }
+ Operator::F32x4Abs | Operator::F64x2Abs => {
+ let a = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().fabs(a))
+ }
+ Operator::F32x4ConvertI32x4S => {
+ let a = pop1_with_bitcast(state, I32X4, builder);
+ state.push1(builder.ins().fcvt_from_sint(F32X4, a))
+ }
+ Operator::F32x4ConvertI32x4U => {
+ let a = pop1_with_bitcast(state, I32X4, builder);
+ state.push1(builder.ins().fcvt_from_uint(F32X4, a))
+ }
+ Operator::I32x4TruncSatF32x4S => {
+ let a = pop1_with_bitcast(state, F32X4, builder);
+ state.push1(builder.ins().fcvt_to_sint_sat(I32X4, a))
+ }
+ Operator::I32x4TruncSatF32x4U => {
+ let a = pop1_with_bitcast(state, F32X4, builder);
+ state.push1(builder.ins().fcvt_to_uint_sat(I32X4, a))
+ }
+ Operator::I8x16NarrowI16x8S => {
+ let (a, b) = pop2_with_bitcast(state, I16X8, builder);
+ state.push1(builder.ins().snarrow(a, b))
+ }
+ Operator::I16x8NarrowI32x4S => {
+ let (a, b) = pop2_with_bitcast(state, I32X4, builder);
+ state.push1(builder.ins().snarrow(a, b))
+ }
+ Operator::I8x16NarrowI16x8U => {
+ let (a, b) = pop2_with_bitcast(state, I16X8, builder);
+ state.push1(builder.ins().unarrow(a, b))
+ }
+ Operator::I16x8NarrowI32x4U => {
+ let (a, b) = pop2_with_bitcast(state, I32X4, builder);
+ state.push1(builder.ins().unarrow(a, b))
+ }
+ Operator::I16x8WidenLowI8x16S => {
+ let a = pop1_with_bitcast(state, I8X16, builder);
+ state.push1(builder.ins().swiden_low(a))
+ }
+ Operator::I16x8WidenHighI8x16S => {
+ let a = pop1_with_bitcast(state, I8X16, builder);
+ state.push1(builder.ins().swiden_high(a))
+ }
+ Operator::I16x8WidenLowI8x16U => {
+ let a = pop1_with_bitcast(state, I8X16, builder);
+ state.push1(builder.ins().uwiden_low(a))
+ }
+ Operator::I16x8WidenHighI8x16U => {
+ let a = pop1_with_bitcast(state, I8X16, builder);
+ state.push1(builder.ins().uwiden_high(a))
+ }
+ Operator::I32x4WidenLowI16x8S => {
+ let a = pop1_with_bitcast(state, I16X8, builder);
+ state.push1(builder.ins().swiden_low(a))
+ }
+ Operator::I32x4WidenHighI16x8S => {
+ let a = pop1_with_bitcast(state, I16X8, builder);
+ state.push1(builder.ins().swiden_high(a))
+ }
+ Operator::I32x4WidenLowI16x8U => {
+ let a = pop1_with_bitcast(state, I16X8, builder);
+ state.push1(builder.ins().uwiden_low(a))
+ }
+ Operator::I32x4WidenHighI16x8U => {
+ let a = pop1_with_bitcast(state, I16X8, builder);
+ state.push1(builder.ins().uwiden_high(a))
+ }
+
+ Operator::F32x4Ceil | Operator::F64x2Ceil => {
+ // This is something of a misuse of `type_of`, because that produces the return type
+ // of `op`. In this case we want the arg type, but we know it's the same as the
+ // return type. Same for the 3 cases below.
+ let arg = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().ceil(arg));
+ }
+ Operator::F32x4Floor | Operator::F64x2Floor => {
+ let arg = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().floor(arg));
+ }
+ Operator::F32x4Trunc | Operator::F64x2Trunc => {
+ let arg = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().trunc(arg));
+ }
+ Operator::F32x4Nearest | Operator::F64x2Nearest => {
+ let arg = pop1_with_bitcast(state, type_of(op), builder);
+ state.push1(builder.ins().nearest(arg));
+ }
+
+ Operator::I32x4DotI16x8S => {
+ let (a, b) = pop2_with_bitcast(state, I16X8, builder);
+ state.push1(builder.ins().widening_pairwise_dot_product_s(a, b));
+ }
+
+ Operator::ReturnCall { .. } | Operator::ReturnCallIndirect { .. } => {
+ return Err(wasm_unsupported!("proposed tail-call operator {:?}", op));
+ }
+ };
+ Ok(())
+}
+
+// Clippy warns us of some fields we are deliberately ignoring
+#[cfg_attr(feature = "cargo-clippy", allow(clippy::unneeded_field_pattern))]
+/// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them
+/// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable
+/// portion so the translation state must be updated accordingly.
+fn translate_unreachable_operator<FE: FuncEnvironment + ?Sized>(
+ validator: &FuncValidator<impl WasmModuleResources>,
+ op: &Operator,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<()> {
+ debug_assert!(!state.reachable);
+ match *op {
+ Operator::If { ty } => {
+ // Push a placeholder control stack entry. The if isn't reachable,
+ // so we don't have any branches anywhere.
+ state.push_if(
+ ir::Block::reserved_value(),
+ ElseData::NoElse {
+ branch_inst: ir::Inst::reserved_value(),
+ },
+ 0,
+ 0,
+ ty,
+ );
+ }
+ Operator::Loop { ty: _ } | Operator::Block { ty: _ } => {
+ state.push_block(ir::Block::reserved_value(), 0, 0);
+ }
+ Operator::Else => {
+ let i = state.control_stack.len() - 1;
+ match state.control_stack[i] {
+ ControlStackFrame::If {
+ ref else_data,
+ head_is_reachable,
+ ref mut consequent_ends_reachable,
+ blocktype,
+ ..
+ } => {
+ debug_assert!(consequent_ends_reachable.is_none());
+ *consequent_ends_reachable = Some(state.reachable);
+
+ if head_is_reachable {
+ // We have a branch from the head of the `if` to the `else`.
+ state.reachable = true;
+
+ let else_block = match *else_data {
+ ElseData::NoElse { branch_inst } => {
+ let (params, _results) =
+ blocktype_params_results(validator, blocktype)?;
+ let else_block = block_with_params(builder, params, environ)?;
+ let frame = state.control_stack.last().unwrap();
+ frame.truncate_value_stack_to_else_params(&mut state.stack);
+
+ // We change the target of the branch instruction.
+ builder.change_jump_destination(branch_inst, else_block);
+ builder.seal_block(else_block);
+ else_block
+ }
+ ElseData::WithElse { else_block } => {
+ let frame = state.control_stack.last().unwrap();
+ frame.truncate_value_stack_to_else_params(&mut state.stack);
+ else_block
+ }
+ };
+
+ builder.switch_to_block(else_block);
+
+ // Again, no need to push the parameters for the `else`,
+ // since we already did when we saw the original `if`. See
+ // the comment for translating `Operator::Else` in
+ // `translate_operator` for details.
+ }
+ }
+ _ => unreachable!(),
+ }
+ }
+ Operator::End => {
+ let stack = &mut state.stack;
+ let control_stack = &mut state.control_stack;
+ let frame = control_stack.pop().unwrap();
+
+ // Pop unused parameters from stack.
+ frame.truncate_value_stack_to_original_size(stack);
+
+ let reachable_anyway = match frame {
+ // If it is a loop we also have to seal the body loop block
+ ControlStackFrame::Loop { header, .. } => {
+ builder.seal_block(header);
+ // And loops can't have branches to the end.
+ false
+ }
+ // If we never set `consequent_ends_reachable` then that means
+ // we are finishing the consequent now, and there was no
+ // `else`. Whether the following block is reachable depends only
+ // on if the head was reachable.
+ ControlStackFrame::If {
+ head_is_reachable,
+ consequent_ends_reachable: None,
+ ..
+ } => head_is_reachable,
+ // Since we are only in this function when in unreachable code,
+ // we know that the alternative just ended unreachable. Whether
+ // the following block is reachable depends on if the consequent
+ // ended reachable or not.
+ ControlStackFrame::If {
+ head_is_reachable,
+ consequent_ends_reachable: Some(consequent_ends_reachable),
+ ..
+ } => head_is_reachable && consequent_ends_reachable,
+ // All other control constructs are already handled.
+ _ => false,
+ };
+
+ if frame.exit_is_branched_to() || reachable_anyway {
+ builder.switch_to_block(frame.following_code());
+ builder.seal_block(frame.following_code());
+
+ // And add the return values of the block but only if the next block is reachable
+ // (which corresponds to testing if the stack depth is 1)
+ stack.extend_from_slice(builder.block_params(frame.following_code()));
+ state.reachable = true;
+ }
+ }
+ _ => {
+ // We don't translate because this is unreachable code
+ }
+ }
+
+ Ok(())
+}
+
+/// Get the address+offset to use for a heap access.
+fn get_heap_addr(
+ heap: ir::Heap,
+ addr32: ir::Value,
+ offset: u32,
+ width: u32,
+ addr_ty: Type,
+ builder: &mut FunctionBuilder,
+) -> (ir::Value, i32) {
+ let offset_guard_size: u64 = builder.func.heaps[heap].offset_guard_size.into();
+
+ // How exactly the bounds check is performed here and what it's performed
+ // on is a bit tricky. Generally we want to rely on access violations (e.g.
+ // segfaults) to generate traps since that means we don't have to bounds
+ // check anything explicitly.
+ //
+ // If we don't have a guard page of unmapped memory, though, then we can't
+ // rely on this trapping behavior through segfaults. Instead we need to
+ // bounds-check the entire memory access here which is everything from
+ // `addr32 + offset` to `addr32 + offset + width` (not inclusive). In this
+ // scenario our adjusted offset that we're checking is `offset + width`.
+ //
+ // If we have a guard page, however, then we can perform a further
+ // optimization of the generated code by only checking multiples of the
+ // offset-guard size to be more CSE-friendly. Knowing that we have at least
+ // 1 page of a guard page we're then able to disregard the `width` since we
+ // know it's always less than one page. Our bounds check will be for the
+ // first byte which will either succeed and be guaranteed to fault if it's
+ // actually out of bounds, or the bounds check itself will fail. In any case
+ // we assert that the width is reasonably small for now so this assumption
+ // can be adjusted in the future if we get larger widths.
+ //
+ // Put another way we can say, where `y < offset_guard_size`:
+ //
+ // n * offset_guard_size + y = offset
+ //
+ // We'll then pass `n * offset_guard_size` as the bounds check value. If
+ // this traps then our `offset` would have trapped anyway. If this check
+ // passes we know
+ //
+ // addr32 + n * offset_guard_size < bound
+ //
+ // which means
+ //
+ // addr32 + n * offset_guard_size + y < bound + offset_guard_size
+ //
+ // because `y < offset_guard_size`, which then means:
+ //
+ // addr32 + offset < bound + offset_guard_size
+ //
+ // Since we know that that guard size bytes are all unmapped we're
+ // guaranteed that `offset` and the `width` bytes after it are either
+ // in-bounds or will hit the guard page, meaning we'll get the desired
+ // semantics we want.
+ //
+ // As one final comment on the bits with the guard size here, another goal
+ // of this is to hit an optimization in `heap_addr` where if the heap size
+ // minus the offset is >= 4GB then bounds checks are 100% eliminated. This
+ // means that with huge guard regions (e.g. our 2GB default) most adjusted
+ // offsets we're checking here are zero. This means that we'll hit the fast
+ // path and emit zero conditional traps for bounds checks
+ let adjusted_offset = if offset_guard_size == 0 {
+ u64::from(offset) + u64::from(width)
+ } else {
+ assert!(width < 1024);
+ cmp::max(u64::from(offset) / offset_guard_size * offset_guard_size, 1)
+ };
+ debug_assert!(adjusted_offset > 0); // want to bounds check at least 1 byte
+ let check_size = u32::try_from(adjusted_offset).unwrap_or(u32::MAX);
+ let base = builder.ins().heap_addr(addr_ty, heap, addr32, check_size);
+
+ // Native load/store instructions take a signed `Offset32` immediate, so adjust the base
+ // pointer if necessary.
+ if offset > i32::MAX as u32 {
+ // Offset doesn't fit in the load/store instruction.
+ let adj = builder.ins().iadd_imm(base, i64::from(i32::MAX) + 1);
+ (adj, (offset - (i32::MAX as u32 + 1)) as i32)
+ } else {
+ (base, offset as i32)
+ }
+}
+
+/// Prepare for a load; factors out common functionality between load and load_extend operations.
+fn prepare_load<FE: FuncEnvironment + ?Sized>(
+ memarg: &MemoryImmediate,
+ loaded_bytes: u32,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<(MemFlags, Value, Offset32)> {
+ let addr32 = state.pop1();
+
+ let heap = state.get_heap(builder.func, memarg.memory, environ)?;
+ let (base, offset) = get_heap_addr(
+ heap,
+ addr32,
+ memarg.offset,
+ loaded_bytes,
+ environ.pointer_type(),
+ builder,
+ );
+
+ // Note that we don't set `is_aligned` here, even if the load instruction's
+ // alignment immediate says it's aligned, because WebAssembly's immediate
+ // field is just a hint, while Cranelift's aligned flag needs a guarantee.
+ let flags = MemFlags::new();
+
+ Ok((flags, base, offset.into()))
+}
+
+/// Translate a load instruction.
+fn translate_load<FE: FuncEnvironment + ?Sized>(
+ memarg: &MemoryImmediate,
+ opcode: ir::Opcode,
+ result_ty: Type,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<()> {
+ let (flags, base, offset) = prepare_load(
+ memarg,
+ mem_op_size(opcode, result_ty),
+ builder,
+ state,
+ environ,
+ )?;
+ let (load, dfg) = builder.ins().Load(opcode, result_ty, flags, offset, base);
+ state.push1(dfg.first_result(load));
+ Ok(())
+}
+
+/// Translate a store instruction.
+fn translate_store<FE: FuncEnvironment + ?Sized>(
+ memarg: &MemoryImmediate,
+ opcode: ir::Opcode,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<()> {
+ let (addr32, val) = state.pop2();
+ let val_ty = builder.func.dfg.value_type(val);
+
+ let heap = state.get_heap(builder.func, memarg.memory, environ)?;
+ let (base, offset) = get_heap_addr(
+ heap,
+ addr32,
+ memarg.offset,
+ mem_op_size(opcode, val_ty),
+ environ.pointer_type(),
+ builder,
+ );
+ // See the comments in `prepare_load` about the flags.
+ let flags = MemFlags::new();
+ builder
+ .ins()
+ .Store(opcode, val_ty, flags, offset.into(), val, base);
+ Ok(())
+}
+
+fn mem_op_size(opcode: ir::Opcode, ty: Type) -> u32 {
+ match opcode {
+ ir::Opcode::Istore8 | ir::Opcode::Sload8 | ir::Opcode::Uload8 => 1,
+ ir::Opcode::Istore16 | ir::Opcode::Sload16 | ir::Opcode::Uload16 => 2,
+ ir::Opcode::Istore32 | ir::Opcode::Sload32 | ir::Opcode::Uload32 => 4,
+ ir::Opcode::Store | ir::Opcode::Load | ir::Opcode::LoadSplat => ty.bytes(),
+ _ => panic!("unknown size of mem op for {:?}", opcode),
+ }
+}
+
+fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, state: &mut FuncTranslationState) {
+ let (arg0, arg1) = state.pop2();
+ let val = builder.ins().icmp(cc, arg0, arg1);
+ state.push1(builder.ins().bint(I32, val));
+}
+
+// For an atomic memory operation, emit an alignment check for the linear memory address,
+// and then compute the final effective address.
+fn finalise_atomic_mem_addr<FE: FuncEnvironment + ?Sized>(
+ linear_mem_addr: Value,
+ memarg: &MemoryImmediate,
+ access_ty: Type,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<Value> {
+ // Check the alignment of `linear_mem_addr`.
+ let access_ty_bytes = access_ty.bytes();
+ let final_lma = builder
+ .ins()
+ .iadd_imm(linear_mem_addr, i64::from(memarg.offset));
+ if access_ty_bytes != 1 {
+ assert!(access_ty_bytes == 2 || access_ty_bytes == 4 || access_ty_bytes == 8);
+ let final_lma_misalignment = builder
+ .ins()
+ .band_imm(final_lma, i64::from(access_ty_bytes - 1));
+ let f = builder
+ .ins()
+ .ifcmp_imm(final_lma_misalignment, i64::from(0));
+ builder
+ .ins()
+ .trapif(IntCC::NotEqual, f, ir::TrapCode::HeapMisaligned);
+ }
+
+ // Compute the final effective address.
+ let heap = state.get_heap(builder.func, memarg.memory, environ)?;
+ let (base, offset) = get_heap_addr(
+ heap,
+ final_lma,
+ /*offset=*/ 0,
+ access_ty.bytes(),
+ environ.pointer_type(),
+ builder,
+ );
+
+ let final_effective_address = builder.ins().iadd_imm(base, i64::from(offset));
+ Ok(final_effective_address)
+}
+
+fn translate_atomic_rmw<FE: FuncEnvironment + ?Sized>(
+ widened_ty: Type,
+ access_ty: Type,
+ op: AtomicRmwOp,
+ memarg: &MemoryImmediate,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<()> {
+ let (linear_mem_addr, mut arg2) = state.pop2();
+ let arg2_ty = builder.func.dfg.value_type(arg2);
+
+ // The operation is performed at type `access_ty`, and the old value is zero-extended
+ // to type `widened_ty`.
+ match access_ty {
+ I8 | I16 | I32 | I64 => {}
+ _ => {
+ return Err(wasm_unsupported!(
+ "atomic_rmw: unsupported access type {:?}",
+ access_ty
+ ))
+ }
+ };
+ let w_ty_ok = match widened_ty {
+ I32 | I64 => true,
+ _ => false,
+ };
+ assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());
+
+ assert!(arg2_ty.bytes() >= access_ty.bytes());
+ if arg2_ty.bytes() > access_ty.bytes() {
+ arg2 = builder.ins().ireduce(access_ty, arg2);
+ }
+
+ let final_effective_address =
+ finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
+
+ // See the comments in `prepare_load` about the flags.
+ let flags = MemFlags::new();
+ let mut res = builder
+ .ins()
+ .atomic_rmw(access_ty, flags, op, final_effective_address, arg2);
+ if access_ty != widened_ty {
+ res = builder.ins().uextend(widened_ty, res);
+ }
+ state.push1(res);
+ Ok(())
+}
+
+fn translate_atomic_cas<FE: FuncEnvironment + ?Sized>(
+ widened_ty: Type,
+ access_ty: Type,
+ memarg: &MemoryImmediate,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<()> {
+ let (linear_mem_addr, mut expected, mut replacement) = state.pop3();
+ let expected_ty = builder.func.dfg.value_type(expected);
+ let replacement_ty = builder.func.dfg.value_type(replacement);
+
+ // The compare-and-swap is performed at type `access_ty`, and the old value is zero-extended
+ // to type `widened_ty`.
+ match access_ty {
+ I8 | I16 | I32 | I64 => {}
+ _ => {
+ return Err(wasm_unsupported!(
+ "atomic_cas: unsupported access type {:?}",
+ access_ty
+ ))
+ }
+ };
+ let w_ty_ok = match widened_ty {
+ I32 | I64 => true,
+ _ => false,
+ };
+ assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());
+
+ assert!(expected_ty.bytes() >= access_ty.bytes());
+ if expected_ty.bytes() > access_ty.bytes() {
+ expected = builder.ins().ireduce(access_ty, expected);
+ }
+ assert!(replacement_ty.bytes() >= access_ty.bytes());
+ if replacement_ty.bytes() > access_ty.bytes() {
+ replacement = builder.ins().ireduce(access_ty, replacement);
+ }
+
+ let final_effective_address =
+ finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
+
+ // See the comments in `prepare_load` about the flags.
+ let flags = MemFlags::new();
+ let mut res = builder
+ .ins()
+ .atomic_cas(flags, final_effective_address, expected, replacement);
+ if access_ty != widened_ty {
+ res = builder.ins().uextend(widened_ty, res);
+ }
+ state.push1(res);
+ Ok(())
+}
+
+fn translate_atomic_load<FE: FuncEnvironment + ?Sized>(
+ widened_ty: Type,
+ access_ty: Type,
+ memarg: &MemoryImmediate,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<()> {
+ let linear_mem_addr = state.pop1();
+
+ // The load is performed at type `access_ty`, and the loaded value is zero extended
+ // to `widened_ty`.
+ match access_ty {
+ I8 | I16 | I32 | I64 => {}
+ _ => {
+ return Err(wasm_unsupported!(
+ "atomic_load: unsupported access type {:?}",
+ access_ty
+ ))
+ }
+ };
+ let w_ty_ok = match widened_ty {
+ I32 | I64 => true,
+ _ => false,
+ };
+ assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());
+
+ let final_effective_address =
+ finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
+
+ // See the comments in `prepare_load` about the flags.
+ let flags = MemFlags::new();
+ let mut res = builder
+ .ins()
+ .atomic_load(access_ty, flags, final_effective_address);
+ if access_ty != widened_ty {
+ res = builder.ins().uextend(widened_ty, res);
+ }
+ state.push1(res);
+ Ok(())
+}
+
+fn translate_atomic_store<FE: FuncEnvironment + ?Sized>(
+ access_ty: Type,
+ memarg: &MemoryImmediate,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<()> {
+ let (linear_mem_addr, mut data) = state.pop2();
+ let data_ty = builder.func.dfg.value_type(data);
+
+ // The operation is performed at type `access_ty`, and the data to be stored may first
+ // need to be narrowed accordingly.
+ match access_ty {
+ I8 | I16 | I32 | I64 => {}
+ _ => {
+ return Err(wasm_unsupported!(
+ "atomic_store: unsupported access type {:?}",
+ access_ty
+ ))
+ }
+ };
+ let d_ty_ok = match data_ty {
+ I32 | I64 => true,
+ _ => false,
+ };
+ assert!(d_ty_ok && data_ty.bytes() >= access_ty.bytes());
+
+ if data_ty.bytes() > access_ty.bytes() {
+ data = builder.ins().ireduce(access_ty, data);
+ }
+
+ let final_effective_address =
+ finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
+
+ // See the comments in `prepare_load` about the flags.
+ let flags = MemFlags::new();
+ builder
+ .ins()
+ .atomic_store(flags, data, final_effective_address);
+ Ok(())
+}
+
+fn translate_vector_icmp(
+ cc: IntCC,
+ needed_type: Type,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+) {
+ let (a, b) = state.pop2();
+ let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
+ let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
+ state.push1(builder.ins().icmp(cc, bitcast_a, bitcast_b))
+}
+
+fn translate_fcmp(cc: FloatCC, builder: &mut FunctionBuilder, state: &mut FuncTranslationState) {
+ let (arg0, arg1) = state.pop2();
+ let val = builder.ins().fcmp(cc, arg0, arg1);
+ state.push1(builder.ins().bint(I32, val));
+}
+
+fn translate_vector_fcmp(
+ cc: FloatCC,
+ needed_type: Type,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+) {
+ let (a, b) = state.pop2();
+ let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
+ let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
+ state.push1(builder.ins().fcmp(cc, bitcast_a, bitcast_b))
+}
+
+fn translate_br_if(
+ relative_depth: u32,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+) {
+ let val = state.pop1();
+ let (br_destination, inputs) = translate_br_if_args(relative_depth, state);
+ canonicalise_then_brnz(builder, val, br_destination, inputs);
+
+ let next_block = builder.create_block();
+ canonicalise_then_jump(builder, next_block, &[]);
+ builder.seal_block(next_block); // The only predecessor is the current block.
+ builder.switch_to_block(next_block);
+}
+
+fn translate_br_if_args(
+ relative_depth: u32,
+ state: &mut FuncTranslationState,
+) -> (ir::Block, &mut [ir::Value]) {
+ let i = state.control_stack.len() - 1 - (relative_depth as usize);
+ let (return_count, br_destination) = {
+ let frame = &mut state.control_stack[i];
+ // The values returned by the branch are still available for the reachable
+ // code that comes after it
+ frame.set_branched_to_exit();
+ let return_count = if frame.is_loop() {
+ frame.num_param_values()
+ } else {
+ frame.num_return_values()
+ };
+ (return_count, frame.br_destination())
+ };
+ let inputs = state.peekn_mut(return_count);
+ (br_destination, inputs)
+}
+
+/// Determine the returned value type of a WebAssembly operator
+fn type_of(operator: &Operator) -> Type {
+ match operator {
+ Operator::V128Load { .. }
+ | Operator::V128Store { .. }
+ | Operator::V128Const { .. }
+ | Operator::V128Not
+ | Operator::V128And
+ | Operator::V128AndNot
+ | Operator::V128Or
+ | Operator::V128Xor
+ | Operator::V128Bitselect => I8X16, // default type representing V128
+
+ Operator::I8x16Shuffle { .. }
+ | Operator::I8x16Splat
+ | Operator::V128Load8Splat { .. }
+ | Operator::I8x16ExtractLaneS { .. }
+ | Operator::I8x16ExtractLaneU { .. }
+ | Operator::I8x16ReplaceLane { .. }
+ | Operator::I8x16Eq
+ | Operator::I8x16Ne
+ | Operator::I8x16LtS
+ | Operator::I8x16LtU
+ | Operator::I8x16GtS
+ | Operator::I8x16GtU
+ | Operator::I8x16LeS
+ | Operator::I8x16LeU
+ | Operator::I8x16GeS
+ | Operator::I8x16GeU
+ | Operator::I8x16Neg
+ | Operator::I8x16Abs
+ | Operator::I8x16AnyTrue
+ | Operator::I8x16AllTrue
+ | Operator::I8x16Shl
+ | Operator::I8x16ShrS
+ | Operator::I8x16ShrU
+ | Operator::I8x16Add
+ | Operator::I8x16AddSatS
+ | Operator::I8x16AddSatU
+ | Operator::I8x16Sub
+ | Operator::I8x16SubSatS
+ | Operator::I8x16SubSatU
+ | Operator::I8x16MinS
+ | Operator::I8x16MinU
+ | Operator::I8x16MaxS
+ | Operator::I8x16MaxU
+ | Operator::I8x16RoundingAverageU
+ | Operator::I8x16Bitmask => I8X16,
+
+ Operator::I16x8Splat
+ | Operator::V128Load16Splat { .. }
+ | Operator::I16x8ExtractLaneS { .. }
+ | Operator::I16x8ExtractLaneU { .. }
+ | Operator::I16x8ReplaceLane { .. }
+ | Operator::I16x8Eq
+ | Operator::I16x8Ne
+ | Operator::I16x8LtS
+ | Operator::I16x8LtU
+ | Operator::I16x8GtS
+ | Operator::I16x8GtU
+ | Operator::I16x8LeS
+ | Operator::I16x8LeU
+ | Operator::I16x8GeS
+ | Operator::I16x8GeU
+ | Operator::I16x8Neg
+ | Operator::I16x8Abs
+ | Operator::I16x8AnyTrue
+ | Operator::I16x8AllTrue
+ | Operator::I16x8Shl
+ | Operator::I16x8ShrS
+ | Operator::I16x8ShrU
+ | Operator::I16x8Add
+ | Operator::I16x8AddSatS
+ | Operator::I16x8AddSatU
+ | Operator::I16x8Sub
+ | Operator::I16x8SubSatS
+ | Operator::I16x8SubSatU
+ | Operator::I16x8MinS
+ | Operator::I16x8MinU
+ | Operator::I16x8MaxS
+ | Operator::I16x8MaxU
+ | Operator::I16x8RoundingAverageU
+ | Operator::I16x8Mul
+ | Operator::I16x8Bitmask => I16X8,
+
+ Operator::I32x4Splat
+ | Operator::V128Load32Splat { .. }
+ | Operator::I32x4ExtractLane { .. }
+ | Operator::I32x4ReplaceLane { .. }
+ | Operator::I32x4Eq
+ | Operator::I32x4Ne
+ | Operator::I32x4LtS
+ | Operator::I32x4LtU
+ | Operator::I32x4GtS
+ | Operator::I32x4GtU
+ | Operator::I32x4LeS
+ | Operator::I32x4LeU
+ | Operator::I32x4GeS
+ | Operator::I32x4GeU
+ | Operator::I32x4Neg
+ | Operator::I32x4Abs
+ | Operator::I32x4AnyTrue
+ | Operator::I32x4AllTrue
+ | Operator::I32x4Shl
+ | Operator::I32x4ShrS
+ | Operator::I32x4ShrU
+ | Operator::I32x4Add
+ | Operator::I32x4Sub
+ | Operator::I32x4Mul
+ | Operator::I32x4MinS
+ | Operator::I32x4MinU
+ | Operator::I32x4MaxS
+ | Operator::I32x4MaxU
+ | Operator::F32x4ConvertI32x4S
+ | Operator::F32x4ConvertI32x4U
+ | Operator::I32x4Bitmask
+ | Operator::V128Load32Zero { .. } => I32X4,
+
+ Operator::I64x2Splat
+ | Operator::V128Load64Splat { .. }
+ | Operator::I64x2ExtractLane { .. }
+ | Operator::I64x2ReplaceLane { .. }
+ | Operator::I64x2Neg
+ | Operator::I64x2Shl
+ | Operator::I64x2ShrS
+ | Operator::I64x2ShrU
+ | Operator::I64x2Add
+ | Operator::I64x2Sub
+ | Operator::I64x2Mul
+ | Operator::V128Load64Zero { .. } => I64X2,
+
+ Operator::F32x4Splat
+ | Operator::F32x4ExtractLane { .. }
+ | Operator::F32x4ReplaceLane { .. }
+ | Operator::F32x4Eq
+ | Operator::F32x4Ne
+ | Operator::F32x4Lt
+ | Operator::F32x4Gt
+ | Operator::F32x4Le
+ | Operator::F32x4Ge
+ | Operator::F32x4Abs
+ | Operator::F32x4Neg
+ | Operator::F32x4Sqrt
+ | Operator::F32x4Add
+ | Operator::F32x4Sub
+ | Operator::F32x4Mul
+ | Operator::F32x4Div
+ | Operator::F32x4Min
+ | Operator::F32x4Max
+ | Operator::F32x4PMin
+ | Operator::F32x4PMax
+ | Operator::I32x4TruncSatF32x4S
+ | Operator::I32x4TruncSatF32x4U
+ | Operator::F32x4Ceil
+ | Operator::F32x4Floor
+ | Operator::F32x4Trunc
+ | Operator::F32x4Nearest => F32X4,
+
+ Operator::F64x2Splat
+ | Operator::F64x2ExtractLane { .. }
+ | Operator::F64x2ReplaceLane { .. }
+ | Operator::F64x2Eq
+ | Operator::F64x2Ne
+ | Operator::F64x2Lt
+ | Operator::F64x2Gt
+ | Operator::F64x2Le
+ | Operator::F64x2Ge
+ | Operator::F64x2Abs
+ | Operator::F64x2Neg
+ | Operator::F64x2Sqrt
+ | Operator::F64x2Add
+ | Operator::F64x2Sub
+ | Operator::F64x2Mul
+ | Operator::F64x2Div
+ | Operator::F64x2Min
+ | Operator::F64x2Max
+ | Operator::F64x2PMin
+ | Operator::F64x2PMax
+ | Operator::F64x2Ceil
+ | Operator::F64x2Floor
+ | Operator::F64x2Trunc
+ | Operator::F64x2Nearest => F64X2,
+
+ _ => unimplemented!(
+ "Currently only SIMD instructions are mapped to their return type; the \
+ following instruction is not mapped: {:?}",
+ operator
+ ),
+ }
+}
+
+/// Some SIMD operations only operate on I8X16 in CLIF; this will convert them to that type by
+/// adding a raw_bitcast if necessary.
+fn optionally_bitcast_vector(
+ value: Value,
+ needed_type: Type,
+ builder: &mut FunctionBuilder,
+) -> Value {
+ if builder.func.dfg.value_type(value) != needed_type {
+ builder.ins().raw_bitcast(needed_type, value)
+ } else {
+ value
+ }
+}
+
+#[inline(always)]
+fn is_non_canonical_v128(ty: ir::Type) -> bool {
+ match ty {
+ B8X16 | B16X8 | B32X4 | B64X2 | I64X2 | I32X4 | I16X8 | F32X4 | F64X2 => true,
+ _ => false,
+ }
+}
+
+/// Cast to I8X16, any vector values in `values` that are of "non-canonical" type (meaning, not
+/// I8X16), and return them in a slice. A pre-scan is made to determine whether any casts are
+/// actually necessary, and if not, the original slice is returned. Otherwise the cast values
+/// are returned in a slice that belongs to the caller-supplied `SmallVec`.
+fn canonicalise_v128_values<'a>(
+ tmp_canonicalised: &'a mut SmallVec<[ir::Value; 16]>,
+ builder: &mut FunctionBuilder,
+ values: &'a [ir::Value],
+) -> &'a [ir::Value] {
+ debug_assert!(tmp_canonicalised.is_empty());
+ // First figure out if any of the parameters need to be cast. Mostly they don't need to be.
+ let any_non_canonical = values
+ .iter()
+ .any(|v| is_non_canonical_v128(builder.func.dfg.value_type(*v)));
+ // Hopefully we take this exit most of the time, hence doing no heap allocation.
+ if !any_non_canonical {
+ return values;
+ }
+ // Otherwise we'll have to cast, and push the resulting `Value`s into `canonicalised`.
+ for v in values {
+ tmp_canonicalised.push(if is_non_canonical_v128(builder.func.dfg.value_type(*v)) {
+ builder.ins().raw_bitcast(I8X16, *v)
+ } else {
+ *v
+ });
+ }
+ tmp_canonicalised.as_slice()
+}
+
+/// Generate a `jump` instruction, but first cast all 128-bit vector values to I8X16 if they
+/// don't have that type. This is done in somewhat roundabout way so as to ensure that we
+/// almost never have to do any heap allocation.
+fn canonicalise_then_jump(
+ builder: &mut FunctionBuilder,
+ destination: ir::Block,
+ params: &[ir::Value],
+) -> ir::Inst {
+ let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new();
+ let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);
+ builder.ins().jump(destination, canonicalised)
+}
+
+/// The same but for a `brz` instruction.
+fn canonicalise_then_brz(
+ builder: &mut FunctionBuilder,
+ cond: ir::Value,
+ destination: ir::Block,
+ params: &[Value],
+) -> ir::Inst {
+ let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new();
+ let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);
+ builder.ins().brz(cond, destination, canonicalised)
+}
+
+/// The same but for a `brnz` instruction.
+fn canonicalise_then_brnz(
+ builder: &mut FunctionBuilder,
+ cond: ir::Value,
+ destination: ir::Block,
+ params: &[Value],
+) -> ir::Inst {
+ let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new();
+ let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);
+ builder.ins().brnz(cond, destination, canonicalised)
+}
+
+/// A helper for popping and bitcasting a single value; since SIMD values can lose their type by
+/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF
+/// typing issues.
+fn pop1_with_bitcast(
+ state: &mut FuncTranslationState,
+ needed_type: Type,
+ builder: &mut FunctionBuilder,
+) -> Value {
+ optionally_bitcast_vector(state.pop1(), needed_type, builder)
+}
+
+/// A helper for popping and bitcasting two values; since SIMD values can lose their type by
+/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF
+/// typing issues.
+fn pop2_with_bitcast(
+ state: &mut FuncTranslationState,
+ needed_type: Type,
+ builder: &mut FunctionBuilder,
+) -> (Value, Value) {
+ let (a, b) = state.pop2();
+ let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
+ let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
+ (bitcast_a, bitcast_b)
+}
+
+/// A helper for bitcasting a sequence of values (e.g. function arguments). If a value is a
+/// vector type that does not match its expected type, this will modify the value in place to point
+/// to the result of a `raw_bitcast`. This conversion is necessary to translate Wasm code that
+/// uses `V128` as function parameters (or implicitly in block parameters) and still use specific
+/// CLIF types (e.g. `I32X4`) in the function body.
+pub fn bitcast_arguments(
+ arguments: &mut [Value],
+ expected_types: &[Type],
+ builder: &mut FunctionBuilder,
+) {
+ assert_eq!(arguments.len(), expected_types.len());
+ for (i, t) in expected_types.iter().enumerate() {
+ if t.is_vector() {
+ assert!(
+ builder.func.dfg.value_type(arguments[i]).is_vector(),
+ "unexpected type mismatch: expected {}, argument {} was actually of type {}",
+ t,
+ arguments[i],
+ builder.func.dfg.value_type(arguments[i])
+ );
+ arguments[i] = optionally_bitcast_vector(arguments[i], *t, builder)
+ }
+ }
+}
+
+/// A helper to extract all the `Type` listings of each variable in `params`
+/// for only parameters the return true for `is_wasm`, typically paired with
+/// `is_wasm_return` or `is_wasm_parameter`.
+pub fn wasm_param_types(params: &[ir::AbiParam], is_wasm: impl Fn(usize) -> bool) -> Vec<Type> {
+ let mut ret = Vec::with_capacity(params.len());
+ for (i, param) in params.iter().enumerate() {
+ if is_wasm(i) {
+ ret.push(param.value_type);
+ }
+ }
+ ret
+}
diff --git a/third_party/rust/cranelift-wasm/src/environ/dummy.rs b/third_party/rust/cranelift-wasm/src/environ/dummy.rs
new file mode 100644
index 0000000000..d0b59ee6ab
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/environ/dummy.rs
@@ -0,0 +1,783 @@
+//! "Dummy" implementations of `ModuleEnvironment` and `FuncEnvironment` for testing
+//! wasm translation. For complete implementations of `ModuleEnvironment` and
+//! `FuncEnvironment`, see [wasmtime-environ] in [Wasmtime].
+//!
+//! [wasmtime-environ]: https://crates.io/crates/wasmtime-environ
+//! [Wasmtime]: https://github.com/bytecodealliance/wasmtime
+
+use crate::environ::{
+ FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment,
+ WasmFuncType, WasmResult,
+};
+use crate::func_translator::FuncTranslator;
+use crate::translation_utils::{
+ DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex,
+ Table, TableIndex, TypeIndex,
+};
+use core::convert::TryFrom;
+use cranelift_codegen::cursor::FuncCursor;
+use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
+use cranelift_codegen::ir::types::*;
+use cranelift_codegen::ir::{self, InstBuilder};
+use cranelift_codegen::isa::TargetFrontendConfig;
+use cranelift_entity::{EntityRef, PrimaryMap, SecondaryMap};
+use cranelift_frontend::FunctionBuilder;
+use std::boxed::Box;
+use std::string::String;
+use std::vec::Vec;
+use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmFeatures};
+
+/// Compute a `ir::ExternalName` for a given wasm function index.
+fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
+ ir::ExternalName::user(0, func_index.as_u32())
+}
+
+/// A collection of names under which a given entity is exported.
+pub struct Exportable<T> {
+ /// A wasm entity.
+ pub entity: T,
+
+ /// Names under which the entity is exported.
+ pub export_names: Vec<String>,
+}
+
+impl<T> Exportable<T> {
+ pub fn new(entity: T) -> Self {
+ Self {
+ entity,
+ export_names: Vec::new(),
+ }
+ }
+}
+
+/// The main state belonging to a `DummyEnvironment`. This is split out from
+/// `DummyEnvironment` to allow it to be borrowed separately from the
+/// `FuncTranslator` field.
+pub struct DummyModuleInfo {
+ /// Target description relevant to frontends producing Cranelift IR.
+ config: TargetFrontendConfig,
+
+ /// Signatures as provided by `declare_signature`.
+ pub signatures: PrimaryMap<TypeIndex, ir::Signature>,
+
+ /// Module and field names of imported functions as provided by `declare_func_import`.
+ pub imported_funcs: Vec<(String, String)>,
+
+ /// Module and field names of imported globals as provided by `declare_global_import`.
+ pub imported_globals: Vec<(String, String)>,
+
+ /// Module and field names of imported tables as provided by `declare_table_import`.
+ pub imported_tables: Vec<(String, String)>,
+
+ /// Module and field names of imported memories as provided by `declare_memory_import`.
+ pub imported_memories: Vec<(String, String)>,
+
+ /// Functions, imported and local.
+ pub functions: PrimaryMap<FuncIndex, Exportable<TypeIndex>>,
+
+ /// Function bodies.
+ pub function_bodies: PrimaryMap<DefinedFuncIndex, ir::Function>,
+
+ /// Tables as provided by `declare_table`.
+ pub tables: PrimaryMap<TableIndex, Exportable<Table>>,
+
+ /// Memories as provided by `declare_memory`.
+ pub memories: PrimaryMap<MemoryIndex, Exportable<Memory>>,
+
+ /// Globals as provided by `declare_global`.
+ pub globals: PrimaryMap<GlobalIndex, Exportable<Global>>,
+
+ /// The start function.
+ pub start_func: Option<FuncIndex>,
+}
+
+impl DummyModuleInfo {
+ /// Creates a new `DummyModuleInfo` instance.
+ pub fn new(config: TargetFrontendConfig) -> Self {
+ Self {
+ config,
+ signatures: PrimaryMap::new(),
+ imported_funcs: Vec::new(),
+ imported_globals: Vec::new(),
+ imported_tables: Vec::new(),
+ imported_memories: Vec::new(),
+ functions: PrimaryMap::new(),
+ function_bodies: PrimaryMap::new(),
+ tables: PrimaryMap::new(),
+ memories: PrimaryMap::new(),
+ globals: PrimaryMap::new(),
+ start_func: None,
+ }
+ }
+}
+
+/// This `ModuleEnvironment` implementation is a "naïve" one, doing essentially nothing and
+/// emitting placeholders when forced to. Don't try to execute code translated for this
+/// environment, essentially here for translation debug purposes.
+pub struct DummyEnvironment {
+ /// Module information.
+ pub info: DummyModuleInfo,
+
+ /// Function translation.
+ trans: FuncTranslator,
+
+ /// Vector of wasm bytecode size for each function.
+ pub func_bytecode_sizes: Vec<usize>,
+
+ /// How to return from functions.
+ return_mode: ReturnMode,
+
+ /// Instructs to collect debug data during translation.
+ debug_info: bool,
+
+ /// Name of the module from the wasm file.
+ pub module_name: Option<String>,
+
+ /// Function names.
+ function_names: SecondaryMap<FuncIndex, String>,
+}
+
+impl DummyEnvironment {
+ /// Creates a new `DummyEnvironment` instance.
+ pub fn new(config: TargetFrontendConfig, return_mode: ReturnMode, debug_info: bool) -> Self {
+ Self {
+ info: DummyModuleInfo::new(config),
+ trans: FuncTranslator::new(),
+ func_bytecode_sizes: Vec::new(),
+ return_mode,
+ debug_info,
+ module_name: None,
+ function_names: SecondaryMap::new(),
+ }
+ }
+
+ /// Return a `DummyFuncEnvironment` for translating functions within this
+ /// `DummyEnvironment`.
+ pub fn func_env(&self) -> DummyFuncEnvironment {
+ DummyFuncEnvironment::new(&self.info, self.return_mode)
+ }
+
+ fn get_func_type(&self, func_index: FuncIndex) -> TypeIndex {
+ self.info.functions[func_index].entity
+ }
+
+ /// Return the number of imported functions within this `DummyEnvironment`.
+ pub fn get_num_func_imports(&self) -> usize {
+ self.info.imported_funcs.len()
+ }
+
+ /// Return the name of the function, if a name for the function with
+ /// the corresponding index exists.
+ pub fn get_func_name(&self, func_index: FuncIndex) -> Option<&str> {
+ self.function_names.get(func_index).map(String::as_ref)
+ }
+}
+
+/// The `FuncEnvironment` implementation for use by the `DummyEnvironment`.
+pub struct DummyFuncEnvironment<'dummy_environment> {
+ pub mod_info: &'dummy_environment DummyModuleInfo,
+
+ return_mode: ReturnMode,
+}
+
+impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
+ pub fn new(mod_info: &'dummy_environment DummyModuleInfo, return_mode: ReturnMode) -> Self {
+ Self {
+ mod_info,
+ return_mode,
+ }
+ }
+
+ // Create a signature for `sigidx` amended with a `vmctx` argument after the standard wasm
+ // arguments.
+ fn vmctx_sig(&self, sigidx: TypeIndex) -> ir::Signature {
+ let mut sig = self.mod_info.signatures[sigidx].clone();
+ sig.params.push(ir::AbiParam::special(
+ self.pointer_type(),
+ ir::ArgumentPurpose::VMContext,
+ ));
+ sig
+ }
+
+ fn reference_type(&self) -> ir::Type {
+ match self.pointer_type() {
+ ir::types::I32 => ir::types::R32,
+ ir::types::I64 => ir::types::R64,
+ _ => panic!("unsupported pointer type"),
+ }
+ }
+}
+
+impl<'dummy_environment> TargetEnvironment for DummyFuncEnvironment<'dummy_environment> {
+ fn target_config(&self) -> TargetFrontendConfig {
+ self.mod_info.config
+ }
+}
+
+impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> {
+ fn return_mode(&self) -> ReturnMode {
+ self.return_mode
+ }
+
+ fn make_global(
+ &mut self,
+ func: &mut ir::Function,
+ index: GlobalIndex,
+ ) -> WasmResult<GlobalVariable> {
+ // Just create a dummy `vmctx` global.
+ let offset = i32::try_from((index.index() * 8) + 8).unwrap().into();
+ let vmctx = func.create_global_value(ir::GlobalValueData::VMContext {});
+ Ok(GlobalVariable::Memory {
+ gv: vmctx,
+ offset,
+ ty: self.mod_info.globals[index].entity.ty,
+ })
+ }
+
+ fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> WasmResult<ir::Heap> {
+ // Create a static heap whose base address is stored at `vmctx+0`.
+ let addr = func.create_global_value(ir::GlobalValueData::VMContext);
+ let gv = func.create_global_value(ir::GlobalValueData::Load {
+ base: addr,
+ offset: Offset32::new(0),
+ global_type: self.pointer_type(),
+ readonly: true,
+ });
+
+ Ok(func.create_heap(ir::HeapData {
+ base: gv,
+ min_size: 0.into(),
+ offset_guard_size: 0x8000_0000.into(),
+ style: ir::HeapStyle::Static {
+ bound: 0x1_0000_0000.into(),
+ },
+ index_type: I32,
+ }))
+ }
+
+ fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> WasmResult<ir::Table> {
+ // Create a table whose base address is stored at `vmctx+0`.
+ let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
+ let base_gv = func.create_global_value(ir::GlobalValueData::Load {
+ base: vmctx,
+ offset: Offset32::new(0),
+ global_type: self.pointer_type(),
+ readonly: true, // when tables in wasm become "growable", revisit whether this can be readonly or not.
+ });
+ let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
+ base: vmctx,
+ offset: Offset32::new(0),
+ global_type: I32,
+ readonly: true,
+ });
+
+ Ok(func.create_table(ir::TableData {
+ base_gv,
+ min_size: Uimm64::new(0),
+ bound_gv,
+ element_size: Uimm64::from(u64::from(self.pointer_bytes()) * 2),
+ index_type: I32,
+ }))
+ }
+
+ fn make_indirect_sig(
+ &mut self,
+ func: &mut ir::Function,
+ index: TypeIndex,
+ ) -> WasmResult<ir::SigRef> {
+ // A real implementation would probably change the calling convention and add `vmctx` and
+ // signature index arguments.
+ Ok(func.import_signature(self.vmctx_sig(index)))
+ }
+
+ fn make_direct_func(
+ &mut self,
+ func: &mut ir::Function,
+ index: FuncIndex,
+ ) -> WasmResult<ir::FuncRef> {
+ let sigidx = self.mod_info.functions[index].entity;
+ // A real implementation would probably add a `vmctx` argument.
+ // And maybe attempt some signature de-duplication.
+ let signature = func.import_signature(self.vmctx_sig(sigidx));
+ let name = get_func_name(index);
+ Ok(func.import_function(ir::ExtFuncData {
+ name,
+ signature,
+ colocated: false,
+ }))
+ }
+
+ fn translate_call_indirect(
+ &mut self,
+ mut pos: FuncCursor,
+ _table_index: TableIndex,
+ _table: ir::Table,
+ _sig_index: TypeIndex,
+ sig_ref: ir::SigRef,
+ callee: ir::Value,
+ call_args: &[ir::Value],
+ ) -> WasmResult<ir::Inst> {
+ // Pass the current function's vmctx parameter on to the callee.
+ let vmctx = pos
+ .func
+ .special_param(ir::ArgumentPurpose::VMContext)
+ .expect("Missing vmctx parameter");
+
+ // The `callee` value is an index into a table of function pointers.
+ // Apparently, that table is stored at absolute address 0 in this dummy environment.
+ // TODO: Generate bounds checking code.
+ let ptr = self.pointer_type();
+ let callee_offset = if ptr == I32 {
+ pos.ins().imul_imm(callee, 4)
+ } else {
+ let ext = pos.ins().uextend(I64, callee);
+ pos.ins().imul_imm(ext, 4)
+ };
+ let mflags = ir::MemFlags::trusted();
+ let func_ptr = pos.ins().load(ptr, mflags, callee_offset, 0);
+
+ // Build a value list for the indirect call instruction containing the callee, call_args,
+ // and the vmctx parameter.
+ let mut args = ir::ValueList::default();
+ args.push(func_ptr, &mut pos.func.dfg.value_lists);
+ args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
+ args.push(vmctx, &mut pos.func.dfg.value_lists);
+
+ Ok(pos
+ .ins()
+ .CallIndirect(ir::Opcode::CallIndirect, INVALID, sig_ref, args)
+ .0)
+ }
+
+ fn translate_call(
+ &mut self,
+ mut pos: FuncCursor,
+ _callee_index: FuncIndex,
+ callee: ir::FuncRef,
+ call_args: &[ir::Value],
+ ) -> WasmResult<ir::Inst> {
+ // Pass the current function's vmctx parameter on to the callee.
+ let vmctx = pos
+ .func
+ .special_param(ir::ArgumentPurpose::VMContext)
+ .expect("Missing vmctx parameter");
+
+ // Build a value list for the call instruction containing the call_args and the vmctx
+ // parameter.
+ let mut args = ir::ValueList::default();
+ args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
+ args.push(vmctx, &mut pos.func.dfg.value_lists);
+
+ Ok(pos.ins().Call(ir::Opcode::Call, INVALID, callee, args).0)
+ }
+
+ fn translate_memory_grow(
+ &mut self,
+ mut pos: FuncCursor,
+ _index: MemoryIndex,
+ _heap: ir::Heap,
+ _val: ir::Value,
+ ) -> WasmResult<ir::Value> {
+ Ok(pos.ins().iconst(I32, -1))
+ }
+
+ fn translate_memory_size(
+ &mut self,
+ mut pos: FuncCursor,
+ _index: MemoryIndex,
+ _heap: ir::Heap,
+ ) -> WasmResult<ir::Value> {
+ Ok(pos.ins().iconst(I32, -1))
+ }
+
+ fn translate_memory_copy(
+ &mut self,
+ _pos: FuncCursor,
+ _src_index: MemoryIndex,
+ _src_heap: ir::Heap,
+ _dst_index: MemoryIndex,
+ _dst_heap: ir::Heap,
+ _dst: ir::Value,
+ _src: ir::Value,
+ _len: ir::Value,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn translate_memory_fill(
+ &mut self,
+ _pos: FuncCursor,
+ _index: MemoryIndex,
+ _heap: ir::Heap,
+ _dst: ir::Value,
+ _val: ir::Value,
+ _len: ir::Value,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn translate_memory_init(
+ &mut self,
+ _pos: FuncCursor,
+ _index: MemoryIndex,
+ _heap: ir::Heap,
+ _seg_index: u32,
+ _dst: ir::Value,
+ _src: ir::Value,
+ _len: ir::Value,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn translate_data_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn translate_table_size(
+ &mut self,
+ mut pos: FuncCursor,
+ _index: TableIndex,
+ _table: ir::Table,
+ ) -> WasmResult<ir::Value> {
+ Ok(pos.ins().iconst(I32, -1))
+ }
+
+ fn translate_table_grow(
+ &mut self,
+ mut pos: FuncCursor,
+ _table_index: TableIndex,
+ _table: ir::Table,
+ _delta: ir::Value,
+ _init_value: ir::Value,
+ ) -> WasmResult<ir::Value> {
+ Ok(pos.ins().iconst(I32, -1))
+ }
+
+ fn translate_table_get(
+ &mut self,
+ builder: &mut FunctionBuilder,
+ _table_index: TableIndex,
+ _table: ir::Table,
+ _index: ir::Value,
+ ) -> WasmResult<ir::Value> {
+ Ok(builder.ins().null(self.reference_type()))
+ }
+
+ fn translate_table_set(
+ &mut self,
+ _builder: &mut FunctionBuilder,
+ _table_index: TableIndex,
+ _table: ir::Table,
+ _value: ir::Value,
+ _index: ir::Value,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn translate_table_copy(
+ &mut self,
+ _pos: FuncCursor,
+ _dst_index: TableIndex,
+ _dst_table: ir::Table,
+ _src_index: TableIndex,
+ _src_table: ir::Table,
+ _dst: ir::Value,
+ _src: ir::Value,
+ _len: ir::Value,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn translate_table_fill(
+ &mut self,
+ _pos: FuncCursor,
+ _table_index: TableIndex,
+ _dst: ir::Value,
+ _val: ir::Value,
+ _len: ir::Value,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn translate_table_init(
+ &mut self,
+ _pos: FuncCursor,
+ _seg_index: u32,
+ _table_index: TableIndex,
+ _table: ir::Table,
+ _dst: ir::Value,
+ _src: ir::Value,
+ _len: ir::Value,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn translate_elem_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn translate_ref_func(
+ &mut self,
+ mut pos: FuncCursor,
+ _func_index: FuncIndex,
+ ) -> WasmResult<ir::Value> {
+ Ok(pos.ins().null(self.reference_type()))
+ }
+
+ fn translate_custom_global_get(
+ &mut self,
+ mut pos: FuncCursor,
+ _global_index: GlobalIndex,
+ ) -> WasmResult<ir::Value> {
+ Ok(pos.ins().iconst(I32, -1))
+ }
+
+ fn translate_custom_global_set(
+ &mut self,
+ _pos: FuncCursor,
+ _global_index: GlobalIndex,
+ _val: ir::Value,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn translate_atomic_wait(
+ &mut self,
+ mut pos: FuncCursor,
+ _index: MemoryIndex,
+ _heap: ir::Heap,
+ _addr: ir::Value,
+ _expected: ir::Value,
+ _timeout: ir::Value,
+ ) -> WasmResult<ir::Value> {
+ Ok(pos.ins().iconst(I32, -1))
+ }
+
+ fn translate_atomic_notify(
+ &mut self,
+ mut pos: FuncCursor,
+ _index: MemoryIndex,
+ _heap: ir::Heap,
+ _addr: ir::Value,
+ _count: ir::Value,
+ ) -> WasmResult<ir::Value> {
+ Ok(pos.ins().iconst(I32, 0))
+ }
+}
+
+impl TargetEnvironment for DummyEnvironment {
+ fn target_config(&self) -> TargetFrontendConfig {
+ self.info.config
+ }
+}
+
+impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
+ fn declare_type_func(&mut self, _wasm: WasmFuncType, sig: ir::Signature) -> WasmResult<()> {
+ self.info.signatures.push(sig);
+ Ok(())
+ }
+
+ fn declare_func_import(
+ &mut self,
+ index: TypeIndex,
+ module: &'data str,
+ field: &'data str,
+ ) -> WasmResult<()> {
+ assert_eq!(
+ self.info.functions.len(),
+ self.info.imported_funcs.len(),
+ "Imported functions must be declared first"
+ );
+ self.info.functions.push(Exportable::new(index));
+ self.info
+ .imported_funcs
+ .push((String::from(module), String::from(field)));
+ Ok(())
+ }
+
+ fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()> {
+ self.info.functions.push(Exportable::new(index));
+ Ok(())
+ }
+
+ fn declare_global(&mut self, global: Global) -> WasmResult<()> {
+ self.info.globals.push(Exportable::new(global));
+ Ok(())
+ }
+
+ fn declare_global_import(
+ &mut self,
+ global: Global,
+ module: &'data str,
+ field: &'data str,
+ ) -> WasmResult<()> {
+ self.info.globals.push(Exportable::new(global));
+ self.info
+ .imported_globals
+ .push((String::from(module), String::from(field)));
+ Ok(())
+ }
+
+ fn declare_table(&mut self, table: Table) -> WasmResult<()> {
+ self.info.tables.push(Exportable::new(table));
+ Ok(())
+ }
+
+ fn declare_table_import(
+ &mut self,
+ table: Table,
+ module: &'data str,
+ field: &'data str,
+ ) -> WasmResult<()> {
+ self.info.tables.push(Exportable::new(table));
+ self.info
+ .imported_tables
+ .push((String::from(module), String::from(field)));
+ Ok(())
+ }
+
+ fn declare_table_elements(
+ &mut self,
+ _table_index: TableIndex,
+ _base: Option<GlobalIndex>,
+ _offset: usize,
+ _elements: Box<[FuncIndex]>,
+ ) -> WasmResult<()> {
+ // We do nothing
+ Ok(())
+ }
+
+ fn declare_passive_element(
+ &mut self,
+ _elem_index: ElemIndex,
+ _segments: Box<[FuncIndex]>,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn declare_passive_data(
+ &mut self,
+ _elem_index: DataIndex,
+ _segments: &'data [u8],
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
+ self.info.memories.push(Exportable::new(memory));
+ Ok(())
+ }
+
+ fn declare_memory_import(
+ &mut self,
+ memory: Memory,
+ module: &'data str,
+ field: &'data str,
+ ) -> WasmResult<()> {
+ self.info.memories.push(Exportable::new(memory));
+ self.info
+ .imported_memories
+ .push((String::from(module), String::from(field)));
+ Ok(())
+ }
+
+ fn declare_data_initialization(
+ &mut self,
+ _memory_index: MemoryIndex,
+ _base: Option<GlobalIndex>,
+ _offset: usize,
+ _data: &'data [u8],
+ ) -> WasmResult<()> {
+ // We do nothing
+ Ok(())
+ }
+
+ fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()> {
+ self.info.functions[func_index]
+ .export_names
+ .push(String::from(name));
+ Ok(())
+ }
+
+ fn declare_table_export(
+ &mut self,
+ table_index: TableIndex,
+ name: &'data str,
+ ) -> WasmResult<()> {
+ self.info.tables[table_index]
+ .export_names
+ .push(String::from(name));
+ Ok(())
+ }
+
+ fn declare_memory_export(
+ &mut self,
+ memory_index: MemoryIndex,
+ name: &'data str,
+ ) -> WasmResult<()> {
+ self.info.memories[memory_index]
+ .export_names
+ .push(String::from(name));
+ Ok(())
+ }
+
+ fn declare_global_export(
+ &mut self,
+ global_index: GlobalIndex,
+ name: &'data str,
+ ) -> WasmResult<()> {
+ self.info.globals[global_index]
+ .export_names
+ .push(String::from(name));
+ Ok(())
+ }
+
+ fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> {
+ debug_assert!(self.info.start_func.is_none());
+ self.info.start_func = Some(func_index);
+ Ok(())
+ }
+
+ fn define_function_body(
+ &mut self,
+ mut validator: FuncValidator<ValidatorResources>,
+ body: FunctionBody<'data>,
+ ) -> WasmResult<()> {
+ self.func_bytecode_sizes
+ .push(body.get_binary_reader().bytes_remaining());
+ let func = {
+ let mut func_environ = DummyFuncEnvironment::new(&self.info, self.return_mode);
+ let func_index =
+ FuncIndex::new(self.get_num_func_imports() + self.info.function_bodies.len());
+ let name = get_func_name(func_index);
+ let sig = func_environ.vmctx_sig(self.get_func_type(func_index));
+ let mut func = ir::Function::with_name_signature(name, sig);
+ if self.debug_info {
+ func.collect_debug_info();
+ }
+ self.trans
+ .translate_body(&mut validator, body, &mut func, &mut func_environ)?;
+ func
+ };
+ self.info.function_bodies.push(func);
+ Ok(())
+ }
+
+ fn declare_module_name(&mut self, name: &'data str) {
+ self.module_name = Some(String::from(name));
+ }
+
+ fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) {
+ self.function_names[func_index] = String::from(name);
+ }
+
+ fn wasm_features(&self) -> WasmFeatures {
+ WasmFeatures {
+ multi_value: true,
+ simd: true,
+ reference_types: true,
+ bulk_memory: true,
+ ..WasmFeatures::default()
+ }
+ }
+}
diff --git a/third_party/rust/cranelift-wasm/src/environ/mod.rs b/third_party/rust/cranelift-wasm/src/environ/mod.rs
new file mode 100644
index 0000000000..bb4b7cc34e
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/environ/mod.rs
@@ -0,0 +1,11 @@
+//! Support for configurable wasm translation.
+
+mod dummy;
+#[macro_use]
+mod spec;
+
+pub use crate::environ::dummy::DummyEnvironment;
+pub use crate::environ::spec::{
+ FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment, WasmError,
+ WasmFuncType, WasmResult, WasmType,
+};
diff --git a/third_party/rust/cranelift-wasm/src/environ/spec.rs b/third_party/rust/cranelift-wasm/src/environ/spec.rs
new file mode 100644
index 0000000000..149e55d6dc
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/environ/spec.rs
@@ -0,0 +1,915 @@
+//! All the runtime support necessary for the wasm to cranelift translation is formalized by the
+//! traits `FunctionEnvironment` and `ModuleEnvironment`.
+//!
+//! There are skeleton implementations of these traits in the `dummy` module, and complete
+//! implementations in [Wasmtime].
+//!
+//! [Wasmtime]: https://github.com/bytecodealliance/wasmtime
+
+use crate::state::FuncTranslationState;
+use crate::translation_utils::{
+ DataIndex, ElemIndex, EntityType, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, Table,
+ TableIndex, TypeIndex,
+};
+use core::convert::From;
+use core::convert::TryFrom;
+use cranelift_codegen::cursor::FuncCursor;
+use cranelift_codegen::ir::immediates::Offset32;
+use cranelift_codegen::ir::{self, InstBuilder};
+use cranelift_codegen::isa::TargetFrontendConfig;
+use cranelift_frontend::FunctionBuilder;
+#[cfg(feature = "enable-serde")]
+use serde::{Deserialize, Serialize};
+use std::boxed::Box;
+use std::string::ToString;
+use thiserror::Error;
+use wasmparser::ValidatorResources;
+use wasmparser::{BinaryReaderError, FuncValidator, FunctionBody, Operator, WasmFeatures};
+
+/// WebAssembly value type -- equivalent of `wasmparser`'s Type.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub enum WasmType {
+ /// I32 type
+ I32,
+ /// I64 type
+ I64,
+ /// F32 type
+ F32,
+ /// F64 type
+ F64,
+ /// V128 type
+ V128,
+ /// FuncRef type
+ FuncRef,
+ /// ExternRef type
+ ExternRef,
+}
+
+impl TryFrom<wasmparser::Type> for WasmType {
+ type Error = WasmError;
+ fn try_from(ty: wasmparser::Type) -> Result<Self, Self::Error> {
+ use wasmparser::Type::*;
+ match ty {
+ I32 => Ok(WasmType::I32),
+ I64 => Ok(WasmType::I64),
+ F32 => Ok(WasmType::F32),
+ F64 => Ok(WasmType::F64),
+ V128 => Ok(WasmType::V128),
+ FuncRef => Ok(WasmType::FuncRef),
+ ExternRef => Ok(WasmType::ExternRef),
+ EmptyBlockType | Func => Err(WasmError::InvalidWebAssembly {
+ message: "unexpected value type".to_string(),
+ offset: 0,
+ }),
+ }
+ }
+}
+
+impl From<WasmType> for wasmparser::Type {
+ fn from(ty: WasmType) -> wasmparser::Type {
+ match ty {
+ WasmType::I32 => wasmparser::Type::I32,
+ WasmType::I64 => wasmparser::Type::I64,
+ WasmType::F32 => wasmparser::Type::F32,
+ WasmType::F64 => wasmparser::Type::F64,
+ WasmType::V128 => wasmparser::Type::V128,
+ WasmType::FuncRef => wasmparser::Type::FuncRef,
+ WasmType::ExternRef => wasmparser::Type::ExternRef,
+ }
+ }
+}
+
+/// WebAssembly function type -- equivalent of `wasmparser`'s FuncType.
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct WasmFuncType {
+ /// Function params types.
+ pub params: Box<[WasmType]>,
+ /// Returns params types.
+ pub returns: Box<[WasmType]>,
+}
+
+impl TryFrom<wasmparser::FuncType> for WasmFuncType {
+ type Error = WasmError;
+ fn try_from(ty: wasmparser::FuncType) -> Result<Self, Self::Error> {
+ Ok(Self {
+ params: ty
+ .params
+ .into_vec()
+ .into_iter()
+ .map(WasmType::try_from)
+ .collect::<Result<_, Self::Error>>()?,
+ returns: ty
+ .returns
+ .into_vec()
+ .into_iter()
+ .map(WasmType::try_from)
+ .collect::<Result<_, Self::Error>>()?,
+ })
+ }
+}
+
+/// The value of a WebAssembly global variable.
+#[derive(Clone, Copy)]
+pub enum GlobalVariable {
+ /// This is a constant global with a value known at compile time.
+ Const(ir::Value),
+
+ /// This is a variable in memory that should be referenced through a `GlobalValue`.
+ Memory {
+ /// The address of the global variable storage.
+ gv: ir::GlobalValue,
+ /// An offset to add to the address.
+ offset: Offset32,
+ /// The global variable's type.
+ ty: ir::Type,
+ },
+
+ /// This is a global variable that needs to be handled by the environment.
+ Custom,
+}
+
+/// A WebAssembly translation error.
+///
+/// When a WebAssembly function can't be translated, one of these error codes will be returned
+/// to describe the failure.
+#[derive(Error, Debug)]
+pub enum WasmError {
+ /// The input WebAssembly code is invalid.
+ ///
+ /// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
+ /// code. This should never happen for validated WebAssembly code.
+ #[error("Invalid input WebAssembly code at offset {offset}: {message}")]
+ InvalidWebAssembly {
+ /// A string describing the validation error.
+ message: std::string::String,
+ /// The bytecode offset where the error occurred.
+ offset: usize,
+ },
+
+ /// A feature used by the WebAssembly code is not supported by the embedding environment.
+ ///
+ /// Embedding environments may have their own limitations and feature restrictions.
+ #[error("Unsupported feature: {0}")]
+ Unsupported(std::string::String),
+
+ /// An implementation limit was exceeded.
+ ///
+ /// Cranelift can compile very large and complicated functions, but the [implementation has
+ /// limits][limits] that cause compilation to fail when they are exceeded.
+ ///
+ /// [limits]: https://github.com/bytecodealliance/wasmtime/blob/main/cranelift/docs/ir.md#implementation-limits
+ #[error("Implementation limit exceeded")]
+ ImplLimitExceeded,
+
+ /// Any user-defined error.
+ #[error("User error: {0}")]
+ User(std::string::String),
+}
+
+/// Return an `Err(WasmError::Unsupported(msg))` where `msg` the string built by calling `format!`
+/// on the arguments to this macro.
+#[macro_export]
+macro_rules! wasm_unsupported {
+ ($($arg:tt)*) => { $crate::environ::WasmError::Unsupported(format!($($arg)*)) }
+}
+
+impl From<BinaryReaderError> for WasmError {
+ /// Convert from a `BinaryReaderError` to a `WasmError`.
+ fn from(e: BinaryReaderError) -> Self {
+ Self::InvalidWebAssembly {
+ message: e.message().into(),
+ offset: e.offset(),
+ }
+ }
+}
+
+/// A convenient alias for a `Result` that uses `WasmError` as the error type.
+pub type WasmResult<T> = Result<T, WasmError>;
+
+/// How to return from functions.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum ReturnMode {
+ /// Use normal return instructions as needed.
+ NormalReturns,
+ /// Use a single fallthrough return at the end of the function.
+ FallthroughReturn,
+}
+
+/// Environment affecting the translation of a WebAssembly.
+pub trait TargetEnvironment {
+ /// Get the information needed to produce Cranelift IR for the given target.
+ fn target_config(&self) -> TargetFrontendConfig;
+
+ /// Get the Cranelift integer type to use for native pointers.
+ ///
+ /// This returns `I64` for 64-bit architectures and `I32` for 32-bit architectures.
+ fn pointer_type(&self) -> ir::Type {
+ ir::Type::int(u16::from(self.target_config().pointer_bits())).unwrap()
+ }
+
+ /// Get the size of a native pointer, in bytes.
+ fn pointer_bytes(&self) -> u8 {
+ self.target_config().pointer_bytes()
+ }
+
+ /// Get the Cranelift reference type to use for the given Wasm reference
+ /// type.
+ ///
+ /// By default, this returns `R64` for 64-bit architectures and `R32` for
+ /// 32-bit architectures. If you override this, then you should also
+ /// override `FuncEnvironment::{translate_ref_null, translate_ref_is_null}`
+ /// as well.
+ fn reference_type(&self, ty: WasmType) -> ir::Type {
+ let _ = ty;
+ match self.pointer_type() {
+ ir::types::I32 => ir::types::R32,
+ ir::types::I64 => ir::types::R64,
+ _ => panic!("unsupported pointer type"),
+ }
+ }
+}
+
+/// Environment affecting the translation of a single WebAssembly function.
+///
+/// A `FuncEnvironment` trait object is required to translate a WebAssembly function to Cranelift
+/// IR. The function environment provides information about the WebAssembly module as well as the
+/// runtime environment.
+pub trait FuncEnvironment: TargetEnvironment {
+ /// Is the given parameter of the given function a wasm-level parameter, as opposed to a hidden
+ /// parameter added for use by the implementation?
+ fn is_wasm_parameter(&self, signature: &ir::Signature, index: usize) -> bool {
+ signature.params[index].purpose == ir::ArgumentPurpose::Normal
+ }
+
+ /// Is the given return of the given function a wasm-level parameter, as
+ /// opposed to a hidden parameter added for use by the implementation?
+ fn is_wasm_return(&self, signature: &ir::Signature, index: usize) -> bool {
+ signature.returns[index].purpose == ir::ArgumentPurpose::Normal
+ }
+
+ /// Should the code be structured to use a single `fallthrough_return` instruction at the end
+ /// of the function body, rather than `return` instructions as needed? This is used by VMs
+ /// to append custom epilogues.
+ fn return_mode(&self) -> ReturnMode {
+ ReturnMode::NormalReturns
+ }
+
+ /// Set up the necessary preamble definitions in `func` to access the global variable
+ /// identified by `index`.
+ ///
+ /// The index space covers both imported globals and globals defined by the module.
+ ///
+ /// Return the global variable reference that should be used to access the global and the
+ /// WebAssembly type of the global.
+ fn make_global(
+ &mut self,
+ func: &mut ir::Function,
+ index: GlobalIndex,
+ ) -> WasmResult<GlobalVariable>;
+
+ /// Set up the necessary preamble definitions in `func` to access the linear memory identified
+ /// by `index`.
+ ///
+ /// The index space covers both imported and locally declared memories.
+ fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult<ir::Heap>;
+
+ /// Set up the necessary preamble definitions in `func` to access the table identified
+ /// by `index`.
+ ///
+ /// The index space covers both imported and locally declared tables.
+ fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> WasmResult<ir::Table>;
+
+ /// Set up a signature definition in the preamble of `func` that can be used for an indirect
+ /// call with signature `index`.
+ ///
+ /// The signature may contain additional arguments needed for an indirect call, but the
+ /// arguments marked as `ArgumentPurpose::Normal` must correspond to the WebAssembly signature
+ /// arguments.
+ ///
+ /// The signature will only be used for indirect calls, even if the module has direct function
+ /// calls with the same WebAssembly type.
+ fn make_indirect_sig(
+ &mut self,
+ func: &mut ir::Function,
+ index: TypeIndex,
+ ) -> WasmResult<ir::SigRef>;
+
+ /// Set up an external function definition in the preamble of `func` that can be used to
+ /// directly call the function `index`.
+ ///
+ /// The index space covers both imported functions and functions defined in the current module.
+ ///
+ /// The function's signature may contain additional arguments needed for a direct call, but the
+ /// arguments marked as `ArgumentPurpose::Normal` must correspond to the WebAssembly signature
+ /// arguments.
+ ///
+ /// The function's signature will only be used for direct calls, even if the module has
+ /// indirect calls with the same WebAssembly type.
+ fn make_direct_func(
+ &mut self,
+ func: &mut ir::Function,
+ index: FuncIndex,
+ ) -> WasmResult<ir::FuncRef>;
+
+ /// Translate a `call_indirect` WebAssembly instruction at `pos`.
+ ///
+ /// Insert instructions at `pos` for an indirect call to the function `callee` in the table
+ /// `table_index` with WebAssembly signature `sig_index`. The `callee` value will have type
+ /// `i32`.
+ ///
+ /// The signature `sig_ref` was previously created by `make_indirect_sig()`.
+ ///
+ /// Return the call instruction whose results are the WebAssembly return values.
+ #[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
+ fn translate_call_indirect(
+ &mut self,
+ pos: FuncCursor,
+ table_index: TableIndex,
+ table: ir::Table,
+ sig_index: TypeIndex,
+ sig_ref: ir::SigRef,
+ callee: ir::Value,
+ call_args: &[ir::Value],
+ ) -> WasmResult<ir::Inst>;
+
+ /// Translate a `call` WebAssembly instruction at `pos`.
+ ///
+ /// Insert instructions at `pos` for a direct call to the function `callee_index`.
+ ///
+ /// The function reference `callee` was previously created by `make_direct_func()`.
+ ///
+ /// Return the call instruction whose results are the WebAssembly return values.
+ fn translate_call(
+ &mut self,
+ mut pos: FuncCursor,
+ _callee_index: FuncIndex,
+ callee: ir::FuncRef,
+ call_args: &[ir::Value],
+ ) -> WasmResult<ir::Inst> {
+ Ok(pos.ins().call(callee, call_args))
+ }
+
+ /// Translate a `memory.grow` WebAssembly instruction.
+ ///
+ /// The `index` provided identifies the linear memory to grow, and `heap` is the heap reference
+ /// returned by `make_heap` for the same index.
+ ///
+ /// The `val` value is the requested memory size in pages.
+ ///
+ /// Returns the old size (in pages) of the memory.
+ fn translate_memory_grow(
+ &mut self,
+ pos: FuncCursor,
+ index: MemoryIndex,
+ heap: ir::Heap,
+ val: ir::Value,
+ ) -> WasmResult<ir::Value>;
+
+ /// Translates a `memory.size` WebAssembly instruction.
+ ///
+ /// The `index` provided identifies the linear memory to query, and `heap` is the heap reference
+ /// returned by `make_heap` for the same index.
+ ///
+ /// Returns the size in pages of the memory.
+ fn translate_memory_size(
+ &mut self,
+ pos: FuncCursor,
+ index: MemoryIndex,
+ heap: ir::Heap,
+ ) -> WasmResult<ir::Value>;
+
+ /// Translate a `memory.copy` WebAssembly instruction.
+ ///
+ /// The `index` provided identifies the linear memory to query, and `heap` is the heap reference
+ /// returned by `make_heap` for the same index.
+ fn translate_memory_copy(
+ &mut self,
+ pos: FuncCursor,
+ src_index: MemoryIndex,
+ src_heap: ir::Heap,
+ dst_index: MemoryIndex,
+ dst_heap: ir::Heap,
+ dst: ir::Value,
+ src: ir::Value,
+ len: ir::Value,
+ ) -> WasmResult<()>;
+
+ /// Translate a `memory.fill` WebAssembly instruction.
+ ///
+ /// The `index` provided identifies the linear memory to query, and `heap` is the heap reference
+ /// returned by `make_heap` for the same index.
+ fn translate_memory_fill(
+ &mut self,
+ pos: FuncCursor,
+ index: MemoryIndex,
+ heap: ir::Heap,
+ dst: ir::Value,
+ val: ir::Value,
+ len: ir::Value,
+ ) -> WasmResult<()>;
+
+ /// Translate a `memory.init` WebAssembly instruction.
+ ///
+ /// The `index` provided identifies the linear memory to query, and `heap` is the heap reference
+ /// returned by `make_heap` for the same index. `seg_index` is the index of the segment to copy
+ /// from.
+ #[allow(clippy::too_many_arguments)]
+ fn translate_memory_init(
+ &mut self,
+ pos: FuncCursor,
+ index: MemoryIndex,
+ heap: ir::Heap,
+ seg_index: u32,
+ dst: ir::Value,
+ src: ir::Value,
+ len: ir::Value,
+ ) -> WasmResult<()>;
+
+ /// Translate a `data.drop` WebAssembly instruction.
+ fn translate_data_drop(&mut self, pos: FuncCursor, seg_index: u32) -> WasmResult<()>;
+
+ /// Translate a `table.size` WebAssembly instruction.
+ fn translate_table_size(
+ &mut self,
+ pos: FuncCursor,
+ index: TableIndex,
+ table: ir::Table,
+ ) -> WasmResult<ir::Value>;
+
+ /// Translate a `table.grow` WebAssembly instruction.
+ fn translate_table_grow(
+ &mut self,
+ pos: FuncCursor,
+ table_index: TableIndex,
+ table: ir::Table,
+ delta: ir::Value,
+ init_value: ir::Value,
+ ) -> WasmResult<ir::Value>;
+
+ /// Translate a `table.get` WebAssembly instruction.
+ fn translate_table_get(
+ &mut self,
+ builder: &mut FunctionBuilder,
+ table_index: TableIndex,
+ table: ir::Table,
+ index: ir::Value,
+ ) -> WasmResult<ir::Value>;
+
+ /// Translate a `table.set` WebAssembly instruction.
+ fn translate_table_set(
+ &mut self,
+ builder: &mut FunctionBuilder,
+ table_index: TableIndex,
+ table: ir::Table,
+ value: ir::Value,
+ index: ir::Value,
+ ) -> WasmResult<()>;
+
+ /// Translate a `table.copy` WebAssembly instruction.
+ #[allow(clippy::too_many_arguments)]
+ fn translate_table_copy(
+ &mut self,
+ pos: FuncCursor,
+ dst_table_index: TableIndex,
+ dst_table: ir::Table,
+ src_table_index: TableIndex,
+ src_table: ir::Table,
+ dst: ir::Value,
+ src: ir::Value,
+ len: ir::Value,
+ ) -> WasmResult<()>;
+
+ /// Translate a `table.fill` WebAssembly instruction.
+ fn translate_table_fill(
+ &mut self,
+ pos: FuncCursor,
+ table_index: TableIndex,
+ dst: ir::Value,
+ val: ir::Value,
+ len: ir::Value,
+ ) -> WasmResult<()>;
+
+ /// Translate a `table.init` WebAssembly instruction.
+ #[allow(clippy::too_many_arguments)]
+ fn translate_table_init(
+ &mut self,
+ pos: FuncCursor,
+ seg_index: u32,
+ table_index: TableIndex,
+ table: ir::Table,
+ dst: ir::Value,
+ src: ir::Value,
+ len: ir::Value,
+ ) -> WasmResult<()>;
+
+ /// Translate a `elem.drop` WebAssembly instruction.
+ fn translate_elem_drop(&mut self, pos: FuncCursor, seg_index: u32) -> WasmResult<()>;
+
+ /// Translate a `ref.null T` WebAssembly instruction.
+ ///
+ /// By default, translates into a null reference type.
+ ///
+ /// Override this if you don't use Cranelift reference types for all Wasm
+ /// reference types (e.g. you use a raw pointer for `funcref`s) or if the
+ /// null sentinel is not a null reference type pointer for your type. If you
+ /// override this method, then you should also override
+ /// `translate_ref_is_null` as well.
+ fn translate_ref_null(&mut self, mut pos: FuncCursor, ty: WasmType) -> WasmResult<ir::Value> {
+ let _ = ty;
+ Ok(pos.ins().null(self.reference_type(ty)))
+ }
+
+ /// Translate a `ref.is_null` WebAssembly instruction.
+ ///
+ /// By default, assumes that `value` is a Cranelift reference type, and that
+ /// a null Cranelift reference type is the null value for all Wasm reference
+ /// types.
+ ///
+ /// If you override this method, you probably also want to override
+ /// `translate_ref_null` as well.
+ fn translate_ref_is_null(
+ &mut self,
+ mut pos: FuncCursor,
+ value: ir::Value,
+ ) -> WasmResult<ir::Value> {
+ let is_null = pos.ins().is_null(value);
+ Ok(pos.ins().bint(ir::types::I32, is_null))
+ }
+
+ /// Translate a `ref.func` WebAssembly instruction.
+ fn translate_ref_func(
+ &mut self,
+ pos: FuncCursor,
+ func_index: FuncIndex,
+ ) -> WasmResult<ir::Value>;
+
+ /// Translate a `global.get` WebAssembly instruction at `pos` for a global
+ /// that is custom.
+ fn translate_custom_global_get(
+ &mut self,
+ pos: FuncCursor,
+ global_index: GlobalIndex,
+ ) -> WasmResult<ir::Value>;
+
+ /// Translate a `global.set` WebAssembly instruction at `pos` for a global
+ /// that is custom.
+ fn translate_custom_global_set(
+ &mut self,
+ pos: FuncCursor,
+ global_index: GlobalIndex,
+ val: ir::Value,
+ ) -> WasmResult<()>;
+
+ /// Translate an `i32.atomic.wait` or `i64.atomic.wait` WebAssembly instruction.
+ /// The `index` provided identifies the linear memory containing the value
+ /// to wait on, and `heap` is the heap reference returned by `make_heap`
+ /// for the same index. Whether the waited-on value is 32- or 64-bit can be
+ /// determined by examining the type of `expected`, which must be only I32 or I64.
+ ///
+ /// Returns an i32, which is negative if the helper call failed.
+ fn translate_atomic_wait(
+ &mut self,
+ pos: FuncCursor,
+ index: MemoryIndex,
+ heap: ir::Heap,
+ addr: ir::Value,
+ expected: ir::Value,
+ timeout: ir::Value,
+ ) -> WasmResult<ir::Value>;
+
+ /// Translate an `atomic.notify` WebAssembly instruction.
+ /// The `index` provided identifies the linear memory containing the value
+ /// to wait on, and `heap` is the heap reference returned by `make_heap`
+ /// for the same index.
+ ///
+ /// Returns an i64, which is negative if the helper call failed.
+ fn translate_atomic_notify(
+ &mut self,
+ pos: FuncCursor,
+ index: MemoryIndex,
+ heap: ir::Heap,
+ addr: ir::Value,
+ count: ir::Value,
+ ) -> WasmResult<ir::Value>;
+
+ /// Emit code at the beginning of every wasm loop.
+ ///
+ /// This can be used to insert explicit interrupt or safepoint checking at
+ /// the beginnings of loops.
+ fn translate_loop_header(&mut self, _pos: FuncCursor) -> WasmResult<()> {
+ // By default, don't emit anything.
+ Ok(())
+ }
+
+ /// Optional callback for the `FunctionEnvironment` performing this translation to maintain
+ /// internal state or prepare custom state for the operator to translate
+ fn before_translate_operator(
+ &mut self,
+ _op: &Operator,
+ _builder: &mut FunctionBuilder,
+ _state: &FuncTranslationState,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Optional callback for the `FunctionEnvironment` performing this translation to maintain
+ /// internal state or finalize custom state for the operator that was translated
+ fn after_translate_operator(
+ &mut self,
+ _op: &Operator,
+ _builder: &mut FunctionBuilder,
+ _state: &FuncTranslationState,
+ ) -> WasmResult<()> {
+ Ok(())
+ }
+}
+
+/// An object satisfying the `ModuleEnvironment` trait can be passed as argument to the
+/// [`translate_module`](fn.translate_module.html) function. These methods should not be called
+/// by the user, they are only for `cranelift-wasm` internal use.
+pub trait ModuleEnvironment<'data>: TargetEnvironment {
+ /// Provides the number of types up front. By default this does nothing, but
+ /// implementations can use this to preallocate memory if desired.
+ fn reserve_types(&mut self, _num: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Declares a function signature to the environment.
+ fn declare_type_func(
+ &mut self,
+ wasm_func_type: WasmFuncType,
+ sig: ir::Signature,
+ ) -> WasmResult<()>;
+
+ /// Declares a module type signature to the environment.
+ fn declare_type_module(
+ &mut self,
+ imports: &[(&'data str, Option<&'data str>, EntityType)],
+ exports: &[(&'data str, EntityType)],
+ ) -> WasmResult<()> {
+ drop((imports, exports));
+ Err(WasmError::Unsupported("module linking".to_string()))
+ }
+
+ /// Declares an instance type signature to the environment.
+ fn declare_type_instance(&mut self, exports: &[(&'data str, EntityType)]) -> WasmResult<()> {
+ drop(exports);
+ Err(WasmError::Unsupported("module linking".to_string()))
+ }
+
+ /// Provides the number of imports up front. By default this does nothing, but
+ /// implementations can use this to preallocate memory if desired.
+ fn reserve_imports(&mut self, _num: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Declares a function import to the environment.
+ fn declare_func_import(
+ &mut self,
+ index: TypeIndex,
+ module: &'data str,
+ field: &'data str,
+ ) -> WasmResult<()>;
+
+ /// Declares a table import to the environment.
+ fn declare_table_import(
+ &mut self,
+ table: Table,
+ module: &'data str,
+ field: &'data str,
+ ) -> WasmResult<()>;
+
+ /// Declares a memory import to the environment.
+ fn declare_memory_import(
+ &mut self,
+ memory: Memory,
+ module: &'data str,
+ field: &'data str,
+ ) -> WasmResult<()>;
+
+ /// Declares a global import to the environment.
+ fn declare_global_import(
+ &mut self,
+ global: Global,
+ module: &'data str,
+ field: &'data str,
+ ) -> WasmResult<()>;
+
+ /// Declares a module import to the environment.
+ fn declare_module_import(
+ &mut self,
+ ty_index: TypeIndex,
+ module: &'data str,
+ field: &'data str,
+ ) -> WasmResult<()> {
+ drop((ty_index, module, field));
+ Err(WasmError::Unsupported("module linking".to_string()))
+ }
+
+ /// Declares an instance import to the environment.
+ fn declare_instance_import(
+ &mut self,
+ ty_index: TypeIndex,
+ module: &'data str,
+ field: &'data str,
+ ) -> WasmResult<()> {
+ drop((ty_index, module, field));
+ Err(WasmError::Unsupported("module linking".to_string()))
+ }
+
+ /// Notifies the implementation that all imports have been declared.
+ fn finish_imports(&mut self) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Provides the number of defined functions up front. By default this does nothing, but
+ /// implementations can use this to preallocate memory if desired.
+ fn reserve_func_types(&mut self, _num: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Declares the type (signature) of a local function in the module.
+ fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()>;
+
+ /// Provides the number of defined tables up front. By default this does nothing, but
+ /// implementations can use this to preallocate memory if desired.
+ fn reserve_tables(&mut self, _num: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Declares a table to the environment.
+ fn declare_table(&mut self, table: Table) -> WasmResult<()>;
+
+ /// Provides the number of defined memories up front. By default this does nothing, but
+ /// implementations can use this to preallocate memory if desired.
+ fn reserve_memories(&mut self, _num: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Declares a memory to the environment
+ fn declare_memory(&mut self, memory: Memory) -> WasmResult<()>;
+
+ /// Provides the number of defined globals up front. By default this does nothing, but
+ /// implementations can use this to preallocate memory if desired.
+ fn reserve_globals(&mut self, _num: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Declares a global to the environment.
+ fn declare_global(&mut self, global: Global) -> WasmResult<()>;
+
+ /// Provides the number of exports up front. By default this does nothing, but
+ /// implementations can use this to preallocate memory if desired.
+ fn reserve_exports(&mut self, _num: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Declares a function export to the environment.
+ fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()>;
+
+ /// Declares a table export to the environment.
+ fn declare_table_export(&mut self, table_index: TableIndex, name: &'data str)
+ -> WasmResult<()>;
+
+ /// Declares a memory export to the environment.
+ fn declare_memory_export(
+ &mut self,
+ memory_index: MemoryIndex,
+ name: &'data str,
+ ) -> WasmResult<()>;
+
+ /// Declares a global export to the environment.
+ fn declare_global_export(
+ &mut self,
+ global_index: GlobalIndex,
+ name: &'data str,
+ ) -> WasmResult<()>;
+
+ /// Notifies the implementation that all exports have been declared.
+ fn finish_exports(&mut self) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Declares the optional start function.
+ fn declare_start_func(&mut self, index: FuncIndex) -> WasmResult<()>;
+
+ /// Provides the number of element initializers up front. By default this does nothing, but
+ /// implementations can use this to preallocate memory if desired.
+ fn reserve_table_elements(&mut self, _num: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Fills a declared table with references to functions in the module.
+ fn declare_table_elements(
+ &mut self,
+ table_index: TableIndex,
+ base: Option<GlobalIndex>,
+ offset: usize,
+ elements: Box<[FuncIndex]>,
+ ) -> WasmResult<()>;
+
+ /// Declare a passive element segment.
+ fn declare_passive_element(
+ &mut self,
+ index: ElemIndex,
+ elements: Box<[FuncIndex]>,
+ ) -> WasmResult<()>;
+
+ /// Provides the number of passive data segments up front.
+ ///
+ /// By default this does nothing, but implementations may use this to
+ /// pre-allocate memory if desired.
+ fn reserve_passive_data(&mut self, count: u32) -> WasmResult<()> {
+ let _ = count;
+ Ok(())
+ }
+
+ /// Declare a passive data segment.
+ fn declare_passive_data(&mut self, data_index: DataIndex, data: &'data [u8]) -> WasmResult<()>;
+
+ /// Indicates how many functions the code section reports and the byte
+ /// offset of where the code sections starts.
+ fn reserve_function_bodies(&mut self, bodies: u32, code_section_offset: u64) {
+ drop((bodies, code_section_offset));
+ }
+
+ /// Provides the contents of a function body.
+ fn define_function_body(
+ &mut self,
+ validator: FuncValidator<ValidatorResources>,
+ body: FunctionBody<'data>,
+ ) -> WasmResult<()>;
+
+ /// Provides the number of data initializers up front. By default this does nothing, but
+ /// implementations can use this to preallocate memory if desired.
+ fn reserve_data_initializers(&mut self, _num: u32) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Fills a declared memory with bytes at module instantiation.
+ fn declare_data_initialization(
+ &mut self,
+ memory_index: MemoryIndex,
+ base: Option<GlobalIndex>,
+ offset: usize,
+ data: &'data [u8],
+ ) -> WasmResult<()>;
+
+ /// Declares the name of a module to the environment.
+ ///
+ /// By default this does nothing, but implementations can use this to read
+ /// the module name subsection of the custom name section if desired.
+ fn declare_module_name(&mut self, _name: &'data str) {}
+
+ /// Declares the name of a function to the environment.
+ ///
+ /// By default this does nothing, but implementations can use this to read
+ /// the function name subsection of the custom name section if desired.
+ fn declare_func_name(&mut self, _func_index: FuncIndex, _name: &'data str) {}
+
+ /// Declares the name of a function's local to the environment.
+ ///
+ /// By default this does nothing, but implementations can use this to read
+ /// the local name subsection of the custom name section if desired.
+ fn declare_local_name(&mut self, _func_index: FuncIndex, _local_index: u32, _name: &'data str) {
+ }
+
+ /// Indicates that a custom section has been found in the wasm file
+ fn custom_section(&mut self, _name: &'data str, _data: &'data [u8]) -> WasmResult<()> {
+ Ok(())
+ }
+
+ /// Returns the list of enabled wasm features this translation will be using.
+ fn wasm_features(&self) -> WasmFeatures {
+ WasmFeatures::default()
+ }
+
+ /// Indicates that this module will have `amount` submodules.
+ ///
+ /// Note that this is just child modules of this module, and each child
+ /// module may have yet more submodules.
+ fn reserve_modules(&mut self, amount: u32) {
+ drop(amount);
+ }
+
+ /// Called at the beginning of translating a module.
+ ///
+ /// The `index` argument is a monotonically increasing index which
+ /// corresponds to the nth module that's being translated. This is not the
+ /// 32-bit index in the current module's index space. For example the first
+ /// call to `module_start` will have index 0.
+ ///
+ /// Note that for nested modules this may be called multiple times.
+ fn module_start(&mut self, index: usize) {
+ drop(index);
+ }
+
+ /// Called at the end of translating a module.
+ ///
+ /// Note that for nested modules this may be called multiple times.
+ fn module_end(&mut self, index: usize) {
+ drop(index);
+ }
+}
diff --git a/third_party/rust/cranelift-wasm/src/func_translator.rs b/third_party/rust/cranelift-wasm/src/func_translator.rs
new file mode 100644
index 0000000000..158271ab96
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/func_translator.rs
@@ -0,0 +1,418 @@
+//! Stand-alone WebAssembly to Cranelift IR translator.
+//!
+//! This module defines the `FuncTranslator` type which can translate a single WebAssembly
+//! function to Cranelift IR guided by a `FuncEnvironment` which provides information about the
+//! WebAssembly module and the runtime environment.
+
+use crate::code_translator::{bitcast_arguments, translate_operator, wasm_param_types};
+use crate::environ::{FuncEnvironment, ReturnMode, WasmResult};
+use crate::state::FuncTranslationState;
+use crate::translation_utils::get_vmctx_value_label;
+use crate::wasm_unsupported;
+use core::convert::TryInto;
+use cranelift_codegen::entity::EntityRef;
+use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
+use cranelift_codegen::timing;
+use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
+use wasmparser::{self, BinaryReader, FuncValidator, FunctionBody, WasmModuleResources};
+
+/// WebAssembly to Cranelift IR function translator.
+///
+/// A `FuncTranslator` is used to translate a binary WebAssembly function into Cranelift IR guided
+/// by a `FuncEnvironment` object. A single translator instance can be reused to translate multiple
+/// functions which will reduce heap allocation traffic.
+pub struct FuncTranslator {
+ func_ctx: FunctionBuilderContext,
+ state: FuncTranslationState,
+}
+
+impl FuncTranslator {
+ /// Create a new translator.
+ pub fn new() -> Self {
+ Self {
+ func_ctx: FunctionBuilderContext::new(),
+ state: FuncTranslationState::new(),
+ }
+ }
+
+ /// Translate a binary WebAssembly function.
+ ///
+ /// The `code` slice contains the binary WebAssembly *function code* as it appears in the code
+ /// section of a WebAssembly module, not including the initial size of the function code. The
+ /// slice is expected to contain two parts:
+ ///
+ /// - The declaration of *locals*, and
+ /// - The function *body* as an expression.
+ ///
+ /// See [the WebAssembly specification][wasm].
+ ///
+ /// [wasm]: https://webassembly.github.io/spec/core/binary/modules.html#code-section
+ ///
+ /// The Cranelift IR function `func` should be completely empty except for the `func.signature`
+ /// and `func.name` fields. The signature may contain special-purpose arguments which are not
+ /// regarded as WebAssembly local variables. Any signature arguments marked as
+ /// `ArgumentPurpose::Normal` are made accessible as WebAssembly local variables.
+ ///
+ pub fn translate<FE: FuncEnvironment + ?Sized>(
+ &mut self,
+ validator: &mut FuncValidator<impl WasmModuleResources>,
+ code: &[u8],
+ code_offset: usize,
+ func: &mut ir::Function,
+ environ: &mut FE,
+ ) -> WasmResult<()> {
+ self.translate_body(
+ validator,
+ FunctionBody::new(code_offset, code),
+ func,
+ environ,
+ )
+ }
+
+ /// Translate a binary WebAssembly function from a `FunctionBody`.
+ pub fn translate_body<FE: FuncEnvironment + ?Sized>(
+ &mut self,
+ validator: &mut FuncValidator<impl WasmModuleResources>,
+ body: FunctionBody<'_>,
+ func: &mut ir::Function,
+ environ: &mut FE,
+ ) -> WasmResult<()> {
+ let _tt = timing::wasm_translate_function();
+ let mut reader = body.get_binary_reader();
+ log::debug!(
+ "translate({} bytes, {}{})",
+ reader.bytes_remaining(),
+ func.name,
+ func.signature
+ );
+ debug_assert_eq!(func.dfg.num_blocks(), 0, "Function must be empty");
+ debug_assert_eq!(func.dfg.num_insts(), 0, "Function must be empty");
+
+ // This clears the `FunctionBuilderContext`.
+ let mut builder = FunctionBuilder::new(func, &mut self.func_ctx);
+ builder.set_srcloc(cur_srcloc(&reader));
+ let entry_block = builder.create_block();
+ builder.append_block_params_for_function_params(entry_block);
+ builder.switch_to_block(entry_block); // This also creates values for the arguments.
+ builder.seal_block(entry_block); // Declare all predecessors known.
+
+ // Make sure the entry block is inserted in the layout before we make any callbacks to
+ // `environ`. The callback functions may need to insert things in the entry block.
+ builder.ensure_inserted_block();
+
+ let num_params = declare_wasm_parameters(&mut builder, entry_block, environ);
+
+ // Set up the translation state with a single pushed control block representing the whole
+ // function and its return values.
+ let exit_block = builder.create_block();
+ builder.append_block_params_for_function_returns(exit_block);
+ self.state.initialize(&builder.func.signature, exit_block);
+
+ parse_local_decls(&mut reader, &mut builder, num_params, environ, validator)?;
+ parse_function_body(validator, reader, &mut builder, &mut self.state, environ)?;
+
+ builder.finalize();
+ Ok(())
+ }
+}
+
+/// Declare local variables for the signature parameters that correspond to WebAssembly locals.
+///
+/// Return the number of local variables declared.
+fn declare_wasm_parameters<FE: FuncEnvironment + ?Sized>(
+ builder: &mut FunctionBuilder,
+ entry_block: Block,
+ environ: &FE,
+) -> usize {
+ let sig_len = builder.func.signature.params.len();
+ let mut next_local = 0;
+ for i in 0..sig_len {
+ let param_type = builder.func.signature.params[i];
+ // There may be additional special-purpose parameters in addition to the normal WebAssembly
+ // signature parameters. For example, a `vmctx` pointer.
+ if environ.is_wasm_parameter(&builder.func.signature, i) {
+ // This is a normal WebAssembly signature parameter, so create a local for it.
+ let local = Variable::new(next_local);
+ builder.declare_var(local, param_type.value_type);
+ next_local += 1;
+
+ let param_value = builder.block_params(entry_block)[i];
+ builder.def_var(local, param_value);
+ }
+ if param_type.purpose == ir::ArgumentPurpose::VMContext {
+ let param_value = builder.block_params(entry_block)[i];
+ builder.set_val_label(param_value, get_vmctx_value_label());
+ }
+ }
+
+ next_local
+}
+
+/// Parse the local variable declarations that precede the function body.
+///
+/// Declare local variables, starting from `num_params`.
+fn parse_local_decls<FE: FuncEnvironment + ?Sized>(
+ reader: &mut BinaryReader,
+ builder: &mut FunctionBuilder,
+ num_params: usize,
+ environ: &mut FE,
+ validator: &mut FuncValidator<impl WasmModuleResources>,
+) -> WasmResult<()> {
+ let mut next_local = num_params;
+ let local_count = reader.read_var_u32()?;
+
+ for _ in 0..local_count {
+ builder.set_srcloc(cur_srcloc(reader));
+ let pos = reader.original_position();
+ let count = reader.read_var_u32()?;
+ let ty = reader.read_type()?;
+ validator.define_locals(pos, count, ty)?;
+ declare_locals(builder, count, ty, &mut next_local, environ)?;
+ }
+
+ Ok(())
+}
+
+/// Declare `count` local variables of the same type, starting from `next_local`.
+///
+/// Fail of too many locals are declared in the function, or if the type is not valid for a local.
+fn declare_locals<FE: FuncEnvironment + ?Sized>(
+ builder: &mut FunctionBuilder,
+ count: u32,
+ wasm_type: wasmparser::Type,
+ next_local: &mut usize,
+ environ: &mut FE,
+) -> WasmResult<()> {
+ // All locals are initialized to 0.
+ use wasmparser::Type::*;
+ let zeroval = match wasm_type {
+ I32 => builder.ins().iconst(ir::types::I32, 0),
+ I64 => builder.ins().iconst(ir::types::I64, 0),
+ F32 => builder.ins().f32const(ir::immediates::Ieee32::with_bits(0)),
+ F64 => builder.ins().f64const(ir::immediates::Ieee64::with_bits(0)),
+ V128 => {
+ let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into());
+ builder.ins().vconst(ir::types::I8X16, constant_handle)
+ }
+ ExternRef | FuncRef => {
+ environ.translate_ref_null(builder.cursor(), wasm_type.try_into()?)?
+ }
+ ty => return Err(wasm_unsupported!("unsupported local type {:?}", ty)),
+ };
+
+ let ty = builder.func.dfg.value_type(zeroval);
+ for _ in 0..count {
+ let local = Variable::new(*next_local);
+ builder.declare_var(local, ty);
+ builder.def_var(local, zeroval);
+ builder.set_val_label(zeroval, ValueLabel::new(*next_local));
+ *next_local += 1;
+ }
+ Ok(())
+}
+
+/// Parse the function body in `reader`.
+///
+/// This assumes that the local variable declarations have already been parsed and function
+/// arguments and locals are declared in the builder.
+fn parse_function_body<FE: FuncEnvironment + ?Sized>(
+ validator: &mut FuncValidator<impl WasmModuleResources>,
+ mut reader: BinaryReader,
+ builder: &mut FunctionBuilder,
+ state: &mut FuncTranslationState,
+ environ: &mut FE,
+) -> WasmResult<()> {
+ // The control stack is initialized with a single block representing the whole function.
+ debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
+
+ while !reader.eof() {
+ let pos = reader.original_position();
+ builder.set_srcloc(cur_srcloc(&reader));
+ let op = reader.read_operator()?;
+ validator.op(pos, &op)?;
+ environ.before_translate_operator(&op, builder, state)?;
+ translate_operator(validator, &op, builder, state, environ)?;
+ environ.after_translate_operator(&op, builder, state)?;
+ }
+ let pos = reader.original_position();
+ validator.finish(pos)?;
+
+ // The final `End` operator left us in the exit block where we need to manually add a return
+ // instruction.
+ //
+ // If the exit block is unreachable, it may not have the correct arguments, so we would
+ // generate a return instruction that doesn't match the signature.
+ if state.reachable {
+ debug_assert!(builder.is_pristine());
+ if !builder.is_unreachable() {
+ match environ.return_mode() {
+ ReturnMode::NormalReturns => {
+ let return_types = wasm_param_types(&builder.func.signature.returns, |i| {
+ environ.is_wasm_return(&builder.func.signature, i)
+ });
+ bitcast_arguments(&mut state.stack, &return_types, builder);
+ builder.ins().return_(&state.stack)
+ }
+ ReturnMode::FallthroughReturn => builder.ins().fallthrough_return(&state.stack),
+ };
+ }
+ }
+
+ // Discard any remaining values on the stack. Either we just returned them,
+ // or the end of the function is unreachable.
+ state.stack.clear();
+
+ Ok(())
+}
+
+/// Get the current source location from a reader.
+fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc {
+ // We record source locations as byte code offsets relative to the beginning of the file.
+ // This will wrap around if byte code is larger than 4 GB.
+ ir::SourceLoc::new(reader.original_position() as u32)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{FuncTranslator, ReturnMode};
+ use crate::environ::DummyEnvironment;
+ use cranelift_codegen::ir::types::I32;
+ use cranelift_codegen::{ir, isa, settings, Context};
+ use log::debug;
+ use target_lexicon::PointerWidth;
+ use wasmparser::{
+ FuncValidator, FunctionBody, Parser, ValidPayload, Validator, ValidatorResources,
+ };
+
+ #[test]
+ fn small1() {
+ // Implicit return.
+ let wasm = wat::parse_str(
+ "
+ (module
+ (func $small2 (param i32) (result i32)
+ (i32.add (get_local 0) (i32.const 1))
+ )
+ )
+ ",
+ )
+ .unwrap();
+
+ let mut trans = FuncTranslator::new();
+ let flags = settings::Flags::new(settings::builder());
+ let runtime = DummyEnvironment::new(
+ isa::TargetFrontendConfig {
+ default_call_conv: isa::CallConv::Fast,
+ pointer_width: PointerWidth::U64,
+ },
+ ReturnMode::NormalReturns,
+ false,
+ );
+
+ let mut ctx = Context::new();
+
+ ctx.func.name = ir::ExternalName::testcase("small1");
+ ctx.func.signature.params.push(ir::AbiParam::new(I32));
+ ctx.func.signature.returns.push(ir::AbiParam::new(I32));
+
+ let (body, mut validator) = extract_func(&wasm);
+ trans
+ .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
+ .unwrap();
+ debug!("{}", ctx.func.display(None));
+ ctx.verify(&flags).unwrap();
+ }
+
+ #[test]
+ fn small2() {
+ // Same as above, but with an explicit return instruction.
+ let wasm = wat::parse_str(
+ "
+ (module
+ (func $small2 (param i32) (result i32)
+ (return (i32.add (get_local 0) (i32.const 1)))
+ )
+ )
+ ",
+ )
+ .unwrap();
+
+ let mut trans = FuncTranslator::new();
+ let flags = settings::Flags::new(settings::builder());
+ let runtime = DummyEnvironment::new(
+ isa::TargetFrontendConfig {
+ default_call_conv: isa::CallConv::Fast,
+ pointer_width: PointerWidth::U64,
+ },
+ ReturnMode::NormalReturns,
+ false,
+ );
+
+ let mut ctx = Context::new();
+
+ ctx.func.name = ir::ExternalName::testcase("small2");
+ ctx.func.signature.params.push(ir::AbiParam::new(I32));
+ ctx.func.signature.returns.push(ir::AbiParam::new(I32));
+
+ let (body, mut validator) = extract_func(&wasm);
+ trans
+ .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
+ .unwrap();
+ debug!("{}", ctx.func.display(None));
+ ctx.verify(&flags).unwrap();
+ }
+
+ #[test]
+ fn infloop() {
+ // An infinite loop, no return instructions.
+ let wasm = wat::parse_str(
+ "
+ (module
+ (func $infloop (result i32)
+ (local i32)
+ (loop (result i32)
+ (i32.add (get_local 0) (i32.const 1))
+ (set_local 0)
+ (br 0)
+ )
+ )
+ )
+ ",
+ )
+ .unwrap();
+
+ let mut trans = FuncTranslator::new();
+ let flags = settings::Flags::new(settings::builder());
+ let runtime = DummyEnvironment::new(
+ isa::TargetFrontendConfig {
+ default_call_conv: isa::CallConv::Fast,
+ pointer_width: PointerWidth::U64,
+ },
+ ReturnMode::NormalReturns,
+ false,
+ );
+
+ let mut ctx = Context::new();
+
+ ctx.func.name = ir::ExternalName::testcase("infloop");
+ ctx.func.signature.returns.push(ir::AbiParam::new(I32));
+
+ let (body, mut validator) = extract_func(&wasm);
+ trans
+ .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
+ .unwrap();
+ debug!("{}", ctx.func.display(None));
+ ctx.verify(&flags).unwrap();
+ }
+
+ fn extract_func(wat: &[u8]) -> (FunctionBody<'_>, FuncValidator<ValidatorResources>) {
+ let mut validator = Validator::new();
+ for payload in Parser::new(0).parse_all(wat) {
+ match validator.payload(&payload.unwrap()).unwrap() {
+ ValidPayload::Func(validator, body) => return (body, validator),
+ _ => {}
+ }
+ }
+ panic!("failed to find function");
+ }
+}
diff --git a/third_party/rust/cranelift-wasm/src/lib.rs b/third_party/rust/cranelift-wasm/src/lib.rs
new file mode 100644
index 0000000000..3e6d4401a1
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/lib.rs
@@ -0,0 +1,76 @@
+//! Performs translation from a wasm module in binary format to the in-memory form
+//! of Cranelift IR. More particularly, it translates the code of all the functions bodies and
+//! interacts with an environment implementing the
+//! [`ModuleEnvironment`](trait.ModuleEnvironment.html)
+//! trait to deal with tables, globals and linear memory.
+//!
+//! The crate provides a `DummyEnvironment` struct that will allow to translate the code of the
+//! functions but will fail at execution.
+//!
+//! The main function of this module is [`translate_module`](fn.translate_module.html).
+
+#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
+#![warn(unused_import_braces)]
+#![cfg_attr(feature = "std", deny(unstable_features))]
+#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
+#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
+#![cfg_attr(
+ feature = "cargo-clippy",
+ warn(
+ clippy::float_arithmetic,
+ clippy::mut_mut,
+ clippy::nonminimal_bool,
+ clippy::map_unwrap_or,
+ clippy::clippy::print_stdout,
+ clippy::unicode_not_nfc,
+ clippy::use_self
+ )
+)]
+#![no_std]
+
+#[cfg(not(feature = "std"))]
+#[macro_use]
+extern crate alloc as std;
+#[cfg(feature = "std")]
+#[macro_use]
+extern crate std;
+
+#[cfg(not(feature = "std"))]
+use hashbrown::{
+ hash_map,
+ hash_map::Entry::{Occupied, Vacant},
+ HashMap,
+};
+#[cfg(feature = "std")]
+use std::collections::{
+ hash_map,
+ hash_map::Entry::{Occupied, Vacant},
+ HashMap,
+};
+
+mod code_translator;
+mod environ;
+mod func_translator;
+mod module_translator;
+mod sections_translator;
+mod state;
+mod translation_utils;
+
+pub use crate::environ::{
+ DummyEnvironment, FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode,
+ TargetEnvironment, WasmError, WasmFuncType, WasmResult, WasmType,
+};
+pub use crate::func_translator::FuncTranslator;
+pub use crate::module_translator::translate_module;
+pub use crate::state::func_state::FuncTranslationState;
+pub use crate::state::module_state::ModuleTranslationState;
+pub use crate::translation_utils::*;
+pub use cranelift_frontend::FunctionBuilder;
+
+// Convenience reexport of the wasmparser crate that we're linking against,
+// since a number of types in `wasmparser` show up in the public API of
+// `cranelift-wasm`.
+pub use wasmparser;
+
+/// Version number of this crate.
+pub const VERSION: &str = env!("CARGO_PKG_VERSION");
diff --git a/third_party/rust/cranelift-wasm/src/module_translator.rs b/third_party/rust/cranelift-wasm/src/module_translator.rs
new file mode 100644
index 0000000000..0b1794158f
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/module_translator.rs
@@ -0,0 +1,159 @@
+//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
+//! to deal with each part of it.
+use crate::environ::{ModuleEnvironment, WasmResult};
+use crate::sections_translator::{
+ parse_data_section, parse_element_section, parse_export_section, parse_function_section,
+ parse_global_section, parse_import_section, parse_memory_section, parse_name_section,
+ parse_start_section, parse_table_section, parse_type_section,
+};
+use crate::state::ModuleTranslationState;
+use cranelift_codegen::timing;
+use std::prelude::v1::*;
+use wasmparser::{NameSectionReader, Parser, Payload, Validator};
+
+/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cranelift IR
+/// [`Function`](cranelift_codegen::ir::Function).
+pub fn translate_module<'data>(
+ data: &'data [u8],
+ environ: &mut dyn ModuleEnvironment<'data>,
+) -> WasmResult<ModuleTranslationState> {
+ let _tt = timing::wasm_translate_module();
+ let mut module_translation_state = ModuleTranslationState::new();
+ let mut validator = Validator::new();
+ validator.wasm_features(environ.wasm_features());
+ let mut stack = Vec::new();
+ let mut modules = 1;
+ let mut cur_module = 0;
+
+ for payload in Parser::new(0).parse_all(data) {
+ match payload? {
+ Payload::Version { num, range } => {
+ validator.version(num, &range)?;
+ environ.module_start(cur_module);
+ }
+ Payload::End => {
+ validator.end()?;
+ environ.module_end(cur_module);
+ if let Some((other, other_index)) = stack.pop() {
+ validator = other;
+ cur_module = other_index;
+ }
+ }
+
+ Payload::TypeSection(types) => {
+ validator.type_section(&types)?;
+ parse_type_section(types, &mut module_translation_state, environ)?;
+ }
+
+ Payload::ImportSection(imports) => {
+ validator.import_section(&imports)?;
+ parse_import_section(imports, environ)?;
+ }
+
+ Payload::FunctionSection(functions) => {
+ validator.function_section(&functions)?;
+ parse_function_section(functions, environ)?;
+ }
+
+ Payload::TableSection(tables) => {
+ validator.table_section(&tables)?;
+ parse_table_section(tables, environ)?;
+ }
+
+ Payload::MemorySection(memories) => {
+ validator.memory_section(&memories)?;
+ parse_memory_section(memories, environ)?;
+ }
+
+ Payload::GlobalSection(globals) => {
+ validator.global_section(&globals)?;
+ parse_global_section(globals, environ)?;
+ }
+
+ Payload::ExportSection(exports) => {
+ validator.export_section(&exports)?;
+ parse_export_section(exports, environ)?;
+ }
+
+ Payload::StartSection { func, range } => {
+ validator.start_section(func, &range)?;
+ parse_start_section(func, environ)?;
+ }
+
+ Payload::ElementSection(elements) => {
+ validator.element_section(&elements)?;
+ parse_element_section(elements, environ)?;
+ }
+
+ Payload::CodeSectionStart { count, range, .. } => {
+ validator.code_section_start(count, &range)?;
+ environ.reserve_function_bodies(count, range.start as u64);
+ }
+
+ Payload::CodeSectionEntry(body) => {
+ let func_validator = validator.code_section_entry()?;
+ environ.define_function_body(func_validator, body)?;
+ }
+
+ Payload::DataSection(data) => {
+ validator.data_section(&data)?;
+ parse_data_section(data, environ)?;
+ }
+
+ Payload::DataCountSection { count, range } => {
+ validator.data_count_section(count, &range)?;
+ environ.reserve_passive_data(count)?;
+ }
+
+ Payload::ModuleSection(s) => {
+ validator.module_section(&s)?;
+ environ.reserve_modules(s.get_count());
+ }
+ Payload::InstanceSection(s) => {
+ validator.instance_section(&s)?;
+ unimplemented!("module linking not implemented yet")
+ }
+ Payload::AliasSection(s) => {
+ validator.alias_section(&s)?;
+ unimplemented!("module linking not implemented yet")
+ }
+ Payload::ModuleCodeSectionStart {
+ count,
+ range,
+ size: _,
+ } => {
+ validator.module_code_section_start(count, &range)?;
+ }
+
+ Payload::ModuleCodeSectionEntry { .. } => {
+ let subvalidator = validator.module_code_section_entry();
+ stack.push((validator, cur_module));
+ validator = subvalidator;
+ cur_module = modules;
+ modules += 1;
+ }
+
+ Payload::CustomSection {
+ name: "name",
+ data,
+ data_offset,
+ } => {
+ let result = NameSectionReader::new(data, data_offset)
+ .map_err(|e| e.into())
+ .and_then(|s| parse_name_section(s, environ));
+ if let Err(e) = result {
+ log::warn!("failed to parse name section {:?}", e);
+ }
+ }
+
+ Payload::CustomSection { name, data, .. } => environ.custom_section(name, data)?,
+
+ Payload::UnknownSection { id, range, .. } => {
+ validator.unknown_section(id, &range)?;
+ unreachable!();
+ }
+ }
+ }
+
+ Ok(module_translation_state)
+}
diff --git a/third_party/rust/cranelift-wasm/src/sections_translator.rs b/third_party/rust/cranelift-wasm/src/sections_translator.rs
new file mode 100644
index 0000000000..842839979d
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/sections_translator.rs
@@ -0,0 +1,454 @@
+//! Helper functions to gather information for each of the non-function sections of a
+//! WebAssembly module.
+//!
+//! The code of these helper functions is straightforward since they only read metadata
+//! about linear memories, tables, globals, etc. and store them for later use.
+//!
+//! The special case of the initialize expressions for table elements offsets or global variables
+//! is handled, according to the semantics of WebAssembly, to only specific expressions that are
+//! interpreted on the fly.
+use crate::environ::{ModuleEnvironment, WasmError, WasmResult};
+use crate::state::ModuleTranslationState;
+use crate::translation_utils::{
+ tabletype_to_type, type_to_type, DataIndex, ElemIndex, EntityType, FuncIndex, Global,
+ GlobalIndex, GlobalInit, Memory, MemoryIndex, Table, TableElementType, TableIndex, TypeIndex,
+};
+use crate::wasm_unsupported;
+use core::convert::TryFrom;
+use core::convert::TryInto;
+use cranelift_codegen::ir::immediates::V128Imm;
+use cranelift_codegen::ir::{self, AbiParam, Signature};
+use cranelift_entity::packed_option::ReservedValue;
+use cranelift_entity::EntityRef;
+use std::boxed::Box;
+use std::vec::Vec;
+use wasmparser::{
+ self, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems, ElementKind,
+ ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader,
+ GlobalSectionReader, GlobalType, ImportSectionEntryType, ImportSectionReader,
+ MemorySectionReader, MemoryType, NameSectionReader, Naming, Operator, TableSectionReader,
+ TableType, TypeDef, TypeSectionReader,
+};
+
+fn entity_type(
+ ty: ImportSectionEntryType,
+ environ: &mut dyn ModuleEnvironment<'_>,
+) -> WasmResult<EntityType> {
+ Ok(match ty {
+ ImportSectionEntryType::Function(sig) => EntityType::Function(TypeIndex::from_u32(sig)),
+ ImportSectionEntryType::Module(sig) => EntityType::Module(TypeIndex::from_u32(sig)),
+ ImportSectionEntryType::Instance(sig) => EntityType::Instance(TypeIndex::from_u32(sig)),
+ ImportSectionEntryType::Memory(ty) => EntityType::Memory(memory(ty)),
+ ImportSectionEntryType::Global(ty) => {
+ EntityType::Global(global(ty, environ, GlobalInit::Import)?)
+ }
+ ImportSectionEntryType::Table(ty) => EntityType::Table(table(ty, environ)?),
+ })
+}
+
+fn memory(ty: MemoryType) -> Memory {
+ match ty {
+ MemoryType::M32 { limits, shared } => Memory {
+ minimum: limits.initial,
+ maximum: limits.maximum,
+ shared: shared,
+ },
+ // FIXME(#2361)
+ MemoryType::M64 { .. } => unimplemented!(),
+ }
+}
+
+fn table(ty: TableType, environ: &mut dyn ModuleEnvironment<'_>) -> WasmResult<Table> {
+ Ok(Table {
+ wasm_ty: ty.element_type.try_into()?,
+ ty: match tabletype_to_type(ty.element_type, environ)? {
+ Some(t) => TableElementType::Val(t),
+ None => TableElementType::Func,
+ },
+ minimum: ty.limits.initial,
+ maximum: ty.limits.maximum,
+ })
+}
+
+fn global(
+ ty: GlobalType,
+ environ: &mut dyn ModuleEnvironment<'_>,
+ initializer: GlobalInit,
+) -> WasmResult<Global> {
+ Ok(Global {
+ wasm_ty: ty.content_type.try_into()?,
+ ty: type_to_type(ty.content_type, environ).unwrap(),
+ mutability: ty.mutable,
+ initializer,
+ })
+}
+
+/// Parses the Type section of the wasm module.
+pub fn parse_type_section<'a>(
+ types: TypeSectionReader<'a>,
+ module_translation_state: &mut ModuleTranslationState,
+ environ: &mut dyn ModuleEnvironment<'a>,
+) -> WasmResult<()> {
+ let count = types.get_count();
+ module_translation_state.wasm_types.reserve(count as usize);
+ environ.reserve_types(count)?;
+
+ for entry in types {
+ match entry? {
+ TypeDef::Func(wasm_func_ty) => {
+ let mut sig =
+ Signature::new(ModuleEnvironment::target_config(environ).default_call_conv);
+ sig.params.extend(wasm_func_ty.params.iter().map(|ty| {
+ let cret_arg: ir::Type = type_to_type(*ty, environ)
+ .expect("only numeric types are supported in function signatures");
+ AbiParam::new(cret_arg)
+ }));
+ sig.returns.extend(wasm_func_ty.returns.iter().map(|ty| {
+ let cret_arg: ir::Type = type_to_type(*ty, environ)
+ .expect("only numeric types are supported in function signatures");
+ AbiParam::new(cret_arg)
+ }));
+ environ.declare_type_func(wasm_func_ty.clone().try_into()?, sig)?;
+ module_translation_state
+ .wasm_types
+ .push((wasm_func_ty.params, wasm_func_ty.returns));
+ }
+ TypeDef::Module(t) => {
+ let imports = t
+ .imports
+ .iter()
+ .map(|i| Ok((i.module, i.field, entity_type(i.ty, environ)?)))
+ .collect::<WasmResult<Vec<_>>>()?;
+ let exports = t
+ .exports
+ .iter()
+ .map(|e| Ok((e.name, entity_type(e.ty, environ)?)))
+ .collect::<WasmResult<Vec<_>>>()?;
+ environ.declare_type_module(&imports, &exports)?;
+ }
+ TypeDef::Instance(t) => {
+ let exports = t
+ .exports
+ .iter()
+ .map(|e| Ok((e.name, entity_type(e.ty, environ)?)))
+ .collect::<WasmResult<Vec<_>>>()?;
+ environ.declare_type_instance(&exports)?;
+ }
+ }
+ }
+ Ok(())
+}
+
+/// Parses the Import section of the wasm module.
+pub fn parse_import_section<'data>(
+ imports: ImportSectionReader<'data>,
+ environ: &mut dyn ModuleEnvironment<'data>,
+) -> WasmResult<()> {
+ environ.reserve_imports(imports.get_count())?;
+
+ for entry in imports {
+ let import = entry?;
+ let module_name = import.module;
+ let field_name = import.field.unwrap(); // TODO Handle error when module linking is implemented.
+ match entity_type(import.ty, environ)? {
+ EntityType::Function(idx) => {
+ environ.declare_func_import(idx, module_name, field_name)?;
+ }
+ EntityType::Module(idx) => {
+ environ.declare_module_import(idx, module_name, field_name)?;
+ }
+ EntityType::Instance(idx) => {
+ environ.declare_instance_import(idx, module_name, field_name)?;
+ }
+ EntityType::Memory(ty) => {
+ environ.declare_memory_import(ty, module_name, field_name)?;
+ }
+ EntityType::Global(ty) => {
+ environ.declare_global_import(ty, module_name, field_name)?;
+ }
+ EntityType::Table(ty) => {
+ environ.declare_table_import(ty, module_name, field_name)?;
+ }
+ }
+ }
+
+ environ.finish_imports()?;
+ Ok(())
+}
+
+/// Parses the Function section of the wasm module.
+pub fn parse_function_section(
+ functions: FunctionSectionReader,
+ environ: &mut dyn ModuleEnvironment,
+) -> WasmResult<()> {
+ let num_functions = functions.get_count();
+ if num_functions == std::u32::MAX {
+ // We reserve `u32::MAX` for our own use in cranelift-entity.
+ return Err(WasmError::ImplLimitExceeded);
+ }
+
+ environ.reserve_func_types(num_functions)?;
+
+ for entry in functions {
+ let sigindex = entry?;
+ environ.declare_func_type(TypeIndex::from_u32(sigindex))?;
+ }
+
+ Ok(())
+}
+
+/// Parses the Table section of the wasm module.
+pub fn parse_table_section(
+ tables: TableSectionReader,
+ environ: &mut dyn ModuleEnvironment,
+) -> WasmResult<()> {
+ environ.reserve_tables(tables.get_count())?;
+
+ for entry in tables {
+ let ty = table(entry?, environ)?;
+ environ.declare_table(ty)?;
+ }
+
+ Ok(())
+}
+
+/// Parses the Memory section of the wasm module.
+pub fn parse_memory_section(
+ memories: MemorySectionReader,
+ environ: &mut dyn ModuleEnvironment,
+) -> WasmResult<()> {
+ environ.reserve_memories(memories.get_count())?;
+
+ for entry in memories {
+ let memory = memory(entry?);
+ environ.declare_memory(memory)?;
+ }
+
+ Ok(())
+}
+
+/// Parses the Global section of the wasm module.
+pub fn parse_global_section(
+ globals: GlobalSectionReader,
+ environ: &mut dyn ModuleEnvironment,
+) -> WasmResult<()> {
+ environ.reserve_globals(globals.get_count())?;
+
+ for entry in globals {
+ let wasmparser::Global { ty, init_expr } = entry?;
+ let mut init_expr_reader = init_expr.get_binary_reader();
+ let initializer = match init_expr_reader.read_operator()? {
+ Operator::I32Const { value } => GlobalInit::I32Const(value),
+ Operator::I64Const { value } => GlobalInit::I64Const(value),
+ Operator::F32Const { value } => GlobalInit::F32Const(value.bits()),
+ Operator::F64Const { value } => GlobalInit::F64Const(value.bits()),
+ Operator::V128Const { value } => {
+ GlobalInit::V128Const(V128Imm::from(value.bytes().to_vec().as_slice()))
+ }
+ Operator::RefNull { ty: _ } => GlobalInit::RefNullConst,
+ Operator::RefFunc { function_index } => {
+ GlobalInit::RefFunc(FuncIndex::from_u32(function_index))
+ }
+ Operator::GlobalGet { global_index } => {
+ GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index))
+ }
+ ref s => {
+ return Err(wasm_unsupported!(
+ "unsupported init expr in global section: {:?}",
+ s
+ ));
+ }
+ };
+ let ty = global(ty, environ, initializer)?;
+ environ.declare_global(ty)?;
+ }
+
+ Ok(())
+}
+
+/// Parses the Export section of the wasm module.
+pub fn parse_export_section<'data>(
+ exports: ExportSectionReader<'data>,
+ environ: &mut dyn ModuleEnvironment<'data>,
+) -> WasmResult<()> {
+ environ.reserve_exports(exports.get_count())?;
+
+ for entry in exports {
+ let Export {
+ field,
+ ref kind,
+ index,
+ } = entry?;
+
+ // The input has already been validated, so we should be able to
+ // assume valid UTF-8 and use `from_utf8_unchecked` if performance
+ // becomes a concern here.
+ let index = index as usize;
+ match *kind {
+ ExternalKind::Function => environ.declare_func_export(FuncIndex::new(index), field)?,
+ ExternalKind::Table => environ.declare_table_export(TableIndex::new(index), field)?,
+ ExternalKind::Memory => {
+ environ.declare_memory_export(MemoryIndex::new(index), field)?
+ }
+ ExternalKind::Global => {
+ environ.declare_global_export(GlobalIndex::new(index), field)?
+ }
+ ExternalKind::Type | ExternalKind::Module | ExternalKind::Instance => {
+ unimplemented!("module linking not implemented yet")
+ }
+ }
+ }
+
+ environ.finish_exports()?;
+ Ok(())
+}
+
+/// Parses the Start section of the wasm module.
+pub fn parse_start_section(index: u32, environ: &mut dyn ModuleEnvironment) -> WasmResult<()> {
+ environ.declare_start_func(FuncIndex::from_u32(index))?;
+ Ok(())
+}
+
+fn read_elems(items: &ElementItems) -> WasmResult<Box<[FuncIndex]>> {
+ let items_reader = items.get_items_reader()?;
+ let mut elems = Vec::with_capacity(usize::try_from(items_reader.get_count()).unwrap());
+ for item in items_reader {
+ let elem = match item? {
+ ElementItem::Null(_ty) => FuncIndex::reserved_value(),
+ ElementItem::Func(index) => FuncIndex::from_u32(index),
+ };
+ elems.push(elem);
+ }
+ Ok(elems.into_boxed_slice())
+}
+
+/// Parses the Element section of the wasm module.
+pub fn parse_element_section<'data>(
+ elements: ElementSectionReader<'data>,
+ environ: &mut dyn ModuleEnvironment,
+) -> WasmResult<()> {
+ environ.reserve_table_elements(elements.get_count())?;
+
+ for (index, entry) in elements.into_iter().enumerate() {
+ let Element { kind, items, ty: _ } = entry?;
+ let segments = read_elems(&items)?;
+ match kind {
+ ElementKind::Active {
+ table_index,
+ init_expr,
+ } => {
+ let mut init_expr_reader = init_expr.get_binary_reader();
+ let (base, offset) = match init_expr_reader.read_operator()? {
+ Operator::I32Const { value } => (None, value as u32 as usize),
+ Operator::GlobalGet { global_index } => {
+ (Some(GlobalIndex::from_u32(global_index)), 0)
+ }
+ ref s => {
+ return Err(wasm_unsupported!(
+ "unsupported init expr in element section: {:?}",
+ s
+ ));
+ }
+ };
+ environ.declare_table_elements(
+ TableIndex::from_u32(table_index),
+ base,
+ offset,
+ segments,
+ )?
+ }
+ ElementKind::Passive => {
+ let index = ElemIndex::from_u32(index as u32);
+ environ.declare_passive_element(index, segments)?;
+ }
+ ElementKind::Declared => {
+ // Nothing to do here.
+ }
+ }
+ }
+ Ok(())
+}
+
+/// Parses the Data section of the wasm module.
+pub fn parse_data_section<'data>(
+ data: DataSectionReader<'data>,
+ environ: &mut dyn ModuleEnvironment<'data>,
+) -> WasmResult<()> {
+ environ.reserve_data_initializers(data.get_count())?;
+
+ for (index, entry) in data.into_iter().enumerate() {
+ let Data { kind, data } = entry?;
+ match kind {
+ DataKind::Active {
+ memory_index,
+ init_expr,
+ } => {
+ let mut init_expr_reader = init_expr.get_binary_reader();
+ let (base, offset) = match init_expr_reader.read_operator()? {
+ Operator::I32Const { value } => (None, value as u32 as usize),
+ Operator::GlobalGet { global_index } => {
+ (Some(GlobalIndex::from_u32(global_index)), 0)
+ }
+ ref s => {
+ return Err(wasm_unsupported!(
+ "unsupported init expr in data section: {:?}",
+ s
+ ))
+ }
+ };
+ environ.declare_data_initialization(
+ MemoryIndex::from_u32(memory_index),
+ base,
+ offset,
+ data,
+ )?;
+ }
+ DataKind::Passive => {
+ let index = DataIndex::from_u32(index as u32);
+ environ.declare_passive_data(index, data)?;
+ }
+ }
+ }
+
+ Ok(())
+}
+
+/// Parses the Name section of the wasm module.
+pub fn parse_name_section<'data>(
+ names: NameSectionReader<'data>,
+ environ: &mut dyn ModuleEnvironment<'data>,
+) -> WasmResult<()> {
+ for subsection in names {
+ match subsection? {
+ wasmparser::Name::Function(f) => {
+ let mut names = f.get_map()?;
+ for _ in 0..names.get_count() {
+ let Naming { index, name } = names.read()?;
+ // We reserve `u32::MAX` for our own use in cranelift-entity.
+ if index != u32::max_value() {
+ environ.declare_func_name(FuncIndex::from_u32(index), name);
+ }
+ }
+ }
+ wasmparser::Name::Module(module) => {
+ let name = module.get_name()?;
+ environ.declare_module_name(name);
+ }
+ wasmparser::Name::Local(l) => {
+ let mut reader = l.get_function_local_reader()?;
+ for _ in 0..reader.get_count() {
+ let f = reader.read()?;
+ if f.func_index == u32::max_value() {
+ continue;
+ }
+ let mut map = f.get_map()?;
+ for _ in 0..map.get_count() {
+ let Naming { index, name } = map.read()?;
+ environ.declare_local_name(FuncIndex::from_u32(f.func_index), index, name)
+ }
+ }
+ }
+ }
+ }
+ Ok(())
+}
diff --git a/third_party/rust/cranelift-wasm/src/state/func_state.rs b/third_party/rust/cranelift-wasm/src/state/func_state.rs
new file mode 100644
index 0000000000..70d62c7240
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/state/func_state.rs
@@ -0,0 +1,543 @@
+//! WebAssembly module and function translation state.
+//!
+//! The `ModuleTranslationState` struct defined in this module is used to keep track of data about
+//! the whole WebAssembly module, such as the decoded type signatures.
+//!
+//! The `FuncTranslationState` struct defined in this module is used to keep track of the WebAssembly
+//! value and control stacks during the translation of a single function.
+
+use crate::environ::{FuncEnvironment, GlobalVariable, WasmResult};
+use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TypeIndex};
+use crate::{HashMap, Occupied, Vacant};
+use cranelift_codegen::ir::{self, Block, Inst, Value};
+use std::vec::Vec;
+
+/// Information about the presence of an associated `else` for an `if`, or the
+/// lack thereof.
+#[derive(Debug)]
+pub enum ElseData {
+ /// The `if` does not already have an `else` block.
+ ///
+ /// This doesn't mean that it will never have an `else`, just that we
+ /// haven't seen it yet.
+ NoElse {
+ /// If we discover that we need an `else` block, this is the jump
+ /// instruction that needs to be fixed up to point to the new `else`
+ /// block rather than the destination block after the `if...end`.
+ branch_inst: Inst,
+ },
+
+ /// We have already allocated an `else` block.
+ ///
+ /// Usually we don't know whether we will hit an `if .. end` or an `if
+ /// .. else .. end`, but sometimes we can tell based on the block's type
+ /// signature that the signature is not valid if there isn't an `else`. In
+ /// these cases, we pre-allocate the `else` block.
+ WithElse {
+ /// This is the `else` block.
+ else_block: Block,
+ },
+}
+
+/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
+/// fields:
+///
+/// - `destination`: reference to the `Block` that will hold the code after the control block;
+/// - `num_return_values`: number of values returned by the control block;
+/// - `original_stack_size`: size of the value stack at the beginning of the control block.
+///
+/// Moreover, the `if` frame has the `branch_inst` field that points to the `brz` instruction
+/// separating the `true` and `false` branch. The `loop` frame has a `header` field that references
+/// the `Block` that contains the beginning of the body of the loop.
+#[derive(Debug)]
+pub enum ControlStackFrame {
+ If {
+ destination: Block,
+ else_data: ElseData,
+ num_param_values: usize,
+ num_return_values: usize,
+ original_stack_size: usize,
+ exit_is_branched_to: bool,
+ blocktype: wasmparser::TypeOrFuncType,
+ /// Was the head of the `if` reachable?
+ head_is_reachable: bool,
+ /// What was the reachability at the end of the consequent?
+ ///
+ /// This is `None` until we're finished translating the consequent, and
+ /// is set to `Some` either by hitting an `else` when we will begin
+ /// translating the alternative, or by hitting an `end` in which case
+ /// there is no alternative.
+ consequent_ends_reachable: Option<bool>,
+ // Note: no need for `alternative_ends_reachable` because that is just
+ // `state.reachable` when we hit the `end` in the `if .. else .. end`.
+ },
+ Block {
+ destination: Block,
+ num_param_values: usize,
+ num_return_values: usize,
+ original_stack_size: usize,
+ exit_is_branched_to: bool,
+ },
+ Loop {
+ destination: Block,
+ header: Block,
+ num_param_values: usize,
+ num_return_values: usize,
+ original_stack_size: usize,
+ },
+}
+
+/// Helper methods for the control stack objects.
+impl ControlStackFrame {
+ pub fn num_return_values(&self) -> usize {
+ match *self {
+ Self::If {
+ num_return_values, ..
+ }
+ | Self::Block {
+ num_return_values, ..
+ }
+ | Self::Loop {
+ num_return_values, ..
+ } => num_return_values,
+ }
+ }
+ pub fn num_param_values(&self) -> usize {
+ match *self {
+ Self::If {
+ num_param_values, ..
+ }
+ | Self::Block {
+ num_param_values, ..
+ }
+ | Self::Loop {
+ num_param_values, ..
+ } => num_param_values,
+ }
+ }
+ pub fn following_code(&self) -> Block {
+ match *self {
+ Self::If { destination, .. }
+ | Self::Block { destination, .. }
+ | Self::Loop { destination, .. } => destination,
+ }
+ }
+ pub fn br_destination(&self) -> Block {
+ match *self {
+ Self::If { destination, .. } | Self::Block { destination, .. } => destination,
+ Self::Loop { header, .. } => header,
+ }
+ }
+ /// Private helper. Use `truncate_value_stack_to_else_params()` or
+ /// `truncate_value_stack_to_original_size()` to restore value-stack state.
+ fn original_stack_size(&self) -> usize {
+ match *self {
+ Self::If {
+ original_stack_size,
+ ..
+ }
+ | Self::Block {
+ original_stack_size,
+ ..
+ }
+ | Self::Loop {
+ original_stack_size,
+ ..
+ } => original_stack_size,
+ }
+ }
+ pub fn is_loop(&self) -> bool {
+ match *self {
+ Self::If { .. } | Self::Block { .. } => false,
+ Self::Loop { .. } => true,
+ }
+ }
+
+ pub fn exit_is_branched_to(&self) -> bool {
+ match *self {
+ Self::If {
+ exit_is_branched_to,
+ ..
+ }
+ | Self::Block {
+ exit_is_branched_to,
+ ..
+ } => exit_is_branched_to,
+ Self::Loop { .. } => false,
+ }
+ }
+
+ pub fn set_branched_to_exit(&mut self) {
+ match *self {
+ Self::If {
+ ref mut exit_is_branched_to,
+ ..
+ }
+ | Self::Block {
+ ref mut exit_is_branched_to,
+ ..
+ } => *exit_is_branched_to = true,
+ Self::Loop { .. } => {}
+ }
+ }
+
+ /// Pop values from the value stack so that it is left at the
+ /// input-parameters to an else-block.
+ pub fn truncate_value_stack_to_else_params(&self, stack: &mut Vec<Value>) {
+ debug_assert!(matches!(self, &ControlStackFrame::If { .. }));
+ stack.truncate(self.original_stack_size());
+ }
+
+ /// Pop values from the value stack so that it is left at the state it was
+ /// before this control-flow frame.
+ pub fn truncate_value_stack_to_original_size(&self, stack: &mut Vec<Value>) {
+ // The "If" frame pushes its parameters twice, so they're available to the else block
+ // (see also `FuncTranslationState::push_if`).
+ // Yet, the original_stack_size member accounts for them only once, so that the else
+ // block can see the same number of parameters as the consequent block. As a matter of
+ // fact, we need to substract an extra number of parameter values for if blocks.
+ let num_duplicated_params = match self {
+ &ControlStackFrame::If {
+ num_param_values, ..
+ } => {
+ debug_assert!(num_param_values <= self.original_stack_size());
+ num_param_values
+ }
+ _ => 0,
+ };
+ stack.truncate(self.original_stack_size() - num_duplicated_params);
+ }
+}
+
+/// Contains information passed along during a function's translation and that records:
+///
+/// - The current value and control stacks.
+/// - The depth of the two unreachable control blocks stacks, that are manipulated when translating
+/// unreachable code;
+pub struct FuncTranslationState {
+ /// A stack of values corresponding to the active values in the input wasm function at this
+ /// point.
+ pub(crate) stack: Vec<Value>,
+ /// A stack of active control flow operations at this point in the input wasm function.
+ pub(crate) control_stack: Vec<ControlStackFrame>,
+ /// Is the current translation state still reachable? This is false when translating operators
+ /// like End, Return, or Unreachable.
+ pub(crate) reachable: bool,
+
+ // Map of global variables that have already been created by `FuncEnvironment::make_global`.
+ globals: HashMap<GlobalIndex, GlobalVariable>,
+
+ // Map of heaps that have been created by `FuncEnvironment::make_heap`.
+ heaps: HashMap<MemoryIndex, ir::Heap>,
+
+ // Map of tables that have been created by `FuncEnvironment::make_table`.
+ pub(crate) tables: HashMap<TableIndex, ir::Table>,
+
+ // Map of indirect call signatures that have been created by
+ // `FuncEnvironment::make_indirect_sig()`.
+ // Stores both the signature reference and the number of WebAssembly arguments
+ signatures: HashMap<TypeIndex, (ir::SigRef, usize)>,
+
+ // Imported and local functions that have been created by
+ // `FuncEnvironment::make_direct_func()`.
+ // Stores both the function reference and the number of WebAssembly arguments
+ functions: HashMap<FuncIndex, (ir::FuncRef, usize)>,
+}
+
+// Public methods that are exposed to non-`cranelift_wasm` API consumers.
+impl FuncTranslationState {
+ /// True if the current translation state expresses reachable code, false if it is unreachable.
+ #[inline]
+ pub fn reachable(&self) -> bool {
+ self.reachable
+ }
+}
+
+impl FuncTranslationState {
+ /// Construct a new, empty, `FuncTranslationState`
+ pub(crate) fn new() -> Self {
+ Self {
+ stack: Vec::new(),
+ control_stack: Vec::new(),
+ reachable: true,
+ globals: HashMap::new(),
+ heaps: HashMap::new(),
+ tables: HashMap::new(),
+ signatures: HashMap::new(),
+ functions: HashMap::new(),
+ }
+ }
+
+ fn clear(&mut self) {
+ debug_assert!(self.stack.is_empty());
+ debug_assert!(self.control_stack.is_empty());
+ self.reachable = true;
+ self.globals.clear();
+ self.heaps.clear();
+ self.tables.clear();
+ self.signatures.clear();
+ self.functions.clear();
+ }
+
+ /// Initialize the state for compiling a function with the given signature.
+ ///
+ /// This resets the state to containing only a single block representing the whole function.
+ /// The exit block is the last block in the function which will contain the return instruction.
+ pub(crate) fn initialize(&mut self, sig: &ir::Signature, exit_block: Block) {
+ self.clear();
+ self.push_block(
+ exit_block,
+ 0,
+ sig.returns
+ .iter()
+ .filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
+ .count(),
+ );
+ }
+
+ /// Push a value.
+ pub(crate) fn push1(&mut self, val: Value) {
+ self.stack.push(val);
+ }
+
+ /// Push multiple values.
+ pub(crate) fn pushn(&mut self, vals: &[Value]) {
+ self.stack.extend_from_slice(vals);
+ }
+
+ /// Pop one value.
+ pub(crate) fn pop1(&mut self) -> Value {
+ self.stack
+ .pop()
+ .expect("attempted to pop a value from an empty stack")
+ }
+
+ /// Peek at the top of the stack without popping it.
+ pub(crate) fn peek1(&self) -> Value {
+ *self
+ .stack
+ .last()
+ .expect("attempted to peek at a value on an empty stack")
+ }
+
+ /// Pop two values. Return them in the order they were pushed.
+ pub(crate) fn pop2(&mut self) -> (Value, Value) {
+ let v2 = self.stack.pop().unwrap();
+ let v1 = self.stack.pop().unwrap();
+ (v1, v2)
+ }
+
+ /// Pop three values. Return them in the order they were pushed.
+ pub(crate) fn pop3(&mut self) -> (Value, Value, Value) {
+ let v3 = self.stack.pop().unwrap();
+ let v2 = self.stack.pop().unwrap();
+ let v1 = self.stack.pop().unwrap();
+ (v1, v2, v3)
+ }
+
+ /// Helper to ensure the the stack size is at least as big as `n`; note that due to
+ /// `debug_assert` this will not execute in non-optimized builds.
+ #[inline]
+ fn ensure_length_is_at_least(&self, n: usize) {
+ debug_assert!(
+ n <= self.stack.len(),
+ "attempted to access {} values but stack only has {} values",
+ n,
+ self.stack.len()
+ )
+ }
+
+ /// Pop the top `n` values on the stack.
+ ///
+ /// The popped values are not returned. Use `peekn` to look at them before popping.
+ pub(crate) fn popn(&mut self, n: usize) {
+ self.ensure_length_is_at_least(n);
+ let new_len = self.stack.len() - n;
+ self.stack.truncate(new_len);
+ }
+
+ /// Peek at the top `n` values on the stack in the order they were pushed.
+ pub(crate) fn peekn(&self, n: usize) -> &[Value] {
+ self.ensure_length_is_at_least(n);
+ &self.stack[self.stack.len() - n..]
+ }
+
+ /// Peek at the top `n` values on the stack in the order they were pushed.
+ pub(crate) fn peekn_mut(&mut self, n: usize) -> &mut [Value] {
+ self.ensure_length_is_at_least(n);
+ let len = self.stack.len();
+ &mut self.stack[len - n..]
+ }
+
+ /// Push a block on the control stack.
+ pub(crate) fn push_block(
+ &mut self,
+ following_code: Block,
+ num_param_types: usize,
+ num_result_types: usize,
+ ) {
+ debug_assert!(num_param_types <= self.stack.len());
+ self.control_stack.push(ControlStackFrame::Block {
+ destination: following_code,
+ original_stack_size: self.stack.len() - num_param_types,
+ num_param_values: num_param_types,
+ num_return_values: num_result_types,
+ exit_is_branched_to: false,
+ });
+ }
+
+ /// Push a loop on the control stack.
+ pub(crate) fn push_loop(
+ &mut self,
+ header: Block,
+ following_code: Block,
+ num_param_types: usize,
+ num_result_types: usize,
+ ) {
+ debug_assert!(num_param_types <= self.stack.len());
+ self.control_stack.push(ControlStackFrame::Loop {
+ header,
+ destination: following_code,
+ original_stack_size: self.stack.len() - num_param_types,
+ num_param_values: num_param_types,
+ num_return_values: num_result_types,
+ });
+ }
+
+ /// Push an if on the control stack.
+ pub(crate) fn push_if(
+ &mut self,
+ destination: Block,
+ else_data: ElseData,
+ num_param_types: usize,
+ num_result_types: usize,
+ blocktype: wasmparser::TypeOrFuncType,
+ ) {
+ debug_assert!(num_param_types <= self.stack.len());
+
+ // Push a second copy of our `if`'s parameters on the stack. This lets
+ // us avoid saving them on the side in the `ControlStackFrame` for our
+ // `else` block (if it exists), which would require a second heap
+ // allocation. See also the comment in `translate_operator` for
+ // `Operator::Else`.
+ self.stack.reserve(num_param_types);
+ for i in (self.stack.len() - num_param_types)..self.stack.len() {
+ let val = self.stack[i];
+ self.stack.push(val);
+ }
+
+ self.control_stack.push(ControlStackFrame::If {
+ destination,
+ else_data,
+ original_stack_size: self.stack.len() - num_param_types,
+ num_param_values: num_param_types,
+ num_return_values: num_result_types,
+ exit_is_branched_to: false,
+ head_is_reachable: self.reachable,
+ consequent_ends_reachable: None,
+ blocktype,
+ });
+ }
+}
+
+/// Methods for handling entity references.
+impl FuncTranslationState {
+ /// Get the `GlobalVariable` reference that should be used to access the global variable
+ /// `index`. Create the reference if necessary.
+ /// Also return the WebAssembly type of the global.
+ pub(crate) fn get_global<FE: FuncEnvironment + ?Sized>(
+ &mut self,
+ func: &mut ir::Function,
+ index: u32,
+ environ: &mut FE,
+ ) -> WasmResult<GlobalVariable> {
+ let index = GlobalIndex::from_u32(index);
+ match self.globals.entry(index) {
+ Occupied(entry) => Ok(*entry.get()),
+ Vacant(entry) => Ok(*entry.insert(environ.make_global(func, index)?)),
+ }
+ }
+
+ /// Get the `Heap` reference that should be used to access linear memory `index`.
+ /// Create the reference if necessary.
+ pub(crate) fn get_heap<FE: FuncEnvironment + ?Sized>(
+ &mut self,
+ func: &mut ir::Function,
+ index: u32,
+ environ: &mut FE,
+ ) -> WasmResult<ir::Heap> {
+ let index = MemoryIndex::from_u32(index);
+ match self.heaps.entry(index) {
+ Occupied(entry) => Ok(*entry.get()),
+ Vacant(entry) => Ok(*entry.insert(environ.make_heap(func, index)?)),
+ }
+ }
+
+ /// Get the `Table` reference that should be used to access table `index`.
+ /// Create the reference if necessary.
+ pub(crate) fn get_or_create_table<FE: FuncEnvironment + ?Sized>(
+ &mut self,
+ func: &mut ir::Function,
+ index: u32,
+ environ: &mut FE,
+ ) -> WasmResult<ir::Table> {
+ let index = TableIndex::from_u32(index);
+ match self.tables.entry(index) {
+ Occupied(entry) => Ok(*entry.get()),
+ Vacant(entry) => Ok(*entry.insert(environ.make_table(func, index)?)),
+ }
+ }
+
+ /// Get the `SigRef` reference that should be used to make an indirect call with signature
+ /// `index`. Also return the number of WebAssembly arguments in the signature.
+ ///
+ /// Create the signature if necessary.
+ pub(crate) fn get_indirect_sig<FE: FuncEnvironment + ?Sized>(
+ &mut self,
+ func: &mut ir::Function,
+ index: u32,
+ environ: &mut FE,
+ ) -> WasmResult<(ir::SigRef, usize)> {
+ let index = TypeIndex::from_u32(index);
+ match self.signatures.entry(index) {
+ Occupied(entry) => Ok(*entry.get()),
+ Vacant(entry) => {
+ let sig = environ.make_indirect_sig(func, index)?;
+ Ok(*entry.insert((sig, num_wasm_parameters(environ, &func.dfg.signatures[sig]))))
+ }
+ }
+ }
+
+ /// Get the `FuncRef` reference that should be used to make a direct call to function
+ /// `index`. Also return the number of WebAssembly arguments in the signature.
+ ///
+ /// Create the function reference if necessary.
+ pub(crate) fn get_direct_func<FE: FuncEnvironment + ?Sized>(
+ &mut self,
+ func: &mut ir::Function,
+ index: u32,
+ environ: &mut FE,
+ ) -> WasmResult<(ir::FuncRef, usize)> {
+ let index = FuncIndex::from_u32(index);
+ match self.functions.entry(index) {
+ Occupied(entry) => Ok(*entry.get()),
+ Vacant(entry) => {
+ let fref = environ.make_direct_func(func, index)?;
+ let sig = func.dfg.ext_funcs[fref].signature;
+ Ok(*entry.insert((
+ fref,
+ num_wasm_parameters(environ, &func.dfg.signatures[sig]),
+ )))
+ }
+ }
+ }
+}
+
+fn num_wasm_parameters<FE: FuncEnvironment + ?Sized>(
+ environ: &FE,
+ signature: &ir::Signature,
+) -> usize {
+ (0..signature.params.len())
+ .filter(|index| environ.is_wasm_parameter(signature, *index))
+ .count()
+}
diff --git a/third_party/rust/cranelift-wasm/src/state/mod.rs b/third_party/rust/cranelift-wasm/src/state/mod.rs
new file mode 100644
index 0000000000..730dc8beb5
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/state/mod.rs
@@ -0,0 +1,14 @@
+//! WebAssembly module and function translation state.
+//!
+//! The `ModuleTranslationState` struct defined in this module is used to keep track of data about
+//! the whole WebAssembly module, such as the decoded type signatures.
+//!
+//! The `FuncTranslationState` struct defined in this module is used to keep track of the WebAssembly
+//! value and control stacks during the translation of a single function.
+
+pub(crate) mod func_state;
+pub(crate) mod module_state;
+
+// Re-export for convenience.
+pub(crate) use func_state::*;
+pub(crate) use module_state::*;
diff --git a/third_party/rust/cranelift-wasm/src/state/module_state.rs b/third_party/rust/cranelift-wasm/src/state/module_state.rs
new file mode 100644
index 0000000000..e1b96b8c82
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/state/module_state.rs
@@ -0,0 +1,70 @@
+use crate::environ::{WasmError, WasmResult};
+use crate::translation_utils::SignatureIndex;
+use cranelift_codegen::ir::{types, Type};
+use cranelift_entity::PrimaryMap;
+use std::boxed::Box;
+use std::vec::Vec;
+
+/// Map of signatures to a function's parameter and return types.
+pub(crate) type WasmTypes =
+ PrimaryMap<SignatureIndex, (Box<[wasmparser::Type]>, Box<[wasmparser::Type]>)>;
+
+/// Contains information decoded from the Wasm module that must be referenced
+/// during each Wasm function's translation.
+///
+/// This is only for data that is maintained by `cranelift-wasm` itself, as
+/// opposed to being maintained by the embedder. Data that is maintained by the
+/// embedder is represented with `ModuleEnvironment`.
+#[derive(Debug)]
+pub struct ModuleTranslationState {
+ /// A map containing a Wasm module's original, raw signatures.
+ ///
+ /// This is used for translating multi-value Wasm blocks inside functions,
+ /// which are encoded to refer to their type signature via index.
+ pub(crate) wasm_types: WasmTypes,
+}
+
+fn cranelift_to_wasmparser_type(ty: Type) -> WasmResult<wasmparser::Type> {
+ Ok(match ty {
+ types::I32 => wasmparser::Type::I32,
+ types::I64 => wasmparser::Type::I64,
+ types::F32 => wasmparser::Type::F32,
+ types::F64 => wasmparser::Type::F64,
+ types::R32 | types::R64 => wasmparser::Type::ExternRef,
+ _ => {
+ return Err(WasmError::Unsupported(format!(
+ "Cannot convert Cranelift type to Wasm signature: {:?}",
+ ty
+ )));
+ }
+ })
+}
+
+impl ModuleTranslationState {
+ /// Creates a new empty ModuleTranslationState.
+ pub fn new() -> Self {
+ Self {
+ wasm_types: PrimaryMap::new(),
+ }
+ }
+
+ /// Create a new ModuleTranslationState with the given function signatures,
+ /// provided in terms of Cranelift types. The provided slice of signatures
+ /// is indexed by signature number, and contains pairs of (args, results)
+ /// slices.
+ pub fn from_func_sigs(sigs: &[(&[Type], &[Type])]) -> WasmResult<Self> {
+ let mut wasm_types = PrimaryMap::with_capacity(sigs.len());
+ for &(ref args, ref results) in sigs {
+ let args: Vec<wasmparser::Type> = args
+ .iter()
+ .map(|&ty| cranelift_to_wasmparser_type(ty))
+ .collect::<Result<_, _>>()?;
+ let results: Vec<wasmparser::Type> = results
+ .iter()
+ .map(|&ty| cranelift_to_wasmparser_type(ty))
+ .collect::<Result<_, _>>()?;
+ wasm_types.push((args.into_boxed_slice(), results.into_boxed_slice()));
+ }
+ Ok(Self { wasm_types })
+ }
+}
diff --git a/third_party/rust/cranelift-wasm/src/translation_utils.rs b/third_party/rust/cranelift-wasm/src/translation_utils.rs
new file mode 100644
index 0000000000..15ad15756e
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/src/translation_utils.rs
@@ -0,0 +1,349 @@
+//! Helper functions and structures for the translation.
+use crate::environ::{TargetEnvironment, WasmResult, WasmType};
+use crate::wasm_unsupported;
+use core::convert::TryInto;
+use core::u32;
+use cranelift_codegen::entity::entity_impl;
+use cranelift_codegen::ir;
+use cranelift_codegen::ir::immediates::V128Imm;
+use cranelift_frontend::FunctionBuilder;
+#[cfg(feature = "enable-serde")]
+use serde::{Deserialize, Serialize};
+use wasmparser::{FuncValidator, WasmFuncType, WasmModuleResources};
+
+/// Index type of a function (imported or defined) inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct FuncIndex(u32);
+entity_impl!(FuncIndex);
+
+/// Index type of a defined function inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+pub struct DefinedFuncIndex(u32);
+entity_impl!(DefinedFuncIndex);
+
+/// Index type of a defined table inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+pub struct DefinedTableIndex(u32);
+entity_impl!(DefinedTableIndex);
+
+/// Index type of a defined memory inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+pub struct DefinedMemoryIndex(u32);
+entity_impl!(DefinedMemoryIndex);
+
+/// Index type of a defined global inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+pub struct DefinedGlobalIndex(u32);
+entity_impl!(DefinedGlobalIndex);
+
+/// Index type of a table (imported or defined) inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct TableIndex(u32);
+entity_impl!(TableIndex);
+
+/// Index type of a global variable (imported or defined) inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct GlobalIndex(u32);
+entity_impl!(GlobalIndex);
+
+/// Index type of a linear memory (imported or defined) inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct MemoryIndex(u32);
+entity_impl!(MemoryIndex);
+
+/// Index type of a signature (imported or defined) inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct SignatureIndex(u32);
+entity_impl!(SignatureIndex);
+
+/// Index type of a passive data segment inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct DataIndex(u32);
+entity_impl!(DataIndex);
+
+/// Index type of a passive element segment inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct ElemIndex(u32);
+entity_impl!(ElemIndex);
+
+/// Index type of a type inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct TypeIndex(u32);
+entity_impl!(TypeIndex);
+
+/// Index type of a module inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct ModuleIndex(u32);
+entity_impl!(ModuleIndex);
+
+/// Index type of an instance inside the WebAssembly module.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct InstanceIndex(u32);
+entity_impl!(InstanceIndex);
+
+/// An index of an entity.
+#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub enum EntityIndex {
+ /// Function index.
+ Function(FuncIndex),
+ /// Table index.
+ Table(TableIndex),
+ /// Memory index.
+ Memory(MemoryIndex),
+ /// Global index.
+ Global(GlobalIndex),
+ /// Module index.
+ Module(ModuleIndex),
+ /// Instance index.
+ Instance(InstanceIndex),
+}
+
+/// A type of an item in a wasm module where an item is typically something that
+/// can be exported.
+#[allow(missing_docs)]
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub enum EntityType {
+ /// A global variable with the specified content type
+ Global(Global),
+ /// A linear memory with the specified limits
+ Memory(Memory),
+ /// A table with the specified element type and limits
+ Table(Table),
+ /// A function type where the index points to the type section and records a
+ /// function signature.
+ Function(TypeIndex),
+ /// An instance where the index points to the type section and records a
+ /// instance's exports.
+ Instance(TypeIndex),
+ /// A module where the index points to the type section and records a
+ /// module's imports and exports.
+ Module(TypeIndex),
+}
+
+/// A WebAssembly global.
+///
+/// Note that we record both the original Wasm type and the Cranelift IR type
+/// used to represent it. This is because multiple different kinds of Wasm types
+/// might be represented with the same Cranelift IR type. For example, both a
+/// Wasm `i64` and a `funcref` might be represented with a Cranelift `i64` on
+/// 64-bit architectures, and when GC is not required for func refs.
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct Global {
+ /// The Wasm type of the value stored in the global.
+ pub wasm_ty: crate::WasmType,
+ /// The Cranelift IR type of the value stored in the global.
+ pub ty: ir::Type,
+ /// A flag indicating whether the value may change at runtime.
+ pub mutability: bool,
+ /// The source of the initial value.
+ pub initializer: GlobalInit,
+}
+
+/// Globals are initialized via the `const` operators or by referring to another import.
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub enum GlobalInit {
+ /// An `i32.const`.
+ I32Const(i32),
+ /// An `i64.const`.
+ I64Const(i64),
+ /// An `f32.const`.
+ F32Const(u32),
+ /// An `f64.const`.
+ F64Const(u64),
+ /// A `vconst`.
+ V128Const(V128Imm),
+ /// A `global.get` of another global.
+ GetGlobal(GlobalIndex),
+ /// A `ref.null`.
+ RefNullConst,
+ /// A `ref.func <index>`.
+ RefFunc(FuncIndex),
+ ///< The global is imported from, and thus initialized by, a different module.
+ Import,
+}
+
+/// WebAssembly table.
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct Table {
+ /// The table elements' Wasm type.
+ pub wasm_ty: WasmType,
+ /// The table elements' Cranelift type.
+ pub ty: TableElementType,
+ /// The minimum number of elements in the table.
+ pub minimum: u32,
+ /// The maximum number of elements in the table.
+ pub maximum: Option<u32>,
+}
+
+/// WebAssembly table element. Can be a function or a scalar type.
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub enum TableElementType {
+ /// A scalar type.
+ Val(ir::Type),
+ /// A function.
+ Func,
+}
+
+/// WebAssembly linear memory.
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
+pub struct Memory {
+ /// The minimum number of pages in the memory.
+ pub minimum: u32,
+ /// The maximum number of pages in the memory.
+ pub maximum: Option<u32>,
+ /// Whether the memory may be shared between multiple threads.
+ pub shared: bool,
+}
+
+/// Helper function translating wasmparser types to Cranelift types when possible.
+pub fn type_to_type<PE: TargetEnvironment + ?Sized>(
+ ty: wasmparser::Type,
+ environ: &PE,
+) -> WasmResult<ir::Type> {
+ match ty {
+ wasmparser::Type::I32 => Ok(ir::types::I32),
+ wasmparser::Type::I64 => Ok(ir::types::I64),
+ wasmparser::Type::F32 => Ok(ir::types::F32),
+ wasmparser::Type::F64 => Ok(ir::types::F64),
+ wasmparser::Type::V128 => Ok(ir::types::I8X16),
+ wasmparser::Type::ExternRef | wasmparser::Type::FuncRef => {
+ Ok(environ.reference_type(ty.try_into()?))
+ }
+ ty => Err(wasm_unsupported!("type_to_type: wasm type {:?}", ty)),
+ }
+}
+
+/// Helper function translating wasmparser possible table types to Cranelift types when possible,
+/// or None for Func tables.
+pub fn tabletype_to_type<PE: TargetEnvironment + ?Sized>(
+ ty: wasmparser::Type,
+ environ: &PE,
+) -> WasmResult<Option<ir::Type>> {
+ match ty {
+ wasmparser::Type::I32 => Ok(Some(ir::types::I32)),
+ wasmparser::Type::I64 => Ok(Some(ir::types::I64)),
+ wasmparser::Type::F32 => Ok(Some(ir::types::F32)),
+ wasmparser::Type::F64 => Ok(Some(ir::types::F64)),
+ wasmparser::Type::V128 => Ok(Some(ir::types::I8X16)),
+ wasmparser::Type::ExternRef => Ok(Some(environ.reference_type(ty.try_into()?))),
+ wasmparser::Type::FuncRef => Ok(None),
+ ty => Err(wasm_unsupported!(
+ "tabletype_to_type: table wasm type {:?}",
+ ty
+ )),
+ }
+}
+
+/// Get the parameter and result types for the given Wasm blocktype.
+pub fn blocktype_params_results<'a, T>(
+ validator: &'a FuncValidator<T>,
+ ty_or_ft: wasmparser::TypeOrFuncType,
+) -> WasmResult<(
+ impl ExactSizeIterator<Item = wasmparser::Type> + Clone + 'a,
+ impl ExactSizeIterator<Item = wasmparser::Type> + Clone + 'a,
+)>
+where
+ T: WasmModuleResources,
+{
+ return Ok(match ty_or_ft {
+ wasmparser::TypeOrFuncType::Type(ty) => {
+ let (params, results): (&'static [wasmparser::Type], &'static [wasmparser::Type]) =
+ match ty {
+ wasmparser::Type::I32 => (&[], &[wasmparser::Type::I32]),
+ wasmparser::Type::I64 => (&[], &[wasmparser::Type::I64]),
+ wasmparser::Type::F32 => (&[], &[wasmparser::Type::F32]),
+ wasmparser::Type::F64 => (&[], &[wasmparser::Type::F64]),
+ wasmparser::Type::V128 => (&[], &[wasmparser::Type::V128]),
+ wasmparser::Type::ExternRef => (&[], &[wasmparser::Type::ExternRef]),
+ wasmparser::Type::FuncRef => (&[], &[wasmparser::Type::FuncRef]),
+ wasmparser::Type::EmptyBlockType => (&[], &[]),
+ ty => return Err(wasm_unsupported!("blocktype_params_results: type {:?}", ty)),
+ };
+ (
+ itertools::Either::Left(params.iter().copied()),
+ itertools::Either::Left(results.iter().copied()),
+ )
+ }
+ wasmparser::TypeOrFuncType::FuncType(ty_index) => {
+ let ty = validator
+ .resources()
+ .func_type_at(ty_index)
+ .expect("should be valid");
+ (
+ itertools::Either::Right(ty.inputs()),
+ itertools::Either::Right(ty.outputs()),
+ )
+ }
+ });
+}
+
+/// Create a `Block` with the given Wasm parameters.
+pub fn block_with_params<PE: TargetEnvironment + ?Sized>(
+ builder: &mut FunctionBuilder,
+ params: impl IntoIterator<Item = wasmparser::Type>,
+ environ: &PE,
+) -> WasmResult<ir::Block> {
+ let block = builder.create_block();
+ for ty in params {
+ match ty {
+ wasmparser::Type::I32 => {
+ builder.append_block_param(block, ir::types::I32);
+ }
+ wasmparser::Type::I64 => {
+ builder.append_block_param(block, ir::types::I64);
+ }
+ wasmparser::Type::F32 => {
+ builder.append_block_param(block, ir::types::F32);
+ }
+ wasmparser::Type::F64 => {
+ builder.append_block_param(block, ir::types::F64);
+ }
+ wasmparser::Type::ExternRef | wasmparser::Type::FuncRef => {
+ builder.append_block_param(block, environ.reference_type(ty.try_into()?));
+ }
+ wasmparser::Type::V128 => {
+ builder.append_block_param(block, ir::types::I8X16);
+ }
+ ty => {
+ return Err(wasm_unsupported!(
+ "block_with_params: type {:?} in multi-value block's signature",
+ ty
+ ))
+ }
+ }
+ }
+ Ok(block)
+}
+
+/// Turns a `wasmparser` `f32` into a `Cranelift` one.
+pub fn f32_translation(x: wasmparser::Ieee32) -> ir::immediates::Ieee32 {
+ ir::immediates::Ieee32::with_bits(x.bits())
+}
+
+/// Turns a `wasmparser` `f64` into a `Cranelift` one.
+pub fn f64_translation(x: wasmparser::Ieee64) -> ir::immediates::Ieee64 {
+ ir::immediates::Ieee64::with_bits(x.bits())
+}
+
+/// Special VMContext value label. It is tracked as 0xffff_fffe label.
+pub fn get_vmctx_value_label() -> ir::ValueLabel {
+ const VMCTX_LABEL: u32 = 0xffff_fffe;
+ ir::ValueLabel::from_u32(VMCTX_LABEL)
+}
diff --git a/third_party/rust/cranelift-wasm/tests/wasm_testsuite.rs b/third_party/rust/cranelift-wasm/tests/wasm_testsuite.rs
new file mode 100644
index 0000000000..73b5508b2f
--- /dev/null
+++ b/third_party/rust/cranelift-wasm/tests/wasm_testsuite.rs
@@ -0,0 +1,96 @@
+use cranelift_codegen::isa;
+use cranelift_codegen::print_errors::pretty_verifier_error;
+use cranelift_codegen::settings::{self, Flags};
+use cranelift_codegen::verifier;
+use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode};
+use std::fs;
+use std::path::Path;
+use std::str::FromStr;
+use target_lexicon::triple;
+
+#[test]
+fn testsuite() {
+ let mut paths: Vec<_> = fs::read_dir("../wasmtests")
+ .unwrap()
+ .map(|r| r.unwrap())
+ .filter(|p| {
+ // Ignore files starting with `.`, which could be editor temporary files
+ if let Some(stem) = p.path().file_stem() {
+ if let Some(stemstr) = stem.to_str() {
+ return !stemstr.starts_with('.');
+ }
+ }
+ false
+ })
+ .collect();
+ paths.sort_by_key(|dir| dir.path());
+ let flags = Flags::new(settings::builder());
+ for path in paths {
+ let path = path.path();
+ println!("=== {} ===", path.display());
+ let data = read_module(&path);
+ handle_module(data, &flags, ReturnMode::NormalReturns);
+ }
+}
+
+#[test]
+fn use_fallthrough_return() {
+ let flags = Flags::new(settings::builder());
+ let path = Path::new("../wasmtests/use_fallthrough_return.wat");
+ let data = read_module(&path);
+ handle_module(data, &flags, ReturnMode::FallthroughReturn);
+}
+
+#[test]
+fn use_name_section() {
+ let data = wat::parse_str(
+ r#"
+ (module $module_name
+ (func $func_name (local $loc_name i32)
+ )
+ )"#,
+ )
+ .unwrap();
+
+ let flags = Flags::new(settings::builder());
+ let triple = triple!("riscv64");
+ let isa = isa::lookup(triple).unwrap().finish(flags.clone());
+ let return_mode = ReturnMode::NormalReturns;
+ let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), return_mode, false);
+
+ translate_module(data.as_ref(), &mut dummy_environ).unwrap();
+
+ assert_eq!(
+ dummy_environ.get_func_name(FuncIndex::from_u32(0)).unwrap(),
+ "func_name"
+ );
+}
+
+fn read_module(path: &Path) -> Vec<u8> {
+ match path.extension() {
+ None => {
+ panic!("the file extension is not wasm or wat");
+ }
+ Some(ext) => match ext.to_str() {
+ Some("wasm") => std::fs::read(path).expect("error reading wasm file"),
+ Some("wat") => wat::parse_file(path)
+ .map_err(|e| e.to_string())
+ .expect("failed to parse wat"),
+ None | Some(&_) => panic!("the file extension for {:?} is not wasm or wat", path),
+ },
+ }
+}
+
+fn handle_module(data: Vec<u8>, flags: &Flags, return_mode: ReturnMode) {
+ let triple = triple!("riscv64");
+ let isa = isa::lookup(triple).unwrap().finish(flags.clone());
+ let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), return_mode, false);
+
+ translate_module(&data, &mut dummy_environ).unwrap();
+
+ for func in dummy_environ.info.function_bodies.values() {
+ verifier::verify_function(func, &*isa)
+ .map_err(|errors| panic!(pretty_verifier_error(func, Some(&*isa), None, errors)))
+ .unwrap();
+ }
+}