From 26ef2bd46bcf44bf740ed825e3c665ac7eb3b462 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 10 Jul 2023 19:14:28 +0200 Subject: [PATCH] wasm interpreter: use lookup table instead of unsafe --- crates/wasm_module/src/opcodes.rs | 193 +++++++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 2 deletions(-) diff --git a/crates/wasm_module/src/opcodes.rs b/crates/wasm_module/src/opcodes.rs index c5245dde831..340ad28571d 100644 --- a/crates/wasm_module/src/opcodes.rs +++ b/crates/wasm_module/src/opcodes.rs @@ -2,6 +2,7 @@ use crate::Serialize; use super::parse::{Parse, ParseError, SkipBytes}; +// NOTE: when adding a new variant, be sure to add it to LOOKUP_TABLE below as well #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum OpCode { @@ -186,9 +187,197 @@ pub enum OpCode { F64REINTERPRETI64 = 0xbf, } +pub const LOOKUP_TABLE: [Option; 256] = { + use OpCode::*; + + let mut result = [None; 256]; + + result[0x00] = Some(UNREACHABLE); + result[0x01] = Some(NOP); + result[0x02] = Some(BLOCK); + result[0x03] = Some(LOOP); + result[0x04] = Some(IF); + result[0x05] = Some(ELSE); + result[0x0b] = Some(END); + result[0x0c] = Some(BR); + result[0x0d] = Some(BRIF); + result[0x0e] = Some(BRTABLE); + result[0x0f] = Some(RETURN); + result[0x10] = Some(CALL); + result[0x11] = Some(CALLINDIRECT); + result[0x1a] = Some(DROP); + result[0x1b] = Some(SELECT); + result[0x20] = Some(GETLOCAL); + result[0x21] = Some(SETLOCAL); + result[0x22] = Some(TEELOCAL); + result[0x23] = Some(GETGLOBAL); + result[0x24] = Some(SETGLOBAL); + result[0x28] = Some(I32LOAD); + result[0x29] = Some(I64LOAD); + result[0x2a] = Some(F32LOAD); + result[0x2b] = Some(F64LOAD); + result[0x2c] = Some(I32LOAD8S); + result[0x2d] = Some(I32LOAD8U); + result[0x2e] = Some(I32LOAD16S); + result[0x2f] = Some(I32LOAD16U); + result[0x30] = Some(I64LOAD8S); + result[0x31] = Some(I64LOAD8U); + result[0x32] = Some(I64LOAD16S); + result[0x33] = Some(I64LOAD16U); + result[0x34] = Some(I64LOAD32S); + result[0x35] = Some(I64LOAD32U); + result[0x36] = Some(I32STORE); + result[0x37] = Some(I64STORE); + result[0x38] = Some(F32STORE); + result[0x39] = Some(F64STORE); + result[0x3a] = Some(I32STORE8); + result[0x3b] = Some(I32STORE16); + result[0x3c] = Some(I64STORE8); + result[0x3d] = Some(I64STORE16); + result[0x3e] = Some(I64STORE32); + result[0x3f] = Some(CURRENTMEMORY); + result[0x40] = Some(GROWMEMORY); + result[0xfc] = Some(MEMORY); + result[0x41] = Some(I32CONST); + result[0x42] = Some(I64CONST); + result[0x43] = Some(F32CONST); + result[0x44] = Some(F64CONST); + result[0x45] = Some(I32EQZ); + result[0x46] = Some(I32EQ); + result[0x47] = Some(I32NE); + result[0x48] = Some(I32LTS); + result[0x49] = Some(I32LTU); + result[0x4a] = Some(I32GTS); + result[0x4b] = Some(I32GTU); + result[0x4c] = Some(I32LES); + result[0x4d] = Some(I32LEU); + result[0x4e] = Some(I32GES); + result[0x4f] = Some(I32GEU); + result[0x50] = Some(I64EQZ); + result[0x51] = Some(I64EQ); + result[0x52] = Some(I64NE); + result[0x53] = Some(I64LTS); + result[0x54] = Some(I64LTU); + result[0x55] = Some(I64GTS); + result[0x56] = Some(I64GTU); + result[0x57] = Some(I64LES); + result[0x58] = Some(I64LEU); + result[0x59] = Some(I64GES); + result[0x5a] = Some(I64GEU); + result[0x5b] = Some(F32EQ); + result[0x5c] = Some(F32NE); + result[0x5d] = Some(F32LT); + result[0x5e] = Some(F32GT); + result[0x5f] = Some(F32LE); + result[0x60] = Some(F32GE); + result[0x61] = Some(F64EQ); + result[0x62] = Some(F64NE); + result[0x63] = Some(F64LT); + result[0x64] = Some(F64GT); + result[0x65] = Some(F64LE); + result[0x66] = Some(F64GE); + result[0x67] = Some(I32CLZ); + result[0x68] = Some(I32CTZ); + result[0x69] = Some(I32POPCNT); + result[0x6a] = Some(I32ADD); + result[0x6b] = Some(I32SUB); + result[0x6c] = Some(I32MUL); + result[0x6d] = Some(I32DIVS); + result[0x6e] = Some(I32DIVU); + result[0x6f] = Some(I32REMS); + result[0x70] = Some(I32REMU); + result[0x71] = Some(I32AND); + result[0x72] = Some(I32OR); + result[0x73] = Some(I32XOR); + result[0x74] = Some(I32SHL); + result[0x75] = Some(I32SHRS); + result[0x76] = Some(I32SHRU); + result[0x77] = Some(I32ROTL); + result[0x78] = Some(I32ROTR); + result[0x79] = Some(I64CLZ); + result[0x7a] = Some(I64CTZ); + result[0x7b] = Some(I64POPCNT); + result[0x7c] = Some(I64ADD); + result[0x7d] = Some(I64SUB); + result[0x7e] = Some(I64MUL); + result[0x7f] = Some(I64DIVS); + result[0x80] = Some(I64DIVU); + result[0x81] = Some(I64REMS); + result[0x82] = Some(I64REMU); + result[0x83] = Some(I64AND); + result[0x84] = Some(I64OR); + result[0x85] = Some(I64XOR); + result[0x86] = Some(I64SHL); + result[0x87] = Some(I64SHRS); + result[0x88] = Some(I64SHRU); + result[0x89] = Some(I64ROTL); + result[0x8a] = Some(I64ROTR); + result[0x8b] = Some(F32ABS); + result[0x8c] = Some(F32NEG); + result[0x8d] = Some(F32CEIL); + result[0x8e] = Some(F32FLOOR); + result[0x8f] = Some(F32TRUNC); + result[0x90] = Some(F32NEAREST); + result[0x91] = Some(F32SQRT); + result[0x92] = Some(F32ADD); + result[0x93] = Some(F32SUB); + result[0x94] = Some(F32MUL); + result[0x95] = Some(F32DIV); + result[0x96] = Some(F32MIN); + result[0x97] = Some(F32MAX); + result[0x98] = Some(F32COPYSIGN); + result[0x99] = Some(F64ABS); + result[0x9a] = Some(F64NEG); + result[0x9b] = Some(F64CEIL); + result[0x9c] = Some(F64FLOOR); + result[0x9d] = Some(F64TRUNC); + result[0x9e] = Some(F64NEAREST); + result[0x9f] = Some(F64SQRT); + result[0xa0] = Some(F64ADD); + result[0xa1] = Some(F64SUB); + result[0xa2] = Some(F64MUL); + result[0xa3] = Some(F64DIV); + result[0xa4] = Some(F64MIN); + result[0xa5] = Some(F64MAX); + result[0xa6] = Some(F64COPYSIGN); + result[0xa7] = Some(I32WRAPI64); + result[0xa8] = Some(I32TRUNCSF32); + result[0xa9] = Some(I32TRUNCUF32); + result[0xaa] = Some(I32TRUNCSF64); + result[0xab] = Some(I32TRUNCUF64); + result[0xac] = Some(I64EXTENDSI32); + result[0xad] = Some(I64EXTENDUI32); + result[0xae] = Some(I64TRUNCSF32); + result[0xaf] = Some(I64TRUNCUF32); + result[0xb0] = Some(I64TRUNCSF64); + result[0xb1] = Some(I64TRUNCUF64); + result[0xb2] = Some(F32CONVERTSI32); + result[0xb3] = Some(F32CONVERTUI32); + result[0xb4] = Some(F32CONVERTSI64); + result[0xb5] = Some(F32CONVERTUI64); + result[0xb6] = Some(F32DEMOTEF64); + result[0xb7] = Some(F64CONVERTSI32); + result[0xb8] = Some(F64CONVERTUI32); + result[0xb9] = Some(F64CONVERTSI64); + result[0xba] = Some(F64CONVERTUI64); + result[0xbb] = Some(F64PROMOTEF32); + result[0xbc] = Some(I32REINTERPRETF32); + result[0xbd] = Some(I64REINTERPRETF64); + result[0xbe] = Some(F32REINTERPRETI32); + result[0xbf] = Some(F64REINTERPRETI64); + + result +}; + impl From for OpCode { - fn from(x: u8) -> Self { - unsafe { std::mem::transmute(x) } + fn from(value: u8) -> Self { + // invalid instruction bytes can be genuine because we don't support all instructions, and + // new ones get added or stabilized. It could also be a bug in the interpreter, e.g. some + // opcode does not move the instruction pointer correctly. + match LOOKUP_TABLE[value as usize] { + None => unreachable!("unsupported instruction byte {value:#x?}"), + Some(op) => op, + } } }