1 //! These tests are intended to exercise various relocation-based logic of
2 //! Wasmtime, especially the "jump veneer" insertion in the object-file-assembly
3 //! for when platform-specific relative call instructors can't always reach
4 //! their destination within the platform-specific limits.
5 //!
6 //! Note that the limits of AArch64 are primarily what's being stressed here
7 //! where the jump target for a call is 26-bits. On x86_64 the jump target is
8 //! 32-bits, and right now object files aren't supported larger than 4gb anyway
9 //! so we would need a lot of other support necessary to exercise that.
10
11 #![cfg(not(miri))]
12
13 use wasmtime::*;
14
15 const MB: usize = 1 << 20;
16 const PADDING: usize = if cfg!(target_pointer_width = "32") {
17 1 * MB
18 } else {
19 128 * MB
20 };
21
store_with_padding(padding: usize) -> Result<Store<()>>22 fn store_with_padding(padding: usize) -> Result<Store<()>> {
23 let mut config = Config::new();
24 // This is an internal debug-only setting specifically recognized for
25 // basically just this set of tests.
26 unsafe {
27 config.cranelift_flag_set(
28 "wasmtime_linkopt_padding_between_functions",
29 &padding.to_string(),
30 );
31 }
32 let engine = Engine::new(&config)?;
33 Ok(Store::new(&engine, ()))
34 }
35
36 #[test]
forward_call_works() -> Result<()>37 fn forward_call_works() -> Result<()> {
38 let mut store = store_with_padding(PADDING)?;
39 let module = Module::new(
40 store.engine(),
41 r#"
42 (module
43 (func (export "foo") (result i32)
44 call 1)
45 (func (result i32)
46 i32.const 4)
47 )
48 "#,
49 )?;
50
51 let i = Instance::new(&mut store, &module, &[])?;
52 let foo = i.get_typed_func::<(), i32>(&mut store, "foo")?;
53 assert_eq!(foo.call(&mut store, ())?, 4);
54 Ok(())
55 }
56
57 #[test]
backwards_call_works() -> Result<()>58 fn backwards_call_works() -> Result<()> {
59 let mut store = store_with_padding(PADDING)?;
60 let module = Module::new(
61 store.engine(),
62 r#"
63 (module
64 (func (result i32)
65 i32.const 4)
66 (func (export "foo") (result i32)
67 call 0)
68 )
69 "#,
70 )?;
71
72 let i = Instance::new(&mut store, &module, &[])?;
73 let foo = i.get_typed_func::<(), i32>(&mut store, "foo")?;
74 assert_eq!(foo.call(&mut store, ())?, 4);
75 Ok(())
76 }
77
78 #[test]
mixed() -> Result<()>79 fn mixed() -> Result<()> {
80 test_many_call_module(store_with_padding(MB)?)
81 }
82
83 #[test]
mixed_forced() -> Result<()>84 fn mixed_forced() -> Result<()> {
85 let mut config = Config::new();
86 unsafe {
87 config.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", "true");
88 }
89 let engine = Engine::new(&config)?;
90 test_many_call_module(Store::new(&engine, ()))
91 }
92
test_many_call_module(mut store: Store<()>) -> Result<()>93 fn test_many_call_module(mut store: Store<()>) -> Result<()> {
94 const N: i32 = 200;
95
96 let mut wat = String::new();
97 wat.push_str("(module\n");
98 wat.push_str("(func $first (result i32) (i32.const 1))\n");
99 for i in 0..N {
100 wat.push_str(&format!("(func (export \"{i}\") (result i32 i32)\n"));
101 wat.push_str("call $first\n");
102 wat.push_str(&format!("i32.const {i}\n"));
103 wat.push_str("i32.add\n");
104 wat.push_str("call $last\n");
105 wat.push_str(&format!("i32.const {i}\n"));
106 wat.push_str("i32.add)\n");
107 }
108 wat.push_str("(func $last (result i32) (i32.const 2))\n");
109 wat.push_str(")\n");
110
111 let module = Module::new(store.engine(), &wat)?;
112
113 let instance = Instance::new(&mut store, &module, &[])?;
114
115 for i in 0..N {
116 let name = i.to_string();
117 let func = instance.get_typed_func::<(), (i32, i32)>(&mut store, &name)?;
118 let (a, b) = func.call(&mut store, ())?;
119 assert_eq!(a, i + 1);
120 assert_eq!(b, i + 2);
121 }
122 Ok(())
123 }
124