Repository: bytecodealliance/wasmtime
Pull request #8081 (2024-03-11 18:43:01)

Emit fixed use constraints for indirect call destinations
Work around https://github.com/bytecodealliance/regalloc2/issues/145 in the same way that we do for tail calls -- using a fixed use constraint.

<!--
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/.github/workflows/main.yml b/.github/workflows/main.yml
index e97968826..dd165d671 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -212,7 +212,7 @@ jobs:
         submodules: true
     - uses: ./.github/actions/install-rust
       with:
-        toolchain: nightly-2024-02-22
+        toolchain: nightly-2024-03-11
 
     # Build C API documentation
     - run: curl -L https://sourceforge.net/projects/doxygen/files/rel-1.9.3/doxygen-1.9.3.linux.bin.tar.gz/download | tar xzf -
@@ -397,7 +397,7 @@ jobs:
     # happen upstream. This is periodically updated through a PR.
     - uses: ./.github/actions/install-rust
       with:
-        toolchain: nightly-2024-02-22
+        toolchain: nightly-2024-03-11
 
     # Ensure that fuzzers sitll build.
     #
@@ -723,7 +723,7 @@ jobs:
         submodules: true
     - uses: ./.github/actions/install-rust
       with:
-        toolchain: nightly-2024-02-22
+        toolchain: nightly-2024-03-11
     - run: rustup component add rust-src miri
     - uses: actions/cache@v4
       with:
diff --git a/Cargo.toml b/Cargo.toml
index 9a8dcd502..d6972080e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -135,7 +135,7 @@ authors = ["The Wasmtime Project Developers"]
 edition = "2021"
 # Wasmtime's current policy is that this number can be no larger than the
 # current stable release of Rust minus 2.
-rust-version = "1.73.0"
+rust-version = "1.74.0"
 
 [workspace.lints.rust]
 # Turn on some lints which are otherwise allow-by-default in rustc.
diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs
index dcd7e947b..440840c4d 100644
--- a/cranelift/codegen/src/isa/aarch64/abi.rs
+++ b/cranelift/codegen/src/isa/aarch64/abi.rs
@@ -110,7 +110,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
     where
         I: IntoIterator<Item = &'a ir::AbiParam>,
     {
-        if call_conv == isa::CallConv::Tail {
+        if matches!(call_conv, isa::CallConv::Tail | isa::CallConv::Winch) {
             return compute_arg_locs_tail(params, add_ret_area_ptr, args);
         }
 
diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
index d6027c074..af7e42fc5 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -867,12 +867,18 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
             collector.reg_clobbers(info.clobbers);
         }
         &Inst::CallInd { ref info, .. } => {
-            if info.callee_callconv == CallConv::Tail {
-                // TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
-                // This shouldn't be a fixed register constraint.
-                collector.reg_fixed_use(info.rn, xreg(1));
-            } else {
-                collector.reg_use(info.rn);
+            match info.callee_callconv {
+                CallConv::Tail => {
+                    // TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
+                    // This shouldn't be a fixed register constraint.
+                    collector.reg_fixed_use(info.rn, xreg(1))
+                }
+                CallConv::Winch => {
+                    // TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
+                    // This shouldn't be a fixed register constraint.
+                    collector.reg_fixed_use(info.rn, xreg(1))
+                }
+                _ => collector.reg_use(info.rn),
             }
             for u in &info.uses {
                 collector.reg_fixed_use(u.vreg, u.preg);
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index 3ebb6ece0..c970888d7 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -2370,6 +2370,11 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
                     // This shouldn't be a fixed register constraint.
                     collector.reg_fixed_use(*reg, regs::r15())
                 }
+                RegMem::Reg { reg } if info.callee_conv == CallConv::Winch => {
+                    // TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
+                    // This shouldn't be a fixed register constraint.
+                    collector.reg_fixed_use(*reg, regs::r15())
+                }
                 _ => dest.get_operands(collector),
             }
             for u in &info.uses {
diff --git a/crates/wasmtime/src/runtime/component/component.rs b/crates/wasmtime/src/runtime/component/component.rs
index af242d85d..25444ef6a 100644
--- a/crates/wasmtime/src/runtime/component/component.rs
+++ b/crates/wasmtime/src/runtime/component/component.rs
@@ -1,3 +1,5 @@
+use crate::component::matching::InstanceType;
+use crate::component::types;
 use crate::{
     code::CodeObject, code_memory::CodeMemory, instantiate::MmapVecWrapper,
     type_registry::TypeCollection, Engine, Module, ResourcesRequired,
@@ -284,6 +286,141 @@ impl Component {
         Component::from_parts(engine, code, None)
     }
 
+    /// Returns the type of this component as a [`types::Component`].
+    ///
+    /// This method enables runtime introspection of the type of a component
+    /// before instantiation, if necessary.
+    ///
+    /// ## Component types and Resources
+    ///
+    /// An important point to note here is that the precise type of imports and
+    /// exports of a component change when it is instantiated with respect to
+    /// resources. For example a [`Component`] represents an un-instantiated
+    /// component meaning that its imported resources are represeted as abstract
+    /// resource types. These abstract types are not equal to any other
+    /// component's types.
+    ///
+    /// For example:
+    ///
+    /// ```
+    /// # use wasmtime::Engine;
+    /// # use wasmtime::component::Component;
+    /// # use wasmtime::component::types::ComponentItem;
+    /// # fn main() -> wasmtime::Result<()> {
+    /// # let engine = Engine::default();
+    /// let a = Component::new(&engine, r#"
+    ///     (component (import "x" (type (sub resource))))
+    /// "#)?;
+    /// let b = Component::new(&engine, r#"
+    ///     (component (import "x" (type (sub resource))))
+    /// "#)?;
+    ///
+    /// let (_, a_ty) = a.component_type().imports(&engine).next().unwrap();
+    /// let (_, b_ty) = b.component_type().imports(&engine).next().unwrap();
+    ///
+    /// let a_ty = match a_ty {
+    ///     ComponentItem::Resource(ty) => ty,
+    ///     _ => unreachable!(),
+    /// };
+    /// let b_ty = match b_ty {
+    ///     ComponentItem::Resource(ty) => ty,
+    ///     _ => unreachable!(),
+    /// };
+    /// assert!(a_ty != b_ty);
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// Additionally, however, these abstract types are "substituted" during
+    /// instantiation meaning that a component type will appear to have changed
+    /// once it is instantiated.
+    ///
+    /// ```
+    /// # use wasmtime::{Engine, Store};
+    /// # use wasmtime::component::{Component, Linker, ResourceType};
+    /// # use wasmtime::component::types::ComponentItem;
+    /// # fn main() -> wasmtime::Result<()> {
+    /// # let engine = Engine::default();
+    /// // Here this component imports a resource and then exports it as-is
+    /// // which means that the export is equal to the import.
+    /// let a = Component::new(&engine, r#"
+    ///     (component
+    ///         (import "x" (type $x (sub resource)))
+    ///         (export "x" (type $x))
+    ///     )
+    /// "#)?;
+    ///
+    /// let (_, import) = a.component_type().imports(&engine).next().unwrap();
+    /// let (_, export) = a.component_type().exports(&engine).next().unwrap();
+    ///
+    /// let import = match import {
+    ///     ComponentItem::Resource(ty) => ty,
+    ///     _ => unreachable!(),
+    /// };
+    /// let export = match export {
+    ///     ComponentItem::Resource(ty) => ty,
+    ///     _ => unreachable!(),
+    /// };
+    /// assert_eq!(import, export);
+    ///
+    /// // However after instantiation the resource type "changes"
+    /// let mut store = Store::new(&engine, ());
+    /// let mut linker = Linker::new(&engine);
+    /// linker.root().resource("x", ResourceType::host::<()>(), |_, _| Ok(()))?;
+    /// let instance = linker.instantiate(&mut store, &a)?;
+    /// let instance_ty = instance.exports(&mut store).root().resource("x").unwrap();
+    ///
+    /// // Here `instance_ty` is not the same as either `import` or `export`,
+    /// // but it is equal to what we provided as an import.
+    /// assert!(instance_ty != import);
+    /// assert!(instance_ty != export);
+    /// assert!(instance_ty == ResourceType::host::<()>());
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// Finally, each instantiation of an exported resource from a component is
+    /// considered "fresh" for all instantiations meaning that different
+    /// instantiations will have different exported resource types:
+    ///
+    /// ```
+    /// # use wasmtime::{Engine, Store};
+    /// # use wasmtime::component::{Component, Linker};
+    /// # fn main() -> wasmtime::Result<()> {
+    /// # let engine = Engine::default();
+    /// let a = Component::new(&engine, r#"
+    ///     (component
+    ///         (type $x (resource (rep i32)))
+    ///         (export "x" (type $x))
+    ///     )
+    /// "#)?;
+    ///
+    /// let mut store = Store::new(&engine, ());
+    /// let linker = Linker::new(&engine);
+    /// let instance1 = linker.instantiate(&mut store, &a)?;
+    /// let instance2 = linker.instantiate(&mut store, &a)?;
+    ///
+    /// let x1 = instance1.exports(&mut store).root().resource("x").unwrap();
+    /// let x2 = instance2.exports(&mut store).root().resource("x").unwrap();
+    ///
+    /// // Despite these two resources being the same export of the same
+    /// // component they come from two different instances meaning that their
+    /// // types will be unique.
+    /// assert!(x1 != x2);
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn component_type(&self) -> types::Component {
+        let resources = Arc::new(PrimaryMap::new());
+        types::Component::from(
+            self.inner.ty,
+            &InstanceType {
+                types: self.types(),
+                resources: &resources,
+            },
+        )
+    }
+
     /// Final assembly step for a component from its in-memory representation.
     ///
     /// If the `artifacts` are specified as `None` here then they will be
diff --git a/crates/wasmtime/src/runtime/component/matching.rs b/crates/wasmtime/src/runtime/component/matching.rs
index 7c8e36a79..8fb2c02c7 100644
--- a/crates/wasmtime/src/runtime/component/matching.rs
+++ b/crates/wasmtime/src/runtime/component/matching.rs
@@ -195,7 +195,10 @@ impl<'a> InstanceType<'a> {
 
     pub fn resource_type(&self, index: TypeResourceTableIndex) -> ResourceType {
         let index = self.types[index].ty;
-        self.resources[index]
+        self.resources
+            .get(index)
+            .copied()
+            .unwrap_or_else(|| ResourceType::uninstantiated(&self.types, index))
     }
 }
 


Test patch:
diff --git a/cranelift/filetests/filetests/isa/aarch64/winch.clif b/cranelift/filetests/filetests/isa/aarch64/winch.clif
index a872dab3e..4168e3a7d 100644
--- a/cranelift/filetests/filetests/isa/aarch64/winch.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/winch.clif
@@ -28,10 +28,10 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   mov fp, sp
 ;   sub sp, sp, #16
 ; block0:
-;   str x0, [sp]
-;   load_ext_name x7, TestCase(%g)+0
-;   blr x7
-;   ldr x0, [sp]
+;   str x2, [sp]
+;   load_ext_name x1, TestCase(%g)+0
+;   blr x1
+;   ldr x2, [sp]
 ;   add sp, sp, #16
 ;   ldp fp, lr, [sp], #16
 ;   ret
@@ -42,13 +42,13 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   mov x29, sp
 ;   sub sp, sp, #0x10
 ; block1: ; offset 0xc
-;   stur x0, [sp]
-;   ldr x7, #0x18
+;   stur x2, [sp]
+;   ldr x1, #0x18
 ;   b #0x20
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   blr x7
-;   ldur x0, [sp]
+;   blr x1
+;   ldur x2, [sp]
 ;   add sp, sp, #0x10
 ;   ldp x29, x30, [sp], #0x10
 ;   ret
@@ -77,8 +77,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   sub sp, sp, #16
 ; block0:
 ;   str x0, [sp]
-;   load_ext_name x7, TestCase(%g)+0
-;   blr x7
+;   load_ext_name x1, TestCase(%g)+0
+;   blr x1
 ;   ldr x0, [sp]
 ;   add sp, sp, #16
 ;   ldp d8, d9, [sp], #16
@@ -109,11 +109,11 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   sub sp, sp, #0x10
 ; block1: ; offset 0x30
 ;   stur x0, [sp]
-;   ldr x7, #0x3c
+;   ldr x1, #0x3c
 ;   b #0x44
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   blr x7
+;   blr x1
 ;   ldur x0, [sp]
 ;   add sp, sp, #0x10
 ;   ldp d8, d9, [sp], #0x10
@@ -141,11 +141,11 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
 ; block0:
-;   mov x6, x0
-;   mov x0, x5
-;   mov x5, x6
-;   load_ext_name x8, TestCase(%g)+0
-;   blr x8
+;   mov x0, x2
+;   mov x2, x7
+;   mov x7, x0
+;   load_ext_name x1, TestCase(%g)+0
+;   blr x1
 ;   ldp fp, lr, [sp], #16
 ;   ret
 ;
@@ -154,14 +154,14 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
 ; block1: ; offset 0x8
-;   mov x6, x0
-;   mov x0, x5
-;   mov x5, x6
-;   ldr x8, #0x1c
+;   mov x0, x2
+;   mov x2, x7
+;   mov x7, x0
+;   ldr x1, #0x1c
 ;   b #0x24
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   blr x8
+;   blr x1
 ;   ldp x29, x30, [sp], #0x10
 ;   ret
 
@@ -187,11 +187,15 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   stp d10, d11, [sp, #-16]!
 ;   stp d8, d9, [sp, #-16]!
 ; block0:
-;   mov x6, x0
-;   mov x0, x5
-;   mov x5, x6
-;   load_ext_name x8, TestCase(%g)+0
-;   blr x8
+;   mov x7, x0
+;   mov x6, x4
+;   mov x4, x2
+;   mov x2, x5
+;   mov x5, x3
+;   mov x3, x1
+;   load_ext_name x1, TestCase(%g)+0
+;   blr x1
+;   mov x0, x2
 ;   ldp d8, d9, [sp], #16
 ;   ldp d10, d11, [sp], #16
 ;   ldp d12, d13, [sp], #16
@@ -218,14 +222,99 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   stp d10, d11, [sp, #-0x10]!
 ;   stp d8, d9, [sp, #-0x10]!
 ; block1: ; offset 0x2c
-;   mov x6, x0
-;   mov x0, x5
-;   mov x5, x6
-;   ldr x8, #0x40
-;   b #0x48
+;   mov x7, x0
+;   mov x6, x4
+;   mov x4, x2
+;   mov x2, x5
+;   mov x5, x3
+;   mov x3, x1
+;   ldr x1, #0x4c
+;   b #0x54
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   blr x8
+;   blr x1
+;   mov x0, x2
+;   ldp d8, d9, [sp], #0x10
+;   ldp d10, d11, [sp], #0x10
+;   ldp d12, d13, [sp], #0x10
+;   ldp d14, d15, [sp], #0x10
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
+;   ldp x29, x30, [sp], #0x10
+;   ret
+
+function u1:0() system_v {
+    sig0 = () winch
+    fn0 = u2:0 sig0
+
+block0:
+    v5 = func_addr.i64 fn0
+    call_indirect sig0, v5()
+    call_indirect sig0, v5()
+    return
+}
+
+; VCode:
+;   stp fp, lr, [sp, #-16]!
+;   mov fp, sp
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
+;   stp d14, d15, [sp, #-16]!
+;   stp d12, d13, [sp, #-16]!
+;   stp d10, d11, [sp, #-16]!
+;   stp d8, d9, [sp, #-16]!
+;   sub sp, sp, #16
+; block0:
+;   load_ext_name x1, User(userextname0)+0
+;   str x1, [sp]
+;   ldr x1, [sp]
+;   blr x1
+;   ldr x1, [sp]
+;   blr x1
+;   add sp, sp, #16
+;   ldp d8, d9, [sp], #16
+;   ldp d10, d11, [sp], #16
+;   ldp d12, d13, [sp], #16
+;   ldp d14, d15, [sp], #16
+;   ldp x19, x20, [sp], #16
+;   ldp x21, x22, [sp], #16
+;   ldp x23, x24, [sp], #16
+;   ldp x25, x26, [sp], #16
+;   ldp x27, x28, [sp], #16
+;   ldp fp, lr, [sp], #16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   stp x29, x30, [sp, #-0x10]!
+;   mov x29, sp
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
+;   stp d14, d15, [sp, #-0x10]!
+;   stp d12, d13, [sp, #-0x10]!
+;   stp d10, d11, [sp, #-0x10]!
+;   stp d8, d9, [sp, #-0x10]!
+;   sub sp, sp, #0x10
+; block1: ; offset 0x30
+;   ldr x1, #0x38
+;   b #0x40
+;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 u2:0 0
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   stur x1, [sp]
+;   ldur x1, [sp]
+;   blr x1
+;   ldur x1, [sp]
+;   blr x1
+;   add sp, sp, #0x10
 ;   ldp d8, d9, [sp], #0x10
 ;   ldp d10, d11, [sp], #0x10
 ;   ldp d12, d13, [sp], #0x10
diff --git a/cranelift/filetests/filetests/isa/x64/winch.clif b/cranelift/filetests/filetests/isa/x64/winch.clif
index 876d6c90f..e1845d9f9 100644
--- a/cranelift/filetests/filetests/isa/x64/winch.clif
+++ b/cranelift/filetests/filetests/isa/x64/winch.clif
@@ -38,8 +38,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   subq    %rsp, $16, %rsp
 ; block0:
 ;   movq    %rdi, rsp(0 + virtual offset)
-;   load_ext_name %g+0, %r10
-;   call    *%r10
+;   load_ext_name %g+0, %r15
+;   call    *%r15
 ;   movq    rsp(0 + virtual offset), %rax
 ;   addq    %rsp, $16, %rsp
 ;   movq    %rbp, %rsp
@@ -53,8 +53,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   subq $0x10, %rsp
 ; block1: ; offset 0x8
 ;   movq %rdi, (%rsp)
-;   movabsq $0, %r10 ; reloc_external Abs8 %g 0
-;   callq *%r10
+;   movabsq $0, %r15 ; reloc_external Abs8 %g 0
+;   callq *%r15
 ;   movq (%rsp), %rax
 ;   addq $0x10, %rsp
 ;   movq %rbp, %rsp
@@ -81,8 +81,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq    %r15, 48(%rsp)
 ; block0:
 ;   movq    %rdi, rsp(0 + virtual offset)
-;   load_ext_name %g+0, %r10
-;   call    *%r10
+;   load_ext_name %g+0, %r15
+;   call    *%r15
 ;   movq    rsp(0 + virtual offset), %rax
 ;   movq    16(%rsp), %rbx
 ;   movq    24(%rsp), %r12
@@ -106,8 +106,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq %r15, 0x30(%rsp)
 ; block1: ; offset 0x21
 ;   movq %rdi, (%rsp)
-;   movabsq $0, %r10 ; reloc_external Abs8 %g 0
-;   callq *%r10
+;   movabsq $0, %r15 ; reloc_external Abs8 %g 0
+;   callq *%r15
 ;   movq (%rsp), %rax
 ;   movq 0x10(%rsp), %rbx
 ;   movq 0x18(%rsp), %r12
@@ -135,8 +135,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq    %rdi, %rax
 ;   movq    %r9, %rdi
 ;   movq    %rax, %r9
-;   load_ext_name %g+0, %r11
-;   call    *%r11
+;   load_ext_name %g+0, %r15
+;   call    *%r15
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -149,8 +149,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq %rdi, %rax
 ;   movq %r9, %rdi
 ;   movq %rax, %r9
-;   movabsq $0, %r11 ; reloc_external Abs8 %g 0
-;   callq *%r11
+;   movabsq $0, %r15 ; reloc_external Abs8 %g 0
+;   callq *%r15
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
@@ -177,8 +177,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq    %rdi, %rax
 ;   movq    %r9, %rdi
 ;   movq    %rax, %r9
-;   load_ext_name %g+0, %r11
-;   call    *%r11
+;   load_ext_name %g+0, %r15
+;   call    *%r15
 ;   movq    0(%rsp), %rbx
 ;   movq    8(%rsp), %r12
 ;   movq    16(%rsp), %r13
@@ -203,8 +203,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq %rdi, %rax
 ;   movq %r9, %rdi
 ;   movq %rax, %r9
-;   movabsq $0, %r11 ; reloc_external Abs8 %g 0
-;   callq *%r11
+;   movabsq $0, %r15 ; reloc_external Abs8 %g 0
+;   callq *%r15
 ;   movq (%rsp), %rbx
 ;   movq 8(%rsp), %r12
 ;   movq 0x10(%rsp), %r13
@@ -215,3 +215,67 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   popq %rbp
 ;   retq
 
+function u1:0() system_v {
+    sig0 = () winch
+    fn0 = u2:0 sig0
+
+block0:
+    v5 = func_addr.i64 fn0
+    call_indirect sig0, v5()
+    call_indirect sig0, v5()
+    return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+;   subq    %rsp, $64, %rsp
+;   movq    %rbx, 16(%rsp)
+;   movq    %r12, 24(%rsp)
+;   movq    %r13, 32(%rsp)
+;   movq    %r14, 40(%rsp)
+;   movq    %r15, 48(%rsp)
+; block0:
+;   load_ext_name userextname0+0, %r15
+;   movq    %r15, rsp(0 + virtual offset)
+;   movq    rsp(0 + virtual offset), %r15
+;   call    *%r15
+;   movq    rsp(0 + virtual offset), %r15
+;   call    *%r15
+;   movq    16(%rsp), %rbx
+;   movq    24(%rsp), %r12
+;   movq    32(%rsp), %r13
+;   movq    40(%rsp), %r14
+;   movq    48(%rsp), %r15
+;   addq    %rsp, $64, %rsp
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+;   subq $0x40, %rsp
+;   movq %rbx, 0x10(%rsp)
+;   movq %r12, 0x18(%rsp)
+;   movq %r13, 0x20(%rsp)
+;   movq %r14, 0x28(%rsp)
+;   movq %r15, 0x30(%rsp)
+; block1: ; offset 0x21
+;   movabsq $0, %r15 ; reloc_external Abs8 u2:0 0
+;   movq %r15, (%rsp)
+;   movq (%rsp), %r15
+;   callq *%r15
+;   movq (%rsp), %r15
+;   callq *%r15
+;   movq 0x10(%rsp), %rbx
+;   movq 0x18(%rsp), %r12
+;   movq 0x20(%rsp), %r13
+;   movq 0x28(%rsp), %r14
+;   movq 0x30(%rsp), %r15
+;   addq $0x40, %rsp
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/tests/all/component_model/aot.rs b/tests/all/component_model/aot.rs
index 5fc5c8890..b94e8f085 100644
--- a/tests/all/component_model/aot.rs
+++ b/tests/all/component_model/aot.rs
@@ -1,5 +1,6 @@
 use anyhow::Result;
-use wasmtime::component::{Component, Linker};
+use wasmtime::component::types::ComponentItem;
+use wasmtime::component::{Component, Linker, Type};
 use wasmtime::{Module, Precompiled, Store};
 
 #[test]
@@ -133,3 +134,33 @@ fn detect_precompiled() -> Result<()> {
     );
     Ok(())
 }
+
+#[test]
+fn reflect_resource_import() -> Result<()> {
+    let engine = super::engine();
+    let c = Component::new(
+        &engine,
+        r#"
+        (component
+            (import "x" (type $x (sub resource)))
+            (import "y" (func (result (own $x))))
+        )
+        "#,
+    )?;
+    let ty = c.component_type();
+    let mut imports = ty.imports(&engine);
+    let (_, x) = imports.next().unwrap();
+    let (_, y) = imports.next().unwrap();
+    let x = match x {
+        ComponentItem::Resource(t) => t,
+        _ => unreachable!(),
+    };
+    let y = match y {
+        ComponentItem::ComponentFunc(t) => t,
+        _ => unreachable!(),
+    };
+    let result = y.results().next().unwrap();
+    assert_eq!(result, Type::Own(x));
+
+    Ok(())
+}