Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/intrinsics.rs
2450 views
1
//! Tests related to the unsafe Wasmtime intrinsics we can give to components
2
//! via `CodeBuilder::expose_unsafe_intrinsics`.
3
4
use super::*;
5
use std::{cell::UnsafeCell, path::Path, sync::Arc};
6
use wasmtime::*;
7
8
#[test]
9
#[cfg_attr(miri, ignore)]
10
fn native_loads_and_stores() -> Result<()> {
11
let engine = Engine::default();
12
13
for (comp_ty, core_ty) in [
14
("u8", "i32"),
15
("u16", "i32"),
16
("u32", "i32"),
17
("u64", "i64"),
18
] {
19
let path = format!("intrinsics::native_loads_and_stores::{comp_ty}");
20
let path = Path::new(&path);
21
let wat = format!(
22
r#"
23
(component
24
(import "host" (instance $host (export "get-pointer" (func (result u64)))))
25
(import "unsafe-intrinsics"
26
(instance $intrinsics
27
(export "{comp_ty}-native-load" (func (param "pointer" u64) (result {comp_ty})))
28
(export "{comp_ty}-native-store" (func (param "pointer" u64) (param "value" {comp_ty})))
29
)
30
)
31
32
(core func $get-pointer' (canon lower (func $host "get-pointer")))
33
(core func $load' (canon lower (func $intrinsics "{comp_ty}-native-load")))
34
(core func $store' (canon lower (func $intrinsics "{comp_ty}-native-store")))
35
36
(core module $m
37
(import "" "get-pointer" (func $get-pointer (result i64)))
38
(import "" "load" (func $load (param i64) (result {core_ty})))
39
(import "" "store" (func $store (param i64 {core_ty})))
40
(func (export "run")
41
(local $pointer i64)
42
43
;; Get a native pointer from the host.
44
(local.set $pointer (call $get-pointer))
45
46
;; Assert that loading from the pointer results in `42`.
47
(if ({core_ty}.ne (call $load (local.get $pointer))
48
({core_ty}.const 42))
49
(then (unreachable)))
50
51
;; Store `-1` through the pointer.
52
(call $store (local.get $pointer) ({core_ty}.const -1))
53
)
54
)
55
56
(core instance $i
57
(instantiate $m
58
(with "" (instance (export "get-pointer" (func $get-pointer'))
59
(export "load" (func $load'))
60
(export "store" (func $store'))))
61
)
62
)
63
64
(func (export "run") (canon lift (core func $i "run")))
65
)
66
"#
67
);
68
let mut code_builder = CodeBuilder::new(&engine);
69
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
70
unsafe {
71
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
72
}
73
let component = code_builder.compile_component()?;
74
75
// Data that will be manipulated directly by Wasm via intrinsics.
76
let mut data = Arc::new(UnsafeCell::new(0x6666666666666666_u64));
77
78
// Write `42` into `data` at the appropriate place for this type.
79
let ptr = data.get();
80
unsafe {
81
match comp_ty {
82
"u8" => ptr.cast::<u8>().write(42),
83
"u16" => ptr.cast::<u16>().write(42),
84
"u32" => ptr.cast::<u32>().write(42),
85
"u64" => ptr.cast::<u64>().write(42),
86
_ => unreachable!(),
87
}
88
}
89
90
// Create a linker and define `host::get-pointer` to return a pointer to
91
// `data`.
92
let mut linker = component::Linker::new(&engine);
93
linker.instance("host")?.func_new("get-pointer", {
94
let ptr = ptr as usize;
95
let ptr = u64::try_from(ptr).unwrap();
96
move |_cx, _, _args, results| {
97
results[0] = component::Val::U64(ptr);
98
Ok(())
99
}
100
})?;
101
102
// Instantiate the component and call its `run` function.
103
let mut store = Store::new(&engine, ());
104
let instance = linker.instantiate(&mut store, &component)?;
105
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
106
run.call(&mut store, ())?;
107
108
// The `run` function should have written `-1` into its view of `data`
109
// and should not have modified any other part of it.
110
drop(linker);
111
let actual = Arc::get_mut(&mut data).unwrap().get_mut().to_ne_bytes();
112
let expected = match comp_ty {
113
"u8" => [0xFF, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66],
114
"u16" => [0xFF, 0xFF, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66],
115
"u32" => [0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x66, 0x66, 0x66],
116
"u64" => [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
117
_ => unreachable!(),
118
};
119
assert_eq!(
120
expected,
121
actual,
122
"expected != actual\n\
123
\texpected: {}\n\
124
\t actual: {}",
125
expected
126
.iter()
127
.map(|b| format!("{b:02x}"))
128
.collect::<Vec<_>>()
129
.join(" "),
130
actual
131
.iter()
132
.map(|b| format!("{b:02x}"))
133
.collect::<Vec<_>>()
134
.join(" "),
135
);
136
}
137
Ok(())
138
}
139
140
#[test]
141
#[cfg_attr(miri, ignore)]
142
fn cannot_enable_unsafe_intrinsics_for_core_module() -> Result<()> {
143
let engine = Engine::default();
144
let mut code_builder = CodeBuilder::new(&engine);
145
code_builder.wasm_binary_or_text("(module)".as_bytes(), None)?;
146
unsafe {
147
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
148
}
149
let err = code_builder.compile_module().unwrap_err();
150
let err = format!("{err:?}");
151
assert!(
152
err.contains("`CodeBuilder::expose_unsafe_intrinsics` can only be used with components"),
153
"unexpected error: {err}"
154
);
155
Ok(())
156
}
157
158
#[test]
159
#[cfg_attr(miri, ignore)]
160
fn ref_func_of_intrinsic() -> Result<()> {
161
let mut config = Config::new();
162
config.wasm_function_references(true);
163
config.wasm_gc(true);
164
let engine = Engine::new(&config)?;
165
166
let path = format!("intrinsics::ref_func_of_intrinsic");
167
let path = Path::new(&path);
168
169
let wat = r#"
170
(component
171
(import "unsafe-intrinsics"
172
(instance $intrinsics
173
(export "u32-native-load" (func (param "pointer" u64) (result u32)))
174
)
175
)
176
177
(core func $load' (canon lower (func $intrinsics "u32-native-load")))
178
179
(core module $m
180
(type $ty (func (param i64) (result i32)))
181
(import "" "load" (func $load (type $ty)))
182
(elem declare func $load)
183
(func (export "run") (param $pointer i64) (result i32)
184
(local $f (ref null func))
185
(local.set $f (ref.func $load))
186
(call_ref $ty (local.get $pointer) (ref.cast (ref $ty) (local.get $f)))
187
)
188
)
189
190
(core instance $i
191
(instantiate $m
192
(with "" (instance (export "load" (func $load'))))
193
)
194
)
195
196
(func (export "run") (param "pointer" u64) (result u32)
197
(canon lift (core func $i "run"))
198
)
199
)
200
"#;
201
202
let mut code_builder = CodeBuilder::new(&engine);
203
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
204
unsafe {
205
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
206
}
207
let component = code_builder.compile_component()?;
208
209
// Instantiate the component and call its `run` function.
210
let linker = component::Linker::new(&engine);
211
let mut store = Store::new(&engine, ());
212
let instance = linker.instantiate(&mut store, &component)?;
213
let run = instance.get_typed_func::<(u64,), (u32,)>(&mut store, "run")?;
214
215
let data = 0x12345678_u32;
216
let (result,) = run.call(&mut store, (&data as *const _ as usize as u64,))?;
217
assert_eq!(result, data);
218
219
Ok(())
220
}
221
222
#[test]
223
#[cfg_attr(miri, ignore)]
224
fn table_element_segment_with_intrinsic() -> Result<()> {
225
let engine = Engine::default();
226
227
let path = format!("intrinsics::ref_func_of_intrinsic");
228
let path = Path::new(&path);
229
230
let wat = r#"
231
(component
232
(import "unsafe-intrinsics"
233
(instance $intrinsics
234
(export "u32-native-load" (func (param "pointer" u64) (result u32)))
235
)
236
)
237
238
(core func $load' (canon lower (func $intrinsics "u32-native-load")))
239
240
(core module $m
241
(type $ty (func (param i64) (result i32)))
242
(import "" "load" (func $load (type $ty)))
243
(table $t 1 1 funcref)
244
(elem (table $t) (i32.const 0) func $load)
245
(func (export "run") (param $pointer i64) (result i32)
246
(call_indirect (type $ty) (local.get $pointer) (i32.const 0))
247
)
248
)
249
250
(core instance $i
251
(instantiate $m
252
(with "" (instance (export "load" (func $load'))))
253
)
254
)
255
256
(func (export "run") (param "pointer" u64) (result u32)
257
(canon lift (core func $i "run"))
258
)
259
)
260
"#;
261
262
let mut code_builder = CodeBuilder::new(&engine);
263
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
264
unsafe {
265
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
266
}
267
let component = code_builder.compile_component()?;
268
269
// Instantiate the component and call its `run` function.
270
let linker = component::Linker::new(&engine);
271
let mut store = Store::new(&engine, ());
272
let instance = linker.instantiate(&mut store, &component)?;
273
let run = instance.get_typed_func::<(u64,), (u32,)>(&mut store, "run")?;
274
275
let data = 0x12345678_u32;
276
let (result,) = run.call(&mut store, (&data as *const _ as usize as u64,))?;
277
assert_eq!(result, data);
278
279
Ok(())
280
}
281
282
#[test]
283
#[cfg_attr(miri, ignore)]
284
fn intrinsics_not_listed_in_imports() -> Result<()> {
285
let engine = Engine::default();
286
287
let path = format!("intrinsics::ref_func_of_intrinsic");
288
let path = Path::new(&path);
289
290
let wat = r#"
291
(component
292
(import "unsafe-intrinsics"
293
(instance $intrinsics
294
(export "u32-native-load" (func (param "pointer" u64) (result u32)))
295
)
296
)
297
)
298
"#;
299
300
let mut code_builder = CodeBuilder::new(&engine);
301
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
302
unsafe {
303
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
304
}
305
let component = code_builder.compile_component()?;
306
307
let component_type = component.component_type();
308
let imports = component_type.imports(&engine);
309
assert_eq!(imports.count(), 0);
310
311
Ok(())
312
}
313
314
#[test]
315
#[cfg_attr(miri, ignore)]
316
fn unknown_intrinsic_function() -> Result<()> {
317
let engine = Engine::default();
318
319
let path = format!("intrinsics::ref_func_of_intrinsic");
320
let path = Path::new(&path);
321
322
let wat = r#"
323
(component
324
(import "unsafe-intrinsics"
325
(instance $intrinsics
326
(export "unknown" (func (param "pointer" u64) (result u32)))
327
)
328
)
329
)
330
"#;
331
332
let mut code_builder = CodeBuilder::new(&engine);
333
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
334
unsafe {
335
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
336
}
337
let err = code_builder.compile_component().map(|_| ()).unwrap_err();
338
let err = format!("{err:?}");
339
assert!(
340
err.contains("invalid unsafe intrinsic: \"unknown\""),
341
"unexpected error: {err}"
342
);
343
344
Ok(())
345
}
346
347
#[test]
348
#[cfg_attr(miri, ignore)]
349
fn bad_extra_param() -> Result<()> {
350
let engine = Engine::default();
351
352
let path = format!("intrinsics::ref_func_of_intrinsic");
353
let path = Path::new(&path);
354
355
let wat = r#"
356
(component
357
(import "unsafe-intrinsics"
358
(instance $intrinsics
359
(export "u8-native-load" (func (param "pointer" u64) (param "extra" u64) (result u8)))
360
)
361
)
362
)
363
"#;
364
365
let mut code_builder = CodeBuilder::new(&engine);
366
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
367
unsafe {
368
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
369
}
370
let err = code_builder.compile_component().map(|_| ()).unwrap_err();
371
let err = format!("{err:?}");
372
assert!(
373
err.contains(
374
"bad unsafe intrinsics import at `unsafe-intrinsics`: function `u8-native-load` \
375
must have 1 parameters, found 2"
376
),
377
"unexpected error: {err}"
378
);
379
380
Ok(())
381
}
382
383
#[test]
384
#[cfg_attr(miri, ignore)]
385
fn bad_missing_param() -> Result<()> {
386
let engine = Engine::default();
387
388
let path = format!("intrinsics::ref_func_of_intrinsic");
389
let path = Path::new(&path);
390
391
let wat = r#"
392
(component
393
(import "unsafe-intrinsics"
394
(instance $intrinsics
395
(export "u8-native-load" (func (result u8)))
396
)
397
)
398
)
399
"#;
400
401
let mut code_builder = CodeBuilder::new(&engine);
402
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
403
unsafe {
404
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
405
}
406
let err = code_builder.compile_component().map(|_| ()).unwrap_err();
407
let err = format!("{err:?}");
408
assert!(
409
err.contains(
410
"bad unsafe intrinsics import at `unsafe-intrinsics`: function `u8-native-load` \
411
must have 1 parameters, found 0"
412
),
413
"unexpected error: {err}"
414
);
415
416
Ok(())
417
}
418
419
#[test]
420
#[cfg_attr(miri, ignore)]
421
fn bad_missing_return() -> Result<()> {
422
let engine = Engine::default();
423
424
let path = format!("intrinsics::ref_func_of_intrinsic");
425
let path = Path::new(&path);
426
427
let wat = r#"
428
(component
429
(import "unsafe-intrinsics"
430
(instance $intrinsics
431
(export "u8-native-load" (func (param "pointer" u64)))
432
)
433
)
434
)
435
"#;
436
437
let mut code_builder = CodeBuilder::new(&engine);
438
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
439
unsafe {
440
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
441
}
442
let err = code_builder.compile_component().map(|_| ()).unwrap_err();
443
let err = format!("{err:?}");
444
assert!(
445
err.contains(
446
"bad unsafe intrinsics import at `unsafe-intrinsics`: function `u8-native-load` must \
447
have 1 results, found 0"
448
),
449
"unexpected error: {err}"
450
);
451
452
Ok(())
453
}
454
455
#[test]
456
#[cfg_attr(miri, ignore)]
457
fn bad_extra_return() -> Result<()> {
458
let engine = Engine::default();
459
460
let path = format!("intrinsics::ref_func_of_intrinsic");
461
let path = Path::new(&path);
462
463
let wat = r#"
464
(component
465
(import "unsafe-intrinsics"
466
(instance $intrinsics
467
(export "u8-native-store" (func (param "pointer" u64) (param "value" u8) (result u32)))
468
)
469
)
470
)
471
"#;
472
473
let mut code_builder = CodeBuilder::new(&engine);
474
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
475
unsafe {
476
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
477
}
478
let err = code_builder.compile_component().map(|_| ()).unwrap_err();
479
let err = format!("{err:?}");
480
assert!(
481
err.contains(
482
"bad unsafe intrinsics import at `unsafe-intrinsics`: function `u8-native-store` \
483
must have 0 results, found 1"
484
),
485
"unexpected error: {err}"
486
);
487
488
Ok(())
489
}
490
491
#[test]
492
#[cfg_attr(miri, ignore)]
493
fn bad_param_type() -> Result<()> {
494
let engine = Engine::default();
495
496
let path = format!("intrinsics::ref_func_of_intrinsic");
497
let path = Path::new(&path);
498
499
let wat = r#"
500
(component
501
(import "unsafe-intrinsics"
502
(instance $intrinsics
503
(export "u8-native-store" (func (param "pointer" u64) (param "value" u16)))
504
)
505
)
506
)
507
"#;
508
509
let mut code_builder = CodeBuilder::new(&engine);
510
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
511
unsafe {
512
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
513
}
514
let err = code_builder.compile_component().map(|_| ()).unwrap_err();
515
let err = format!("{err:?}");
516
assert!(
517
err.contains(
518
"bad unsafe intrinsics import at `unsafe-intrinsics`: parameters[1] for function \
519
`u8-native-store` must be `U8`, found `Primitive(U16)`"
520
),
521
"unexpected error: {err}"
522
);
523
524
Ok(())
525
}
526
527
#[test]
528
#[cfg_attr(miri, ignore)]
529
fn bad_result_type() -> Result<()> {
530
let engine = Engine::default();
531
532
let path = format!("intrinsics::ref_func_of_intrinsic");
533
let path = Path::new(&path);
534
535
let wat = r#"
536
(component
537
(import "unsafe-intrinsics"
538
(instance $intrinsics
539
(export "u8-native-load" (func (param "pointer" u64) (result u16)))
540
)
541
)
542
)
543
"#;
544
545
let mut code_builder = CodeBuilder::new(&engine);
546
code_builder.wasm_binary_or_text(wat.as_bytes(), Some(path))?;
547
unsafe {
548
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
549
}
550
let err = code_builder.compile_component().map(|_| ()).unwrap_err();
551
let err = format!("{err:?}");
552
assert!(
553
err.contains(
554
"bad unsafe intrinsics import at `unsafe-intrinsics`: results[0] for function \
555
`u8-native-load` must be `U8`, found `Primitive(U16)`"
556
),
557
"unexpected error: {err}"
558
);
559
560
Ok(())
561
}
562
563
#[test]
564
#[cfg_attr(miri, ignore)]
565
fn store_data_address() -> Result<()> {
566
let engine = Engine::default();
567
568
let wat = r#"
569
(component
570
(import "unsafe-intrinsics"
571
(instance $intrinsics
572
(export "store-data-address" (func (result u64)))
573
(export "u8-native-load" (func (param "pointer" u64) (result u8)))
574
(export "u8-native-store" (func (param "pointer" u64) (param "value" u8)))
575
)
576
)
577
578
(core func $store-data-address' (canon lower (func $intrinsics "store-data-address")))
579
(core func $u8-native-load' (canon lower (func $intrinsics "u8-native-load")))
580
(core func $u8-native-store' (canon lower (func $intrinsics "u8-native-store")))
581
582
(core module $m
583
(import "" "store-data-address" (func $store-data-address (result i64)))
584
(import "" "u8-native-load" (func $load (param i64) (result i32)))
585
(import "" "u8-native-store" (func $store (param i64 i32)))
586
(func (export "run")
587
;; Load the store data, it should be 42.
588
(if (i32.ne (call $load (call $store-data-address))
589
(i32.const 42))
590
(then (unreachable)))
591
592
;; Store 36 into the store data.
593
(call $store (call $store-data-address) (i32.const 36))
594
)
595
)
596
597
(core instance $i
598
(instantiate $m
599
(with "" (instance (export "store-data-address" (func $store-data-address'))
600
(export "u8-native-load" (func $u8-native-load'))
601
(export "u8-native-store" (func $u8-native-store'))))
602
)
603
)
604
605
(func (export "run")
606
(canon lift (core func $i "run"))
607
)
608
)
609
"#;
610
611
let mut code_builder = CodeBuilder::new(&engine);
612
code_builder.wasm_binary_or_text(wat.as_bytes(), None)?;
613
unsafe {
614
code_builder.expose_unsafe_intrinsics("unsafe-intrinsics");
615
}
616
let component = code_builder.compile_component()?;
617
618
// Instantiate the component and call its `run` function.
619
let linker = component::Linker::new(&engine);
620
let mut store = Store::new(&engine, 42_u8);
621
let instance = linker.instantiate(&mut store, &component)?;
622
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
623
624
run.call(&mut store, ())?;
625
assert_eq!(*store.data(), 36);
626
627
Ok(())
628
}
629
630
#[test]
631
#[cfg_attr(miri, ignore)]
632
fn other_import_name() -> Result<()> {
633
let engine = Engine::default();
634
635
let wat = r#"
636
(component
637
(import "other-name"
638
(instance
639
(export "u32-native-load" (func (param "pointer" u64) (result u32)))
640
)
641
)
642
)
643
"#;
644
645
let mut code_builder = CodeBuilder::new(&engine);
646
code_builder.wasm_binary_or_text(wat.as_bytes(), None)?;
647
unsafe {
648
code_builder.expose_unsafe_intrinsics("other-name");
649
}
650
code_builder.compile_component()?;
651
652
Ok(())
653
}
654
655