Repository: bytecodealliance/wasmtime
Pull request #6017 (2023-03-14 17:14:38)

winch: Introduce `winch-environ`
This commit introduces the `winch-environ` crate. This crate's responsibility is
to provide a shared implementatation of the `winch_codegen::FuncEnv` trait,
which is Winch's function compilation environment, used to resolve module and
runtime specific information needed by the code generation, such as resolving
all the details about a callee in a WebAssembly module, or resolving specific
information from the `VMContext`.

As of this change, the implementation only includes the necessary pieces to
resolve a function callee in a WebAssembly module. The idea is to evolve the
`winch_codegen::FuncEnv` trait as we evolve Winch's code generation.

-------

<!--

Please ensure that the following steps are all taken care of before submitting
the PR.

- [ ] This has been discussed in issue #..., or if not, please tell us why
  here.
- [ ] A short description of what this does, why it is needed; if the
  description becomes long, the matter should probably be discussed in an issue
  first.
- [ ] This PR contains test cases, if meaningful.
- [ ] A reviewer from the core maintainer team has been assigned for this PR.
  If you don't know who could review this, please indicate so. The list of
  suggested reviewers on the right can help you.

Please ensure all communication adheres to the [code of
conduct](https://github.com/bytecodealliance/wasmtime/blob/master/CODE_OF_CONDUCT.md).
-->

github-actions[bot]: #### Subscribe to Label Action

cc @saulecabrera

<details>
This issue or pull request has been labeled: "winch"

Thus the following users have been cc'd because of the following labels:

* saulecabrera: winch

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index f61028eb3..015c7f822 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4031,6 +4031,15 @@ dependencies = [
  "wasmparser",
 ]
 
+[[package]]
+name = "winch-environ"
+version = "0.6.0"
+dependencies = [
+ "wasmparser",
+ "wasmtime-environ",
+ "winch-codegen",
+]
+
 [[package]]
 name = "winch-filetests"
 version = "0.0.0"
@@ -4045,6 +4054,7 @@ dependencies = [
  "wasmtime-environ",
  "wat",
  "winch-codegen",
+ "winch-environ",
  "winch-test-macros",
 ]
 
@@ -4075,6 +4085,7 @@ dependencies = [
  "wasmtime-environ",
  "wat",
  "winch-codegen",
+ "winch-environ",
  "winch-filetests",
  "winch-test-macros",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index f2634cffc..6f081ca40 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -101,7 +101,8 @@ members = [
   "examples/tokio/wasm",
   "fuzz",
   "winch",
-  "winch/codegen"
+  "winch/codegen",
+  "winch/environ"
 ]
 exclude = [
   'crates/wasi-common/WASI/tools/witx-cli',
@@ -163,6 +164,7 @@ cranelift-bforest = { path = "cranelift/bforest", version = "0.95.0" }
 cranelift = { path = "cranelift/umbrella", version = "0.95.0" }
 
 winch-codegen = { path = "winch/codegen", version = "=0.6.0" }
+winch-environ = { path = "winch/environ", version = "=0.6.0" }
 winch-filetests = { path = "winch/filetests" }
 winch-test-macros = { path = "winch/test-macros" }
 
diff --git a/scripts/publish.rs b/scripts/publish.rs
index 73a229a54..b64b178db 100644
--- a/scripts/publish.rs
+++ b/scripts/publish.rs
@@ -57,6 +57,7 @@ const CRATES_TO_PUBLISH: &[&str] = &[
     "wasmtime-jit",
     "wasmtime-cache",
     "wasmtime-winch",
+    "winch-environ",
     "wasmtime",
     // wasi-common/wiggle
     "wiggle",
diff --git a/winch/Cargo.toml b/winch/Cargo.toml
index d86a0b22a..825913017 100644
--- a/winch/Cargo.toml
+++ b/winch/Cargo.toml
@@ -13,6 +13,7 @@ path = "src/main.rs"
 
 [dependencies]
 winch-codegen = { workspace = true }
+winch-environ = { workspace = true }
 winch-filetests = { workspace = true }
 winch-test-macros = { workspace = true }
 wasmtime-environ = { workspace = true }
diff --git a/winch/codegen/src/codegen/env.rs b/winch/codegen/src/codegen/env.rs
new file mode 100644
index 000000000..f05ac47fc
--- /dev/null
+++ b/winch/codegen/src/codegen/env.rs
@@ -0,0 +1,19 @@
+use wasmparser::FuncType;
+
+/// Function environment used the by the code generation to
+/// resolve module and runtime-specific information.
+pub trait FuncEnv {
+    /// Get the callee information from a given function index.
+    fn callee_from_index(&self, index: u32) -> Callee;
+}
+
+/// Metadata about a function callee.  Use by the code generation
+/// to emit function calls.
+pub struct Callee {
+    /// The function type.
+    pub ty: FuncType,
+    /// A flag to determine if the callee is imported.
+    pub import: bool,
+    /// The callee index in the WebAssembly function index space.
+    pub index: u32,
+}
diff --git a/winch/codegen/src/codegen/mod.rs b/winch/codegen/src/codegen/mod.rs
index aa4ab537f..807912218 100644
--- a/winch/codegen/src/codegen/mod.rs
+++ b/winch/codegen/src/codegen/mod.rs
@@ -7,6 +7,8 @@ use wasmparser::{BinaryReader, FuncValidator, ValType, ValidatorResources, Visit
 
 mod context;
 pub(crate) use context::*;
+mod env;
+pub use env::*;
 
 /// The code generation abstraction.
 pub(crate) struct CodeGen<'a, M>
diff --git a/winch/codegen/src/lib.rs b/winch/codegen/src/lib.rs
index efee4a711..3321998db 100644
--- a/winch/codegen/src/lib.rs
+++ b/winch/codegen/src/lib.rs
@@ -8,6 +8,7 @@
 
 mod abi;
 mod codegen;
+pub use codegen::{Callee, FuncEnv};
 mod frame;
 pub mod isa;
 pub use isa::*;
diff --git a/winch/environ/Cargo.toml b/winch/environ/Cargo.toml
new file mode 100644
index 000000000..df49636df
--- /dev/null
+++ b/winch/environ/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+authors = ["The Winch Project Developers"]
+name = "winch-environ"
+description = "Implementation of Winch's function compilation environment"
+license = "Apache-2.0 WITH LLVM-exception"
+repository = "https://github.com/bytecodealliance/wasmtime"
+version = "0.6.0"
+edition.workspace = true
+
+
+[dependencies]
+winch-codegen = { workspace = true }
+wasmtime-environ = { workspace = true }
+wasmparser = { workspace = true }
diff --git a/winch/environ/src/lib.rs b/winch/environ/src/lib.rs
new file mode 100644
index 000000000..1e46ea6ac
--- /dev/null
+++ b/winch/environ/src/lib.rs
@@ -0,0 +1,41 @@
+//! This crate implements Winch's function compilation environment,
+//! which allows Winch's code generation to resolve module and runtime
+//! specific information.  This crate mainly implements the
+//! `winch_codegen::FuncEnv` trait.
+
+use wasmparser::types::Types;
+use wasmtime_environ::{FuncIndex, Module};
+use winch_codegen::{self, Callee, TargetIsa};
+
+/// Function environment containing module and runtime specific
+/// information.
+pub struct FuncEnv<'a> {
+    /// The translated WebAssembly module.
+    pub module: &'a Module,
+    /// Type information about a module, once it has been validated.
+    pub types: &'a Types,
+    /// The current ISA.
+    pub isa: &'a dyn TargetIsa,
+}
+
+impl<'a> winch_codegen::FuncEnv for FuncEnv<'a> {
+    fn callee_from_index(&self, index: u32) -> Callee {
+        let func = self
+            .types
+            .function_at(index)
+            .unwrap_or_else(|| panic!("function type at index: {}", index));
+
+        Callee {
+            ty: func.clone(),
+            import: self.module.is_imported_function(FuncIndex::from_u32(index)),
+            index,
+        }
+    }
+}
+
+impl<'a> FuncEnv<'a> {
+    /// Create a new function environment.
+    pub fn new(module: &'a Module, types: &'a Types, isa: &'a dyn TargetIsa) -> Self {
+        Self { module, types, isa }
+    }
+}
diff --git a/winch/src/compile.rs b/winch/src/compile.rs
index 508fa56c7..1e05a6aaf 100644
--- a/winch/src/compile.rs
+++ b/winch/src/compile.rs
@@ -4,10 +4,11 @@ use cranelift_codegen::settings;
 use std::{fs, path::PathBuf, str::FromStr};
 use target_lexicon::Triple;
 use wasmtime_environ::{
-    wasmparser::{types::Types, Parser as WasmParser, Validator},
-    DefinedFuncIndex, FunctionBodyData, Module, ModuleEnvironment, Tunables,
+    wasmparser::{Parser as WasmParser, Validator},
+    DefinedFuncIndex, FunctionBodyData, ModuleEnvironment, Tunables,
 };
-use winch_codegen::{lookup, TargetIsa};
+use winch_codegen::lookup;
+use winch_environ::FuncEnv;
 use winch_filetests::disasm::disasm;
 
 #[derive(Parser, Debug)]
@@ -40,32 +41,30 @@ pub fn run(opt: &Options) -> Result<()> {
     let body_inputs = std::mem::take(&mut translation.function_body_inputs);
     let module = &translation.module;
     let types = translation.get_types();
+    let env = FuncEnv::new(module, &types, &*isa);
 
     body_inputs
         .into_iter()
-        .try_for_each(|func| compile(&*isa, module, types, func))?;
+        .try_for_each(|func| compile(&env, func))?;
 
     Ok(())
 }
 
-fn compile(
-    isa: &dyn TargetIsa,
-    module: &Module,
-    types: &Types,
-    f: (DefinedFuncIndex, FunctionBodyData<'_>),
-) -> Result<()> {
-    let index = module.func_index(f.0);
-    let sig = types
+fn compile(env: &FuncEnv, f: (DefinedFuncIndex, FunctionBodyData<'_>)) -> Result<()> {
+    let index = env.module.func_index(f.0);
+    let sig = env
+        .types
         .function_at(index.as_u32())
         .expect(&format!("function type at index {:?}", index.as_u32()));
     let FunctionBodyData { body, validator } = f.1;
     let validator = validator.into_validator(Default::default());
-    let buffer = isa
+    let buffer = env
+        .isa
         .compile_function(&sig, &body, validator)
         .expect("Couldn't compile function");
 
     println!("Disassembly for function: {}", index.as_u32());
-    disasm(buffer.data(), isa)?
+    disasm(buffer.data(), env.isa)?
         .iter()
         .for_each(|s| println!("{}", s));
 


Test patch:
diff --git a/winch/filetests/Cargo.toml b/winch/filetests/Cargo.toml
index 9250078b4..a629e3f68 100644
--- a/winch/filetests/Cargo.toml
+++ b/winch/filetests/Cargo.toml
@@ -12,6 +12,7 @@ edition.workspace = true
 winch-test-macros = {workspace = true}
 target-lexicon = { workspace = true }
 winch-codegen = { workspace = true, features = ['all-arch'] }
+winch-environ = { workspace = true }
 wasmtime-environ = { workspace = true }
 anyhow = { workspace = true }
 wat = { workspace = true }
diff --git a/winch/filetests/src/lib.rs b/winch/filetests/src/lib.rs
index 3f49abddc..0d4f8832d 100644
--- a/winch/filetests/src/lib.rs
+++ b/winch/filetests/src/lib.rs
@@ -10,11 +10,11 @@ mod test {
     use std::str::FromStr;
     use target_lexicon::Triple;
     use wasmtime_environ::{
-        wasmparser::{types::Types, Parser as WasmParser, Validator},
-        DefinedFuncIndex, FunctionBodyData, Module, ModuleEnvironment, Tunables,
+        wasmparser::{Parser as WasmParser, Validator},
+        DefinedFuncIndex, FunctionBodyData, ModuleEnvironment, Tunables,
     };
-    use winch_codegen::isa::TargetIsa;
     use winch_codegen::lookup;
+    use winch_environ::FuncEnv;
     use winch_test_macros::generate_file_tests;
 
     #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -109,10 +109,11 @@ mod test {
         let body_inputs = std::mem::take(&mut translation.function_body_inputs);
         let module = &translation.module;
         let types = translation.get_types();
+        let env = FuncEnv::new(module, &types, &*isa);
 
         let binding = body_inputs
             .into_iter()
-            .map(|func| compile(&*isa, module, types, func).join("\n"))
+            .map(|func| compile(&env, func).join("\n"))
             .collect::<Vec<String>>()
             .join("\n\n");
         let actual = binding.as_str();
@@ -143,23 +144,20 @@ mod test {
         }
     }
 
-    fn compile(
-        isa: &dyn TargetIsa,
-        module: &Module,
-        types: &Types,
-        f: (DefinedFuncIndex, FunctionBodyData<'_>),
-    ) -> Vec<String> {
-        let index = module.func_index(f.0);
-        let sig = types
+    fn compile(env: &FuncEnv, f: (DefinedFuncIndex, FunctionBodyData<'_>)) -> Vec<String> {
+        let index = env.module.func_index(f.0);
+        let sig = env
+            .types
             .function_at(index.as_u32())
             .expect(&format!("function type at index {:?}", index.as_u32()));
         let FunctionBodyData { body, validator } = f.1;
         let validator = validator.into_validator(Default::default());
 
-        let buffer = isa
+        let buffer = env
+            .isa
             .compile_function(&sig, &body, validator)
             .expect("Couldn't compile function");
 
-        disasm(buffer.data(), isa).unwrap()
+        disasm(buffer.data(), env.isa).unwrap()
     }
 }