1use crate::cast;
2use crate::globals::Globals;
3use crate::import::{ImportInvalidError, ImportInvokeError, Importer};
4use crate::memory::Memory;
5use crate::stack::{Stack, StackAccess};
6use crate::table::Table;
7use crate::trap::{Result, Trap, TrapReason};
8use crate::value::{Float, LittleEndian, Value};
9use wain_ast as ast;
10use wain_ast::AsValType;
11
12fn fmin<F: Float>(l: F, r: F) -> F {
19 if l.is_nan() {
22 l.to_arithmetic_nan()
23 } else if r.is_nan() {
24 r.to_arithmetic_nan()
25 } else if l == r {
26 F::from_bits(l.to_bits() | r.to_bits())
27 } else {
28 l.min(r)
29 }
30}
31
32fn fmax<F: Float>(l: F, r: F) -> F {
34 if l.is_nan() {
36 l.to_arithmetic_nan()
37 } else if r.is_nan() {
38 r.to_arithmetic_nan()
39 } else if l == r {
40 F::from_bits(l.to_bits() & r.to_bits())
41 } else {
42 l.max(r)
43 }
44}
45
46enum ExecState {
47 Breaking(u32), Ret, Continue, }
51
52type ExecResult = Result<ExecState>;
53
54pub struct ModuleInstance<'module, 'source> {
56 ast: &'module ast::Module<'source>,
57 table: Table, memory: Memory, globals: Globals,
60}
61
62pub struct Runtime<'module, 'source, I: Importer> {
65 module: ModuleInstance<'module, 'source>,
66 stack: Stack,
67 importer: I,
68}
69
70impl<'m, 's, I: Importer> Runtime<'m, 's, I> {
71 pub fn instantiate(module: &'m ast::Module<'s>, importer: I) -> Result<Self> {
78 fn unknown_import(import: &ast::Import<'_>, at: usize) -> Box<Trap> {
81 Trap::new(
82 TrapReason::UnknownImport {
83 mod_name: import.mod_name.0.to_string(),
84 name: import.name.0.to_string(),
85 kind: "function",
86 },
87 at,
88 )
89 }
90
91 for func in module.funcs.iter() {
92 match &func.kind {
93 ast::FuncKind::Body { .. } => break, ast::FuncKind::Import(i) => {
95 let mod_name = &i.mod_name.0;
96 if mod_name != I::MODULE_NAME {
97 return Err(unknown_import(i, func.start));
98 }
99
100 let fty = &module.types[func.idx as usize];
101 let name = &i.name.0;
102 match importer.validate(name, &fty.params, fty.results.get(0).copied()) {
103 Some(ImportInvalidError::NotFound) => {
104 return Err(unknown_import(i, func.start));
105 }
106 Some(ImportInvalidError::SignatureMismatch {
107 expected_params,
108 expected_ret,
109 }) => {
110 return Err(Trap::new(
111 TrapReason::FuncSignatureMismatch {
112 import: Some((mod_name.to_string(), name.to_string())),
113 expected_params: expected_params.to_vec(),
114 expected_results: expected_ret.into_iter().collect(),
115 actual_params: fty.params.to_vec(),
116 actual_results: fty.results.clone(),
117 },
118 func.start,
119 ))
120 }
121 None => { }
122 }
123 }
124 }
125 }
126
127 let globals = Globals::instantiate(&module.globals)?;
129
130 let mut table = Table::allocate(&module.tables)?;
137 let mut memory = Memory::allocate(&module.memories)?;
139
140 let stack = Stack::default();
142
143 for elem in module.elems.iter() {
145 table.new_elem(elem, &globals)?;
146 }
147
148 for data in module.data.iter() {
150 memory.new_data(data, &globals)?;
151 }
152
153 let mut runtime = Self {
156 module: ModuleInstance {
157 ast: module,
158 table,
159 memory,
160 globals,
161 },
162 stack,
163 importer,
164 };
165
166 if let Some(start) = &runtime.module.ast.entrypoint {
168 runtime.invoke_by_funcidx(start.idx)?;
170 }
171
172 Ok(runtime)
173 }
174
175 pub fn module(&self) -> &'m ast::Module<'s> {
176 self.module.ast
177 }
178
179 pub fn memory(&self) -> &Memory {
180 &self.module.memory
181 }
182
183 pub fn get_global(&self, name: &str) -> Option<Value> {
184 self.module
185 .ast
186 .exports
187 .iter()
188 .find_map(|e| match e.kind {
189 ast::ExportKind::Global(idx) if e.name.0 == name => Some(idx),
190 _ => None,
191 })
192 .map(|idx| {
193 let ty = self.module.ast.globals[idx as usize].ty;
194 self.module.globals.get_any(idx, ty)
195 })
196 }
197
198 fn invoke_import(&mut self, import: &ast::Import<'s>, has_ret: bool, pos: usize) -> Result<bool> {
200 if import.mod_name.0 == I::MODULE_NAME {
201 match self
202 .importer
203 .call(&import.name.0, &mut self.stack, &mut self.module.memory)
204 {
205 Ok(()) => return Ok(has_ret),
206 Err(ImportInvokeError::Fatal { message }) => {
207 return Err(Trap::new(
208 TrapReason::ImportFuncCallFail {
209 mod_name: import.mod_name.0.to_string(),
210 name: import.name.0.to_string(),
211 msg: message,
212 },
213 pos,
214 ))
215 }
216 }
217 }
218 unreachable!(
219 "fatal: invalid import at runtime: {}::{}",
220 import.mod_name.0, import.name.0
221 );
222 }
223
224 fn invoke_by_funcidx(&mut self, funcidx: u32) -> Result<bool> {
228 let func = &self.module.ast.funcs[funcidx as usize];
229 let fty = &self.module.ast.types[func.idx as usize];
230
231 let (locals, body) = match &func.kind {
233 ast::FuncKind::Import(i) => return self.invoke_import(i, !fty.results.is_empty(), func.start),
234 ast::FuncKind::Body { locals, expr } => (locals, expr),
235 };
236
237 let prev_frame = self.stack.push_frame(&fty.params, locals);
238
239 for insn in body {
240 match insn.execute(self)? {
241 ExecState::Continue => {}
242 ExecState::Ret | ExecState::Breaking(_) => break,
247 }
248 }
249
250 let has_result = !fty.results.is_empty();
251 self.stack.pop_frame(prev_frame, has_result);
252 Ok(has_result)
253 }
254
255 pub fn invoke(&mut self, name: impl AsRef<str>, args: &[Value]) -> Result<Option<Value>> {
257 fn find_func_to_invoke(name: &str, exports: &[ast::Export<'_>]) -> Result<(u32, usize)> {
258 for export in exports {
259 if export.name.0 == name {
260 let actual = match export.kind {
261 ast::ExportKind::Func(idx) => return Ok((idx, export.start)),
262 ast::ExportKind::Table(_) => "table",
263 ast::ExportKind::Memory(_) => "memory",
264 ast::ExportKind::Global(_) => "global variable",
265 };
266 return Err(Trap::new(
267 TrapReason::WrongInvokeTarget {
268 name: name.to_string(),
269 actual: Some(actual),
270 },
271 export.start,
272 ));
273 }
274 }
275 Err(Trap::new(
276 TrapReason::WrongInvokeTarget {
277 name: name.to_string(),
278 actual: None,
279 },
280 0,
281 ))
282 }
283
284 let name = name.as_ref();
285 let (funcidx, start) = find_func_to_invoke(name, &self.module.ast.exports)?;
286 let arg_types = &self.module.ast.types[self.module.ast.funcs[funcidx as usize].idx as usize].params;
287
288 if args.iter().map(Value::valtype).ne(arg_types.iter().copied()) {
290 return Err(Trap::new(
291 TrapReason::InvokeInvalidArgs {
292 name: name.to_string(),
293 args: args.to_vec(),
294 arg_types: arg_types.to_vec(),
295 },
296 start,
297 ));
298 }
299
300 for arg in args {
302 self.stack.push(arg.clone());
303 }
304
305 if self.invoke_by_funcidx(funcidx)? {
306 Ok(Some(self.stack.pop()))
307 } else {
308 Ok(None)
309 }
310 }
311
312 fn mem_addr(&mut self, mem: &ast::Mem) -> usize {
313 let addr = self.stack.pop::<i32>() as u32 as usize;
314 addr + mem.offset as usize
315 }
316
317 fn load<V: LittleEndian>(&mut self, mem: &ast::Mem, at: usize) -> Result<V> {
318 let addr = self.mem_addr(mem);
319 self.module.memory.load(addr, at)
320 }
321
322 fn store<V: LittleEndian>(&mut self, mem: &ast::Mem, v: V, at: usize) -> Result<()> {
323 let addr = self.mem_addr(mem);
324 self.module.memory.store(addr, v, at)?;
325 Ok(())
326 }
327
328 fn unop<T, F>(&mut self, op: F)
330 where
331 T: StackAccess + LittleEndian,
332 F: FnOnce(T) -> T,
333 {
334 let ret = op(self.stack.top());
336 self.stack.write_top_bytes(ret);
337 }
338
339 fn binop<T, F>(&mut self, op: F)
341 where
342 T: StackAccess + LittleEndian,
343 F: FnOnce(T, T) -> T,
344 {
345 let c2 = self.stack.pop();
347 let c1 = self.stack.top();
348 let ret = op(c1, c2);
349 self.stack.write_top_bytes(ret);
350 }
351
352 fn binop_trap<T, F>(&mut self, op: F) -> Result<()>
353 where
354 T: StackAccess + LittleEndian,
355 F: FnOnce(T, T) -> Result<T>,
356 {
357 let c2 = self.stack.pop();
359 let c1 = self.stack.top();
360 let ret = op(c1, c2)?;
361 self.stack.write_top_bytes(ret);
362 Ok(())
363 }
364
365 fn testop<T, F>(&mut self, op: F)
367 where
368 T: StackAccess + LittleEndian,
369 F: FnOnce(T) -> bool,
370 {
371 let ret = op(self.stack.top());
372 self.stack.write_top::<T, i32>(if ret { 1 } else { 0 });
373 }
374
375 fn relop<T, F>(&mut self, op: F)
377 where
378 T: StackAccess + LittleEndian,
379 F: FnOnce(T, T) -> bool,
380 {
381 let c2 = self.stack.pop();
382 let c1 = self.stack.top();
383 let ret = op(c1, c2);
384 self.stack.write_top::<T, i32>(if ret { 1i32 } else { 0 });
385 }
386
387 fn cvtop<T, U, F>(&mut self, op: F)
389 where
390 T: StackAccess,
391 U: StackAccess + LittleEndian + AsValType,
392 F: FnOnce(T) -> U,
393 {
394 let ret = op(self.stack.top());
395 self.stack.write_top::<T, U>(ret);
396 }
397
398 fn cvtop_trap<T, U, F>(&mut self, op: F) -> Result<()>
399 where
400 T: StackAccess,
401 U: StackAccess + LittleEndian + AsValType,
402 F: FnOnce(T) -> Result<U>,
403 {
404 let ret = op(self.stack.top())?;
405 self.stack.write_top::<T, U>(ret);
406 Ok(())
407 }
408}
409
410trait Execute<'m, 's, I: Importer> {
411 fn execute(&self, runtime: &mut Runtime<'m, 's, I>) -> ExecResult;
412}
413
414impl<'m, 's, I: Importer> Execute<'m, 's, I> for Vec<ast::Instruction> {
416 fn execute(&self, runtime: &mut Runtime<'m, 's, I>) -> ExecResult {
417 for insn in self {
419 match insn.execute(runtime)? {
420 ExecState::Continue => {}
421 state => return Ok(state), }
423 }
424 Ok(ExecState::Continue)
425 }
426}
427
428impl<'m, 's, I: Importer> Execute<'m, 's, I> for ast::Instruction {
430 #[allow(clippy::cognitive_complexity)]
431 fn execute(&self, runtime: &mut Runtime<'m, 's, I>) -> ExecResult {
432 use ast::InsnKind::*;
433 use std::ops::*;
434 #[allow(clippy::float_cmp)]
435 match &self.kind {
436 Block { ty, body } => {
439 let label = runtime.stack.push_label();
440 match body.execute(runtime)? {
441 ExecState::Continue => {}
442 ExecState::Ret => return Ok(ExecState::Ret),
443 ExecState::Breaking(0) => runtime.stack.pop_label(&label, ty.is_some()),
444 ExecState::Breaking(level) => return Ok(ExecState::Breaking(level - 1)),
445 }
446 }
447 Loop { body, .. } => {
449 let label = runtime.stack.push_label();
450 loop {
451 match body.execute(runtime)? {
454 ExecState::Continue => break,
455 ExecState::Ret => return Ok(ExecState::Ret),
456 ExecState::Breaking(0) => runtime.stack.pop_label(&label, false),
457 ExecState::Breaking(level) => return Ok(ExecState::Breaking(level - 1)),
458 }
459 }
460 }
461 If {
463 ty,
464 then_body,
465 else_body,
466 } => {
467 let cond: i32 = runtime.stack.pop();
468 let label = runtime.stack.push_label();
469 let insns = if cond != 0 { then_body } else { else_body };
470 match insns.execute(runtime)? {
471 ExecState::Continue => {}
472 ExecState::Ret => return Ok(ExecState::Ret),
473 ExecState::Breaking(0) => runtime.stack.pop_label(&label, ty.is_some()),
474 ExecState::Breaking(level) => return Ok(ExecState::Breaking(level - 1)),
475 }
476 }
477 Unreachable => return Err(Trap::new(TrapReason::ReachUnreachable, self.start)),
479 Nop => { }
481 Br(labelidx) => return Ok(ExecState::Breaking(*labelidx)),
483 BrIf(labelidx) => {
485 let cond: i32 = runtime.stack.pop();
486 if cond != 0 {
487 return Ok(ExecState::Breaking(*labelidx));
488 }
489 }
490 BrTable { labels, default_label } => {
492 let idx = runtime.stack.pop::<i32>() as u32;
493 let idx = idx as usize;
494 let labelidx = if idx < labels.len() {
495 labels[idx]
496 } else {
497 *default_label
498 };
499 return Ok(ExecState::Breaking(labelidx));
500 }
501 Return => return Ok(ExecState::Ret),
503 Call(funcidx) => {
505 runtime.invoke_by_funcidx(*funcidx)?;
506 }
507 CallIndirect(typeidx) => {
509 let expected = &runtime.module.ast.types[*typeidx as usize];
510 let elemidx = runtime.stack.pop::<i32>() as u32;
511 let funcidx = runtime.module.table.at(elemidx as usize, self.start)?;
512 let func = &runtime.module.ast.funcs[funcidx as usize];
513 let actual = &runtime.module.ast.types[func.idx as usize];
514 if expected.params != actual.params || expected.results != actual.results {
515 return Err(Trap::new(
516 TrapReason::FuncSignatureMismatch {
517 import: None,
518 expected_params: expected.params.clone(),
519 expected_results: expected.results.clone(),
520 actual_params: actual.params.clone(),
521 actual_results: actual.results.clone(),
522 },
523 self.start,
524 ));
525 }
526 runtime.invoke_by_funcidx(funcidx)?;
527 }
528 Drop => {
531 runtime.stack.pop::<Value>();
532 }
533 Select => {
535 let cond: i32 = runtime.stack.pop();
536 let val2: Value = runtime.stack.pop();
537 if cond == 0 {
542 let _val1: Value = runtime.stack.pop();
543 runtime.stack.push(val2);
544 }
545 }
546 LocalGet(localidx) => {
549 runtime.stack.read_local(*localidx);
550 }
551 LocalSet(localidx) => {
553 runtime.stack.write_local(*localidx);
554 let _val: Value = runtime.stack.pop();
555 }
556 LocalTee(localidx) => {
558 runtime.stack.write_local(*localidx);
560 }
561 GlobalGet(globalidx) => match runtime.module.ast.globals[*globalidx as usize].ty {
563 ast::ValType::I32 => runtime.stack.push(runtime.module.globals.get::<i32>(*globalidx)),
564 ast::ValType::I64 => runtime.stack.push(runtime.module.globals.get::<i64>(*globalidx)),
565 ast::ValType::F32 => runtime.stack.push(runtime.module.globals.get::<f32>(*globalidx)),
566 ast::ValType::F64 => runtime.stack.push(runtime.module.globals.get::<f64>(*globalidx)),
567 },
568 GlobalSet(globalidx) => runtime.module.globals.set_any(*globalidx, runtime.stack.top()),
570 I32Load(mem) => {
573 let v: i32 = runtime.load(mem, self.start)?;
574 runtime.stack.push(v);
575 }
576 I64Load(mem) => {
577 let v: i64 = runtime.load(mem, self.start)?;
578 runtime.stack.push(v);
579 }
580 F32Load(mem) => {
581 let v: f32 = runtime.load(mem, self.start)?;
582 runtime.stack.push(v);
583 }
584 F64Load(mem) => {
585 let v: f64 = runtime.load(mem, self.start)?;
586 runtime.stack.push(v);
587 }
588 I32Load8S(mem) => {
589 let v: i8 = runtime.load(mem, self.start)?;
590 runtime.stack.push(v as i32);
591 }
592 I32Load8U(mem) => {
593 let v: u8 = runtime.load(mem, self.start)?;
594 runtime.stack.push(v as i32);
595 }
596 I32Load16S(mem) => {
597 let v: i16 = runtime.load(mem, self.start)?;
598 runtime.stack.push(v as i32);
599 }
600 I32Load16U(mem) => {
601 let v: u16 = runtime.load(mem, self.start)?;
602 runtime.stack.push(v as i32);
603 }
604 I64Load8S(mem) => {
605 let v: i8 = runtime.load(mem, self.start)?;
606 runtime.stack.push(v as i64);
607 }
608 I64Load8U(mem) => {
609 let v: u8 = runtime.load(mem, self.start)?;
610 runtime.stack.push(v as i64);
611 }
612 I64Load16S(mem) => {
613 let v: i16 = runtime.load(mem, self.start)?;
614 runtime.stack.push(v as i64);
615 }
616 I64Load16U(mem) => {
617 let v: u16 = runtime.load(mem, self.start)?;
618 runtime.stack.push(v as i64);
619 }
620 I64Load32S(mem) => {
621 let v: i32 = runtime.load(mem, self.start)?;
622 runtime.stack.push(v as i64);
623 }
624 I64Load32U(mem) => {
625 let v: u32 = runtime.load(mem, self.start)?;
626 runtime.stack.push(v as i64);
627 }
628 I32Store(mem) => {
630 let v: i32 = runtime.stack.pop();
631 runtime.store(mem, v, self.start)?;
632 }
633 I64Store(mem) => {
634 let v: i64 = runtime.stack.pop();
635 runtime.store(mem, v, self.start)?;
636 }
637 F32Store(mem) => {
638 let v: f32 = runtime.stack.pop();
639 runtime.store(mem, v, self.start)?;
640 }
641 F64Store(mem) => {
642 let v: f64 = runtime.stack.pop();
643 runtime.store(mem, v, self.start)?;
644 }
645 I32Store8(mem) => {
646 let v: i32 = runtime.stack.pop();
647 runtime.store(mem, v as i8, self.start)?;
648 }
649 I32Store16(mem) => {
650 let v: i32 = runtime.stack.pop();
651 runtime.store(mem, v as i16, self.start)?;
652 }
653 I64Store8(mem) => {
654 let v: i64 = runtime.stack.pop();
655 runtime.store(mem, v as i8, self.start)?;
656 }
657 I64Store16(mem) => {
658 let v: i64 = runtime.stack.pop();
659 runtime.store(mem, v as i16, self.start)?;
660 }
661 I64Store32(mem) => {
662 let v: i64 = runtime.stack.pop();
663 runtime.store(mem, v as i32, self.start)?;
664 }
665 MemorySize => runtime.stack.push(runtime.module.memory.size() as i32),
667 MemoryGrow => {
669 let pages: i32 = runtime.stack.pop();
670 let prev_pages = runtime.module.memory.grow(pages as u32);
671 runtime.stack.push(prev_pages);
672 }
673 I32Const(i) => runtime.stack.push(*i),
676 I64Const(i) => runtime.stack.push(*i),
677 F32Const(f) => runtime.stack.push(*f),
678 F64Const(f) => runtime.stack.push(*f),
679 I32Clz => runtime.unop(|v: i32| v.leading_zeros() as i32),
682 I64Clz => runtime.unop(|v: i64| v.leading_zeros() as i64),
683 I32Ctz => runtime.unop(|v: i32| v.trailing_zeros() as i32),
685 I64Ctz => runtime.unop(|v: i64| v.trailing_zeros() as i64),
686 I32Popcnt => runtime.unop(|v: i32| v.count_ones() as i32),
688 I64Popcnt => runtime.unop(|v: i64| v.count_ones() as i64),
689 I32Add => runtime.binop(i32::wrapping_add),
691 I64Add => runtime.binop(i64::wrapping_add),
692 I32Sub => runtime.binop(i32::wrapping_sub),
694 I64Sub => runtime.binop(i64::wrapping_sub),
695 I32Mul => runtime.binop(i32::wrapping_mul),
697 I64Mul => runtime.binop(i64::wrapping_mul),
698 I32DivS => runtime.binop_trap(|l: i32, r| match l.checked_div(r) {
702 Some(i) => Ok(i),
703 None => Err(Trap::new(TrapReason::DivByZeroOrOverflow, self.start)),
704 })?,
705 I64DivS => runtime.binop_trap(|l: i64, r| match l.checked_div(r) {
706 Some(i) => Ok(i),
707 None => Err(Trap::new(TrapReason::DivByZeroOrOverflow, self.start)),
708 })?,
709 I32DivU => runtime.binop_trap(|l: i32, r| match (l as u32).checked_div(r as u32) {
711 Some(u) => Ok(u as i32),
712 None => Err(Trap::new(TrapReason::DivByZeroOrOverflow, self.start)),
713 })?,
714 I64DivU => runtime.binop_trap(|l: i64, r| match (l as u64).checked_div(r as u64) {
715 Some(u) => Ok(u as i64),
716 None => Err(Trap::new(TrapReason::DivByZeroOrOverflow, self.start)),
717 })?,
718 I32RemS => runtime.binop_trap(|l: i32, r| {
724 if r == 0 {
725 Err(Trap::new(TrapReason::RemZeroDivisor, self.start))
726 } else {
727 Ok(l.wrapping_rem(r))
728 }
729 })?,
730 I64RemS => runtime.binop_trap(|l: i64, r| {
731 if r == 0 {
732 Err(Trap::new(TrapReason::RemZeroDivisor, self.start))
733 } else {
734 Ok(l.wrapping_rem(r))
735 }
736 })?,
737 I32RemU => runtime.binop_trap(|l: i32, r| {
739 if r == 0 {
740 Err(Trap::new(TrapReason::RemZeroDivisor, self.start))
741 } else {
742 Ok((l as u32 % r as u32) as i32) }
744 })?,
745 I64RemU => runtime.binop_trap(|l: i64, r| {
746 if r == 0 {
747 Err(Trap::new(TrapReason::RemZeroDivisor, self.start))
748 } else {
749 Ok((l as u64 % r as u64) as i64) }
751 })?,
752 I32And => runtime.binop(i32::bitand),
754 I64And => runtime.binop(i64::bitand),
755 I32Or => runtime.binop(i32::bitor),
757 I64Or => runtime.binop(i64::bitor),
758 I32Xor => runtime.binop(i32::bitxor),
760 I64Xor => runtime.binop(i64::bitxor),
761 I32Shl => runtime.binop(|l: i32, r| l.wrapping_shl(r as u32)),
763 I64Shl => runtime.binop(|l: i64, r| l.wrapping_shl(r as u32)),
764 I32ShrS => runtime.binop(|l: i32, r| l.wrapping_shr(r as u32)),
766 I64ShrS => runtime.binop(|l: i64, r| l.wrapping_shr(r as u32)),
767 I32ShrU => runtime.binop(|l: i32, r| (l as u32).wrapping_shr(r as u32) as i32),
769 I64ShrU => runtime.binop(|l: i64, r| (l as u64).wrapping_shr(r as u32) as i64),
770 I32Rotl => runtime.binop(|l: i32, r| l.rotate_left(r as u32)),
772 I64Rotl => runtime.binop(|l: i64, r| l.rotate_left(r as u32)),
773 I32Rotr => runtime.binop(|l: i32, r| l.rotate_right(r as u32)),
775 I64Rotr => runtime.binop(|l: i64, r| l.rotate_right(r as u32)),
776 F32Abs => runtime.unop(f32::abs),
779 F64Abs => runtime.unop(f64::abs),
780 F32Neg => runtime.unop(f32::neg),
782 F64Neg => runtime.unop(f64::neg),
783 F32Ceil => runtime.unop(f32::ceil),
785 F64Ceil => runtime.unop(f64::ceil),
786 F32Floor => runtime.unop(f32::floor),
788 F64Floor => runtime.unop(f64::floor),
789 F32Trunc => runtime.unop(f32::trunc),
791 F64Trunc => runtime.unop(f64::trunc),
792 F32Nearest => runtime.unop(|f: f32| {
794 let fround = f.round();
798 if (f - fround).abs() == 0.5 && fround % 2.0 != 0.0 {
799 f.trunc()
800 } else {
801 fround
802 }
803 }),
804 F64Nearest => runtime.unop(|f: f64| {
805 let fround = f.round();
807 if (f - fround).abs() == 0.5 && fround % 2.0 != 0.0 {
808 f.trunc()
809 } else {
810 fround
811 }
812 }),
813 F32Sqrt => runtime.unop(f32::sqrt),
815 F64Sqrt => runtime.unop(f64::sqrt),
816 F32Add => runtime.binop(f32::add),
818 F64Add => runtime.binop(f64::add),
819 F32Sub => runtime.binop(f32::sub),
821 F64Sub => runtime.binop(f64::sub),
822 F32Mul => runtime.binop(f32::mul),
824 F64Mul => runtime.binop(f64::mul),
825 F32Div => runtime.binop(f32::div),
827 F64Div => runtime.binop(f64::div),
828 F32Min => runtime.binop(fmin::<f32>),
830 F64Min => runtime.binop(fmin::<f64>),
831 F32Max => runtime.binop(fmax::<f32>),
833 F64Max => runtime.binop(fmax::<f64>),
834 F32Copysign => runtime.binop(f32::copysign),
836 F64Copysign => runtime.binop(f64::copysign),
837 I32Eqz => runtime.testop(|i: i32| i == 0),
840 I64Eqz => runtime.testop(|i: i64| i == 0),
841 I32Eq => runtime.relop(|l: i32, r| l == r),
843 I64Eq => runtime.relop(|l: i64, r| l == r),
844 I32Ne => runtime.relop(|l: i32, r| l != r),
846 I64Ne => runtime.relop(|l: i64, r| l != r),
847 I32LtS => runtime.relop(|l: i32, r| l < r),
849 I64LtS => runtime.relop(|l: i64, r| l < r),
850 I32LtU => runtime.relop(|l: i32, r| (l as u32) < r as u32),
852 I64LtU => runtime.relop(|l: i64, r| (l as u64) < r as u64),
853 I32GtS => runtime.relop(|l: i32, r| l > r),
855 I64GtS => runtime.relop(|l: i64, r| l > r),
856 I32GtU => runtime.relop(|l: i32, r| l as u32 > r as u32),
858 I64GtU => runtime.relop(|l: i64, r| l as u64 > r as u64),
859 I32LeS => runtime.relop(|l: i32, r| l <= r),
861 I64LeS => runtime.relop(|l: i64, r| l <= r),
862 I32LeU => runtime.relop(|l: i32, r| l as u32 <= r as u32),
864 I64LeU => runtime.relop(|l: i64, r| l as u64 <= r as u64),
865 I32GeS => runtime.relop(|l: i32, r| l >= r),
867 I64GeS => runtime.relop(|l: i64, r| l >= r),
868 I32GeU => runtime.relop(|l: i32, r| l as u32 >= r as u32),
870 I64GeU => runtime.relop(|l: i64, r| l as u64 >= r as u64),
871 F32Eq => runtime.relop(|l: f32, r| l == r),
874 F64Eq => runtime.relop(|l: f64, r| l == r),
875 F32Ne => runtime.relop(|l: f32, r| l != r),
877 F64Ne => runtime.relop(|l: f64, r| l != r),
878 F32Lt => runtime.relop(|l: f32, r| l < r),
880 F64Lt => runtime.relop(|l: f64, r| l < r),
881 F32Gt => runtime.relop(|l: f32, r| l > r),
883 F64Gt => runtime.relop(|l: f64, r| l > r),
884 F32Le => runtime.relop(|l: f32, r| l <= r),
886 F64Le => runtime.relop(|l: f64, r| l <= r),
887 F32Ge => runtime.relop(|l: f32, r| l >= r),
889 F64Ge => runtime.relop(|l: f64, r| l >= r),
890 I64ExtendI32U => runtime.cvtop(|v: i32| v as u32 as i64),
893 I64ExtendI32S => runtime.cvtop(|v: i32| v as i64),
895 I32WrapI64 => runtime.cvtop(|v: i64| v as i32),
897 I32TruncF32U => runtime.cvtop_trap(|v: f32| match cast::f32_to_u32(v) {
899 Some(u) => Ok(u as i32),
900 None => Err(Trap::out_of_range(v, "u32", self.start)),
901 })?,
902 I32TruncF64U => runtime.cvtop_trap(|v: f64| match cast::f64_to_u32(v) {
903 Some(u) => Ok(u as i32),
904 None => Err(Trap::out_of_range(v, "u32", self.start)),
905 })?,
906 I64TruncF32U => runtime.cvtop_trap(|v: f32| match cast::f32_to_u64(v) {
907 Some(u) => Ok(u as i64),
908 None => Err(Trap::out_of_range(v, "u64", self.start)),
909 })?,
910 I64TruncF64U => runtime.cvtop_trap(|v: f64| match cast::f64_to_u64(v) {
911 Some(u) => Ok(u as i64),
912 None => Err(Trap::out_of_range(v, "u64", self.start)),
913 })?,
914 I32TruncF32S => runtime.cvtop_trap(|v: f32| match cast::f32_to_i32(v) {
916 Some(u) => Ok(u),
917 None => Err(Trap::out_of_range(v, "i32", self.start)),
918 })?,
919 I32TruncF64S => runtime.cvtop_trap(|v: f64| match cast::f64_to_i32(v) {
920 Some(u) => Ok(u),
921 None => Err(Trap::out_of_range(v, "i32", self.start)),
922 })?,
923 I64TruncF32S => runtime.cvtop_trap(|v: f32| match cast::f32_to_i64(v) {
924 Some(u) => Ok(u),
925 None => Err(Trap::out_of_range(v, "i64", self.start)),
926 })?,
927 I64TruncF64S => runtime.cvtop_trap(|v: f64| match cast::f64_to_i64(v) {
928 Some(u) => Ok(u),
929 None => Err(Trap::out_of_range(v, "i64", self.start)),
930 })?,
931 F64PromoteF32 => runtime.cvtop(|v: f32| v as f64),
933 F32DemoteF64 => runtime.cvtop(|v: f64| v as f32),
935 F32ConvertI32U => runtime.cvtop(|v: i32| v as u32 as f32),
937 F32ConvertI64U => runtime.cvtop(|v: i64| v as u64 as f32),
938 F64ConvertI32U => runtime.cvtop(|v: i32| v as u32 as f64),
939 F64ConvertI64U => runtime.cvtop(|v: i64| v as u64 as f64),
940 F32ConvertI32S => runtime.cvtop(|v: i32| v as f32),
942 F32ConvertI64S => runtime.cvtop(|v: i64| v as f32),
943 F64ConvertI32S => runtime.cvtop(|v: i32| v as f64),
944 F64ConvertI64S => runtime.cvtop(|v: i64| v as f64),
945 I32ReinterpretF32 => runtime.stack.write_top_type(i32::VAL_TYPE),
948 I64ReinterpretF64 => runtime.stack.write_top_type(i64::VAL_TYPE),
949 F32ReinterpretI32 => runtime.stack.write_top_type(f32::VAL_TYPE),
950 F64ReinterpretI64 => runtime.stack.write_top_type(f64::VAL_TYPE),
951 I32Extend8S => runtime.cvtop(|v: i32| i32::from(v as i8)),
953 I32Extend16S => runtime.cvtop(|v: i32| i32::from(v as i16)),
954 I64Extend8S => runtime.cvtop(|v: i64| i64::from(v as i8)),
955 I64Extend16S => runtime.cvtop(|v: i64| i64::from(v as i16)),
956 I64Extend32S => runtime.cvtop(|v: i64| i64::from(v as i32)),
957 }
958 Ok(ExecState::Continue)
959 }
960}
961
962#[cfg(test)]
963mod tests {
964 use super::*;
965 use crate::import::DefaultImporter;
966 use std::borrow::Cow;
967 use std::env;
968 use std::fmt;
969 use std::fs;
970 use std::io::{self, Read, Write};
971 use std::path::PathBuf;
972 use std::result;
973 use wain_syntax_text::parse;
974 use wain_validate::validate;
975
976 struct Discard;
977
978 impl Read for Discard {
979 fn read(&mut self, b: &mut [u8]) -> io::Result<usize> {
980 Ok(b.len())
981 }
982 }
983
984 impl Write for Discard {
985 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
986 Ok(buf.len())
987 }
988 fn flush(&mut self) -> io::Result<()> {
989 Ok(())
990 }
991 }
992
993 #[test]
994 fn hello_world() {
995 fn unwrap<T, E: fmt::Display>(res: result::Result<T, E>) -> T {
996 match res {
997 Ok(x) => x,
998 Err(e) => panic!("unwrap failed with error message:\n{}", e),
999 }
1000 }
1001
1002 fn exec(file: PathBuf) -> Result<Vec<u8>> {
1003 let source = fs::read_to_string(file).unwrap();
1004 let ast = unwrap(parse(&source));
1005 unwrap(validate(&ast));
1006 let mut stdout = vec![];
1007 {
1008 let importer = DefaultImporter::with_stdio(Discard, &mut stdout);
1009 let mut runtime = unwrap(Runtime::instantiate(&ast.module, importer));
1010 runtime.invoke("_start", &[])?;
1011 }
1012 Ok(stdout)
1013 }
1014
1015 let mut dir = env::current_dir().unwrap();
1016 dir.pop();
1017 dir.push("examples");
1018 dir.push("hello");
1019 let dir = dir;
1020
1021 let stdout = exec(dir.join("hello.wat")).unwrap();
1022 assert_eq!(stdout, b"Hello, world\n");
1023
1024 let stdout = exec(dir.join("hello_global.wat")).unwrap();
1025 assert_eq!(stdout, b"Hello, world\n");
1026
1027 let stdout = exec(dir.join("hello_indirect_call.wat")).unwrap();
1028 assert_eq!(stdout, b"Hello, world\n");
1029
1030 let stdout = exec(dir.join("hello_struct.wat")).unwrap();
1031 assert_eq!(stdout, b"Hello, world\n");
1032 }
1033
1034 fn exec_insns(ty: ast::ValType, insns: Vec<ast::InsnKind>) -> Result<Option<Value>> {
1035 let expr = insns
1036 .into_iter()
1037 .map(|kind| ast::Instruction { start: 0, kind })
1038 .collect();
1039
1040 let mut module = ast::Module::default();
1041 module.memories.push(ast::Memory {
1042 start: 0,
1043 ty: ast::MemType {
1044 limit: ast::Limits::From(0),
1045 },
1046 import: None,
1047 });
1048 module.types.push(ast::FuncType {
1049 start: 0,
1050 params: vec![],
1051 results: vec![ty],
1052 });
1053 module.funcs.push(ast::Func {
1054 start: 0,
1055 idx: 0,
1056 kind: ast::FuncKind::Body { locals: vec![], expr },
1057 });
1058 module.exports.push(ast::Export {
1059 start: 0,
1060 name: ast::Name(Cow::Borrowed("test")),
1061 kind: ast::ExportKind::Func(0),
1062 });
1063
1064 let importer = DefaultImporter::with_stdio(Discard, Discard);
1065 let mut runtime = Runtime::instantiate(&module, importer)?;
1066 runtime.invoke("test", &[])
1067 }
1068
1069 #[test]
1070 fn nearest_edge_cases() {
1071 use ast::InsnKind::*;
1072 use ast::ValType::*;
1073
1074 let f = exec_insns(F32, vec![F32Const(4.5), F32Nearest]).unwrap().unwrap();
1075 assert!(matches!(f, Value::F32(f) if f == 4.0));
1076
1077 let f = exec_insns(F32, vec![F32Const(3.5), F32Nearest]).unwrap().unwrap();
1078 assert!(matches!(f, Value::F32(f) if f == 4.0));
1079
1080 let f = exec_insns(F32, vec![F32Const(-0.5), F32Nearest]).unwrap().unwrap();
1081 assert!(matches!(f, Value::F32(f) if f == 0.0 && f.is_sign_negative())); let f = exec_insns(F32, vec![F32Const(0.5), F32Nearest]).unwrap().unwrap();
1084 assert!(matches!(f, Value::F32(f) if f == 0.0 && f.is_sign_positive())); let f = exec_insns(F64, vec![F64Const(4.5), F64Nearest]).unwrap().unwrap();
1087 assert!(matches!(f, Value::F64(f) if f == 4.0));
1088
1089 let f = exec_insns(F64, vec![F64Const(3.5), F64Nearest]).unwrap().unwrap();
1090 assert!(matches!(f, Value::F64(f) if f == 4.0));
1091
1092 let f = exec_insns(F64, vec![F64Const(-0.5), F64Nearest]).unwrap().unwrap();
1093 assert!(matches!(f, Value::F64(f) if f == 0.0 && f.is_sign_negative())); let f = exec_insns(F64, vec![F64Const(0.5), F64Nearest]).unwrap().unwrap();
1096 assert!(matches!(f, Value::F64(f) if f == 0.0 && f.is_sign_positive() ));
1097 }
1098
1099 #[test]
1100 fn int_overflow() {
1101 use ast::InsnKind::*;
1102 use ast::ValType::*;
1103
1104 let i = exec_insns(I32, vec![I32Const(i32::MAX), I32Const(1), I32Add])
1105 .unwrap()
1106 .unwrap();
1107 assert!(matches!(i, Value::I32(i) if i == i32::MIN));
1108
1109 let i = exec_insns(I32, vec![I32Const(i32::MIN), I32Const(1), I32Sub])
1110 .unwrap()
1111 .unwrap();
1112 assert!(matches!(i, Value::I32(i) if i == i32::MAX));
1113
1114 let i = exec_insns(I32, vec![I32Const(i32::MIN), I32Const(-1), I32Mul])
1115 .unwrap()
1116 .unwrap();
1117 assert!(matches!(i, Value::I32(i) if i == i32::MIN));
1118
1119 let i = exec_insns(I64, vec![I64Const(i64::MAX), I64Const(1), I64Add])
1120 .unwrap()
1121 .unwrap();
1122 assert!(matches!(i, Value::I64(i) if i == i64::MIN));
1123
1124 let i = exec_insns(I64, vec![I64Const(i64::MIN), I64Const(1), I64Sub])
1125 .unwrap()
1126 .unwrap();
1127 assert!(matches!(i, Value::I64(i) if i == i64::MAX));
1128
1129 let i = exec_insns(I64, vec![I64Const(i64::MIN), I64Const(-1), I64Mul])
1130 .unwrap()
1131 .unwrap();
1132 assert!(matches!(i, Value::I64(i) if i == i64::MIN));
1133 }
1134
1135 #[test]
1136 fn div_rem_edge_cases() {
1137 use ast::InsnKind::*;
1138 use ast::ValType::*;
1139
1140 let i = exec_insns(I32, vec![I32Const(i32::MIN), I32Const(-1), I32RemS])
1141 .unwrap()
1142 .unwrap();
1143 assert!(matches!(i, Value::I32(0)), "{:?}", i);
1144
1145 let i = exec_insns(I64, vec![I64Const(i64::MIN), I64Const(-1), I64RemS])
1146 .unwrap()
1147 .unwrap();
1148 assert!(matches!(i, Value::I64(0)), "{:?}", i);
1149
1150 let e = exec_insns(I32, vec![I32Const(1), I32Const(0), I32RemS]).unwrap_err();
1151 assert!(matches!(e.reason, TrapReason::RemZeroDivisor));
1152 let e = exec_insns(I32, vec![I32Const(1), I32Const(0), I32RemU]).unwrap_err();
1153 assert!(matches!(e.reason, TrapReason::RemZeroDivisor));
1154 let e = exec_insns(I64, vec![I64Const(1), I64Const(0), I64RemS]).unwrap_err();
1155 assert!(matches!(e.reason, TrapReason::RemZeroDivisor));
1156 let e = exec_insns(I64, vec![I64Const(1), I64Const(0), I64RemU]).unwrap_err();
1157 assert!(matches!(e.reason, TrapReason::RemZeroDivisor));
1158 }
1159
1160 #[test]
1161 fn fmin_edge_cases() {
1162 use ast::InsnKind::*;
1163 use ast::ValType::*;
1164
1165 let i = exec_insns(F32, vec![F32Const(0.0), F32Const(-0.0), F32Min])
1166 .unwrap()
1167 .unwrap();
1168 assert!(matches!(i, Value::F32(f) if f.to_bits() == 0x8000_0000));
1169 let i = exec_insns(F32, vec![F32Const(-0.0), F32Const(0.0), F32Min])
1170 .unwrap()
1171 .unwrap();
1172 assert!(matches!(i, Value::F32(f) if f.to_bits() == 0x8000_0000));
1173 let i = exec_insns(F32, vec![F32Const(1.0), F32Const(1.0), F32Min])
1174 .unwrap()
1175 .unwrap();
1176 assert!(matches!(i, Value::F32(f) if f == 1.0));
1177 let i = exec_insns(F32, vec![F32Const(-42.0), F32Const(-42.0), F32Min])
1178 .unwrap()
1179 .unwrap();
1180 assert!(matches!(i, Value::F32(f) if f == -42.0));
1181 let i = exec_insns(
1182 F32,
1183 vec![
1184 F32Const(f32::NEG_INFINITY),
1185 F32Const(f32::from_bits(0x7f80_0001)),
1186 F32Min,
1187 ],
1188 )
1189 .unwrap()
1190 .unwrap();
1191 assert!(matches!(i, Value::F32(f) if f.is_nan() && f.to_bits() == 0x7fc0_0001));
1192 let i = exec_insns(
1193 F32,
1194 vec![
1195 F32Const(f32::from_bits(0x7fff_ffff)),
1196 F32Const(f32::NEG_INFINITY),
1197 F32Min,
1198 ],
1199 )
1200 .unwrap()
1201 .unwrap();
1202 assert!(matches!(i, Value::F32(f) if f.is_nan() && f.to_bits() == 0x7fff_ffff));
1203 let i = exec_insns(
1204 F32,
1205 vec![
1206 F32Const(f32::from_bits(0x7f80_0001)),
1207 F32Const(f32::from_bits(0x7fff_ffff)),
1208 F32Min,
1209 ],
1210 )
1211 .unwrap()
1212 .unwrap();
1213 assert!(matches!(i, Value::F32(f) if f.is_nan() && f.to_bits() == 0x7fc0_0001));
1214
1215 let i = exec_insns(F64, vec![F64Const(0.0), F64Const(-0.0), F64Min])
1216 .unwrap()
1217 .unwrap();
1218 assert!(matches!(i, Value::F64(f) if f.to_bits() == 0x8000_0000_0000_0000));
1219 let i = exec_insns(F64, vec![F64Const(-0.0), F64Const(0.0), F64Min])
1220 .unwrap()
1221 .unwrap();
1222 assert!(matches!(i, Value::F64(f) if f.to_bits() == 0x8000_0000_0000_0000));
1223 let i = exec_insns(F64, vec![F64Const(1.0), F64Const(1.0), F64Min])
1224 .unwrap()
1225 .unwrap();
1226 assert!(matches!(i, Value::F64(f) if f == 1.0));
1227 let i = exec_insns(F64, vec![F64Const(-42.0), F64Const(-42.0), F64Min])
1228 .unwrap()
1229 .unwrap();
1230 assert!(matches!(i, Value::F64(f) if f == -42.0));
1231 let i = exec_insns(
1232 F64,
1233 vec![
1234 F64Const(f64::NEG_INFINITY),
1235 F64Const(f64::from_bits(0x7ff0_0000_0000_0001)),
1236 F64Min,
1237 ],
1238 )
1239 .unwrap()
1240 .unwrap();
1241 assert!(matches!(i, Value::F64(f) if f.is_nan() && f.to_bits() == 0x7ff8_0000_0000_0001));
1242 let i = exec_insns(
1243 F64,
1244 vec![
1245 F64Const(f64::from_bits(0x7fff_ffff_ffff_ffff)),
1246 F64Const(f64::NEG_INFINITY),
1247 F64Min,
1248 ],
1249 )
1250 .unwrap()
1251 .unwrap();
1252 assert!(matches!(i, Value::F64(f) if f.is_nan() && f.to_bits() == 0x7fff_ffff_ffff_ffff));
1253 let i = exec_insns(
1254 F64,
1255 vec![
1256 F64Const(f64::from_bits(0x7ff0_0000_0000_0001)),
1257 F64Const(f64::from_bits(0x7fff_ffff_ffff_ffff)),
1258 F64Min,
1259 ],
1260 )
1261 .unwrap()
1262 .unwrap();
1263 assert!(matches!(i, Value::F64(f) if f.is_nan() && f.to_bits() == 0x7ff8_0000_0000_0001));
1264 }
1265
1266 #[test]
1267 fn fmax_edge_cases() {
1268 use ast::InsnKind::*;
1269 use ast::ValType::*;
1270
1271 let i = exec_insns(F32, vec![F32Const(0.0), F32Const(-0.0), F32Max])
1272 .unwrap()
1273 .unwrap();
1274 assert!(matches!(i, Value::F32(f) if f.to_bits() == 0x0000_0000));
1275 let i = exec_insns(F32, vec![F32Const(-0.0), F32Const(0.0), F32Max])
1276 .unwrap()
1277 .unwrap();
1278 assert!(matches!(i, Value::F32(f) if f.to_bits() == 0x0000_0000));
1279 let i = exec_insns(F32, vec![F32Const(1.0), F32Const(1.0), F32Max])
1280 .unwrap()
1281 .unwrap();
1282 assert!(matches!(i, Value::F32(f) if f == 1.0));
1283 let i = exec_insns(F32, vec![F32Const(-42.0), F32Const(-42.0), F32Max])
1284 .unwrap()
1285 .unwrap();
1286 assert!(matches!(i, Value::F32(f) if f == -42.0));
1287 let i = exec_insns(
1288 F32,
1289 vec![F32Const(f32::INFINITY), F32Const(f32::from_bits(0x7f80_0001)), F32Max],
1290 )
1291 .unwrap()
1292 .unwrap();
1293 assert!(matches!(i, Value::F32(f) if f.is_nan() && f.to_bits() == 0x7fc0_0001));
1294 let i = exec_insns(
1295 F32,
1296 vec![F32Const(f32::from_bits(0x7fff_ffff)), F32Const(f32::INFINITY), F32Max],
1297 )
1298 .unwrap()
1299 .unwrap();
1300 assert!(matches!(i, Value::F32(f) if f.is_nan() && f.to_bits() == 0x7fff_ffff));
1301 let i = exec_insns(
1302 F32,
1303 vec![
1304 F32Const(f32::from_bits(0x7f80_0001)),
1305 F32Const(f32::from_bits(0x7fff_ffff)),
1306 F32Max,
1307 ],
1308 )
1309 .unwrap()
1310 .unwrap();
1311 assert!(matches!(i, Value::F32(f) if f.is_nan() && f.to_bits() == 0x7fc0_0001));
1312
1313 let i = exec_insns(F64, vec![F64Const(0.0), F64Const(-0.0), F64Max])
1314 .unwrap()
1315 .unwrap();
1316 assert!(matches!(i, Value::F64(f) if f.to_bits() == 0x0000_0000_0000_0000));
1317 let i = exec_insns(F64, vec![F64Const(-0.0), F64Const(0.0), F64Max])
1318 .unwrap()
1319 .unwrap();
1320 assert!(matches!(i, Value::F64(f) if f.to_bits() == 0x0000_0000_0000_0000));
1321 let i = exec_insns(F64, vec![F64Const(1.0), F64Const(1.0), F64Max])
1322 .unwrap()
1323 .unwrap();
1324 assert!(matches!(i, Value::F64(f) if f == 1.0));
1325 let i = exec_insns(F64, vec![F64Const(-42.0), F64Const(-42.0), F64Max])
1326 .unwrap()
1327 .unwrap();
1328 assert!(matches!(i, Value::F64(f) if f == -42.0));
1329 let i = exec_insns(
1330 F64,
1331 vec![
1332 F64Const(f64::INFINITY),
1333 F64Const(f64::from_bits(0x7ff0_0000_0000_0001)),
1334 F64Max,
1335 ],
1336 )
1337 .unwrap()
1338 .unwrap();
1339 assert!(matches!(i, Value::F64(f) if f.is_nan() && f.to_bits() == 0x7ff8_0000_0000_0001));
1340 let i = exec_insns(
1341 F64,
1342 vec![
1343 F64Const(f64::from_bits(0x7fff_ffff_ffff_ffff)),
1344 F64Const(f64::INFINITY),
1345 F64Max,
1346 ],
1347 )
1348 .unwrap()
1349 .unwrap();
1350 assert!(matches!(i, Value::F64(f) if f.is_nan() && f.to_bits() == 0x7fff_ffff_ffff_ffff));
1351 let i = exec_insns(
1352 F64,
1353 vec![
1354 F64Const(f64::from_bits(0x7ff0_0000_0000_0001)),
1355 F64Const(f64::from_bits(0x7fff_ffff_ffff_ffff)),
1356 F64Max,
1357 ],
1358 )
1359 .unwrap()
1360 .unwrap();
1361 assert!(matches!(i, Value::F64(f) if f.is_nan() && f.to_bits() == 0x7ff8_0000_0000_0001));
1362 }
1363}