Repository: bytecodealliance/wasmtime
Pull request #7655 (2023-12-07 19:01:04)

Move wasmtime-wasi's Table to wasmtime::component::ResourceTable
This abstraction is useful for all component model Resource lookups, not exclusive to WASI, so we are promoting it to live in wasmtime::component.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/cranelift/codegen/src/opts/arithmetic.isle b/cranelift/codegen/src/opts/arithmetic.isle
index bbb40dbf2..3ac4381a9 100644
--- a/cranelift/codegen/src/opts/arithmetic.isle
+++ b/cranelift/codegen/src/opts/arithmetic.isle
@@ -20,6 +20,17 @@
                       x))
       (ineg ty x))
 
+;; x + -y == -y + x == -(y - x) == x - y
+(rule (simplify (iadd ty x (ineg ty y)))
+      (isub ty x y))
+(rule (simplify (iadd ty (ineg ty y) x))
+      (isub ty x y))
+(rule (simplify (ineg ty (isub ty y x)))
+      (isub ty x y))
+;; x - -y == x + y
+(rule (simplify (isub ty x (ineg ty y)))
+      (iadd ty x y))
+
 ;; ineg(ineg(x)) == x.
 (rule (simplify (ineg ty (ineg ty x))) (subsume x))
 
@@ -55,24 +66,16 @@
       (if-let -1 (i64_sextend_imm64 ty c))
       (ineg ty x))
 
-;; (!x) + 1 == 1 + (!x) == !(x) - (-1) == ineg(x)
+;; (!x) + 1 == ineg(x)
 (rule (simplify (iadd ty (bnot ty x) (iconst ty (u64_from_imm64 1))))
       (ineg ty x))
-(rule (simplify (iadd ty (iconst ty (u64_from_imm64 1)) (bnot ty x)))
-      (ineg ty x))
-(rule (simplify (isub ty (bnot ty x) (iconst ty c)))
-      (if-let -1 (i64_sextend_imm64 ty c))
-      (ineg ty x))
 
-;; !(x - 1) == !(x + (-1)) == !((-1) + x) == ineg(x)
+;; !(x - 1) == !(x + (-1)) == ineg(x)
 (rule (simplify (bnot ty (isub ty x (iconst ty (u64_from_imm64 1)))))
       (ineg ty x))
 (rule (simplify (bnot ty (iadd ty x (iconst ty c))))
       (if-let -1 (i64_sextend_imm64 ty c))
       (ineg ty x))
