Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wast/src/core.rs
3068 views
1
use crate::WastContext;
2
use json_from_wast::{CoreConst, FloatConst, V128};
3
use std::fmt::{Display, LowerHex};
4
use wasmtime::{Result, Store, Val, bail, error::Context as _, format_err};
5
6
/// Translate from a `script::Value` to a `RuntimeValue`.
7
pub fn val(ctx: &mut WastContext, v: &CoreConst) -> Result<Val> {
8
use CoreConst::*;
9
10
Ok(match v {
11
I32 { value } => Val::I32(value.0),
12
I64 { value } => Val::I64(value.0),
13
F32 { value } => Val::F32(value.to_bits()),
14
F64 { value } => Val::F64(value.to_bits()),
15
V128(value) => Val::V128(value.to_u128().into()),
16
FuncRef {
17
value: None | Some(json_from_wast::FuncRef::Null),
18
} => Val::FuncRef(None),
19
20
ExternRef {
21
value: None | Some(json_from_wast::ExternRef::Null),
22
} => Val::ExternRef(None),
23
ExternRef {
24
value: Some(json_from_wast::ExternRef::Host(x)),
25
} => Val::ExternRef(if let Some(rt) = ctx.async_runtime.as_ref() {
26
Some(rt.block_on(wasmtime::ExternRef::new_async(&mut ctx.core_store, x.0))?)
27
} else {
28
Some(wasmtime::ExternRef::new(&mut ctx.core_store, x.0)?)
29
}),
30
31
AnyRef {
32
value: None | Some(json_from_wast::AnyRef::Null),
33
} => Val::AnyRef(None),
34
AnyRef {
35
value: Some(json_from_wast::AnyRef::Host(x)),
36
} => {
37
let x = if let Some(rt) = ctx.async_runtime.as_ref() {
38
rt.block_on(wasmtime::ExternRef::new_async(&mut ctx.core_store, x.0))?
39
} else {
40
wasmtime::ExternRef::new(&mut ctx.core_store, x.0)?
41
};
42
let x = wasmtime::AnyRef::convert_extern(&mut ctx.core_store, x)?;
43
Val::AnyRef(Some(x))
44
}
45
NullRef => Val::AnyRef(None),
46
other => bail!("couldn't convert {other:?} to a runtime value"),
47
})
48
}
49
50
fn extract_lane_as_i8(bytes: u128, lane: usize) -> i8 {
51
(bytes >> (lane * 8)) as i8
52
}
53
54
fn extract_lane_as_i16(bytes: u128, lane: usize) -> i16 {
55
(bytes >> (lane * 16)) as i16
56
}
57
58
fn extract_lane_as_i32(bytes: u128, lane: usize) -> i32 {
59
(bytes >> (lane * 32)) as i32
60
}
61
62
fn extract_lane_as_i64(bytes: u128, lane: usize) -> i64 {
63
(bytes >> (lane * 64)) as i64
64
}
65
66
pub fn match_val(store: &mut Store<()>, actual: &Val, expected: &CoreConst) -> Result<()> {
67
match (actual, expected) {
68
(_, CoreConst::Either { values }) => {
69
for expected in values {
70
if match_val(store, actual, expected).is_ok() {
71
return Ok(());
72
}
73
}
74
match_val(store, actual, &values[0])
75
}
76
77
(Val::I32(a), CoreConst::I32 { value }) => match_int(a, &value.0),
78
(Val::I64(a), CoreConst::I64 { value }) => match_int(a, &value.0),
79
80
// Note that these float comparisons are comparing bits, not float
81
// values, so we're testing for bit-for-bit equivalence
82
(Val::F32(a), CoreConst::F32 { value }) => match_f32(*a, value),
83
(Val::F64(a), CoreConst::F64 { value }) => match_f64(*a, value),
84
(Val::V128(a), CoreConst::V128(value)) => match_v128(a.as_u128(), value),
85
86
// Null references, or blanket "any reference" assertions
87
(
88
Val::FuncRef(None) | Val::ExternRef(None) | Val::AnyRef(None) | Val::ExnRef(None),
89
CoreConst::RefNull,
90
)
91
| (Val::FuncRef(_), CoreConst::FuncRef { value: None })
92
| (Val::AnyRef(_), CoreConst::AnyRef { value: None })
93
| (Val::ExternRef(_), CoreConst::ExternRef { value: None })
94
| (Val::AnyRef(None), CoreConst::NullRef)
95
| (Val::FuncRef(None), CoreConst::NullFuncRef)
96
| (Val::ExternRef(None), CoreConst::NullExternRef)
97
| (Val::ExnRef(None), CoreConst::NullExnRef)
98
| (
99
Val::FuncRef(None),
100
CoreConst::FuncRef {
101
value: Some(json_from_wast::FuncRef::Null),
102
},
103
)
104
| (
105
Val::AnyRef(None),
106
CoreConst::AnyRef {
107
value: Some(json_from_wast::AnyRef::Null),
108
},
109
)
110
| (
111
Val::ExternRef(None),
112
CoreConst::ExternRef {
113
value: Some(json_from_wast::ExternRef::Null),
114
},
115
)
116
| (
117
Val::ExnRef(None),
118
CoreConst::ExnRef {
119
value: Some(json_from_wast::ExnRef::Null),
120
},
121
) => Ok(()),
122
123
// Ideally we'd compare the actual index, but Wasmtime doesn't expose
124
// the raw index a function in the embedder API.
125
(
126
Val::FuncRef(Some(_)),
127
CoreConst::FuncRef {
128
value: Some(json_from_wast::FuncRef::Index(_)),
129
},
130
) => Ok(()),
131
132
(
133
Val::ExternRef(Some(x)),
134
CoreConst::ExternRef {
135
value: Some(json_from_wast::ExternRef::Host(y)),
136
},
137
) => {
138
let x = x
139
.data(store)?
140
.ok_or_else(|| {
141
format_err!("expected an externref of a u32, found externref without host data")
142
})?
143
.downcast_ref::<u32>()
144
.expect("only u32 externrefs created in wast test suites");
145
if *x == y.0 {
146
Ok(())
147
} else {
148
bail!("expected {} found {x}", y.0);
149
}
150
}
151
152
(Val::AnyRef(Some(x)), CoreConst::EqRef) => {
153
if x.is_eqref(store)? {
154
Ok(())
155
} else {
156
bail!("expected an eqref, found {x:?}");
157
}
158
}
159
(Val::AnyRef(Some(x)), CoreConst::I31Ref) => {
160
if x.is_i31(store)? {
161
Ok(())
162
} else {
163
bail!("expected a `(ref i31)`, found {x:?}");
164
}
165
}
166
(Val::AnyRef(Some(x)), CoreConst::StructRef) => {
167
if x.is_struct(store)? {
168
Ok(())
169
} else {
170
bail!("expected a struct reference, found {x:?}")
171
}
172
}
173
(Val::AnyRef(Some(x)), CoreConst::ArrayRef) => {
174
if x.is_array(store)? {
175
Ok(())
176
} else {
177
bail!("expected a array reference, found {x:?}")
178
}
179
}
180
(
181
Val::AnyRef(Some(x)),
182
CoreConst::AnyRef {
183
value: Some(json_from_wast::AnyRef::Host(y)),
184
},
185
) => {
186
let x = wasmtime::ExternRef::convert_any(&mut *store, *x)?;
187
let x = x
188
.data(&mut *store)?
189
.ok_or_else(|| {
190
format_err!(
191
"expected anyref of externref of u32, found anyref that is \
192
not a converted externref"
193
)
194
})?
195
.downcast_ref::<u32>()
196
.expect("only u32 externrefs created in wast test suites");
197
if *x == y.0 {
198
Ok(())
199
} else {
200
bail!(
201
"expected anyref of externref of {}, found anyref of externref of {x}",
202
y.0
203
)
204
}
205
}
206
207
_ => bail!("expected {expected:?} got {actual:?}"),
208
}
209
}
210
211
pub fn match_int<T>(actual: &T, expected: &T) -> Result<()>
212
where
213
T: Eq + Display + LowerHex,
214
{
215
if actual == expected {
216
Ok(())
217
} else {
218
bail!(
219
"expected {expected:18} / {expected:#018x}\n\
220
actual {actual:18} / {actual:#018x}"
221
)
222
}
223
}
224
225
pub fn match_f32(actual: u32, expected: &FloatConst<f32>) -> Result<()> {
226
match expected {
227
// Check if an f32 (as u32 bits to avoid possible quieting when moving values in registers, e.g.
228
// https://developer.arm.com/documentation/ddi0344/i/neon-and-vfp-programmers-model/modes-of-operation/default-nan-mode?lang=en)
229
// is a canonical NaN:
230
// - the sign bit is unspecified,
231
// - the 8-bit exponent is set to all 1s
232
// - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0.
233
// See https://webassembly.github.io/spec/core/syntax/values.html#floating-point.
234
FloatConst::CanonicalNan => {
235
let canon_nan = 0x7fc0_0000;
236
if (actual & 0x7fff_ffff) == canon_nan {
237
Ok(())
238
} else {
239
bail!(
240
"expected {:10} / {:#010x}\n\
241
actual {:10} / {:#010x}",
242
"canon-nan",
243
canon_nan,
244
f32::from_bits(actual),
245
actual,
246
)
247
}
248
}
249
250
// Check if an f32 (as u32, see comments above) is an arithmetic NaN.
251
// This is the same as a canonical NaN including that the payload MSB is
252
// set to 1, but one or more of the remaining payload bits MAY BE set to
253
// 1 (a canonical NaN specifies all 0s). See
254
// https://webassembly.github.io/spec/core/syntax/values.html#floating-point.
255
FloatConst::ArithmeticNan => {
256
const AF32_NAN: u32 = 0x7f80_0000;
257
let is_nan = actual & AF32_NAN == AF32_NAN;
258
const AF32_PAYLOAD_MSB: u32 = 0x0040_0000;
259
let is_msb_set = actual & AF32_PAYLOAD_MSB == AF32_PAYLOAD_MSB;
260
if is_nan && is_msb_set {
261
Ok(())
262
} else {
263
bail!(
264
"expected {:>10} / {:>10}\n\
265
actual {:10} / {:#010x}",
266
"arith-nan",
267
"0x7fc*****",
268
f32::from_bits(actual),
269
actual,
270
)
271
}
272
}
273
FloatConst::Value(expected_value) => {
274
if actual == expected_value.to_bits() {
275
Ok(())
276
} else {
277
bail!(
278
"expected {:10} / {:#010x}\n\
279
actual {:10} / {:#010x}",
280
expected_value,
281
expected_value.to_bits(),
282
f32::from_bits(actual),
283
actual,
284
)
285
}
286
}
287
}
288
}
289
290
pub fn match_f64(actual: u64, expected: &FloatConst<f64>) -> Result<()> {
291
match expected {
292
// Check if an f64 (as u64 bits to avoid possible quieting when moving values in registers, e.g.
293
// https://developer.arm.com/documentation/ddi0344/i/neon-and-vfp-programmers-model/modes-of-operation/default-nan-mode?lang=en)
294
// is a canonical NaN:
295
// - the sign bit is unspecified,
296
// - the 11-bit exponent is set to all 1s
297
// - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0.
298
// See https://webassembly.github.io/spec/core/syntax/values.html#floating-point.
299
FloatConst::CanonicalNan => {
300
let canon_nan = 0x7ff8_0000_0000_0000;
301
if (actual & 0x7fff_ffff_ffff_ffff) == canon_nan {
302
Ok(())
303
} else {
304
bail!(
305
"expected {:18} / {:#018x}\n\
306
actual {:18} / {:#018x}",
307
"canon-nan",
308
canon_nan,
309
f64::from_bits(actual),
310
actual,
311
)
312
}
313
}
314
315
// Check if an f64 (as u64, see comments above) is an arithmetic NaN. This is the same as a
316
// canonical NaN including that the payload MSB is set to 1, but one or more of the remaining
317
// payload bits MAY BE set to 1 (a canonical NaN specifies all 0s). See
318
// https://webassembly.github.io/spec/core/syntax/values.html#floating-point.
319
FloatConst::ArithmeticNan => {
320
const AF64_NAN: u64 = 0x7ff0_0000_0000_0000;
321
let is_nan = actual & AF64_NAN == AF64_NAN;
322
const AF64_PAYLOAD_MSB: u64 = 0x0008_0000_0000_0000;
323
let is_msb_set = actual & AF64_PAYLOAD_MSB == AF64_PAYLOAD_MSB;
324
if is_nan && is_msb_set {
325
Ok(())
326
} else {
327
bail!(
328
"expected {:>18} / {:>18}\n\
329
actual {:18} / {:#018x}",
330
"arith-nan",
331
"0x7ff8************",
332
f64::from_bits(actual),
333
actual,
334
)
335
}
336
}
337
FloatConst::Value(expected_value) => {
338
if actual == expected_value.to_bits() {
339
Ok(())
340
} else {
341
bail!(
342
"expected {:18} / {:#018x}\n\
343
actual {:18} / {:#018x}",
344
expected_value,
345
expected_value.to_bits(),
346
f64::from_bits(actual),
347
actual,
348
)
349
}
350
}
351
}
352
}
353
354
fn match_v128(actual: u128, expected: &V128) -> Result<()> {
355
match expected {
356
V128::I8 { value } => {
357
let actual = [
358
extract_lane_as_i8(actual, 0),
359
extract_lane_as_i8(actual, 1),
360
extract_lane_as_i8(actual, 2),
361
extract_lane_as_i8(actual, 3),
362
extract_lane_as_i8(actual, 4),
363
extract_lane_as_i8(actual, 5),
364
extract_lane_as_i8(actual, 6),
365
extract_lane_as_i8(actual, 7),
366
extract_lane_as_i8(actual, 8),
367
extract_lane_as_i8(actual, 9),
368
extract_lane_as_i8(actual, 10),
369
extract_lane_as_i8(actual, 11),
370
extract_lane_as_i8(actual, 12),
371
extract_lane_as_i8(actual, 13),
372
extract_lane_as_i8(actual, 14),
373
extract_lane_as_i8(actual, 15),
374
];
375
if actual == value.map(|i| i.0) {
376
return Ok(());
377
}
378
bail!(
379
"expected {expected:4?}\n\
380
actual {actual:4?}\n\
381
\n\
382
expected (hex) {expected:02x?}\n\
383
actual (hex) {actual:02x?}",
384
)
385
}
386
V128::I16 { value } => {
387
let actual = [
388
extract_lane_as_i16(actual, 0),
389
extract_lane_as_i16(actual, 1),
390
extract_lane_as_i16(actual, 2),
391
extract_lane_as_i16(actual, 3),
392
extract_lane_as_i16(actual, 4),
393
extract_lane_as_i16(actual, 5),
394
extract_lane_as_i16(actual, 6),
395
extract_lane_as_i16(actual, 7),
396
];
397
if actual == value.map(|i| i.0) {
398
return Ok(());
399
}
400
bail!(
401
"expected {expected:6?}\n\
402
actual {actual:6?}\n\
403
\n\
404
expected (hex) {expected:04x?}\n\
405
actual (hex) {actual:04x?}",
406
)
407
}
408
V128::I32 { value } => {
409
let actual = [
410
extract_lane_as_i32(actual, 0),
411
extract_lane_as_i32(actual, 1),
412
extract_lane_as_i32(actual, 2),
413
extract_lane_as_i32(actual, 3),
414
];
415
if actual == value.map(|i| i.0) {
416
return Ok(());
417
}
418
bail!(
419
"expected {expected:11?}\n\
420
actual {actual:11?}\n\
421
\n\
422
expected (hex) {expected:08x?}\n\
423
actual (hex) {actual:08x?}",
424
)
425
}
426
V128::I64 { value } => {
427
let actual = [
428
extract_lane_as_i64(actual, 0),
429
extract_lane_as_i64(actual, 1),
430
];
431
if actual == value.map(|i| i.0) {
432
return Ok(());
433
}
434
bail!(
435
"expected {expected:20?}\n\
436
actual {actual:20?}\n\
437
\n\
438
expected (hex) {expected:016x?}\n\
439
actual (hex) {actual:016x?}",
440
)
441
}
442
V128::F32 { value } => {
443
for (i, expected) in value.iter().enumerate() {
444
let a = extract_lane_as_i32(actual, i) as u32;
445
match_f32(a, expected).with_context(|| format!("difference in lane {i}"))?;
446
}
447
Ok(())
448
}
449
V128::F64 { value } => {
450
for (i, expected) in value.iter().enumerate() {
451
let a = extract_lane_as_i64(actual, i) as u64;
452
match_f64(a, expected).with_context(|| format!("difference in lane {i}"))?;
453
}
454
Ok(())
455
}
456
}
457
}
458
459