Repository: bytecodealliance/wasmtime
Pull request #7314 (2023-10-20 19:40:32)

Pull in the upstream changes to wasi-http
Pull in the recent doc changes to wasi-http, updating both wasmtime-wasi and wasmtime-wasi-http.
<!--
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/egraph.rs b/cranelift/codegen/src/egraph.rs
index 411300ed4..b54c1e7b6 100644
--- a/cranelift/codegen/src/egraph.rs
+++ b/cranelift/codegen/src/egraph.rs
@@ -670,7 +670,7 @@ pub(crate) struct Stats {
     pub(crate) elaborate_visit_node: u64,
     pub(crate) elaborate_memoize_hit: u64,
     pub(crate) elaborate_memoize_miss: u64,
-    pub(crate) elaborate_memoize_miss_remat: u64,
+    pub(crate) elaborate_remat: u64,
     pub(crate) elaborate_licm_hoist: u64,
     pub(crate) elaborate_func: u64,
     pub(crate) elaborate_func_pre_insts: u64,
diff --git a/cranelift/codegen/src/egraph/elaborate.rs b/cranelift/codegen/src/egraph/elaborate.rs
index 5815352a5..26c7378b2 100644
--- a/cranelift/codegen/src/egraph/elaborate.rs
+++ b/cranelift/codegen/src/egraph/elaborate.rs
@@ -5,7 +5,8 @@ use super::cost::{pure_op_cost, Cost};
 use super::domtree::DomTreeWithChildren;
 use super::Stats;
 use crate::dominator_tree::DominatorTree;
-use crate::fx::FxHashSet;
+use crate::fx::{FxHashMap, FxHashSet};
+use crate::hash_map::Entry as HashEntry;
 use crate::ir::{Block, Function, Inst, Value, ValueDef};
 use crate::loop_analysis::{Loop, LoopAnalysis};
 use crate::scoped_hash_map::ScopedHashMap;
@@ -56,6 +57,8 @@ pub(crate) struct Elaborator<'a> {
     elab_result_stack: Vec<ElaboratedValue>,
     /// Explicitly-unrolled block elaboration stack.
     block_stack: Vec<BlockStackEntry>,
+    /// Copies of values that have been rematerialized.
+    remat_copies: FxHashMap<(Block, Value), Value>,
     /// Stats for various events during egraph processing, to help
     /// with optimization of this infrastructure.
     stats: &'a mut Stats,
@@ -95,7 +98,6 @@ enum ElabStackEntry {
         inst: Inst,
         result_idx: usize,
         num_args: usize,
-        remat: bool,
         before: Inst,
     },
 }
@@ -134,6 +136,7 @@ impl<'a> Elaborator<'a> {
             elab_stack: vec![],
             elab_result_stack: vec![],
             block_stack: vec![],
+            remat_copies: FxHashMap::default(),
             stats,
         }
     }
@@ -256,6 +259,45 @@ impl<'a> Elaborator<'a> {
         self.elab_result_stack.pop().unwrap()
     }
 