-(rule (simplify (bnot ty (iadd ty (iconst ty c) x)))
-      (if-let -1 (i64_sextend_imm64 ty c))
-      (ineg ty x))
 
 ;; x/1 == x.
 (rule (simplify (sdiv ty
diff --git a/cranelift/codegen/src/opts/bitops.isle b/cranelift/codegen/src/opts/bitops.isle
index 6c89e0f07..3c385a050 100644
--- a/cranelift/codegen/src/opts/bitops.isle
+++ b/cranelift/codegen/src/opts/bitops.isle
@@ -1,26 +1,18 @@
 ;; Rewrites for `band`, `bnot`, `bor`, `bxor`
 
-;; x | 0 == 0 | x == x | x == x.
+;; x | 0 == x | x == x.
 (rule (simplify (bor ty
                      x
                      (iconst ty (u64_from_imm64 0))))
       (subsume x))
-(rule (simplify (bor ty
-                     (iconst ty (u64_from_imm64 0))
-                     x))
-      (subsume x))
 (rule (simplify (bor ty x x))
       (subsume x))
 
-;; x ^ 0 == 0 ^ x == x.
+;; x ^ 0 == x.
 (rule (simplify (bxor ty
                      x
                      (iconst ty (u64_from_imm64 0))))
       (subsume x))
-(rule (simplify (bxor ty
-                     (iconst ty (u64_from_imm64 0))
-                     x))
-      (subsume x))
 
 ;; x ^ x == 0.
 (rule (simplify (bxor (fits_in_64 (ty_int ty)) x x))
@@ -34,18 +26,14 @@
 (rule (simplify (bor (fits_in_64 (ty_int ty)) x (bnot ty x))) (subsume (iconst ty (imm64 (ty_mask ty)))))
 (rule (simplify (bor (fits_in_64 (ty_int ty)) (bnot ty x) x)) (subsume (iconst ty (imm64 (ty_mask ty)))))
 
-;; x & -1 == -1 & x == x & x == x.
+;; x & x == x & -1 == x.
 (rule (simplify (band ty x x)) (subsume x))
 (rule (simplify (band ty x (iconst ty k)))
       (if-let -1 (i64_sextend_imm64 ty k))
       (subsume x))
-(rule (simplify (band ty (iconst ty k) x))
-      (if-let -1 (i64_sextend_imm64 ty k))
-      (subsume x))
 
-;; x & 0 == 0 & x == x & not(x) == not(x) & x == 0.
+;; x & 0 == x & not(x) == not(x) & x == 0.
 (rule (simplify (band ty _ zero @ (iconst ty (u64_from_imm64 0)))) (subsume zero))
-(rule (simplify (band ty zero @ (iconst ty (u64_from_imm64 0)) _)) (subsume zero))
 (rule (simplify (band (fits_in_64 (ty_int ty)) x (bnot ty x))) (subsume (iconst ty (imm64 0))))
 (rule (simplify (band (fits_in_64 (ty_int ty)) (bnot ty x) x)) (subsume (iconst ty (imm64 0))))
 
diff --git a/cranelift/codegen/src/opts/cprop.isle b/cranelift/codegen/src/opts/cprop.isle
index e44404e05..a76ecdd71 100644
--- a/cranelift/codegen/src/opts/cprop.isle
+++ b/cranelift/codegen/src/opts/cprop.isle
@@ -173,6 +173,17 @@
        (select ty (iconst _ (u64_from_imm64 0)) x y))
       y)
 
+;; Replace subtraction by a "negative" constant with addition.
+;; Notably, this gives `x - (-1) == x + 1`, so other patterns don't have to
+;; match the subtract-negative-one version too.
+;; TODO: it would be nice to do this for `x + (-1) == x - 1` as well, but
+;; that needs work in lowering first to avoid regressing addressing modes.
+
+(rule (simplify (isub ty x (iconst ty k1)))
+      (if-let k2 (i64_sextend_imm64 ty k1))
+      (if-let $true (u64_lt (i64_as_u64 (i64_neg k2)) (i64_as_u64 k2)))
+      (iadd ty x (iconst ty (imm64_masked ty (i64_as_u64 (i64_neg k2))))))
+
 ;; TODO: fadd, fsub, fmul, fdiv, fneg, fabs
 
 ;; A splat of a constant can become a direct `vconst` with the appropriate bit
diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs
index 8e1709ff1..f474dcecf 100644
--- a/crates/cli-flags/src/lib.rs
+++ b/crates/cli-flags/src/lib.rs
@@ -259,6 +259,10 @@ wasmtime_option_group! {
         pub inherit_network: Option<bool>,
         /// Indicates whether `wasi:sockets/ip-name-lookup` is enabled or not.
         pub allow_ip_name_lookup: Option<bool>,
+        /// Indicates whether `wasi:sockets` TCP support is enabled or not.
+        pub tcp: Option<bool>,
+        /// Indicates whether `wasi:sockets` UDP support is enabled or not.
+        pub udp: Option<bool>,
         /// Allows imports from the `wasi_unstable` core wasm module.
         pub preview0: Option<bool>,
     }
diff --git a/crates/wasi-common/src/error.rs b/crates/wasi-common/src/error.rs
index b1a5135bb..11b43ecd4 100644
--- a/crates/wasi-common/src/error.rs
+++ b/crates/wasi-common/src/error.rs
@@ -7,7 +7,7 @@
 //! can be provided with the [`Error::context`] method. This context is only observable with the
 //! `Display` and `Debug` impls of the error.
 
-pub use crate::snapshots::preview_1::error::{Errno, Error, ErrorExt};
+pub use crate::snapshots::preview_1::error::{Error, ErrorExt};
 use std::fmt;
 
 /// An error returned from the `proc_exit` host syscall.
diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs
index 1657d9a36..2a9a92b98 100644
--- a/crates/wasi-http/src/types.rs
+++ b/crates/wasi-http/src/types.rs
@@ -14,8 +14,8 @@ use std::sync::Arc;
 use std::time::Duration;
 use tokio::net::TcpStream;
 use tokio::time::timeout;
-use wasmtime::component::Resource;
-use wasmtime_wasi::preview2::{self, AbortOnDropJoinHandle, Subscribe, Table};
+use wasmtime::component::{Resource, ResourceTable};
+use wasmtime_wasi::preview2::{self, AbortOnDropJoinHandle, Subscribe};
 
 /// Capture the state necessary for use in the wasi-http API implementation.
 pub struct WasiHttpCtx;
@@ -31,7 +31,7 @@ pub struct OutgoingRequest {
 
 pub trait WasiHttpView: Send {
     fn ctx(&mut self) -> &mut WasiHttpCtx;
-    fn table(&mut self) -> &mut Table;
+    fn table(&mut self) -> &mut ResourceTable;
 
     fn new_incoming_request(
         &mut self,
diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs
index 74ee18c2f..351f8f59d 100644
--- a/crates/wasi-http/src/types_impl.rs
+++ b/crates/wasi-http/src/types_impl.rs
@@ -11,10 +11,10 @@ use crate::{
 use anyhow::Context;
 use std::any::Any;
 use std::str::FromStr;
-use wasmtime::component::Resource;
+use wasmtime::component::{Resource, ResourceTable};
 use wasmtime_wasi::preview2::{
     bindings::io::streams::{InputStream, OutputStream},
-    Pollable, Table,
+    Pollable,
 };
 
 impl<T: WasiHttpView> crate::bindings::http::types::Host for T {
@@ -49,7 +49,7 @@ fn get_content_length(fields: &FieldMap) -> Result<Option<u64>, ()> {
 
 /// Take ownership of the underlying [`FieldMap`] associated with this fields resource. If the
 /// fields resource references another fields, the returned [`FieldMap`] will be cloned.
-fn move_fields(table: &mut Table, id: Resource<HostFields>) -> wasmtime::Result<FieldMap> {
+fn move_fields(table: &mut ResourceTable, id: Resource<HostFields>) -> wasmtime::Result<FieldMap> {
     match table.delete(id)? {
         HostFields::Ref { parent, get_fields } => {
             let entry = table.get_any_mut(parent)?;
@@ -61,7 +61,7 @@ fn move_fields(table: &mut Table, id: Resource<HostFields>) -> wasmtime::Result<
 }
 
 fn get_fields<'a>(
-    table: &'a mut Table,
+    table: &'a mut ResourceTable,
     id: &Resource<HostFields>,
 ) -> wasmtime::Result<&'a FieldMap> {
     let fields = table.get(&id)?;
@@ -80,7 +80,7 @@ fn get_fields<'a>(
 }
 
 fn get_fields_mut<'a>(
-    table: &'a mut Table,
+    table: &'a mut ResourceTable,
     id: &Resource<HostFields>,
 ) -> wasmtime::Result<Result<&'a mut FieldMap, types::HeaderError>> {
     match table.get_mut(&id)? {
diff --git a/crates/wasi/src/preview2/ctx.rs b/crates/wasi/src/preview2/ctx.rs
index 73cd0e116..5d527b241 100644
--- a/crates/wasi/src/preview2/ctx.rs
+++ b/crates/wasi/src/preview2/ctx.rs
@@ -4,7 +4,7 @@ use crate::preview2::{
     filesystem::Dir,
     pipe, random, stdio,
     stdio::{StdinStream, StdoutStream},
-    DirPerms, FilePerms, Table,
+    DirPerms, FilePerms,
 };
 use cap_rand::{Rng, RngCore, SeedableRng};
 use cap_std::ipnet::{self, IpNet};
@@ -12,6 +12,7 @@ use cap_std::net::Pool;
 use cap_std::{ambient_authority, AmbientAuthority};
 use std::mem;
 use std::net::{Ipv4Addr, Ipv6Addr};
+use wasmtime::component::ResourceTable;
 
 pub struct WasiCtxBuilder {
     stdin: Box<dyn StdinStream>,
@@ -27,7 +28,7 @@ pub struct WasiCtxBuilder {
     insecure_random_seed: u128,
     wall_clock: Box<dyn HostWallClock + Send + Sync>,
     monotonic_clock: Box<dyn HostMonotonicClock + Send + Sync>,
-    allow_ip_name_lookup: bool,
+    allowed_network_uses: AllowedNetworkUses,
     built: bool,
 }
 
@@ -77,7 +78,7 @@ impl WasiCtxBuilder {
             insecure_random_seed,
             wall_clock: wall_clock(),
             monotonic_clock: monotonic_clock(),
-            allow_ip_name_lookup: false,
+            allowed_network_uses: AllowedNetworkUses::default(),
             built: false,
         }
     }
@@ -250,7 +251,19 @@ impl WasiCtxBuilder {
 
     /// Allow usage of `wasi:sockets/ip-name-lookup`
     pub fn allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self {
-        self.allow_ip_name_lookup = enable;
+        self.allowed_network_uses.ip_name_lookup = enable;
+        self
+    }
+
+    /// Allow usage of UDP
+    pub fn allow_udp(&mut self, enable: bool) -> &mut Self {
+        self.allowed_network_uses.udp = enable;
+        self
+    }
+
+    /// Allow usage of TCP
+    pub fn allow_tcp(&mut self, enable: bool) -> &mut Self {
+        self.allowed_network_uses.tcp = enable;
         self
     }
 
@@ -278,7 +291,7 @@ impl WasiCtxBuilder {
             insecure_random_seed,
             wall_clock,
             monotonic_clock,
-            allow_ip_name_lookup,
+            allowed_network_uses,
             built: _,
         } = mem::replace(self, Self::new());
         self.built = true;
@@ -296,14 +309,14 @@ impl WasiCtxBuilder {
             insecure_random_seed,
             wall_clock,
             monotonic_clock,
-            allow_ip_name_lookup,
+            allowed_network_uses,
         }
     }
 }
 
 pub trait WasiView: Send {
-    fn table(&self) -> &Table;
-    fn table_mut(&mut self) -> &mut Table;
+    fn table(&self) -> &ResourceTable;
+    fn table_mut(&mut self) -> &mut ResourceTable;
     fn ctx(&self) -> &WasiCtx;
     fn ctx_mut(&mut self) -> &mut WasiCtx;
 }
@@ -321,5 +334,47 @@ pub struct WasiCtx {
     pub(crate) stdout: Box<dyn StdoutStream>,
     pub(crate) stderr: Box<dyn StdoutStream>,
     pub(crate) pool: Pool,
-    pub(crate) allow_ip_name_lookup: bool,
+    pub(crate) allowed_network_uses: AllowedNetworkUses,
+}
+
+pub struct AllowedNetworkUses {
+    pub ip_name_lookup: bool,
+    pub udp: bool,
+    pub tcp: bool,
+}
+
+impl Default for AllowedNetworkUses {
+    fn default() -> Self {
+        Self {
+            ip_name_lookup: false,
+            udp: true,
+            tcp: true,
+        }
+    }
+}
+
+impl AllowedNetworkUses {
+    pub(crate) fn check_allowed_udp(&self) -> std::io::Result<()> {
+        if !self.udp {
+            return Err(std::io::Error::new(
+                std::io::ErrorKind::PermissionDenied,
+                "UDP is not allowed",
+            )
+            .into());
+        }
+
+        Ok(())
+    }
+
+    pub(crate) fn check_allowed_tcp(&self) -> std::io::Result<()> {
+        if !self.tcp {
+            return Err(std::io::Error::new(
+                std::io::ErrorKind::PermissionDenied,
+                "TCP is not allowed",
+            )
+            .into());
+        }
+
+        Ok(())
+    }
 }
diff --git a/crates/wasi/src/preview2/filesystem.rs b/crates/wasi/src/preview2/filesystem.rs
index d8dc2bc68..1f1d149dd 100644
--- a/crates/wasi/src/preview2/filesystem.rs
+++ b/crates/wasi/src/preview2/filesystem.rs
@@ -1,7 +1,6 @@
 use crate::preview2::bindings::filesystem::types;
 use crate::preview2::{
-    spawn_blocking, AbortOnDropJoinHandle, HostOutputStream, StreamError, Subscribe, TableError,
-    TrappableError,
+    spawn_blocking, AbortOnDropJoinHandle, HostOutputStream, StreamError, Subscribe, TrappableError,
 };
 use anyhow::anyhow;
 use bytes::{Bytes, BytesMut};
@@ -13,8 +12,8 @@ pub type FsResult<T> = Result<T, FsError>;
 
 pub type FsError = TrappableError<types::ErrorCode>;
 
-impl From<TableError> for FsError {
-    fn from(error: TableError) -> Self {
+impl From<wasmtime::component::ResourceTableError> for FsError {
+    fn from(error: wasmtime::component::ResourceTableError) -> Self {
         Self::trap(error)
     }
 }
diff --git a/crates/wasi/src/preview2/host/clocks.rs b/crates/wasi/src/preview2/host/clocks.rs
index 4467b3681..2f16b9e10 100644
--- a/crates/wasi/src/preview2/host/clocks.rs
+++ b/crates/wasi/src/preview2/host/clocks.rs
@@ -43,7 +43,7 @@ impl<T: WasiView> wall_clock::Host for T {
 }
 
 fn subscribe_to_duration(
-    table: &mut crate::preview2::Table,
+    table: &mut wasmtime::component::ResourceTable,
     duration: tokio::time::Duration,
 ) -> anyhow::Result<Resource<Pollable>> {
     let sleep = if duration.is_zero() {
diff --git a/crates/wasi/src/preview2/host/filesystem.rs b/crates/wasi/src/preview2/host/filesystem.rs
index 39559ed43..4a70efee5 100644
--- a/crates/wasi/src/preview2/host/filesystem.rs
+++ b/crates/wasi/src/preview2/host/filesystem.rs
@@ -6,9 +6,9 @@ use crate::preview2::bindings::filesystem::types::{
 use crate::preview2::bindings::io::streams::{InputStream, OutputStream};
 use crate::preview2::filesystem::{Descriptor, Dir, File, ReaddirIterator};
 use crate::preview2::filesystem::{FileInputStream, FileOutputStream};
-use crate::preview2::{DirPerms, FilePerms, FsError, FsResult, Table, WasiView};
+use crate::preview2::{DirPerms, FilePerms, FsError, FsResult, WasiView};
 use anyhow::Context;
-use wasmtime::component::Resource;
+use wasmtime::component::{Resource, ResourceTable};
 
 mod sync;
 
@@ -840,7 +840,7 @@ impl<T: WasiView> HostDirectoryEntryStream for T {
 }
 
 async fn get_descriptor_metadata(
-    table: &Table,
+    table: &ResourceTable,
     fd: Resource<types::Descriptor>,
 ) -> FsResult<cap_std::fs::Metadata> {
     match table.get(&fd)? {
@@ -1059,7 +1059,7 @@ mod test {
     use super::*;
     #[test]
     fn table_readdir_works() {
-        let mut table = Table::new();
+        let mut table = ResourceTable::new();
         let ix = table
             .push(ReaddirIterator::new(std::iter::empty()))
             .unwrap();
diff --git a/crates/wasi/src/preview2/host/instance_network.rs b/crates/wasi/src/preview2/host/instance_network.rs
index 6c36a7384..428151dcc 100644
--- a/crates/wasi/src/preview2/host/instance_network.rs
+++ b/crates/wasi/src/preview2/host/instance_network.rs
@@ -7,7 +7,7 @@ impl<T: WasiView> instance_network::Host for T {
     fn instance_network(&mut self) -> Result<Resource<Network>, anyhow::Error> {
         let network = Network {
             pool: self.ctx().pool.clone(),
-            allow_ip_name_lookup: self.ctx().allow_ip_name_lookup,
+            allow_ip_name_lookup: self.ctx().allowed_network_uses.ip_name_lookup,
         };
         let network = self.table_mut().push(network)?;
         Ok(network)
diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs
index 0f977133f..436a92367 100644
--- a/crates/wasi/src/preview2/host/tcp.rs
+++ b/crates/wasi/src/preview2/host/tcp.rs
@@ -28,6 +28,7 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
         network: Resource<Network>,
         local_address: IpSocketAddress,
     ) -> SocketResult<()> {
+        self.ctx().allowed_network_uses.check_allowed_tcp()?;
         let table = self.table_mut();
         let socket = table.get(&this)?;
         let network = table.get(&network)?;
@@ -88,6 +89,7 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
         network: Resource<Network>,
         remote_address: IpSocketAddress,
     ) -> SocketResult<()> {
+        self.ctx().allowed_network_uses.check_allowed_tcp()?;
         let table = self.table_mut();
         let r = {
             let socket = table.get(&this)?;
@@ -186,6 +188,7 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
     }
 
     fn start_listen(&mut self, this: Resource<tcp::TcpSocket>) -> SocketResult<()> {
+        self.ctx().allowed_network_uses.check_allowed_tcp()?;
         let table = self.table_mut();
         let socket = table.get_mut(&this)?;
 
@@ -233,6 +236,7 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
         Resource<InputStream>,
         Resource<OutputStream>,
     )> {
+        self.ctx().allowed_network_uses.check_allowed_tcp()?;
         let table = self.table();
         let socket = table.get(&this)?;
 
diff --git a/crates/wasi/src/preview2/host/udp.rs b/crates/wasi/src/preview2/host/udp.rs
index 9c6d20890..9b5955511 100644
--- a/crates/wasi/src/preview2/host/udp.rs
+++ b/crates/wasi/src/preview2/host/udp.rs
@@ -33,6 +33,7 @@ impl<T: WasiView> udp::HostUdpSocket for T {
         network: Resource<Network>,
         local_address: IpSocketAddress,
     ) -> SocketResult<()> {
+        self.ctx().allowed_network_uses.check_allowed_udp()?;
         let table = self.table_mut();
         let socket = table.get(&this)?;
         let network = table.get(&network)?;
diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs
index d4eb0e409..f7fc9004e 100644
--- a/crates/wasi/src/preview2/mod.rs
+++ b/crates/wasi/src/preview2/mod.rs
@@ -36,7 +36,6 @@ pub mod preview1;
 mod random;
 mod stdio;
 mod stream;
-mod table;
 mod tcp;
 mod udp;
 mod write_stream;
@@ -54,9 +53,9 @@ pub use self::stdio::{
 pub use self::stream::{
     HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult,
 };
-pub use self::table::{Table, TableError};
 pub use cap_fs_ext::SystemTimeSpec;
 pub use cap_rand::RngCore;
+pub use wasmtime::component::{ResourceTable, ResourceTableError};
 
 pub mod bindings {
     // Generate traits for synchronous bindings.
diff --git a/crates/wasi/src/preview2/network.rs b/crates/wasi/src/preview2/network.rs
index 65c380bd1..13575202b 100644
--- a/crates/wasi/src/preview2/network.rs
+++ b/crates/wasi/src/preview2/network.rs
@@ -1,6 +1,6 @@
 use crate::preview2::bindings::sockets::network::{Ipv4Address, Ipv6Address};
 use crate::preview2::bindings::wasi::sockets::network::ErrorCode;
-use crate::preview2::{TableError, TrappableError};
+use crate::preview2::TrappableError;
 use cap_std::net::Pool;
 
 pub struct Network {
@@ -12,8 +12,8 @@ pub type SocketResult<T> = Result<T, SocketError>;
 
 pub type SocketError = TrappableError<ErrorCode>;
 
-impl From<TableError> for SocketError {
-    fn from(error: TableError) -> Self {
+impl From<wasmtime::component::ResourceTableError> for SocketError {
+    fn from(error: wasmtime::component::ResourceTableError) -> Self {
         Self::trap(error)
     }
 }
diff --git a/crates/wasi/src/preview2/poll.rs b/crates/wasi/src/preview2/poll.rs
index 8e2b5385f..4fba2a994 100644
--- a/crates/wasi/src/preview2/poll.rs
+++ b/crates/wasi/src/preview2/poll.rs
@@ -1,11 +1,11 @@
-use crate::preview2::{bindings::io::poll, Table, WasiView};
+use crate::preview2::{bindings::io::poll, WasiView};
 use anyhow::Result;
 use std::any::Any;
 use std::collections::HashMap;
 use std::future::Future;
 use std::pin::Pin;
 use std::task::{Context, Poll};
-use wasmtime::component::Resource;
+use wasmtime::component::{Resource, ResourceTable};
 
 pub type PollableFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
 pub type MakeFuture = for<'a> fn(&'a mut dyn Any) -> PollableFuture<'a>;
@@ -20,7 +20,7 @@ pub type ClosureFuture = Box<dyn Fn() -> PollableFuture<'static> + Send + Sync +
 pub struct Pollable {
     index: u32,
     make_future: MakeFuture,
-    remove_index_on_delete: Option<fn(&mut Table, u32) -> Result<()>>,
+    remove_index_on_delete: Option<fn(&mut ResourceTable, u32) -> Result<()>>,
 }
 
 #[async_trait::async_trait]
@@ -35,7 +35,7 @@ pub trait Subscribe: Send + Sync + 'static {
 /// resource is deleted. Otherwise the returned resource is considered a "child"
 /// of the given `resource` which means that the given resource cannot be
 /// deleted while the `pollable` is still alive.
-pub fn subscribe<T>(table: &mut Table, resource: Resource<T>) -> Result<Resource<Pollable>>
+pub fn subscribe<T>(table: &mut ResourceTable, resource: Resource<T>) -> Result<Resource<Pollable>>
 where
     T: Subscribe,
 {
diff --git a/crates/wasi/src/preview2/preview1.rs b/crates/wasi/src/preview2/preview1.rs
index 967bceb07..8a0c64bd7 100644
--- a/crates/wasi/src/preview2/preview1.rs
+++ b/crates/wasi/src/preview2/preview1.rs
@@ -8,7 +8,7 @@ use crate::preview2::bindings::{
     filesystem::{preopens, types as filesystem},
     io::{poll, streams},
 };
-use crate::preview2::{FsError, IsATTY, StreamError, StreamResult, TableError, WasiView};
+use crate::preview2::{FsError, IsATTY, StreamError, StreamResult, WasiView};
 use anyhow::{bail, Context};
 use std::borrow::Borrow;
 use std::collections::{BTreeMap, HashSet};
@@ -325,7 +325,7 @@ pub trait WasiPreview1View: WasiView {
 // `&mut self` receivers and so this struct lets us extend the lifetime of the `&mut self` borrow
 // of the [`WasiPreview1View`] to provide means to return mutably and immutably borrowed [`Descriptors`]
 // without having to rely on something like `Arc<Mutex<Descriptors>>`, while also being able to
-// call methods like [`TableFsExt::is_file`] and hiding complexity from preview1 method implementations.
+// call methods like [`Descriptor::is_file`] and hiding complexity from preview1 method implementations.
 struct Transaction<'a, T: WasiPreview1View + ?Sized> {
     view: &'a mut T,
     descriptors: Descriptors,
@@ -772,8 +772,8 @@ impl From<filesystem::ErrorCode> for types::Error {
     }
 }
 
-impl From<TableError> for types::Error {
-    fn from(err: TableError) -> Self {
+impl From<wasmtime::component::ResourceTableError> for types::Error {
+    fn from(err: wasmtime::component::ResourceTableError) -> Self {
         types::Error::trap(err.into())
     }
 }
diff --git a/crates/wasi/src/preview2/stream.rs b/crates/wasi/src/preview2/stream.rs
index ee13b6afa..bf2ce502b 100644
--- a/crates/wasi/src/preview2/stream.rs
+++ b/crates/wasi/src/preview2/stream.rs
@@ -1,6 +1,5 @@
 use crate::preview2::filesystem::FileInputStream;
 use crate::preview2::poll::Subscribe;
-use crate::preview2::TableError;
 use anyhow::Result;
 use bytes::Bytes;
 
@@ -73,8 +72,8 @@ impl std::error::Error for StreamError {
     }
 }
 
-impl From<TableError> for StreamError {
-    fn from(error: TableError) -> Self {
+impl From<wasmtime::component::ResourceTableError> for StreamError {
+    fn from(error: wasmtime::component::ResourceTableError) -> Self {
         Self::Trap(error.into())
     }
 }
diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs
index e2028a2be..364942b53 100644
--- a/crates/wasmtime/src/component/mod.rs
+++ b/crates/wasmtime/src/component/mod.rs
@@ -10,6 +10,7 @@ mod func;
 mod instance;
 mod linker;
 mod matching;
+mod resource_table;
 mod resources;
 mod storage;
 mod store;
@@ -21,6 +22,7 @@ pub use self::func::{
 };
 pub use self::instance::{ExportInstance, Exports, Instance, InstancePre};
 pub use self::linker::{Linker, LinkerInstance};
+pub use self::resource_table::{ResourceTable, ResourceTableError};
 pub use self::resources::{Resource, ResourceAny};
 pub use self::types::{ResourceType, Type};
 pub use self::values::{Enum, Flags, List, OptionVal, Record, ResultVal, Tuple, Val, Variant};
diff --git a/crates/wasi/src/preview2/table.rs b/crates/wasmtime/src/component/resource_table.rs
similarity index 77%
rename from crates/wasi/src/preview2/table.rs
rename to crates/wasmtime/src/component/resource_table.rs
index 9e60cd88d..fe5244d36 100644
--- a/crates/wasi/src/preview2/table.rs
+++ b/crates/wasmtime/src/component/resource_table.rs
@@ -1,28 +1,36 @@
+use super::Resource;
 use std::any::Any;
 use std::collections::{BTreeSet, HashMap};
-use wasmtime::component::Resource;
 
-#[derive(thiserror::Error, Debug)]
-pub enum TableError {
-    #[error("table has no free keys")]
+#[derive(Debug)]
+/// Errors returned by operations on `ResourceTable`
+pub enum ResourceTableError {
+    /// ResourceTable has no free keys
     Full,
-    #[error("value not present")]
+    /// Resource not present in table
     NotPresent,
-    #[error("value is of another type")]
+    /// Resource present in table, but with a different type
     WrongType,
-    #[error("entry still has children")]
+    /// Resource cannot be deleted because child resources exist in the table. Consult wit docs for
+    /// the particular resource to see which methods may return child resources.
     HasChildren,
 }
 
-/// The `Table` type is designed to map u32 handles to resources. The table is now part of the
-/// public interface to a `WasiCtx` - it is reference counted so that it can be shared beyond a
-/// `WasiCtx` with other WASI proposals (e.g. `wasi-crypto` and `wasi-nn`) to manage their
-/// resources. Elements in the `Table` are `Any` typed.
-///
-/// The `Table` type is intended to model how the Interface Types concept of Resources is shaping
-/// up. Right now it is just an approximation.
+impl std::fmt::Display for ResourceTableError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Full => write!(f, "resource table has no free keys"),
+            Self::NotPresent => write!(f, "resource not present"),
+            Self::WrongType => write!(f, "resource is of another type"),
+            Self::HasChildren => write!(f, "resource has children"),
+        }
+    }
+}
+impl std::error::Error for ResourceTableError {}
+
+/// The `ResourceTable` type maps a `Resource<T>` to its `T`.
 #[derive(Debug)]
-pub struct Table {
+pub struct ResourceTable {
     entries: Vec<Entry>,
     free_head: Option<usize>,
 }
@@ -86,10 +94,10 @@ impl TableEntry {
     }
 }
 
-impl Table {
+impl ResourceTable {
     /// Create an empty table
     pub fn new() -> Self {
-        Table {
+        ResourceTable {
             entries: Vec::new(),
             free_head: None,
         }
@@ -97,7 +105,7 @@ impl Table {
 
     /// Create an empty table with at least the specified capacity.
     pub fn with_capacity(capacity: usize) -> Self {
-        Table {
+        ResourceTable {
             entries: Vec::with_capacity(capacity),
             free_head: None,
         }
@@ -105,7 +113,7 @@ impl Table {
 
     /// Inserts a new value `T` into this table, returning a corresponding
     /// `Resource<T>` which can be used to refer to it after it was inserted.
-    pub fn push<T>(&mut self, entry: T) -> Result<Resource<T>, TableError>
+    pub fn push<T>(&mut self, entry: T) -> Result<Resource<T>, ResourceTableError>
     where
         T: Send + Sync + 'static,
     {
@@ -146,7 +154,7 @@ impl Table {
 
     /// Push a new entry into the table, returning its handle. This will prefer to use free entries
     /// if they exist, falling back on pushing new entries onto the end of the table.
-    fn push_(&mut self, e: TableEntry) -> Result<u32, TableError> {
+    fn push_(&mut self, e: TableEntry) -> Result<u32, ResourceTableError> {
         if let Some(free) = self.pop_free_list() {
             self.entries[free] = Entry::Occupied { entry: e };
             Ok(free as u32)
@@ -155,32 +163,33 @@ impl Table {
                 .entries
                 .len()
                 .try_into()
-                .map_err(|_| TableError::Full)?;
+                .map_err(|_| ResourceTableError::Full)?;
             self.entries.push(Entry::Occupied { entry: e });
             Ok(ix)
         }
     }
 
-    fn occupied(&self, key: u32) -> Result<&TableEntry, TableError> {
+    fn occupied(&self, key: u32) -> Result<&TableEntry, ResourceTableError> {
         self.entries
             .get(key as usize)
             .and_then(Entry::occupied)
-            .ok_or(TableError::NotPresent)
+            .ok_or(ResourceTableError::NotPresent)
     }
 
-    fn occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, TableError> {
+    fn occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, ResourceTableError> {
         self.entries
             .get_mut(key as usize)
             .and_then(Entry::occupied_mut)
-            .ok_or(TableError::NotPresent)
+            .ok_or(ResourceTableError::NotPresent)
     }
 
     /// Insert a resource at the next available index, and track that it has a
     /// parent resource.
     ///
     /// The parent must exist to create a child. All children resources must
-    /// be destroyed before a parent can be destroyed - otherwise [`Table::delete`]
-    /// will fail with [`TableError::HasChildren`].
+    /// be destroyed before a parent can be destroyed - otherwise
+    /// [`ResourceTable::delete`] will fail with
+    /// [`ResourceTableError::HasChildren`].
     ///
     /// Parent-child relationships are tracked inside the table to ensure that
     /// a parent resource is not deleted while it has live children. This
@@ -191,13 +200,14 @@ impl Table {
     /// possibility for deadlocks.
     ///
     /// Parent-child relationships may not be modified once created. There
-    /// is no way to observe these relationships through the [`Table`] methods
-    /// except for erroring on deletion, or the [`std::fmt::Debug`] impl.
+    /// is no way to observe these relationships through the [`ResourceTable`]
+    /// methods except for erroring on deletion, or the [`std::fmt::Debug`]
+    /// impl.
     pub fn push_child<T, U>(
         &mut self,
         entry: T,
         parent: &Resource<U>,
-    ) -> Result<Resource<T>, TableError>
+    ) -> Result<Resource<T>, ResourceTableError>
     where
         T: Send + Sync + 'static,
         U: 'static,
@@ -213,33 +223,36 @@ impl Table {
     /// index.
     ///
     /// Multiple shared references can be borrowed at any given time.
-    pub fn get<T: Any + Sized>(&self, key: &Resource<T>) -> Result<&T, TableError> {
+    pub fn get<T: Any + Sized>(&self, key: &Resource<T>) -> Result<&T, ResourceTableError> {
         self.get_(key.rep())?
             .downcast_ref()
-            .ok_or(TableError::WrongType)
+            .ok_or(ResourceTableError::WrongType)
     }
 
-    fn get_(&self, key: u32) -> Result<&dyn Any, TableError> {
+    fn get_(&self, key: u32) -> Result<&dyn Any, ResourceTableError> {
         let r = self.occupied(key)?;
         Ok(&*r.entry)
     }
 
     /// Get an mutable reference to a resource of a given type at a given
     /// index.
-    pub fn get_mut<T: Any + Sized>(&mut self, key: &Resource<T>) -> Result<&mut T, TableError> {
+    pub fn get_mut<T: Any + Sized>(
+        &mut self,
+        key: &Resource<T>,
+    ) -> Result<&mut T, ResourceTableError> {
         self.get_any_mut(key.rep())?
             .downcast_mut()
-            .ok_or(TableError::WrongType)
+            .ok_or(ResourceTableError::WrongType)
     }
 
     /// Returns the raw `Any` at the `key` index provided.
-    pub fn get_any_mut(&mut self, key: u32) -> Result<&mut dyn Any, TableError> {
+    pub fn get_any_mut(&mut self, key: u32) -> Result<&mut dyn Any, ResourceTableError> {
         let r = self.occupied_mut(key)?;
         Ok(&mut *r.entry)
     }
 
     /// Same as `delete`, but typed
-    pub fn delete<T>(&mut self, resource: Resource<T>) -> Result<T, TableError>
+    pub fn delete<T>(&mut self, resource: Resource<T>) -> Result<T, ResourceTableError>
     where
         T: Any,
     {
@@ -247,13 +260,13 @@ impl Table {
         let entry = self.delete_entry(resource.rep())?;
         match entry.entry.downcast() {
             Ok(t) => Ok(*t),
-            Err(_e) => Err(TableError::WrongType),
+            Err(_e) => Err(ResourceTableError::WrongType),
         }
     }
 
-    fn delete_entry(&mut self, key: u32) -> Result<TableEntry, TableError> {
+    fn delete_entry(&mut self, key: u32) -> Result<TableEntry, ResourceTableError> {
         if !self.occupied(key)?.children.is_empty() {
-            return Err(TableError::HasChildren);
+            return Err(ResourceTableError::HasChildren);
         }
         let e = self.free_entry(key as usize);
         if let Some(parent) = e.parent {
@@ -269,11 +282,11 @@ impl Table {
 
     /// Zip the values of the map with mutable references to table entries corresponding to each
     /// key. As the keys in the [HashMap] are unique, this iterator can give mutable references
-    /// with the same lifetime as the mutable reference to the [Table].
+    /// with the same lifetime as the mutable reference to the [ResourceTable].
     pub fn iter_entries<'a, T>(
         &'a mut self,
         map: HashMap<u32, T>,
-    ) -> impl Iterator<Item = (Result<&'a mut dyn Any, TableError>, T)> {
+    ) -> impl Iterator<Item = (Result<&'a mut dyn Any, ResourceTableError>, T)> {
         map.into_iter().map(move |(k, v)| {
             let item = self
                 .occupied_mut(k)
@@ -288,7 +301,7 @@ impl Table {
     pub fn iter_children<T>(
         &self,
         parent: &Resource<T>,
-    ) -> Result<impl Iterator<Item = &(dyn Any + Send + Sync)>, TableError>
+    ) -> Result<impl Iterator<Item = &(dyn Any + Send + Sync)>, ResourceTableError>
     where
         T: 'static,
     {
@@ -300,15 +313,15 @@ impl Table {
     }
 }
 
-impl Default for Table {
+impl Default for ResourceTable {
     fn default() -> Self {
-        Table::new()
+        ResourceTable::new()
     }
 }
 
 #[test]
 pub fn test_free_list() {
-    let mut table = Table::new();
+    let mut table = ResourceTable::new();
 
     let x = table.push(()).unwrap();
     assert_eq!(x.rep(), 0);
diff --git a/src/commands/run.rs b/src/commands/run.rs
index 5f43c5a54..b5b465535 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -778,6 +778,12 @@ impl RunCommand {
         if let Some(enable) = self.run.common.wasi.allow_ip_name_lookup {
             builder.allow_ip_name_lookup(enable);
         }
+        if let Some(enable) = self.run.common.wasi.tcp {
+            builder.allow_tcp(enable);
+        }
+        if let Some(enable) = self.run.common.wasi.udp {
+            builder.allow_tcp(enable);
+        }
 
         store.data_mut().preview2_ctx = Some(Arc::new(builder.build()));
         Ok(())
@@ -791,7 +797,7 @@ struct Host {
 
     // Resource table for preview2 if the `preview2_ctx` is in use, otherwise
     // "just" an empty table.
-    preview2_table: Arc<preview2::Table>,
+    preview2_table: Arc<wasmtime::component::ResourceTable>,
 
     // State necessary for the preview1 implementation of WASI backed by the
     // preview2 host implementation. Only used with the `--preview2` flag right
@@ -810,11 +816,11 @@ struct Host {
 }
 
 impl preview2::WasiView for Host {
-    fn table(&self) -> &preview2::Table {
+    fn table(&self) -> &wasmtime::component::ResourceTable {
         &self.preview2_table
     }
 
-    fn table_mut(&mut self) -> &mut preview2::Table {
+    fn table_mut(&mut self) -> &mut wasmtime::component::ResourceTable {
         Arc::get_mut(&mut self.preview2_table).expect("preview2 is not compatible with threads")
     }
 
@@ -845,7 +851,7 @@ impl wasmtime_wasi_http::types::WasiHttpView for Host {
         Arc::get_mut(ctx).expect("preview2 is not compatible with threads")
     }
 
-    fn table(&mut self) -> &mut preview2::Table {
+    fn table(&mut self) -> &mut wasmtime::component::ResourceTable {
         Arc::get_mut(&mut self.preview2_table).expect("preview2 is not compatible with threads")
     }
 }
diff --git a/src/commands/serve.rs b/src/commands/serve.rs
index 629ec8b57..5601b3ddd 100644
--- a/src/commands/serve.rs
+++ b/src/commands/serve.rs
@@ -11,9 +11,7 @@ use std::{
 };
 use wasmtime::component::{InstancePre, Linker};
 use wasmtime::{Engine, Store, StoreLimits};
-use wasmtime_wasi::preview2::{
-    self, StreamError, StreamResult, Table, WasiCtx, WasiCtxBuilder, WasiView,
-};
+use wasmtime_wasi::preview2::{self, StreamError, StreamResult, WasiCtx, WasiCtxBuilder, WasiView};
 use wasmtime_wasi_http::io::TokioIo;
 use wasmtime_wasi_http::{
     bindings::http::types as http_types, body::HyperOutgoingBody, hyper_response_error,
@@ -24,7 +22,7 @@ use wasmtime_wasi_http::{
 use wasmtime_wasi_nn::WasiNnCtx;
 
 struct Host {
-    table: Table,
+    table: wasmtime::component::ResourceTable,
     ctx: WasiCtx,
     http: WasiHttpCtx,
 
@@ -35,11 +33,11 @@ struct Host {
 }
 
 impl WasiView for Host {
-    fn table(&self) -> &Table {
+    fn table(&self) -> &wasmtime::component::ResourceTable {
         &self.table
     }
 
-    fn table_mut(&mut self) -> &mut Table {
+    fn table_mut(&mut self) -> &mut wasmtime::component::ResourceTable {
         &mut self.table
     }
 
@@ -53,7 +51,7 @@ impl WasiView for Host {
 }
 
 impl WasiHttpView for Host {
-    fn table(&mut self) -> &mut Table {
+    fn table(&mut self) -> &mut wasmtime::component::ResourceTable {
         &mut self.table
     }
 
@@ -157,7 +155,7 @@ impl ServeCommand {
         });
 
         let mut host = Host {
-            table: Table::new(),
+            table: wasmtime::component::ResourceTable::new(),
             ctx: builder.build(),
             http: WasiHttpCtx,
 


Test patch:
diff --git a/cranelift/filetests/filetests/egraph/arithmetic.clif b/cranelift/filetests/filetests/egraph/arithmetic.clif
index 542fde629..1abdeae1e 100644
--- a/cranelift/filetests/filetests/egraph/arithmetic.clif
+++ b/cranelift/filetests/filetests/egraph/arithmetic.clif
@@ -20,6 +20,42 @@ block0(v0: i32):
     ; check: return v0
 }
 
+function %add_ineg(i32, i32) -> i32 {
+block0(v0: i32, v1: i32):
+    v2 = ineg v1
+    v3 = iadd v0, v2
+    return v3
+    ; check: v4 = isub v0, v1
+    ; check: return v4
+}
+
+function %ineg_add(i32, i32) -> i32 {
+block0(v0: i32, v1: i32):
+    v2 = ineg v0
+    v3 = iadd v2, v1
+    return v3
+    ; check: v4 = isub v1, v0
+    ; check: return v4
+}
+
+function %sub_ineg(i32, i32) -> i32 {
+block0(v0: i32, v1: i32):
+    v2 = ineg v1
+    v3 = isub v0, v2
+    return v3
+    ; check: v4 = iadd v0, v1
+    ; check: return v4
+}
+
+function %sub_then_ineg(i32, i32) -> i32 {
+block0(v0: i32, v1: i32):
+    v2 = isub v0, v1
+    v3 = ineg v2
+    return v3
+    ; check: v4 = isub v1, v0
+    ; check: return v4
+}
+
 function %imul_ineg_cancel(i32, i32) -> i32 {
 block0(v0: i32, v1: i32):
     v2 = ineg v0
@@ -125,8 +161,8 @@ block0(v0: i32):
     return v3
 }
 
-; check: v4 = ineg v0
-; check: return v4
+; check: v5 = ineg v0
+; check: return v5
 
 function %ineg_not_minus_neg_1(i32) -> i32 {
 block0(v0: i32):
@@ -136,8 +172,8 @@ block0(v0: i32):
     return v3
 }
 
-; check: v4 = ineg v0
-; check: return v4
+; check: v6 = ineg v0
+; check: return v6
 
 function %ineg_not_sub_one(i32) -> i32 {
 block0(v0: i32):
@@ -172,7 +208,17 @@ block0(v0: i32):
 ; check: v6 = ineg v0
 ; check: return v6
 
+function %byte_sub_smax_twice(i8) -> i8 {
+block0(v0: i8):
+    v1 = iconst.i8 127
+    v2 = isub v0, v1
+    v3 = isub v2, v1
+    return v3
+}
 
+; check: v6 = iconst.i8 2
+; check: v7 = iadd v0, v6  ; v6 = 2
+; check: return v7
 
 function %double_fneg(f32) -> f32 {
 block0(v1: f32):
diff --git a/cranelift/filetests/filetests/egraph/cprop.clif b/cranelift/filetests/filetests/egraph/cprop.clif
index 6de9aa695..6c2218a49 100644
--- a/cranelift/filetests/filetests/egraph/cprop.clif
+++ b/cranelift/filetests/filetests/egraph/cprop.clif
@@ -260,3 +260,25 @@ block0:
 
 ; check: v3 = iconst.i32 0xfff6
 ; nextln: return v3
+
+function %sub_u8max(i8) -> i8 {
+block0(v0: i8):
+    v1 = iconst.i8 255
+    v2 = isub v0, v1
+    return v2
+}
+
+; check: v3 = iconst.i8 1
+; nextln: v4 = iadd v0, v3  ; v3 = 1
+; nextln: return v4
+
+function %sub_u32max(i32) -> i32 {
+block0(v0: i32):
+    v1 = iconst.i32 0xFFFF_FFFF
+    v2 = isub v0, v1
+    return v2
+}
+
+; check: v3 = iconst.i32 1
+; nextln: v4 = iadd v0, v3  ; v3 = 1
+; nextln: return v4
diff --git a/cranelift/filetests/filetests/egraph/shifts.clif b/cranelift/filetests/filetests/egraph/shifts.clif
index a9b321b20..c0df30564 100644
--- a/cranelift/filetests/filetests/egraph/shifts.clif
+++ b/cranelift/filetests/filetests/egraph/shifts.clif
@@ -701,8 +701,8 @@ block0(v0: i8):
 }
 
 ; check: v2 = iconst.i8 1
-; check: v21 = rotr v0, v2  ; v2 = 1
-; check: return v21
+; check: v16 = rotr v0, v2  ; v2 = 1
+; check: return v16
 
 function %rotl_rotr_add(i8, i8, i8) -> i8 {
 block0(v0: i8, v1: i8, v2: i8):
@@ -725,8 +725,8 @@ block0(v0: i8):
 }
 
 ; check: v2 = iconst.i8 1
-; check: v21 = rotl v0, v2  ; v2 = 1
-; check: return v21
+; check: v16 = rotl v0, v2  ; v2 = 1
+; check: return v16
 
 function %rotl_rotr_add(i8, i8, i8) -> i8 {
 block0(v0: i8, v1: i8, v2: i8):
diff --git a/crates/test-programs/src/bin/cli_no_tcp.rs b/crates/test-programs/src/bin/cli_no_tcp.rs
new file mode 100644
index 000000000..58ca3de89
--- /dev/null
+++ b/crates/test-programs/src/bin/cli_no_tcp.rs
@@ -0,0 +1,28 @@
+//! This test assumes that it will be run without tcp support enabled
+use test_programs::wasi::sockets::{
+    network::IpAddress,
+    tcp::{ErrorCode, IpAddressFamily, IpSocketAddress, Network, TcpSocket},
+};
+
+fn main() {
+    let net = Network::default();
+    let family = IpAddressFamily::Ipv4;
+    let remote1 = IpSocketAddress::new(IpAddress::new_loopback(family), 4321);
+    let sock = TcpSocket::new(family).unwrap();
+
+    let bind = sock.blocking_bind(&net, remote1);
+    eprintln!("Result of binding: {bind:?}");
+    assert!(matches!(bind, Err(ErrorCode::AccessDenied)));
+
+    let listen = sock.blocking_listen();
+    eprintln!("Result of listen: {listen:?}");
+    assert!(matches!(listen, Err(ErrorCode::AccessDenied)));
+
+    let connect = sock.blocking_connect(&net, remote1);
+    eprintln!("Result of connect: {connect:?}");
+    assert!(matches!(connect, Err(ErrorCode::AccessDenied)));
+
+    let accept = sock.blocking_accept();
+    eprintln!("Result of accept: {accept:?}");
+    assert!(matches!(accept, Err(ErrorCode::AccessDenied)));
+}
diff --git a/crates/wasi-http/tests/all/main.rs b/crates/wasi-http/tests/all/main.rs
index 002e28b79..f078a30de 100644
--- a/crates/wasi-http/tests/all/main.rs
+++ b/crates/wasi-http/tests/all/main.rs
@@ -8,12 +8,10 @@ use sha2::{Digest, Sha256};
 use std::{collections::HashMap, iter, net::Ipv4Addr, str, sync::Arc};
 use tokio::task;
 use wasmtime::{
-    component::{Component, Linker, Resource},
+    component::{Component, Linker, Resource, ResourceTable},
     Config, Engine, Store,
 };
-use wasmtime_wasi::preview2::{
-    self, pipe::MemoryOutputPipe, Table, WasiCtx, WasiCtxBuilder, WasiView,
-};
+use wasmtime_wasi::preview2::{self, pipe::MemoryOutputPipe, WasiCtx, WasiCtxBuilder, WasiView};
 use wasmtime_wasi_http::{
     bindings::http::types::ErrorCode,
     body::HyperIncomingBody,
@@ -31,7 +29,7 @@ type RequestSender = Arc<
 >;
 
 struct Ctx {
-    table: Table,
+    table: ResourceTable,
     wasi: WasiCtx,
     http: WasiHttpCtx,
     stdout: MemoryOutputPipe,
@@ -40,10 +38,10 @@ struct Ctx {
 }
 
 impl WasiView for Ctx {
-    fn table(&self) -> &Table {
+    fn table(&self) -> &ResourceTable {
         &self.table
     }
-    fn table_mut(&mut self) -> &mut Table {
+    fn table_mut(&mut self) -> &mut ResourceTable {
         &mut self.table
     }
     fn ctx(&self) -> &WasiCtx {
@@ -59,7 +57,7 @@ impl WasiHttpView for Ctx {
         &mut self.http
     }
 
-    fn table(&mut self) -> &mut Table {
+    fn table(&mut self) -> &mut ResourceTable {
         &mut self.table
     }
 
@@ -89,7 +87,7 @@ fn store(engine: &Engine, server: &Server) -> Store<Ctx> {
     builder.stderr(stderr.clone());
     builder.env("HTTP_SERVER", server.addr().to_string());
     let ctx = Ctx {
-        table: Table::new(),
+        table: ResourceTable::new(),
         wasi: builder.build(),
         http: WasiHttpCtx {},
         stderr,
@@ -132,7 +130,7 @@ async fn run_wasi_http(
 ) -> anyhow::Result<Result<hyper::Response<Collected<Bytes>>, ErrorCode>> {
     let stdout = MemoryOutputPipe::new(4096);
     let stderr = MemoryOutputPipe::new(4096);
-    let table = Table::new();
+    let table = ResourceTable::new();
 
     let mut config = Config::new();
     config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable);
diff --git a/crates/wasi/tests/all/api.rs b/crates/wasi/tests/all/api.rs
index 4abf8a119..413713aa1 100644
--- a/crates/wasi/tests/all/api.rs
+++ b/crates/wasi/tests/all/api.rs
@@ -4,26 +4,25 @@ use cap_std::fs::Dir;
 use std::io::Write;
 use std::sync::Mutex;
 use std::time::Duration;
-use wasmtime::component::{Component, Linker};
+use wasmtime::component::{Component, Linker, ResourceTable};
 use wasmtime::{Config, Engine, Store};
 use wasmtime_wasi::preview2::bindings::wasi::clocks::wall_clock;
 use wasmtime_wasi::preview2::bindings::wasi::filesystem::types as filesystem;
 use wasmtime_wasi::preview2::command::{add_to_linker, Command};
 use wasmtime_wasi::preview2::{
-    self, DirPerms, FilePerms, HostMonotonicClock, HostWallClock, Table, WasiCtx, WasiCtxBuilder,
-    WasiView,
+    self, DirPerms, FilePerms, HostMonotonicClock, HostWallClock, WasiCtx, WasiCtxBuilder, WasiView,
 };
 
 struct CommandCtx {
-    table: Table,
+    table: ResourceTable,
     wasi: WasiCtx,
 }
 
 impl WasiView for CommandCtx {
-    fn table(&self) -> &Table {
+    fn table(&self) -> &ResourceTable {
         &self.table
     }
-    fn table_mut(&mut self) -> &mut Table {
+    fn table_mut(&mut self) -> &mut ResourceTable {
         &mut self.table
     }
     fn ctx(&self) -> &WasiCtx {
@@ -82,7 +81,7 @@ async fn api_time() -> Result<()> {
         }
     }
 
-    let table = Table::new();
+    let table = ResourceTable::new();
     let wasi = WasiCtxBuilder::new()
         .monotonic_clock(FakeMonotonicClock { now: Mutex::new(0) })
         .wall_clock(FakeWallClock)
@@ -104,7 +103,7 @@ async fn api_read_only() -> Result<()> {
     std::fs::File::create(dir.path().join("bar.txt"))?.write_all(b"And stood awhile in thought")?;
     std::fs::create_dir(dir.path().join("sub"))?;
 
-    let table = Table::new();
+    let table = ResourceTable::new();
     let open_dir = Dir::open_ambient_dir(dir.path(), ambient_authority())?;
     let wasi = WasiCtxBuilder::new()
         .preopened_dir(open_dir, DirPerms::READ, FilePerms::READ, "/")
@@ -155,7 +154,7 @@ wasmtime::component::bindgen!({
 
 #[test_log::test(tokio::test)]
 async fn api_reactor() -> Result<()> {
-    let table = Table::new();
+    let table = ResourceTable::new();
     let wasi = WasiCtxBuilder::new().env("GOOD_DOG", "gussie").build();
 
     let mut config = Config::new();
diff --git a/crates/wasi/tests/all/async_.rs b/crates/wasi/tests/all/async_.rs
index be7359e92..49ba0c52a 100644
--- a/crates/wasi/tests/all/async_.rs
+++ b/crates/wasi/tests/all/async_.rs
@@ -351,6 +351,6 @@ async fn preview2_stream_pollable_traps() {
         .unwrap_err();
     assert_eq!(
         format!("{}", e.source().expect("trap source")),
-        "entry still has children"
+        "resource has children"
     )
 }
diff --git a/crates/wasi/tests/all/main.rs b/crates/wasi/tests/all/main.rs
index 1af5503f0..03cf8031b 100644
--- a/crates/wasi/tests/all/main.rs
+++ b/crates/wasi/tests/all/main.rs
@@ -2,17 +2,17 @@ use anyhow::Result;
 use cap_std::ambient_authority;
 use tempfile::TempDir;
 use wasmtime::{
-    component::{Component, Linker},
+    component::{Component, Linker, ResourceTable},
     Config, Engine, Store,
 };
 use wasmtime_wasi::preview2::{
     pipe::MemoryOutputPipe,
     preview1::{WasiPreview1Adapter, WasiPreview1View},
-    DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView,
+    DirPerms, FilePerms, WasiCtx, WasiCtxBuilder, WasiView,
 };
 
 struct Ctx {
-    table: Table,
+    table: ResourceTable,
     wasi: WasiCtx,
     stdout: MemoryOutputPipe,
     stderr: MemoryOutputPipe,
@@ -20,10 +20,10 @@ struct Ctx {
 }
 
 impl WasiView for Ctx {
-    fn table(&self) -> &Table {
+    fn table(&self) -> &ResourceTable {
         &self.table
     }
-    fn table_mut(&mut self) -> &mut Table {
+    fn table_mut(&mut self) -> &mut ResourceTable {
         &mut self.table
     }
     fn ctx(&self) -> &WasiCtx {
@@ -75,7 +75,7 @@ fn store(engine: &Engine, name: &str, inherit_stdio: bool) -> Result<(Store<Ctx>
     }
 
     let ctx = Ctx {
-        table: Table::new(),
+        table: ResourceTable::new(),
         wasi: builder.build(),
         stderr,
         stdout,
diff --git a/crates/wasi/tests/all/sync.rs b/crates/wasi/tests/all/sync.rs
index 61d4cf69b..c005ac35e 100644
--- a/crates/wasi/tests/all/sync.rs
+++ b/crates/wasi/tests/all/sync.rs
@@ -290,6 +290,6 @@ fn preview2_stream_pollable_traps() {
     let e = run(PREVIEW2_STREAM_POLLABLE_TRAPS_COMPONENT, false).unwrap_err();
     assert_eq!(
         format!("{}", e.source().expect("trap source")),
-        "entry still has children"
+        "resource has children"
     )
 }
diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs
index 03783844b..7fb807eb1 100644
--- a/tests/all/cli_tests.rs
+++ b/tests/all/cli_tests.rs
@@ -1498,4 +1498,20 @@ mod test_programs {
         ])?;
         Ok(())
     }
+
+    #[test]
+    fn cli_no_tcp() -> Result<()> {
+        let output = super::run_wasmtime_for_output(
+            &[
+                "-Wcomponent-model",
+                // Turn on network but turn off TCP
+                "-Sinherit-network,tcp=no",
+                CLI_NO_TCP_COMPONENT,
+            ],
+            None,
+        )?;
+        println!("{}", String::from_utf8_lossy(&output.stderr));
+        assert!(output.status.success());
+        Ok(())
+    }
 }