+    /// Possibly rematerialize the instruction producing the value in
+    /// `arg` and rewrite `arg` to refer to it, if needed. Returns
+    /// `true` if a rewrite occurred.
+    fn maybe_remat_arg(
+        remat_values: &FxHashSet<Value>,
+        func: &mut Function,
+        remat_copies: &mut FxHashMap<(Block, Value), Value>,
+        insert_block: Block,
+        before: Inst,
+        arg: &mut ElaboratedValue,
+        stats: &mut Stats,
+    ) -> bool {
+        // TODO (#7313): we may want to consider recursive
+        // rematerialization as well. We could process the arguments of
+        // the rematerialized instruction up to a certain depth. This
+        // would affect, e.g., adds-with-one-constant-arg, which are
+        // currently rematerialized. Right now we don't do this, to
+        // avoid the need for another fixpoint loop here.
+        if arg.in_block != insert_block && remat_values.contains(&arg.value) {
+            let new_value = match remat_copies.entry((insert_block, arg.value)) {
+                HashEntry::Occupied(o) => *o.get(),
+                HashEntry::Vacant(v) => {
+                    let inst = func.dfg.value_def(arg.value).inst().unwrap();
+                    debug_assert_eq!(func.dfg.inst_results(inst).len(), 1);
+                    let new_inst = func.dfg.clone_inst(inst);
+                    func.layout.insert_inst(new_inst, before);
+                    let new_result = func.dfg.inst_results(new_inst)[0];
+                    *v.insert(new_result)
+                }
+            };
+            trace!("rematerialized {} as {}", arg.value, new_value);
+            arg.value = new_value;
+            stats.elaborate_remat += 1;
+            true
+        } else {
+            false
+        }
+    }
+
     fn process_elab_stack(&mut self) {
         while let Some(entry) = self.elab_stack.pop() {
             match entry {
@@ -278,39 +320,17 @@ impl<'a> Elaborator<'a> {
                     // eclass.
                     trace!("looking up best value for {}", value);
                     let (_, best_value) = self.value_to_best_value[value];
-                    debug_assert_ne!(best_value, Value::reserved_value());
                     trace!("elaborate: value {} -> best {}", value, best_value);
+                    debug_assert_ne!(best_value, Value::reserved_value());
+
+                    if let Some(elab_val) = self.value_to_elaborated_value.get(&canonical_value) {
+                        // Value is available; use it.
+                        trace!("elaborate: value {} -> {:?}", value, elab_val);
+                        self.stats.elaborate_memoize_hit += 1;
+                        self.elab_result_stack.push(*elab_val);
+                        continue;
+                    }
 
-                    let remat = if let Some(elab_val) =
-                        self.value_to_elaborated_value.get(&canonical_value)
-                    {
-                        // Value is available. Look at the defined
-                        // block, and determine whether this node kind
-                        // allows rematerialization if the value comes
-                        // from another block. If so, ignore the hit
-                        // and recompute below.
-                        let remat = elab_val.in_block != self.cur_block
-                            && self.remat_values.contains(&best_value);
-                        if !remat {
-                            trace!("elaborate: value {} -> {:?}", value, elab_val);
-                            self.stats.elaborate_memoize_hit += 1;
-                            self.elab_result_stack.push(*elab_val);
-                            continue;
-                        }
-                        trace!("elaborate: value {} -> remat", canonical_value);
-                        self.stats.elaborate_memoize_miss_remat += 1;
-                        // The op is pure at this point, so it is always valid to
-                        // remove from this map.
-                        self.value_to_elaborated_value.remove(&canonical_value);
-                        true
-                    } else {
-                        // Value not available; but still look up
-                        // whether it's been flagged for remat because
-                        // this affects placement.
-                        let remat = self.remat_values.contains(&best_value);
-                        trace!(" -> not present in map; remat = {}", remat);
-                        remat
-                    };
                     self.stats.elaborate_memoize_miss += 1;
 
                     // Now resolve the value to its definition to see
@@ -358,7 +378,6 @@ impl<'a> Elaborator<'a> {
                         inst,
                         result_idx,
                         num_args,
-                        remat,
                         before,
                     });
 
@@ -375,15 +394,13 @@ impl<'a> Elaborator<'a> {
                     inst,
                     result_idx,
                     num_args,
-                    remat,
                     before,
                 } => {
                     trace!(
-                        "PendingInst: {} result {} args {} remat {} before {}",
+                        "PendingInst: {} result {} args {} before {}",
                         inst,
                         result_idx,
                         num_args,
-                        remat,
                         before
                     );
 
@@ -391,7 +408,7 @@ impl<'a> Elaborator<'a> {
                     // point. Grab them and drain them out, removing
                     // them.
                     let arg_idx = self.elab_result_stack.len() - num_args;
-                    let arg_values = &self.elab_result_stack[arg_idx..];
+                    let arg_values = &mut self.elab_result_stack[arg_idx..];
 
                     // Compute max loop depth.
                     //
@@ -437,16 +454,15 @@ impl<'a> Elaborator<'a> {
 
                     // We know that this is a pure inst, because
                     // non-pure roots have already been placed in the
-                    // value-to-elab'd-value map and are never subject
-                    // to remat, so they will not reach this stage of
-                    // processing.
+                    // value-to-elab'd-value map, so they will not
+                    // reach this stage of processing.
                     //
                     // We now must determine the location at which we
                     // place the instruction. This is the current
                     // block *unless* we hoist above a loop when all
                     // args are loop-invariant (and this op is pure).
                     let (scope_depth, before, insert_block) =
-                        if loop_hoist_level == self.loop_stack.len() || remat {
+                        if loop_hoist_level == self.loop_stack.len() {
                             // Depends on some value at the current
                             // loop depth, or remat forces it here:
                             // place it at the current location.
@@ -479,16 +495,39 @@ impl<'a> Elaborator<'a> {
                         insert_block
                     );
 
-                    //  Now we need to place `inst` at the computed
-                    //  location (just before `before`). Note that
-                    //  `inst` may already have been placed somewhere
-                    //  else, because a pure node may be elaborated at
-                    //  more than one place. In this case, we need to
-                    //  duplicate the instruction (and return the
-                    //  `Value`s for that duplicated instance
-                    //  instead).
+                    // Now that we have the location for the
+                    // instruction, check if any of its args are remat
+                    // values. If so, and if we don't have a copy of
+                    // the rematerializing instruction for this block
+                    // yet, create one.
+                    let mut remat_arg = false;
+                    for arg_value in arg_values.iter_mut() {
+                        if Self::maybe_remat_arg(
+                            &self.remat_values,
+                            &mut self.func,
+                            &mut self.remat_copies,
+                            insert_block,
+                            before,
+                            arg_value,
+                            &mut self.stats,
+                        ) {
+                            remat_arg = true;
+                        }
+                    }
+
+                    // Now we need to place `inst` at the computed
+                    // location (just before `before`). Note that
+                    // `inst` may already have been placed somewhere
+                    // else, because a pure node may be elaborated at
+                    // more than one place. In this case, we need to
+                    // duplicate the instruction (and return the
+                    // `Value`s for that duplicated instance instead).
+                    //
+                    // Also clone if we rematerialized, because we
+                    // don't want to rewrite the args in the original
+                    // copy.
                     trace!("need inst {} before {}", inst, before);
-                    let inst = if self.func.layout.inst_block(inst).is_some() {
+                    let inst = if self.func.layout.inst_block(inst).is_some() || remat_arg {
                         // Clone the inst!
                         let new_inst = self.func.dfg.clone_inst(inst);
                         trace!(
@@ -605,7 +644,16 @@ impl<'a> Elaborator<'a> {
                 // Elaborate the arg, placing any newly-inserted insts
                 // before `before`. Get the updated value, which may
                 // be different than the original.
-                let new_arg = self.elaborate_eclass_use(*arg, before);
+                let mut new_arg = self.elaborate_eclass_use(*arg, before);
+                Self::maybe_remat_arg(
+                    &self.remat_values,
+                    &mut self.func,
+                    &mut self.remat_copies,
+                    block,
+                    inst,
+                    &mut new_arg,
+                    &mut self.stats,
+                );
                 trace!("   -> rewrote arg to {:?}", new_arg);
                 *arg = new_arg.value;
             }
diff --git a/cranelift/codegen/src/scoped_hash_map.rs b/cranelift/codegen/src/scoped_hash_map.rs
index 1b8fde1a5..7f64ae3c3 100644
--- a/cranelift/codegen/src/scoped_hash_map.rs
+++ b/cranelift/codegen/src/scoped_hash_map.rs
@@ -188,19 +188,6 @@ where
             .checked_sub(1)
             .expect("generation_by_depth cannot be empty")
     }
-
-    /// Remote an entry.
-    pub fn remove(&mut self, key: &K) -> Option<V> {
-        self.map.remove(key).and_then(|val| {
-            let entry_generation = val.generation;
-            let entry_depth = val.level as usize;
-            if self.generation_by_depth.get(entry_depth).cloned() == Some(entry_generation) {
-                Some(val.value)
-            } else {
-                None
-            }
-        })
-    }
 }
 
 #[cfg(test)]
diff --git a/crates/wasi-http/wit/deps/http/handler.wit b/crates/wasi-http/wit/deps/http/handler.wit
new file mode 100644
index 000000000..21b97a354
--- /dev/null
+++ b/crates/wasi-http/wit/deps/http/handler.wit
@@ -0,0 +1,41 @@
+/// This interface defines a handler of incoming HTTP Requests. It should
+/// be exported by components which can respond to HTTP Requests.
+interface incoming-handler {
+  use types.{incoming-request, response-outparam};
+
+  /// This function is invoked with an incoming HTTP Request, and a resource
+  /// `response-outparam` which provides the capability to reply with an HTTP
+  /// Response. The response is sent by calling the `response-outparam.set`
+  /// method, which allows execution to continue after the response has been
+  /// sent. This enables both streaming to the response body, and performing other
+  /// work.
+  ///
+  /// The implementor of this function must write a response to the
+  /// `response-outparam` before returning, or else the caller will respond
+  /// with an error on its behalf.
+  handle: func(
+    request: incoming-request,
+    response-out: response-outparam
+  );
+}
+
+/// This interface defines a handler of outgoing HTTP Requests. It should be
+/// imported by components which wish to make HTTP Requests.
+interface outgoing-handler {
+  use types.{outgoing-request, request-options, future-incoming-response, error};
+
+  /// This function is invoked with an outgoing HTTP Request, and it returns
+  /// a resource `future-incoming-response` which represents an HTTP Response
+  /// which may arrive in the future.
+  ///
+  /// The `options` argument accepts optional parameters for the HTTP
+  /// protocol's transport layer.
+  ///
+  /// This function may return an error if the `outgoing-request` is invalid
+  /// or not allowed to be made. Otherwise, protocol errors are reported
+  /// through the `future-incoming-response`.
+  handle: func(
+    request: outgoing-request,
+    options: option<request-options>
+  ) -> result<future-incoming-response, error>;
+}
diff --git a/crates/wasi-http/wit/deps/http/incoming-handler.wit b/crates/wasi-http/wit/deps/http/incoming-handler.wit
deleted file mode 100644
index 6968d6331..000000000
--- a/crates/wasi-http/wit/deps/http/incoming-handler.wit
+++ /dev/null
@@ -1,24 +0,0 @@
-// The `wasi:http/incoming-handler` interface is meant to be exported by
-// components and called by the host in response to a new incoming HTTP
-// response.
-//
-//   NOTE: in Preview3, this interface will be merged with
-//   `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface
-//   that takes a `request` parameter and returns a `response` result.
-//
-interface incoming-handler {
-  use types.{incoming-request, response-outparam};
-
-  // The `handle` function takes an outparam instead of returning its response
-  // so that the component may stream its response while streaming any other
-  // request or response bodies. The callee MUST write a response to the
-  // `response-outparam` and then finish the response before returning. The `handle`
-  // function is allowed to continue execution after finishing the response's
-  // output stream. While this post-response execution is taken off the
-  // critical path, since there is no return value, there is no way to report
-  // its success or failure.
-  handle: func(
-    request: incoming-request,
-    response-out: response-outparam
-  );
-}
diff --git a/crates/wasi-http/wit/deps/http/outgoing-handler.wit b/crates/wasi-http/wit/deps/http/outgoing-handler.wit
deleted file mode 100644
index 286e2833b..000000000
--- a/crates/wasi-http/wit/deps/http/outgoing-handler.wit
+++ /dev/null
@@ -1,20 +0,0 @@
-// The `wasi:http/outgoing-handler` interface is meant to be imported by
-// components and implemented by the host.
-//
-//   NOTE: in Preview3, this interface will be merged with
-//   `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface
-//   that takes a `request` parameter and returns a `response` result.
-//
-interface outgoing-handler {
-  use types.{outgoing-request, request-options, future-incoming-response, error};
-
-  // The parameter and result types of the `handle` function allow the caller
-  // to concurrently stream the bodies of the outgoing request and the incoming
-  // response.
-  // Consumes the outgoing-request. Gives an error if the outgoing-request
-  // is invalid or cannot be satisfied by this handler.
-  handle: func(
-    request: outgoing-request,
-    options: option<request-options>
-  ) -> result<future-incoming-response, error>;
-}
diff --git a/crates/wasi-http/wit/deps/http/proxy.wit b/crates/wasi-http/wit/deps/http/proxy.wit
index 7d0e84eb2..3fa8b9c63 100644
--- a/crates/wasi-http/wit/deps/http/proxy.wit
+++ b/crates/wasi-http/wit/deps/http/proxy.wit
@@ -1,34 +1,34 @@
 package wasi:http@0.2.0-rc-2023-11-05;
 
-// The `wasi:http/proxy` world captures a widely-implementable intersection of
-// hosts that includes HTTP forward and reverse proxies. Components targeting
-// this world may concurrently stream in and out any number of incoming and
-// outgoing HTTP requests.
+/// The `wasi:http/proxy` world captures a widely-implementable intersection of
+/// hosts that includes HTTP forward and reverse proxies. Components targeting
+/// this world may concurrently stream in and out any number of incoming and
+/// outgoing HTTP requests.
 world proxy {
-  // HTTP proxies have access to time and randomness.
+  /// HTTP proxies have access to time and randomness.
   import wasi:clocks/wall-clock@0.2.0-rc-2023-11-05;
   import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-05;
   import wasi:clocks/timezone@0.2.0-rc-2023-11-05;
   import wasi:random/random@0.2.0-rc-2023-11-05;
 
-  // Proxies have standard output and error streams which are expected to
-  // terminate in a developer-facing console provided by the host.
+  /// Proxies have standard output and error streams which are expected to
+  /// terminate in a developer-facing console provided by the host.
   import wasi:cli/stdout@0.2.0-rc-2023-11-05;
   import wasi:cli/stderr@0.2.0-rc-2023-11-05;
 
-  // TODO: this is a temporary workaround until component tooling is able to
-  // gracefully handle the absence of stdin. Hosts must return an eof stream
-  // for this import, which is what wasi-libc + tooling will do automatically
-  // when this import is properly removed.
+  /// TODO: this is a temporary workaround until component tooling is able to
+  /// gracefully handle the absence of stdin. Hosts must return an eof stream
+  /// for this import, which is what wasi-libc + tooling will do automatically
+  /// when this import is properly removed.
   import wasi:cli/stdin@0.2.0-rc-2023-11-05;
 
-  // This is the default handler to use when user code simply wants to make an
-  // HTTP request (e.g., via `fetch()`).
+  /// This is the default handler to use when user code simply wants to make an
+  /// HTTP request (e.g., via `fetch()`).
   import outgoing-handler;
 
-  // The host delivers incoming HTTP requests to a component by calling the
-  // `handle` function of this exported interface. A host may arbitrarily reuse
-  // or not reuse component instance when delivering incoming HTTP requests and
-  // thus a component must be able to handle 0..N calls to `handle`.
+  /// The host delivers incoming HTTP requests to a component by calling the
+  /// `handle` function of this exported interface. A host may arbitrarily reuse
+  /// or not reuse component instance when delivering incoming HTTP requests and
+  /// thus a component must be able to handle 0..N calls to `handle`.
   export incoming-handler;
 }
diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit
index bca0d6528..d9f195054 100644
--- a/crates/wasi-http/wit/deps/http/types.wit
+++ b/crates/wasi-http/wit/deps/http/types.wit
@@ -1,11 +1,11 @@
-// The `wasi:http/types` interface is meant to be imported by components to
-// define the HTTP resource types and operations used by the component's
-// imported and exported interfaces.
+/// This interface defines all of the types and methods for implementing
+/// HTTP Requests and Responses, both incoming and outgoing, as well as
+/// their headers, trailers, and bodies.
 interface types {
   use wasi:io/streams@0.2.0-rc-2023-11-05.{input-stream, output-stream};
   use wasi:io/poll@0.2.0-rc-2023-11-05.{pollable};
 
-  // This type corresponds to HTTP standard Methods.
+  /// This type corresponds to HTTP standard Methods.
   variant method {
     get,
     head,
@@ -19,16 +19,16 @@ interface types {
     other(string)
   }
 
-  // This type corresponds to HTTP standard Related Schemes.
+  /// This type corresponds to HTTP standard Related Schemes.
   variant scheme {
     HTTP,
     HTTPS,
     other(string)
   }
 
-  // TODO: perhaps better align with HTTP semantics?
-  // This type enumerates the different kinds of errors that may occur when
-  // initially returning a response.
+  /// TODO: perhaps better align with HTTP semantics?
+  /// This type enumerates the different kinds of errors that may occur when
+  /// initially returning a response.
   variant error {
     invalid-url(string),
     timeout-error(string),
@@ -36,61 +36,97 @@ interface types {
     unexpected-error(string)
   }
 
-  // This following block defines the `fields` resource which corresponds to
-  // HTTP standard Fields. Soon, when resource types are added, the `type
-  // fields = u32` type alias can be replaced by a proper `resource fields`
-  // definition containing all the functions using the method syntactic sugar.
-  resource fields {
-    // Multiple values for a header are multiple entries in the list with the
-    // same key.
-    constructor(entries: list<tuple<string,list<u8>>>);
-
-    // Values off wire are not necessarily well formed, so they are given by
-    // list<u8> instead of string.
-    get: func(name: string) -> list<list<u8>>;
+  /// Field keys are always strings.
+  type field-key = string;
 
-    // Values off wire are not necessarily well formed, so they are given by
-    // list<u8> instead of string.
-    set: func(name: string, value: list<list<u8>>);
-    delete: func(name: string);
-    append: func(name: string, value: list<u8>);
+  /// Field values should always be ASCII strings. However, in
+  /// reality, HTTP implementations often have to interpret malformed values,
+  /// so they are provided as a list of bytes.
+  type field-value = list<u8>;
 
-    // Values off wire are not necessarily well formed, so they are given by
-    // list<u8> instead of string.
-    entries: func() -> list<tuple<string,list<u8>>>;
+  /// This following block defines the `fields` resource which corresponds to
+  /// HTTP standard Fields. Fields are a common representation used for both
+  /// Headers and Trailers.
+  resource fields {
 
-    // Deep copy of all contents in a fields.
+    /// Construct an HTTP Fields.
+    ///
+    /// The list represents each key-value pair in the Fields. Keys
+    /// which have multiple values are represented by multiple entries in this
+    /// list with the same key.
+    ///
+    /// The tuple is a pair of the field key, represented as a string, and
+    /// Value, represented as a list of bytes. In a valid Fields, all keys
+    /// and values are valid UTF-8 strings. However, values are not always
+    /// well-formed, so they are represented as a raw list of bytes.
+    constructor(entries: list<tuple<field-key,field-value>>);
+
+    /// Get all of the values corresponding to a key.
+    get: func(name: field-key) -> list<field-value>;
+
+    /// Set all of the values for a key. Clears any existing values for that
+    /// key, if they have been set.
+    set: func(name: field-key, value: list<field-value>);
+
+    /// Delete all values for a key. Does nothing if no values for the key
+    /// exist.
+    delete: func(name: field-key);
+
+    /// Append a value for a key. Does not change or delete any existing
+    /// values for that key.
+    append: func(name: field-key, value: field-value);
+
+
+    /// Retrieve the full set of keys and values in the Fields. Like the
+    /// constructor, the list represents each key-value pair. 
+    ///
+    /// The outer list represents each key-value pair in the Fields. Keys
+    /// which have multiple values are represented by multiple entries in this
+    /// list with the same key.
+    entries: func() -> list<tuple<field-key,field-value>>;
+
+    /// Make a deep copy of the Fields. Equivelant in behavior to calling the
+    /// `fields` constructor on the return value of `entries` 
     clone: func() -> fields;
   }
 
+  /// Headers is an alias for Fields.
   type headers = fields;
+
+  /// Trailers is an alias for Fields.
   type trailers = fields;
 
-  // The following block defines the `incoming-request` and `outgoing-request`
-  // resource types that correspond to HTTP standard Requests. Soon, when
-  // resource types are added, the `u32` type aliases can be replaced by
-  // proper `resource` type definitions containing all the functions as
-  // methods. Later, Preview2 will allow both types to be merged together into
-  // a single `request` type (that uses the single `stream` type mentioned
-  // above). The `consume` and `write` methods may only be called once (and
-  // return failure thereafter).
+  /// Represents an incoming HTTP Request.
   resource incoming-request {
+
+    /// Returns the method of the incoming request.
     method: func() -> method;
 
+    /// Returns the path with query parameters from the request, as a string.
     path-with-query: func() -> option<string>;
 
+    /// Returns the protocol scheme from the request.
     scheme: func() -> option<scheme>;
 
+    /// Returns the authority from the request, if it was present.
     authority: func() -> option<string>;
 
-    headers: func() -> /* child */ headers;
-    // Will return the input-stream child at most once. If called more than
-    // once, subsequent calls will return error.
+    /// Returns the `headers` from the request.
+    ///
+    /// The `headers` returned are a child resource: it must be dropped before
+    /// the parent `incoming-request` is dropped. Dropping this
+    /// `incoming-request` before all children are dropped will trap.
+    headers: func() -> headers;
 
+    /// Gives the `incoming-body` associated with this request. Will only
+    /// return success at most once, and subsequent calls will return error.
     consume: func() -> result<incoming-body>;
   }
 
+  /// Represents an outgoing HTTP Request.
   resource outgoing-request {
+
+    /// Construct a new `outgoing-request`.
     constructor(
       method: method,
       path-with-query: option<string>,
@@ -99,116 +135,204 @@ interface types {
       headers: borrow<headers>
     );
 
-    // Will return the outgoing-body child at most once. If called more than
-    // once, subsequent calls will return error.
-    write: func() -> result< /* child */ outgoing-body>;
+    /// Will return the outgoing-body child at most once. If called more than
+    /// once, subsequent calls will return error.
+    write: func() -> result<outgoing-body>;
   }
 
-  // Additional optional parameters that can be set when making a request.
+  /// Parameters for making an HTTP Request. Each of these parameters is an
+  /// optional timeout, with the unit in milliseconds, applicable to the
+  /// transport layer of the HTTP protocol.
+  ///
+  /// These timeouts are separate from any the user may use to bound a
+  /// blocking call to `wasi:io/poll.poll-list`.
+  ///
+  /// FIXME: Make this a resource to allow it to be optionally extended by
+  /// future evolution of the standard and/or other interfaces at some later
+  /// date?
   record request-options {
-    // The following timeouts are specific to the HTTP protocol and work
-    // independently of the overall timeouts passed to `io.poll.poll-list`.
 
-    // The timeout for the initial connect.
+    /// The timeout for the initial connect to the HTTP Server.
     connect-timeout-ms: option<u32>,
 
-    // The timeout for receiving the first byte of the response body.
+    /// The timeout for receiving the first byte of the Response body.
     first-byte-timeout-ms: option<u32>,
 
-    // The timeout for receiving the next chunk of bytes in the response body
-    // stream.
+    /// The timeout for receiving subsequent chunks of bytes in the Response
+    /// body stream.
     between-bytes-timeout-ms: option<u32>
   }
 
-  // The following block defines a special resource type used by the
-  // `wasi:http/incoming-handler` interface. When resource types are added, this
-  // block can be replaced by a proper `resource response-outparam { ... }`
-  // definition. Later, with Preview3, the need for an outparam goes away entirely
-  // (the `wasi:http/handler` interface used for both incoming and outgoing can
-  // simply return a `stream`).
+  /// Represents the ability to send an HTTP Response.
+  ///
+  /// This resource is used by the `wasi:http/incoming-handler` interface to
+  /// allow a Response to be sent corresponding to the Request provided as the
+  /// other argument to `incoming-handler.handle`.
   resource response-outparam {
-    set: static func(param: response-outparam, response: result<outgoing-response, error>);
+
+    /// Set the value of the `response-outparam` to either send a response,
+    /// or indicate an error.
+    ///
+    /// This method consumes the `response-outparam` to ensure that it is
+    /// called at most once. If it is never called, the implementation
+    /// will respond with an error.
+    set: static func(
+      param: response-outparam,
+      response: result<outgoing-response, error>,
+    );
   }
 
-  // This type corresponds to the HTTP standard Status Code.
+  /// This type corresponds to the HTTP standard Status Code.
   type status-code = u16;
 
-  // The following block defines the `incoming-response` and `outgoing-response`
-  // resource types that correspond to HTTP standard Responses. Soon, when
-  // resource types are added, the `u32` type aliases can be replaced by proper
-  // `resource` type definitions containing all the functions as methods. Later,
-  // Preview2 will allow both types to be merged together into a single `response`
-  // type (that uses the single `stream` type mentioned above). The `consume` and
-  // `write` methods may only be called once (and return failure thereafter).
+  /// Represents an incoming HTTP Response.
   resource incoming-response {
+
+    /// Returns the status code from the incoming response.
     status: func() -> status-code;
 
-    headers: func() -> /* child */ headers;
+    /// Returns the headers from the incoming response.
+    headers: func() -> headers;
 
-    // May be called at most once. returns error if called additional times.
-    // TODO: make incoming-request-consume work the same way, giving a child
-    // incoming-body.
+    /// Returns the incoming body. May be called at most once. Returns error
+    /// if called additional times.
     consume: func() -> result<incoming-body>;
   }
 
+  /// Represents an incoming HTTP Request or Response's Body.
+  ///
+  /// A body has both its contents - a stream of bytes - and a (possibly
+  /// empty) set of trailers, indicating that the full contents of the
+  /// body have been received. This resource represents the contents as
+  /// an `input-stream` and the delivery of trailers as a `future-trailers`,
+  /// and ensures that the user of this interface may only be consuming either
+  /// the body contents or waiting on trailers at any given time.
   resource incoming-body {
-    // returned input-stream is a child - the implementation may trap if
-    // incoming-body is dropped (or consumed by call to
-    // incoming-body-finish) before the input-stream is dropped.
-    // May be called at most once. returns error if called additional times.
-    %stream: func() -> result</* child */ input-stream>;
-
-    // takes ownership of incoming-body. this will trap if the
-    // incoming-body-stream child is still alive!
-    finish: static func(this: incoming-body) ->
-    /* transitive child of the incoming-response of incoming-body */ future-trailers;
+
+    /// Returns the contents of the body, as a stream of bytes.
+    ///
+    /// Returns success on first call: the stream representing the contents
+    /// can be retrieved at most once. Subsequent calls will return error.
+    ///
+    /// The returned `input-stream` resource is a child: it must be dropped
+    /// before the parent `incoming-body` is dropped, or consumed by
+    /// `incoming-body.finish`.
+    ///
+    /// This invariant ensures that the implementation can determine whether
+    /// the user is consuming the contents of the body, waiting on the
+    /// `future-trailers` to be ready, or neither. This allows for network
+    /// backpressure is to be applied when the user is consuming the body,
+    /// and for that backpressure to not inhibit delivery of the trailers if
+    /// the user does not read the entire body.
+    %stream: func() -> result<input-stream>;
+
+    /// Takes ownership of `incoming-body`, and returns a `future-trailers`.
+    /// This function will trap if the `input-stream` child is still alive.
+    finish: static func(this: incoming-body) -> future-trailers;
   }
 
+  /// Represents a future which may eventaully return trailers, or an error.
+  ///
+  /// In the case that the incoming HTTP Request or Response did not have any
+  /// trailers, this future will resolve to the empty set of trailers once the
+  /// complete Request or Response body has been received.
   resource future-trailers {
-    /// Pollable that resolves when the body has been fully read, and the trailers
-    /// are ready to be consumed.
-    subscribe: func() -> /* child */ pollable;
 
-    /// Retrieve reference to trailers, if they are ready.
-    get: func() -> option<result</* child */ trailers, error>>;
+    /// Returns a pollable which becomes ready when either the trailers have
+    /// been received, or an error has occured. When this pollable is ready,
+    /// the `get` method will return `some`.
+    subscribe: func() -> pollable;
+
+    /// Returns the contents of the trailers, or an error which occured,
+    /// once the future is ready.
+    ///
+    /// The outer `option` represents future readiness. Users can wait on this
+    /// `option` to become `some` using the `subscribe` method.
+    ///
+    /// The `result` represents that either the HTTP Request or Response body,
+    /// as well as any trailers, were received successfully, or that an error
+    /// occured receiving them.
+    get: func() -> option<result<trailers, error>>;
   }
 
+  /// Represents an outgoing HTTP Response.
   resource outgoing-response {
+
+    /// Construct an `outgoing-response`.
     constructor(status-code: status-code, headers: borrow<headers>);
 
-    /// Will give the child outgoing-response at most once. subsequent calls will
-    /// return an error.
+    /// Returns the resource corresponding to the outgoing Body for this Response.
+    ///
+    /// Returns success on the first call: the `outgoing-body` resource for
+    /// this `outgoing-response` can be retrieved at most once. Sunsequent
+    /// calls will return error.
+    ///
+    /// FIXME: rename this method to `body`.
     write: func() -> result<outgoing-body>;
   }
 
+  /// Represents an outgoing HTTP Request or Response's Body.
+  ///
+  /// A body has both its contents - a stream of bytes - and a (possibly
+  /// empty) set of trailers, inducating the full contents of the body
+  /// have been sent. This resource represents the contents as an
+  /// `output-stream` child resource, and the completion of the body (with
+  /// optional trailers) with a static function that consumes the
+  /// `outgoing-body` resource, and ensures that the user of this interface
+  /// may not write to the body contents after the body has been finished.
+  ///
+  /// If the user code drops this resource, as opposed to calling the static
+  /// method `finish`, the implementation should treat the body as incomplete,
+  /// and that an error has occured. The implementation should propogate this
+  /// error to the HTTP protocol by whatever means it has available,
+  /// including: corrupting the body on the wire, aborting the associated
+  /// Request, or sending a late status code for the Response.
   resource outgoing-body {
-    /// Will give the child output-stream at most once. subsequent calls will
-    /// return an error.
-    write: func() -> result</* child */ output-stream>;
+
+    /// Returns a stream for writing the body contents.
+    ///
+    /// The returned `output-stream` is a child resource: it must be dropped
+    /// before the parent `outgoing-body` resource is dropped (or finished),
+    /// otherwise the `outgoing-body` drop or `finish` will trap.
+    ///
+    /// Returns success on the first call: the `output-stream` resource for
+    /// this `outgoing-body` may be retrieved at most once. Subsequent calls
+    /// will return error.
+    write: func() -> result<output-stream>;
 
     /// Finalize an outgoing body, optionally providing trailers. This must be
-    /// called to signal that the response is complete. If the `outgoing-body` is
-    /// dropped without calling `outgoing-body-finalize`, the implementation
+    /// called to signal that the response is complete. If the `outgoing-body`
+    /// is dropped without calling `outgoing-body.finalize`, the implementation
     /// should treat the body as corrupted.
     finish: static func(this: outgoing-body, trailers: option<trailers>);
   }
 
-  /// The following block defines a special resource type used by the
-  /// `wasi:http/outgoing-handler` interface to emulate
-  /// `future<result<response, error>>` in advance of Preview3. Given a
-  /// `future-incoming-response`, the client can call the non-blocking `get`
-  /// method to get the result if it is available. If the result is not available,
-  /// the client can call `listen` to get a `pollable` that can be passed to
-  /// `wasi:io/poll.poll-list`.
+  /// Represents a future which may eventaully return an incoming HTTP
+  /// Response, or an error.
+  ///
+  /// This resource is returned by the `wasi:http/outgoing-handler` interface to
+  /// provide the HTTP Response corresponding to the sent Request.
   resource future-incoming-response {
-    /// option indicates readiness.
-    /// outer result indicates you are allowed to get the
-    /// incoming-response-or-error at most once. subsequent calls after ready
-    /// will return an error here.
-    /// inner result indicates whether the incoming-response was available, or an
-    /// error occured.
-    get: func() -> option<result<result</* NOT a child*/ incoming-response, error>>>;
-
-    subscribe: func() -> /* child */ pollable;
+    /// Returns a pollable which becomes ready when either the Response has
+    /// been received, or an error has occured. When this pollable is ready,
+    /// the `get` method will return `some`.
+    subscribe: func() -> pollable;
+
+    /// Returns the incoming HTTP Response, or an error, once one is ready.
+    ///
+    /// The outer `option` represents future readiness. Users can wait on this
+    /// `option` to become `some` using the `subscribe` method.
+    ///
+    /// The outer `result` is used to retrieve the response or error at most
+    /// once. It will be success on the first call in which the outer option
+    /// is `some`, and error on subsequent calls.
+    ///
+    /// The inner `result` represents that either the incoming HTTP Response
+    /// status and headers have recieved successfully, or that an error
+    /// occured. Errors may also occur while consuming the response body,
+    /// but those will be reported by the `incoming-body` and its
+    /// `output-stream` child.
+    get: func() -> option<result<result<incoming-response, error>>>;
+
   }
 }
diff --git a/crates/wasi/wit/deps/http/handler.wit b/crates/wasi/wit/deps/http/handler.wit
new file mode 100644
index 000000000..21b97a354
--- /dev/null
+++ b/crates/wasi/wit/deps/http/handler.wit
@@ -0,0 +1,41 @@
+/// This interface defines a handler of incoming HTTP Requests. It should
+/// be exported by components which can respond to HTTP Requests.
+interface incoming-handler {
+  use types.{incoming-request, response-outparam};
+
+  /// This function is invoked with an incoming HTTP Request, and a resource
+  /// `response-outparam` which provides the capability to reply with an HTTP
+  /// Response. The response is sent by calling the `response-outparam.set`
+  /// method, which allows execution to continue after the response has been
+  /// sent. This enables both streaming to the response body, and performing other
+  /// work.
+  ///
+  /// The implementor of this function must write a response to the
+  /// `response-outparam` before returning, or else the caller will respond
+  /// with an error on its behalf.
+  handle: func(
+    request: incoming-request,
+    response-out: response-outparam
+  );
+}
+
+/// This interface defines a handler of outgoing HTTP Requests. It should be
+/// imported by components which wish to make HTTP Requests.
+interface outgoing-handler {
+  use types.{outgoing-request, request-options, future-incoming-response, error};
+
+  /// This function is invoked with an outgoing HTTP Request, and it returns
+  /// a resource `future-incoming-response` which represents an HTTP Response
+  /// which may arrive in the future.
+  ///
+  /// The `options` argument accepts optional parameters for the HTTP
+  /// protocol's transport layer.
+  ///
+  /// This function may return an error if the `outgoing-request` is invalid
+  /// or not allowed to be made. Otherwise, protocol errors are reported
+  /// through the `future-incoming-response`.
+  handle: func(
+    request: outgoing-request,
+    options: option<request-options>
+  ) -> result<future-incoming-response, error>;
+}
diff --git a/crates/wasi/wit/deps/http/incoming-handler.wit b/crates/wasi/wit/deps/http/incoming-handler.wit
deleted file mode 100644
index 6968d6331..000000000
--- a/crates/wasi/wit/deps/http/incoming-handler.wit
+++ /dev/null
@@ -1,24 +0,0 @@
-// The `wasi:http/incoming-handler` interface is meant to be exported by
-// components and called by the host in response to a new incoming HTTP
-// response.
-//
-//   NOTE: in Preview3, this interface will be merged with
-//   `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface
-//   that takes a `request` parameter and returns a `response` result.
-//
-interface incoming-handler {
-  use types.{incoming-request, response-outparam};
-
-  // The `handle` function takes an outparam instead of returning its response
-  // so that the component may stream its response while streaming any other
-  // request or response bodies. The callee MUST write a response to the
-  // `response-outparam` and then finish the response before returning. The `handle`
-  // function is allowed to continue execution after finishing the response's
-  // output stream. While this post-response execution is taken off the
-  // critical path, since there is no return value, there is no way to report
-  // its success or failure.
-  handle: func(
-    request: incoming-request,
-    response-out: response-outparam
-  );
-}
diff --git a/crates/wasi/wit/deps/http/outgoing-handler.wit b/crates/wasi/wit/deps/http/outgoing-handler.wit
deleted file mode 100644
index 286e2833b..000000000
--- a/crates/wasi/wit/deps/http/outgoing-handler.wit
+++ /dev/null
@@ -1,20 +0,0 @@
-// The `wasi:http/outgoing-handler` interface is meant to be imported by
-// components and implemented by the host.
-//
-//   NOTE: in Preview3, this interface will be merged with
-//   `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface
-//   that takes a `request` parameter and returns a `response` result.
-//
-interface outgoing-handler {
-  use types.{outgoing-request, request-options, future-incoming-response, error};
-
-  // The parameter and result types of the `handle` function allow the caller
-  // to concurrently stream the bodies of the outgoing request and the incoming
-  // response.
-  // Consumes the outgoing-request. Gives an error if the outgoing-request
-  // is invalid or cannot be satisfied by this handler.
-  handle: func(
-    request: outgoing-request,
-    options: option<request-options>
-  ) -> result<future-incoming-response, error>;
-}
diff --git a/crates/wasi/wit/deps/http/proxy.wit b/crates/wasi/wit/deps/http/proxy.wit
index 7d0e84eb2..3fa8b9c63 100644
--- a/crates/wasi/wit/deps/http/proxy.wit
+++ b/crates/wasi/wit/deps/http/proxy.wit
@@ -1,34 +1,34 @@
 package wasi:http@0.2.0-rc-2023-11-05;
 
-// The `wasi:http/proxy` world captures a widely-implementable intersection of
-// hosts that includes HTTP forward and reverse proxies. Components targeting
-// this world may concurrently stream in and out any number of incoming and
-// outgoing HTTP requests.
+/// The `wasi:http/proxy` world captures a widely-implementable intersection of
+/// hosts that includes HTTP forward and reverse proxies. Components targeting
+/// this world may concurrently stream in and out any number of incoming and
+/// outgoing HTTP requests.
 world proxy {
-  // HTTP proxies have access to time and randomness.
+  /// HTTP proxies have access to time and randomness.
   import wasi:clocks/wall-clock@0.2.0-rc-2023-11-05;
   import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-05;
   import wasi:clocks/timezone@0.2.0-rc-2023-11-05;
   import wasi:random/random@0.2.0-rc-2023-11-05;
 
-  // Proxies have standard output and error streams which are expected to
-  // terminate in a developer-facing console provided by the host.
+  /// Proxies have standard output and error streams which are expected to
+  /// terminate in a developer-facing console provided by the host.
   import wasi:cli/stdout@0.2.0-rc-2023-11-05;
   import wasi:cli/stderr@0.2.0-rc-2023-11-05;
 
-  // TODO: this is a temporary workaround until component tooling is able to
-  // gracefully handle the absence of stdin. Hosts must return an eof stream
-  // for this import, which is what wasi-libc + tooling will do automatically
-  // when this import is properly removed.
+  /// TODO: this is a temporary workaround until component tooling is able to
+  /// gracefully handle the absence of stdin. Hosts must return an eof stream
+  /// for this import, which is what wasi-libc + tooling will do automatically
+  /// when this import is properly removed.
   import wasi:cli/stdin@0.2.0-rc-2023-11-05;
 
-  // This is the default handler to use when user code simply wants to make an
-  // HTTP request (e.g., via `fetch()`).
+  /// This is the default handler to use when user code simply wants to make an
+  /// HTTP request (e.g., via `fetch()`).
   import outgoing-handler;
 
-  // The host delivers incoming HTTP requests to a component by calling the
-  // `handle` function of this exported interface. A host may arbitrarily reuse
-  // or not reuse component instance when delivering incoming HTTP requests and
-  // thus a component must be able to handle 0..N calls to `handle`.
+  /// The host delivers incoming HTTP requests to a component by calling the
+  /// `handle` function of this exported interface. A host may arbitrarily reuse
+  /// or not reuse component instance when delivering incoming HTTP requests and
+  /// thus a component must be able to handle 0..N calls to `handle`.
   export incoming-handler;
 }
diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit
index bca0d6528..d9f195054 100644
--- a/crates/wasi/wit/deps/http/types.wit
+++ b/crates/wasi/wit/deps/http/types.wit
@@ -1,11 +1,11 @@
-// The `wasi:http/types` interface is meant to be imported by components to
-// define the HTTP resource types and operations used by the component's
-// imported and exported interfaces.
+/// This interface defines all of the types and methods for implementing
+/// HTTP Requests and Responses, both incoming and outgoing, as well as
+/// their headers, trailers, and bodies.
 interface types {
   use wasi:io/streams@0.2.0-rc-2023-11-05.{input-stream, output-stream};
   use wasi:io/poll@0.2.0-rc-2023-11-05.{pollable};
 
-  // This type corresponds to HTTP standard Methods.
+  /// This type corresponds to HTTP standard Methods.
   variant method {
     get,
     head,
@@ -19,16 +19,16 @@ interface types {
     other(string)
   }
 
-  // This type corresponds to HTTP standard Related Schemes.
+  /// This type corresponds to HTTP standard Related Schemes.
   variant scheme {
     HTTP,
     HTTPS,
     other(string)
   }
 
-  // TODO: perhaps better align with HTTP semantics?
-  // This type enumerates the different kinds of errors that may occur when
-  // initially returning a response.
+  /// TODO: perhaps better align with HTTP semantics?
+  /// This type enumerates the different kinds of errors that may occur when
+  /// initially returning a response.
   variant error {
     invalid-url(string),
     timeout-error(string),
@@ -36,61 +36,97 @@ interface types {
     unexpected-error(string)
   }
 
-  // This following block defines the `fields` resource which corresponds to
-  // HTTP standard Fields. Soon, when resource types are added, the `type
-  // fields = u32` type alias can be replaced by a proper `resource fields`
-  // definition containing all the functions using the method syntactic sugar.
-  resource fields {
-    // Multiple values for a header are multiple entries in the list with the
-    // same key.
-    constructor(entries: list<tuple<string,list<u8>>>);
-
-    // Values off wire are not necessarily well formed, so they are given by
-    // list<u8> instead of string.
-    get: func(name: string) -> list<list<u8>>;
+  /// Field keys are always strings.
+  type field-key = string;
 
-    // Values off wire are not necessarily well formed, so they are given by
-    // list<u8> instead of string.
-    set: func(name: string, value: list<list<u8>>);
-    delete: func(name: string);
-    append: func(name: string, value: list<u8>);
+  /// Field values should always be ASCII strings. However, in
+  /// reality, HTTP implementations often have to interpret malformed values,
+  /// so they are provided as a list of bytes.
+  type field-value = list<u8>;
 
-    // Values off wire are not necessarily well formed, so they are given by
-    // list<u8> instead of string.
-    entries: func() -> list<tuple<string,list<u8>>>;
+  /// This following block defines the `fields` resource which corresponds to
+  /// HTTP standard Fields. Fields are a common representation used for both
+  /// Headers and Trailers.
+  resource fields {
 
-    // Deep copy of all contents in a fields.
+    /// Construct an HTTP Fields.
+    ///
+    /// The list represents each key-value pair in the Fields. Keys
+    /// which have multiple values are represented by multiple entries in this
+    /// list with the same key.
+    ///
+    /// The tuple is a pair of the field key, represented as a string, and
+    /// Value, represented as a list of bytes. In a valid Fields, all keys
+    /// and values are valid UTF-8 strings. However, values are not always
+    /// well-formed, so they are represented as a raw list of bytes.
+    constructor(entries: list<tuple<field-key,field-value>>);
+
+    /// Get all of the values corresponding to a key.
+    get: func(name: field-key) -> list<field-value>;
+
+    /// Set all of the values for a key. Clears any existing values for that
+    /// key, if they have been set.
+    set: func(name: field-key, value: list<field-value>);
+
+    /// Delete all values for a key. Does nothing if no values for the key
+    /// exist.
+    delete: func(name: field-key);
+
+    /// Append a value for a key. Does not change or delete any existing
+    /// values for that key.
+    append: func(name: field-key, value: field-value);
+
+
+    /// Retrieve the full set of keys and values in the Fields. Like the
+    /// constructor, the list represents each key-value pair. 
+    ///
+    /// The outer list represents each key-value pair in the Fields. Keys
+    /// which have multiple values are represented by multiple entries in this
+    /// list with the same key.
+    entries: func() -> list<tuple<field-key,field-value>>;
+
+    /// Make a deep copy of the Fields. Equivelant in behavior to calling the
+    /// `fields` constructor on the return value of `entries` 
     clone: func() -> fields;
   }
 
+  /// Headers is an alias for Fields.
   type headers = fields;
+
+  /// Trailers is an alias for Fields.
   type trailers = fields;
 
-  // The following block defines the `incoming-request` and `outgoing-request`
-  // resource types that correspond to HTTP standard Requests. Soon, when
-  // resource types are added, the `u32` type aliases can be replaced by
-  // proper `resource` type definitions containing all the functions as
-  // methods. Later, Preview2 will allow both types to be merged together into
-  // a single `request` type (that uses the single `stream` type mentioned
-  // above). The `consume` and `write` methods may only be called once (and
-  // return failure thereafter).
+  /// Represents an incoming HTTP Request.
   resource incoming-request {
+
+    /// Returns the method of the incoming request.
     method: func() -> method;
 
+    /// Returns the path with query parameters from the request, as a string.
     path-with-query: func() -> option<string>;
 
+    /// Returns the protocol scheme from the request.
     scheme: func() -> option<scheme>;
 
+    /// Returns the authority from the request, if it was present.
     authority: func() -> option<string>;
 
-    headers: func() -> /* child */ headers;
-    // Will return the input-stream child at most once. If called more than
-    // once, subsequent calls will return error.
+    /// Returns the `headers` from the request.
+    ///
+    /// The `headers` returned are a child resource: it must be dropped before
+    /// the parent `incoming-request` is dropped. Dropping this
+    /// `incoming-request` before all children are dropped will trap.
+    headers: func() -> headers;
 
+    /// Gives the `incoming-body` associated with this request. Will only
+    /// return success at most once, and subsequent calls will return error.
     consume: func() -> result<incoming-body>;
   }
 
+  /// Represents an outgoing HTTP Request.
   resource outgoing-request {
+
+    /// Construct a new `outgoing-request`.
     constructor(
       method: method,
       path-with-query: option<string>,
@@ -99,116 +135,204 @@ interface types {
       headers: borrow<headers>
     );
 
-    // Will return the outgoing-body child at most once. If called more than
-    // once, subsequent calls will return error.
-    write: func() -> result< /* child */ outgoing-body>;
+    /// Will return the outgoing-body child at most once. If called more than
+    /// once, subsequent calls will return error.
+    write: func() -> result<outgoing-body>;
   }
 
-  // Additional optional parameters that can be set when making a request.
+  /// Parameters for making an HTTP Request. Each of these parameters is an
+  /// optional timeout, with the unit in milliseconds, applicable to the
+  /// transport layer of the HTTP protocol.
+  ///
+  /// These timeouts are separate from any the user may use to bound a
+  /// blocking call to `wasi:io/poll.poll-list`.
+  ///
+  /// FIXME: Make this a resource to allow it to be optionally extended by
+  /// future evolution of the standard and/or other interfaces at some later
+  /// date?
   record request-options {
-    // The following timeouts are specific to the HTTP protocol and work
-    // independently of the overall timeouts passed to `io.poll.poll-list`.
 
-    // The timeout for the initial connect.
+    /// The timeout for the initial connect to the HTTP Server.
     connect-timeout-ms: option<u32>,
 
-    // The timeout for receiving the first byte of the response body.
+    /// The timeout for receiving the first byte of the Response body.
     first-byte-timeout-ms: option<u32>,
 
-    // The timeout for receiving the next chunk of bytes in the response body
-    // stream.
+    /// The timeout for receiving subsequent chunks of bytes in the Response
+    /// body stream.
     between-bytes-timeout-ms: option<u32>
   }
 
-  // The following block defines a special resource type used by the
-  // `wasi:http/incoming-handler` interface. When resource types are added, this
-  // block can be replaced by a proper `resource response-outparam { ... }`
-  // definition. Later, with Preview3, the need for an outparam goes away entirely
-  // (the `wasi:http/handler` interface used for both incoming and outgoing can
-  // simply return a `stream`).
+  /// Represents the ability to send an HTTP Response.
+  ///
+  /// This resource is used by the `wasi:http/incoming-handler` interface to
+  /// allow a Response to be sent corresponding to the Request provided as the
+  /// other argument to `incoming-handler.handle`.
   resource response-outparam {
-    set: static func(param: response-outparam, response: result<outgoing-response, error>);
+
+    /// Set the value of the `response-outparam` to either send a response,
+    /// or indicate an error.
+    ///
+    /// This method consumes the `response-outparam` to ensure that it is
+    /// called at most once. If it is never called, the implementation
+    /// will respond with an error.
+    set: static func(
+      param: response-outparam,
+      response: result<outgoing-response, error>,
+    );
   }
 
-  // This type corresponds to the HTTP standard Status Code.
+  /// This type corresponds to the HTTP standard Status Code.
   type status-code = u16;
 
-  // The following block defines the `incoming-response` and `outgoing-response`
-  // resource types that correspond to HTTP standard Responses. Soon, when
-  // resource types are added, the `u32` type aliases can be replaced by proper
-  // `resource` type definitions containing all the functions as methods. Later,
-  // Preview2 will allow both types to be merged together into a single `response`
-  // type (that uses the single `stream` type mentioned above). The `consume` and
-  // `write` methods may only be called once (and return failure thereafter).
+  /// Represents an incoming HTTP Response.
   resource incoming-response {
+
+    /// Returns the status code from the incoming response.
     status: func() -> status-code;
 
-    headers: func() -> /* child */ headers;
+    /// Returns the headers from the incoming response.
+    headers: func() -> headers;
 
-    // May be called at most once. returns error if called additional times.
-    // TODO: make incoming-request-consume work the same way, giving a child
-    // incoming-body.
+    /// Returns the incoming body. May be called at most once. Returns error
+    /// if called additional times.
     consume: func() -> result<incoming-body>;
   }
 
+  /// Represents an incoming HTTP Request or Response's Body.
+  ///
+  /// A body has both its contents - a stream of bytes - and a (possibly
+  /// empty) set of trailers, indicating that the full contents of the
+  /// body have been received. This resource represents the contents as
+  /// an `input-stream` and the delivery of trailers as a `future-trailers`,
+  /// and ensures that the user of this interface may only be consuming either
+  /// the body contents or waiting on trailers at any given time.
   resource incoming-body {
-    // returned input-stream is a child - the implementation may trap if
-    // incoming-body is dropped (or consumed by call to
-    // incoming-body-finish) before the input-stream is dropped.
-    // May be called at most once. returns error if called additional times.
-    %stream: func() -> result</* child */ input-stream>;
-
-    // takes ownership of incoming-body. this will trap if the
-    // incoming-body-stream child is still alive!
-    finish: static func(this: incoming-body) ->
-    /* transitive child of the incoming-response of incoming-body */ future-trailers;
+
+    /// Returns the contents of the body, as a stream of bytes.
+    ///
+    /// Returns success on first call: the stream representing the contents
+    /// can be retrieved at most once. Subsequent calls will return error.
+    ///
+    /// The returned `input-stream` resource is a child: it must be dropped
+    /// before the parent `incoming-body` is dropped, or consumed by
+    /// `incoming-body.finish`.
+    ///
+    /// This invariant ensures that the implementation can determine whether
+    /// the user is consuming the contents of the body, waiting on the
+    /// `future-trailers` to be ready, or neither. This allows for network
+    /// backpressure is to be applied when the user is consuming the body,
+    /// and for that backpressure to not inhibit delivery of the trailers if
+    /// the user does not read the entire body.
+    %stream: func() -> result<input-stream>;
+
+    /// Takes ownership of `incoming-body`, and returns a `future-trailers`.
+    /// This function will trap if the `input-stream` child is still alive.
+    finish: static func(this: incoming-body) -> future-trailers;
   }
 
+  /// Represents a future which may eventaully return trailers, or an error.
+  ///
+  /// In the case that the incoming HTTP Request or Response did not have any
+  /// trailers, this future will resolve to the empty set of trailers once the
+  /// complete Request or Response body has been received.
   resource future-trailers {
-    /// Pollable that resolves when the body has been fully read, and the trailers
-    /// are ready to be consumed.
-    subscribe: func() -> /* child */ pollable;
 
-    /// Retrieve reference to trailers, if they are ready.
-    get: func() -> option<result</* child */ trailers, error>>;
+    /// Returns a pollable which becomes ready when either the trailers have
+    /// been received, or an error has occured. When this pollable is ready,
+    /// the `get` method will return `some`.
+    subscribe: func() -> pollable;
+
+    /// Returns the contents of the trailers, or an error which occured,
+    /// once the future is ready.
+    ///
+    /// The outer `option` represents future readiness. Users can wait on this
+    /// `option` to become `some` using the `subscribe` method.
+    ///
+    /// The `result` represents that either the HTTP Request or Response body,
+    /// as well as any trailers, were received successfully, or that an error
+    /// occured receiving them.
+    get: func() -> option<result<trailers, error>>;
   }
 
+  /// Represents an outgoing HTTP Response.
   resource outgoing-response {
+
+    /// Construct an `outgoing-response`.
     constructor(status-code: status-code, headers: borrow<headers>);
 
-    /// Will give the child outgoing-response at most once. subsequent calls will
-    /// return an error.
+    /// Returns the resource corresponding to the outgoing Body for this Response.
+    ///
+    /// Returns success on the first call: the `outgoing-body` resource for
+    /// this `outgoing-response` can be retrieved at most once. Sunsequent
+    /// calls will return error.
+    ///
+    /// FIXME: rename this method to `body`.
     write: func() -> result<outgoing-body>;
   }
 
+  /// Represents an outgoing HTTP Request or Response's Body.
+  ///
+  /// A body has both its contents - a stream of bytes - and a (possibly
+  /// empty) set of trailers, inducating the full contents of the body
+  /// have been sent. This resource represents the contents as an
+  /// `output-stream` child resource, and the completion of the body (with
+  /// optional trailers) with a static function that consumes the
+  /// `outgoing-body` resource, and ensures that the user of this interface
+  /// may not write to the body contents after the body has been finished.
+  ///
+  /// If the user code drops this resource, as opposed to calling the static
+  /// method `finish`, the implementation should treat the body as incomplete,
+  /// and that an error has occured. The implementation should propogate this
+  /// error to the HTTP protocol by whatever means it has available,
+  /// including: corrupting the body on the wire, aborting the associated
+  /// Request, or sending a late status code for the Response.
   resource outgoing-body {
-    /// Will give the child output-stream at most once. subsequent calls will
-    /// return an error.
-    write: func() -> result</* child */ output-stream>;
+
+    /// Returns a stream for writing the body contents.
+    ///
+    /// The returned `output-stream` is a child resource: it must be dropped
+    /// before the parent `outgoing-body` resource is dropped (or finished),
+    /// otherwise the `outgoing-body` drop or `finish` will trap.
+    ///
+    /// Returns success on the first call: the `output-stream` resource for
+    /// this `outgoing-body` may be retrieved at most once. Subsequent calls
+    /// will return error.
+    write: func() -> result<output-stream>;
 
     /// Finalize an outgoing body, optionally providing trailers. This must be
-    /// called to signal that the response is complete. If the `outgoing-body` is
-    /// dropped without calling `outgoing-body-finalize`, the implementation
+    /// called to signal that the response is complete. If the `outgoing-body`
+    /// is dropped without calling `outgoing-body.finalize`, the implementation
     /// should treat the body as corrupted.
     finish: static func(this: outgoing-body, trailers: option<trailers>);
   }
 
-  /// The following block defines a special resource type used by the
-  /// `wasi:http/outgoing-handler` interface to emulate
-  /// `future<result<response, error>>` in advance of Preview3. Given a
-  /// `future-incoming-response`, the client can call the non-blocking `get`
-  /// method to get the result if it is available. If the result is not available,
-  /// the client can call `listen` to get a `pollable` that can be passed to
-  /// `wasi:io/poll.poll-list`.
+  /// Represents a future which may eventaully return an incoming HTTP
+  /// Response, or an error.
+  ///
+  /// This resource is returned by the `wasi:http/outgoing-handler` interface to
+  /// provide the HTTP Response corresponding to the sent Request.
   resource future-incoming-response {
-    /// option indicates readiness.
-    /// outer result indicates you are allowed to get the
-    /// incoming-response-or-error at most once. subsequent calls after ready
-    /// will return an error here.
-    /// inner result indicates whether the incoming-response was available, or an
-    /// error occured.
-    get: func() -> option<result<result</* NOT a child*/ incoming-response, error>>>;
-
-    subscribe: func() -> /* child */ pollable;
+    /// Returns a pollable which becomes ready when either the Response has
+    /// been received, or an error has occured. When this pollable is ready,
+    /// the `get` method will return `some`.
+    subscribe: func() -> pollable;
+
+    /// Returns the incoming HTTP Response, or an error, once one is ready.
+    ///
+    /// The outer `option` represents future readiness. Users can wait on this
+    /// `option` to become `some` using the `subscribe` method.
+    ///
+    /// The outer `result` is used to retrieve the response or error at most
+    /// once. It will be success on the first call in which the outer option
+    /// is `some`, and error on subsequent calls.
+    ///
+    /// The inner `result` represents that either the incoming HTTP Response
+    /// status and headers have recieved successfully, or that an error
+    /// occured. Errors may also occur while consuming the response body,
+    /// but those will be reported by the `incoming-body` and its
+    /// `output-stream` child.
+    get: func() -> option<result<result<incoming-response, error>>>;
+
   }
 }


Test patch:
diff --git a/cranelift/filetests/filetests/egraph/licm.clif b/cranelift/filetests/filetests/egraph/licm.clif
index f2f84c583..51fdf38a5 100644
--- a/cranelift/filetests/filetests/egraph/licm.clif
+++ b/cranelift/filetests/filetests/egraph/licm.clif
@@ -20,21 +20,21 @@ block2(v9: i32):
 }
 
 ; check:  block0(v0: i32, v1: i32):
-; nextln:     jump block1(v0)
+; check:      jump block1(v0)
 
 ; check:  block1(v2: i32):
-;; constants are not lifted; they are rematerialized in each block where used
-; check:      v5 = iconst.i32 40
-; check:      v6 = icmp eq v2, v5
-; check:      v3 = iconst.i32 1
-; check:      v8 = iadd v2, v3
-; check:      brif v6, block2, block1(v8)
+;; constants are rematerialized in each block where used
+; check:      v10 = iconst.i32 40
+; check:      v11 = icmp eq v2, v10
+; check:      v12 = iconst.i32 1
+; check:      v13 = iadd v2, v12
+; check:      brif v11, block2, block1(v13)
 
 
 ; check:  block2:
-; check:      v10 = iconst.i32 1
-; check:      v4 = iadd.i32 v1, v10
-; check:      return v4
+; check:      v14 = iconst.i32 1
+; check:      v15 = iadd.i32 v1, v14
+; check:      return v15
 
 function %f(i64x2, i32) -> i64x2 {
 block0(v0: i64x2, v1: i32):
@@ -52,14 +52,14 @@ block2(v8: i64x2):
 }
 
 ; check:  block0(v0: i64x2, v1: i32):
-; nextln:     v4 = vconst.i64x2 const0
+; check:      v4 = vconst.i64x2 const0
 ; nextln:     jump block1(v0, v1)
 ; check:  block1(v2: i64x2, v3: i32):
-; check:      v6 = iconst.i32 1
-; check:      v7 = isub v3, v6
+; check:      v9 = iconst.i32 1
+; check:      v10 = isub v3, v9
 ; check:      v5 = iadd v2, v4
 ; check:      v8 -> v5
-; check:      brif v7, block1(v5, v7), block2
+; check:      brif v10, block1(v5, v10), block2
 ; check:  block2:
 ; check:      return v5
 
@@ -94,13 +94,50 @@ block4:
 ; check:      v8 = vconst.i64x2 const0
 ; check:      jump block2(v3, v1)
 ; check:  block2(v6: i64x2, v7: i32):
-; check:      v10 = iconst.i32 1
-; check:      v11 = isub v7, v10
+; check:      v15 = iconst.i32 1
+; check:      v16 = isub v7, v15
 ; check:      v9 = iadd v6, v8
-; check:      brif v11, block2(v9, v11), block3
+; check:      brif v16, block2(v9, v16), block3
 ; check:  block3:
-; check:      v15 = iconst.i32 1
-; check:      v14 = isub.i32 v5, v15
-; check:      brif v14, block1(v9, v14), block4
+; check:      v17 = iconst.i32 1
+; check:      v18 = isub.i32 v5, v17
+; check:      brif v18, block1(v9, v18), block4
 ; check:  block4:
 ; check:      return v9
+
+;; Don't let a rematerialized iconst inhibit (or even reverse)
+;; LICM. See issue #7283.
+
+function %f(i64, i64) {
+block0(v0: i64, v1: i64):
+    ;; Create a loop-invariant value `v10` which is some operation which
+    ;; includes a constant somewhere.
+    v8 = load.f64 v0+100
+    v9 = f64const 0x1.0000000000000p1
+    v10 = fdiv v8, v9
+
+    ;; jump to the loop
+    v3 = iconst.i64 0
+    jump block2(v3)  ; v3 = 0
+
+block2(v11: i64):
+    ;; store the loop-invariant `v10` to memory "somewhere"
+    v15 = iadd v0, v11
+    store.f64 v10, v15
+
+    ;; loop breakout condition
+    v17 = iadd_imm v11, 1
+    v19 = icmp_imm ne v17, 100
+    brif v19, block2(v17), block1
+
+block1:
+    return
+}
+
+; check: load
+; check: f64const
+; check: fdiv
+; check: block2(v11: i64)
+; check: iadd
+; check: store
+; check: brif
diff --git a/cranelift/filetests/filetests/egraph/load-hoist.clif b/cranelift/filetests/filetests/egraph/load-hoist.clif
index f52a1995c..37cf9ad36 100644
--- a/cranelift/filetests/filetests/egraph/load-hoist.clif
+++ b/cranelift/filetests/filetests/egraph/load-hoist.clif
@@ -41,4 +41,4 @@ function %foo(i64 vmctx, i64, i32, i32) -> i32 fast {
 ; check: block2(v6: i32, v7: i32, v15: i32):
 ; check:    v10 = iadd.i64 v9, v8
 ; check:    v11 = load.i32 little heap v10
-; check:    brif v17, block2(v12, v14, v17), block4
+; check:    brif v19, block2(v12, v21, v19), block4
diff --git a/cranelift/filetests/filetests/egraph/remat.clif b/cranelift/filetests/filetests/egraph/remat.clif
index 861e4a8cd..a1be556ce 100644
--- a/cranelift/filetests/filetests/egraph/remat.clif
+++ b/cranelift/filetests/filetests/egraph/remat.clif
@@ -26,7 +26,6 @@ block2:
 ; check:      v13 = iadd.i32 v0, v11
 ; check:      return v13
 ; check:   block2:
-; check:      v15 = iconst.i32 42
-; check:      v16 = iadd.i32 v0, v15
-; check:      return v16
+; check:      v15 = iadd.i32 v0, v1
+; check:      return v15