#region License
/* 
 * Copyright (C) 1999-2024 John Källén.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#endregion

using Reko.Core.Machine;
using System.Collections.Generic;
using System.Globalization;

namespace Reko.ImageLoaders.WebAssembly
{
    public class WasmInstruction : MachineInstruction
    {
        internal static Dictionary<Mnemonic, string> mpoptostring = new Dictionary<Mnemonic, string>
        {
            { Mnemonic.unreachable, "unreachable" },
            { Mnemonic.nop, "nop" },
            { Mnemonic.block, "block" },
            { Mnemonic.loop, "loop" },
            { Mnemonic.@if, "if" },
            { Mnemonic.@else, "else" },
            { Mnemonic.end, "end" },
            { Mnemonic.br, "br" },
            { Mnemonic.br_if, "br_if" },
            { Mnemonic.br_table, "br_table" },
            { Mnemonic.@return, "return" },

            { Mnemonic.call, "call" },
            { Mnemonic.call_indirect, "call_indirect" },

            { Mnemonic.drop, "drop" },
            { Mnemonic.select, "select" },
            { Mnemonic.get_local, "get_local" },
            { Mnemonic.set_local, "set_local" },
            { Mnemonic.tee_local, "tee_local" },
            { Mnemonic.get_global, "get_global" },
            { Mnemonic.set_global, "set_global" },

            { Mnemonic.table_get, "table.get" },
            { Mnemonic.table_set, "table.set" },

            { Mnemonic.i32_load, "i32.load" },
            { Mnemonic.i64_load, "i64.load" },
            { Mnemonic.f32_load, "f32.load" },
            { Mnemonic.f64_load, "f64.load" },
            { Mnemonic.i32_load8_s, "i32.load8_s" },
            { Mnemonic.i32_load8_u, "i32.load8_u" },
            { Mnemonic.i32_load16_s, "i32.load16_s" },
            { Mnemonic.i32_load16_u, "i32.load16_u" },
            { Mnemonic.i64_load8_s, "i64.load8_s" },
            { Mnemonic.i64_load8_u, "i64.load8_u" },
            { Mnemonic.i64_load16_s, "i64.load16_s" },
            { Mnemonic.i64_load16_u, "i64.load16_u" },
            { Mnemonic.i64_load32_s, "i64.load32_s" },
            { Mnemonic.i64_load32_u, "i64.load32_u" },
            { Mnemonic.i32_store, "i32.store" },
            { Mnemonic.i64_store, "i64.store" },
            { Mnemonic.f32_store, "f32.store" },
            { Mnemonic.f64_store, "f64.store" },
            { Mnemonic.i32_store8, "i32.store8" },
            { Mnemonic.i32_store16, "i32.store16" },
            { Mnemonic.i64_store8, "i64.store8" },
            { Mnemonic.i64_store16, "i64.store16" },
            { Mnemonic.i64_store32, "i64.store32" },
            { Mnemonic.current_memory, "current_memory" },
            { Mnemonic.grow_memory, "grow_memory" },

            { Mnemonic.i32_const, "i32.const" },
            { Mnemonic.i64_const, "i64.const" },
            { Mnemonic.f32_const, "f32.const" },
            { Mnemonic.f64_const, "f64.const" },
            { Mnemonic.i32_eqz, "i32.eqz" },
            { Mnemonic.i32_eq, "i32.eq" },
            { Mnemonic.i32_ne, "i32.ne" },
            { Mnemonic.i32_lt_s, "i32.lt_s" },
            { Mnemonic.i32_lt_u, "i32.lt_u" },
            { Mnemonic.i32_gt_s, "i32.gt_s" },
            { Mnemonic.i32_gt_u, "i32.gt_u" },
            { Mnemonic.i32_le_s, "i32.le_s" },
            { Mnemonic.i32_le_u, "i32.le_u" },
            { Mnemonic.i32_ge_s, "i32.ge_s" },
            { Mnemonic.i32_ge_u, "i32.ge_u" },
            { Mnemonic.i64_eqz, "i64.eqz" },
            { Mnemonic.i64_eq, "i64.eq" },
            { Mnemonic.i64_ne, "i64.ne" },
            { Mnemonic.i64_lt_s, "i64.lt_s" },
            { Mnemonic.i64_lt_u, "i64.lt_u" },
            { Mnemonic.i64_gt_s, "i64.gt_s" },
            { Mnemonic.i64_gt_u, "i64.gt_u" },
            { Mnemonic.i64_le_s, "i64.le_s" },
            { Mnemonic.i64_le_u, "i64.le_u" },
            { Mnemonic.i64_ge_s, "i64.ge_s" },
            { Mnemonic.i64_ge_u, "i64.ge_u" },
            { Mnemonic.f32_eq, "f32.eq" },
            { Mnemonic.f32_ne, "f32.ne" },
            { Mnemonic.f32_lt, "f32.lt" },
            { Mnemonic.f32_gt, "f32.gt" },
            { Mnemonic.f32_le, "f32.le" },
            { Mnemonic.f32_ge, "f32.ge" },
            { Mnemonic.f64_eq, "f64.eq" },
            { Mnemonic.f64_ne, "f64.ne" },
            { Mnemonic.f64_lt, "f64.lt" },
            { Mnemonic.f64_gt, "f64.gt" },
            { Mnemonic.f64_le, "f64.le" },
            { Mnemonic.f64_ge, "f64.ge" },

            { Mnemonic.i32_clz, "i32.clz" },
            { Mnemonic.i32_ctz, "i32.ctz" },
            { Mnemonic.i32_popcnt, "i32.popcnt" },
            { Mnemonic.i32_add, "i32.add" },
            { Mnemonic.i32_sub, "i32.sub" },
            { Mnemonic.i32_mul, "i32.mul" },
            { Mnemonic.i32_div_s, "i32.div_s" },
            { Mnemonic.i32_div_u, "i32.div_u" },
            { Mnemonic.i32_rem_s, "i32.rem_s" },
            { Mnemonic.i32_rem_u, "i32.rem_u" },
            { Mnemonic.i32_and, "i32.and" },
            { Mnemonic.i32_or, "i32.or" },
            { Mnemonic.i32_xor, "i32.xor" },
            { Mnemonic.i32_shl, "i32.shl" },
            { Mnemonic.i32_shr_s, "i32.shr_s" },
            { Mnemonic.i32_shr_u, "i32.shr_u" },
            { Mnemonic.i32_rotl, "i32.rotl" },
            { Mnemonic.i32_rotr, "i32.rotr" },
            { Mnemonic.i64_clz, "i64.clz" },
            { Mnemonic.i64_ctz, "i64.ctz" },
            { Mnemonic.i64_popcnt, "i64.popcnt" },
            { Mnemonic.i64_add, "i64.add" },
            { Mnemonic.i64_sub, "i64.sub" },
            { Mnemonic.i64_mul, "i64.mul" },
            { Mnemonic.i64_div_s, "i64.div_s" },
            { Mnemonic.i64_div_u, "i64.div_u" },
            { Mnemonic.i64_rem_s, "i64.rem_s" },
            { Mnemonic.i64_rem_u, "i64.rem_u" },
            { Mnemonic.i64_and, "i64.and" },
            { Mnemonic.i64_or, "i64.or" },
            { Mnemonic.i64_xor, "i64.xor" },
            { Mnemonic.i64_shl, "i64.shl" },
            { Mnemonic.i64_shr_s, "i64.shr_s" },
            { Mnemonic.i64_shr_u, "i64.shr_u" },
            { Mnemonic.i64_rotl, "i64.rotl" },
            { Mnemonic.i64_rotr, "i64.rotr" },
            { Mnemonic.f32_abs, "f32.abs" },
            { Mnemonic.f32_neg, "f32.neg" },
            { Mnemonic.f32_ceil, "f32.ceil" },
            { Mnemonic.f32_floor, "f32.floor" },
            { Mnemonic.f32_trunc, "f32.trunc" },
            { Mnemonic.f32_nearest, "f32.nearest" },
            { Mnemonic.f32_sqrt, "f32.sqrt" },
            { Mnemonic.f32_add, "f32.add" },
            { Mnemonic.f32_sub, "f32.sub" },
            { Mnemonic.f32_mul, "f32.mul" },
            { Mnemonic.f32_div, "f32.div" },
            { Mnemonic.f32_min, "f32.min" },
            { Mnemonic.f32_max, "f32.max" },
            { Mnemonic.f32_copysign, "f32.copysign" },
            { Mnemonic.f64_abs, "f64.abs" },
            { Mnemonic.f64_neg, "f64.neg" },
            { Mnemonic.f64_ceil, "f64.ceil" },
            { Mnemonic.f64_floor, "f64.floor" },
            { Mnemonic.f64_trunc, "f64.trunc" },
            { Mnemonic.f64_nearest, "f64.nearest" },
            { Mnemonic.f64_sqrt, "f64.sqrt" },
            { Mnemonic.f64_add, "f64.add" },
            { Mnemonic.f64_sub, "f64.sub" },
            { Mnemonic.f64_mul, "f64.mul" },
            { Mnemonic.f64_div, "f64.div" },
            { Mnemonic.f64_min, "f64.min" },
            { Mnemonic.f64_max, "f64.max" },
            { Mnemonic.f64_copysign, "f64.copysign" },

            { Mnemonic.i32_wrap_i64, "i32.wrap/i64" },
            { Mnemonic.i32_trunc_s_f32, "i32.trunc_s/f32" },
            { Mnemonic.i32_trunc_u_f32, "i32.trunc_u/f32" },
            { Mnemonic.i32_trunc_s_f64, "i32.trunc_s/f64" },
            { Mnemonic.i32_trunc_u_f64, "i32.trunc_u/f64" },
            { Mnemonic.i64_extend_s_i32, "i64.extend_s/i32" },
            { Mnemonic.i64_extend_u_i32, "i64.extend_u/i32" },
            { Mnemonic.i64_trunc_s_f32, "i64.trunc_s/f32" },
            { Mnemonic.i64_trunc_u_f32, "i64.trunc_u/f32" },
            { Mnemonic.i64_trunc_s_f64, "i64.trunc_s/f64" },
            { Mnemonic.i64_trunc_u_f64, "i64.trunc_u/f64" },
            { Mnemonic.f32_convert_s_i32, "f32.convert_s/i32" },
            { Mnemonic.f32_convert_u_i32, "f32.convert_u/i32" },
            { Mnemonic.f32_convert_s_i64, "f32.convert_s/i64" },
            { Mnemonic.f32_convert_u_i64, "f32.convert_u/i64" },
            { Mnemonic.f32_demote_f64, "f32.demote/f64" },
            { Mnemonic.f64_convert_s_i32, "f64.convert_s/i32" },
            { Mnemonic.f64_convert_u_i32, "f64.convert_u/i32" },
            { Mnemonic.f64_convert_s_i64, "f64.convert_s/i64" },
            { Mnemonic.f64_convert_u_i64, "f64.convert_u/i64" },
            { Mnemonic.f64_promote_f32, "f64.promote/f32" },

            { Mnemonic.i32_reinterpret_f32, "i32.reinterpret/f32" },
            { Mnemonic.i64_reinterpret_f64, "i64.reinterpret/f64" },
            { Mnemonic.f32_reinterpret_i32, "f32.reinterpret/i32" },
            { Mnemonic.f64_reinterpret_i64, "f64.reinterpret/i64" },

            { Mnemonic.i32_extend8_s, "i32.extends_s" },
            { Mnemonic.ref_null, "ref.null" },
            { Mnemonic.ref_is_null, "ref.is_null" },
            { Mnemonic.ref_func, "ref.func" },
            { Mnemonic.ref_eq, "ref.eq" },
            { Mnemonic.ref_as_non_null, "ref.as_non_null" },
            { Mnemonic.br_on_null, "br_on_null" },
            { Mnemonic.br_on_non_null, "br_on_non_null" },

            { Mnemonic.struct_new, "struct.new" },
            { Mnemonic.struct_new_default, "struct.new_default" },
            { Mnemonic.struct_get, "struct.get" },
            { Mnemonic.struct_get_s, "struct.get_s" },
            { Mnemonic.struct_get_u, "struct.get_u" },
            { Mnemonic.struct_set, "struct.set" },
            { Mnemonic.array_new, "array.new" },
            { Mnemonic.array_new_default, "array.new_default" },
            { Mnemonic.array_new_fixed, "array.new_fixed" },
            { Mnemonic.array_new_data, "array.new_data" },
            { Mnemonic.array_new_elem, "array.new_elem" },
            { Mnemonic.array_get, "array.get" },
            { Mnemonic.array_get_s, "array.get_s" },
            { Mnemonic.array_get_u, "array.get_u" },
            { Mnemonic.array_set, "array.set" },
            { Mnemonic.array_len, "array.len" },
            { Mnemonic.array_fill, "array.fill" },
            { Mnemonic.array_copy, "array.copy" },
            { Mnemonic.array_init_data, "array.init_data" },
            { Mnemonic.array_init_elem, "array.init_elem" },
            { Mnemonic.ref_test, "ref.test(ref ht)" },
            { Mnemonic.ref_test_null, "ref.test(ref null ht)" },
            { Mnemonic.ref_cast, "ref.cast(ref ht)" },
            { Mnemonic.ref_cast_null, "ref.cast(ref null ht)" },
            { Mnemonic.any_convert_extern, "any.convert_extern" },
            { Mnemonic.extern_convert_any, "extern.convert_any" },
            { Mnemonic.ref_i31, "ref.i31" },
            { Mnemonic.i31_get_s, "i31.get_s" },
            { Mnemonic.i31_get_u, "i31.get_u" },

            { Mnemonic.v128_load, "v128.load" },
            { Mnemonic.v128_load8x8_s, "v128.load8x8_s" },
            { Mnemonic.v128_load8x8_u, "v128.load8x8_u" },
            { Mnemonic.v128_load16x4_s, "v128.load16x4_s" },
            { Mnemonic.v128_load16x4_u, "v128.load16x4_u" },
            { Mnemonic.v128_load32x2_s, "v128.load32x2_s" },
            { Mnemonic.v128_load32x2_u, "v128.load32x2_u" },
            { Mnemonic.v128_load8_splat, "v128.load8_splat" },
            { Mnemonic.v128_load16_splat, "v128.load16_splat" },
            { Mnemonic.v128_load32_splat, "v128.load32_splat" },
            { Mnemonic.v128_load64_splat, "v128.load64_splat" },
            { Mnemonic.v128_load32_zero, "v128.load32_zero" },
            { Mnemonic.v128_load64_zero, "v128.load64_zero" },
            { Mnemonic.v128_store, "v128.store" },
            { Mnemonic.v128_load8_lane, "v128.load8_lane" },
            { Mnemonic.v128_load16_lane, "v128.load16_lane" },
            { Mnemonic.v128_load32_lane, "v128.load32_lane" },
            { Mnemonic.v128_load64_lane, "v128.load64_lane" },
            { Mnemonic.v128_store8_lane, "v128.store8_lane" },
            { Mnemonic.v128_store16_lane, "v128.store16_lane" },
            { Mnemonic.v128_store32_lane, "v128.store32_lane" },
            { Mnemonic.v128_store64_lane, "v128.store64_lane" },
            { Mnemonic.v128_const, "v128.const" },
            { Mnemonic.i8x16_shuffle, "i8x16.shuffle" },
            { Mnemonic.i8x16_extract_lane_s, "i8x16.extract_lane_s" },
            { Mnemonic.i8x16_extract_lane_u, "i8x16.extract_lane_u" },
            { Mnemonic.i8x16_replace_lane, "i8x16.replace_lane" },
            { Mnemonic.i16x8_extract_lane_s, "i16x8.extract_lane_s" },
            { Mnemonic.i16x8_extract_lane_u, "i16x8.extract_lane_u" },
            { Mnemonic.i16x8_replace_lane, "i16x8.replace_lane" },
            { Mnemonic.i32x4_extract_lane, "i32x4.extract_lane" },
            { Mnemonic.i32x4_replace_lane, "i32x4.replace_lane" },
            { Mnemonic.i64x2_extract_lane, "i64x2.extract_lane" },
            { Mnemonic.i64x2_replace_lane, "i64x2.replace_lane" },
            { Mnemonic.f32x4_extract_lane, "f32x4.extract_lane" },
            { Mnemonic.f32x4_replace_lane, "f32x4.replace_lane" },
            { Mnemonic.f64x2_extract_lane, "f64x2.extract_lane" },
            { Mnemonic.f64x2_replace_lane, "f64x2.replace_lane" },
            { Mnemonic.i8x16_swizzle, "i8x16.swizzle" },
            { Mnemonic.i8x16_splat, "i8x16.splat" },
            { Mnemonic.i16x8_splat, "i16x8.splat" },
            { Mnemonic.i32x4_splat, "i32x4.splat" },
            { Mnemonic.i64x2_splat, "i64x2.splat" },
            { Mnemonic.f32x4_splat, "f32x4.splat" },
            { Mnemonic.f64x2_splat, "f64x2.splat" },
            { Mnemonic.i8x16_eq, "i8x16.eq" },
            { Mnemonic.i8x16_ne, "i8x16.ne" },
            { Mnemonic.i8x16_lt_s, "i8x16.lt_s" },
            { Mnemonic.i8x16_lt_u, "i8x16.lt_u" },
            { Mnemonic.i8x16_gt_s, "i8x16.gt_s" },
            { Mnemonic.i8x16_gt_u, "i8x16.gt_u" },
            { Mnemonic.i8x16_le_s, "i8x16.le_s" },
            { Mnemonic.i8x16_le_u, "i8x16.le_u" },
            { Mnemonic.i8x16_ge_s, "i8x16.ge_s" },
            { Mnemonic.i8x16_ge_u, "i8x16.ge_u" },
            { Mnemonic.i16x8_eq, "i16x8.eq" },
            { Mnemonic.i16x8_ne, "i16x8.ne" },
            { Mnemonic.i16x8_lt_s, "i16x8.lt_s" },
            { Mnemonic.i16x8_lt_u, "i16x8.lt_u" },
            { Mnemonic.i16x8_gt_s, "i16x8.gt_s" },
            { Mnemonic.i16x8_gt_u, "i16x8.gt_u" },
            { Mnemonic.i16x8_le_s, "i16x8.le_s" },
            { Mnemonic.i16x8_le_u, "i16x8.le_u" },
            { Mnemonic.i16x8_ge_s, "i16x8.ge_s" },
            { Mnemonic.i16x8_ge_u, "i16x8.ge_u" },
            { Mnemonic.i32x4_eq, "i32x4.eq" },
            { Mnemonic.i32x4_ne, "i32x4.ne" },
            { Mnemonic.i32x4_lt_s, "i32x4.lt_s" },
            { Mnemonic.i32x4_lt_u, "i32x4.lt_u" },
            { Mnemonic.i32x4_gt_s, "i32x4.gt_s" },
            { Mnemonic.i32x4_gt_u, "i32x4.gt_u" },
            { Mnemonic.i32x4_le_s, "i32x4.le_s" },
            { Mnemonic.i32x4_le_u, "i32x4.le_u" },
            { Mnemonic.i32x4_ge_s, "i32x4.ge_s" },
            { Mnemonic.i32x4_ge_u, "i32x4.ge_u" },
            { Mnemonic.i64x2_eq, "i64x2.eq" },
            { Mnemonic.i64x2_ne, "i64x2.ne" },
            { Mnemonic.i64x2_lt_s, "i64x2.lt_s" },
            { Mnemonic.i64x2_gt_s, "i64x2.gt_s" },
            { Mnemonic.i64x2_le_s, "i64x2.le_s" },
            { Mnemonic.i64x2_ge_s, "i64x2.ge_s" },
            { Mnemonic.f32x4_eq, "f32x4.eq" },
            { Mnemonic.f32x4_ne, "f32x4.ne" },
            { Mnemonic.f32x4_lt, "f32x4.lt" },
            { Mnemonic.f32x4_gt, "f32x4.gt" },
            { Mnemonic.f32x4_le, "f32x4.le" },
            { Mnemonic.f32x4_ge, "f32x4.ge" },
            { Mnemonic.f64x2_eq, "f64x2.eq" },
            { Mnemonic.f64x2_ne, "f64x2.ne" },
            { Mnemonic.f64x2_lt, "f64x2.lt" },
            { Mnemonic.f64x2_gt, "f64x2.gt" },
            { Mnemonic.f64x2_le, "f64x2.le" },
            { Mnemonic.f64x2_ge, "f64x2.ge" },
            { Mnemonic.v128_not, "v128.not" },
            { Mnemonic.v128_and, "v128.and" },
            { Mnemonic.v128_andnot, "v128.andnot" },
            { Mnemonic.v128_or, "v128.or" },
            { Mnemonic.v128_xor, "v128.xor" },
            { Mnemonic.v128_bitselect, "v128.bitselect" },
            { Mnemonic.v128_any_true, "v128.any_true" },
            { Mnemonic.i8x16_abs, "i8x16.abs" },
            { Mnemonic.i8x16_neg, "i8x16.neg" },
            { Mnemonic.i8x16_popcnt, "i8x16.popcnt" },
            { Mnemonic.i8x16_all_true, "i8x16.all_true" },
            { Mnemonic.i8x16_bitmask, "i8x16.bitmask" },
            { Mnemonic.i8x16_narrow_i16x8_s, "i8x16.narrow_i16x8_s" },
            { Mnemonic.i8x16_narrow_i16x8_u, "i8x16.narrow_i16x8_u" },
            { Mnemonic.i8x16_shl, "i8x16.shl" },
            { Mnemonic.i8x16_shr_s, "i8x16.shr_s" },
            { Mnemonic.i8x16_shr_u, "i8x16.shr_u" },
            { Mnemonic.i8x16_add, "i8x16.add" },
            { Mnemonic.i8x16_add_sat_s, "i8x16.add_sat_s" },
            { Mnemonic.i8x16_add_sat_u, "i8x16.add_sat_u" },
            { Mnemonic.i8x16_sub, "i8x16.sub" },
            { Mnemonic.i8x16_sub_sat_s, "i8x16.sub_sat_s" },
            { Mnemonic.i8x16_sub_sat_u, "i8x16.sub_sat_u" },
            { Mnemonic.i8x16_min_s, "i8x16.min_s" },
            { Mnemonic.i8x16_min_u, "i8x16.min_u" },
            { Mnemonic.i8x16_max_s, "i8x16.max_s" },
            { Mnemonic.i8x16_max_u, "i8x16.max_u" },
            { Mnemonic.i8x16_avgr_u, "i8x16.avgr_u" },
            { Mnemonic.i16x8_extadd_pairwise_i8x16_s, "i16x8.extadd_pairwise_i8x16_s" },
            { Mnemonic.i16x8_extadd_pairwise_i8x16_u, "i16x8.extadd_pairwise_i8x16_u" },
            { Mnemonic.i16x8_abs, "i16x8.abs" },
            { Mnemonic.i16x8_neg, "i16x8.neg" },
            { Mnemonic.i16x8_q15mulr_sat_s, "i16x8.q15mulr_sat_s" },
            { Mnemonic.i16x8_all_true, "i16x8.all_true" },
            { Mnemonic.i16x8_bitmask, "i16x8.bitmask" },
            { Mnemonic.i16x8_narrow_i32x4_s, "i16x8.narrow_i32x4_s" },
            { Mnemonic.i16x8_narrow_i32x4_u, "i16x8.narrow_i32x4_u" },
            { Mnemonic.i16x8_extend_low_i8x16_s, "i16x8.extend_low_i8x16_s" },
            { Mnemonic.i16x8_extend_high_i8x16_s, "i16x8.extend_high_i8x16_s" },
            { Mnemonic.i16x8_extend_low_i8x16_u, "i16x8.extend_low_i8x16_u" },
            { Mnemonic.i16x8_extend_high_i8x16_u, "i16x8.extend_high_i8x16_u" },
            { Mnemonic.i16x8_shl, "i16x8.shl" },
            { Mnemonic.i16x8_shr_s, "i16x8.shr_s" },
            { Mnemonic.i16x8_shr_u, "i16x8.shr_u" },
            { Mnemonic.i16x8_add, "i16x8.add" },
            { Mnemonic.i16x8_add_sat_s, "i16x8.add_sat_s" },
            { Mnemonic.i16x8_add_sat_u, "i16x8.add_sat_u" },
            { Mnemonic.i16x8_sub, "i16x8.sub" },
            { Mnemonic.i16x8_sub_sat_s, "i16x8.sub_sat_s" },
            { Mnemonic.i16x8_sub_sat_u, "i16x8.sub_sat_u" },
            { Mnemonic.i16x8_mul, "i16x8.mul" },
            { Mnemonic.i16x8_min_s, "i16x8.min_s" },
            { Mnemonic.i16x8_min_u, "i16x8.min_u" },
            { Mnemonic.i16x8_max_s, "i16x8.max_s" },
            { Mnemonic.i16x8_max_u, "i16x8.max_u" },
            { Mnemonic.i16x8_avgr_u, "i16x8.avgr_u" },
            { Mnemonic.i16x8_extmul_low_i8x16_s, "i16x8.extmul_low_i8x16_s" },
            { Mnemonic.i16x8_extmul_high_i8x16_s, "i16x8.extmul_high_i8x16_s" },
            { Mnemonic.i16x8_extmul_low_i8x16_u, "i16x8.extmul_low_i8x16_u" },
            { Mnemonic.i16x8_extmul_high_i8x16_u, "i16x8.extmul_high_i8x16_u" },
            { Mnemonic.i32x4_extadd_pairwise_i16x8_s, "i32x4.extadd_pairwise_i16x8_s" },
            { Mnemonic.i32x4_extadd_pairwise_i16x8_u, "i32x4.extadd_pairwise_i16x8_u" },
            { Mnemonic.i32x4_abs, "i32x4.abs" },
            { Mnemonic.i32x4_neg, "i32x4.neg" },
            { Mnemonic.i32x4_all_true, "i32x4.all_true" },
            { Mnemonic.i32x4_bitmask, "i32x4.bitmask" },
            { Mnemonic.i32x4_extend_low_i16x8_s, "i32x4.extend_low_i16x8_s" },
            { Mnemonic.i32x4_extend_high_i16x8_s, "i32x4.extend_high_i16x8_s" },
            { Mnemonic.i32x4_extend_low_i16x8_u, "i32x4.extend_low_i16x8_u" },
            { Mnemonic.i32x4_extend_high_i16x8_u, "i32x4.extend_high_i16x8_u" },
            { Mnemonic.i32x4_shl, "i32x4.shl" },
            { Mnemonic.i32x4_shr_s, "i32x4.shr_s" },
            { Mnemonic.i32x4_shr_u, "i32x4.shr_u" },
            { Mnemonic.i32x4_add, "i32x4.add" },
            { Mnemonic.i32x4_sub, "i32x4.sub" },
            { Mnemonic.i32x4_mul, "i32x4.mul" },
            { Mnemonic.i32x4_min_s, "i32x4.min_s" },
            { Mnemonic.i32x4_min_u, "i32x4.min_u" },
            { Mnemonic.i32x4_max_s, "i32x4.max_s" },
            { Mnemonic.i32x4_max_u, "i32x4.max_u" },
            { Mnemonic.i32x4_dot_i16x8_s, "i32x4.dot_i16x8_s" },
            { Mnemonic.i32x4_extmul_low_i16x8_s, "i32x4.extmul_low_i16x8_s" },
            { Mnemonic.i32x4_extmul_high_i16x8_s, "i32x4.extmul_high_i16x8_s" },
            { Mnemonic.i32x4_extmul_low_i16x8_u, "i32x4.extmul_low_i16x8_u" },
            { Mnemonic.i32x4_extmul_high_i16x8_u, "i32x4.extmul_high_i16x8_u" },
            { Mnemonic.i64x2_abs, "i64x2.abs" },
            { Mnemonic.i64x2_neg, "i64x2.neg" },
            { Mnemonic.i64x2_all_true, "i64x2.all_true" },
            { Mnemonic.i64x2_bitmask, "i64x2.bitmask" },
            { Mnemonic.i64x2_extend_low_i32x4_s, "i64x2.extend_low_i32x4_s" },
            { Mnemonic.i64x2_extend_high_i32x4_s, "i64x2.extend_high_i32x4_s" },
            { Mnemonic.i64x2_extend_low_i32x4_u, "i64x2.extend_low_i32x4_u" },
            { Mnemonic.i64x2_extend_high_i32x4_u, "i64x2.extend_high_i32x4_u" },
            { Mnemonic.i64x2_shl, "i64x2.shl" },
            { Mnemonic.i64x2_shr_s, "i64x2.shr_s" },
            { Mnemonic.i64x2_shr_u, "i64x2.shr_u" },
            { Mnemonic.i64x2_add, "i64x2.add" },
            { Mnemonic.i64x2_sub, "i64x2.sub" },
            { Mnemonic.i64x2_mul, "i64x2.mul" },
            { Mnemonic.i64x2_extmul_low_i32x4_s, "i64x2.extmul_low_i32x4_s" },
            { Mnemonic.i64x2_extmul_high_i32x4_s, "i64x2.extmul_high_i32x4_s" },
            { Mnemonic.i64x2_extmul_low_i32x4_u, "i64x2.extmul_low_i32x4_u" },
            { Mnemonic.i64x2_extmul_high_i32x4_u, "i64x2.extmul_high_i32x4_u" },
            { Mnemonic.f32x4_ceil, "f32x4.ceil" },
            { Mnemonic.f32x4_floor, "f32x4.floor" },
            { Mnemonic.f32x4_trunc, "f32x4.trunc" },
            { Mnemonic.f32x4_nearest, "f32x4.nearest" },
            { Mnemonic.f32x4_abs, "f32x4.abs" },
            { Mnemonic.f32x4_neg, "f32x4.neg" },
            { Mnemonic.f32x4_sqrt, "f32x4.sqrt" },
            { Mnemonic.f32x4_add, "f32x4.add" },
            { Mnemonic.f32x4_sub, "f32x4.sub" },
            { Mnemonic.f32x4_mul, "f32x4.mul" },
            { Mnemonic.f32x4_div, "f32x4.div" },
            { Mnemonic.f32x4_min, "f32x4.min" },
            { Mnemonic.f32x4_max, "f32x4.max" },
            { Mnemonic.f32x4_pmin, "f32x4.pmin" },
            { Mnemonic.f32x4_pmax, "f32x4.pmax" },
            { Mnemonic.f64x2_ceil, "f64x2.ceil" },
            { Mnemonic.f64x2_floor, "f64x2.floor" },
            { Mnemonic.f64x2_trunc, "f64x2.trunc" },
            { Mnemonic.f64x2_nearest, "f64x2.nearest" },
            { Mnemonic.f64x2_abs, "f64x2.abs" },
            { Mnemonic.f64x2_neg, "f64x2.neg" },
            { Mnemonic.f64x2_sqrt, "f64x2.sqrt" },
            { Mnemonic.f64x2_add, "f64x2.add" },
            { Mnemonic.f64x2_sub, "f64x2.sub" },
            { Mnemonic.f64x2_mul, "f64x2.mul" },
            { Mnemonic.f64x2_div, "f64x2.div" },
            { Mnemonic.f64x2_min, "f64x2.min" },
            { Mnemonic.f64x2_max, "f64x2.max" },
            { Mnemonic.f64x2_pmin, "f64x2.pmin" },
            { Mnemonic.f64x2_pmax, "f64x2.pmax" },
            { Mnemonic.i32x4_trunc_sat_f32x4_s, "i32x4.trunc_sat_f32x4_s" },
            { Mnemonic.i32x4_trunc_sat_f32x4_u, "i32x4.trunc_sat_f32x4_u" },
            { Mnemonic.f32x4_convert_i32x4_s, "f32x4.convert_i32x4_s" },
            { Mnemonic.f32x4_convert_i32x4_u, "f32x4.convert_i32x4_u" },
            { Mnemonic.i32x4_trunc_sat_f64x2_s_zero, "i32x4.trunc_sat_f64x2_s_zero" },
            { Mnemonic.i32x4_trunc_sat_f64x2_u_zero, "i32x4.trunc_sat_f64x2_u_zero" },
            { Mnemonic.f64x2_convert_low_i32x4_s, "f64x2.convert_low_i32x4_s" },
            { Mnemonic.f64x2_convert_low_i32x4_u, "f64x2.convert_low_i32x4_u" },
            { Mnemonic.f32x4_demote_f64x2_zero, "f32x4.demote_f64x2_zero" },
            { Mnemonic.f64x2_promote_low_f32x4, "f64x2.promote_low_f32x4" },
            { Mnemonic.i16x8_relaxed_swizzle, "i16x8.relaxed_swizzle" },
            { Mnemonic.i32x4_relaxed_trunc_f32x4_s, "i32x4.relaxed_trunc_f32x4_s" },
            { Mnemonic.i32x4_relaxed_trunc_f32x4_u, "i32x4.relaxed_trunc_f32x4_u" },
            { Mnemonic.i32x4_relaxed_trunc_f32x4_s_zero, "i32x4.relaxed_trunc_f32x4_s_zero" },
            { Mnemonic.i32x4_relaxed_trunc_f32x4_u_zero, "i32x4.relaxed_trunc_f32x4_u_zero" },
            { Mnemonic.f32x4_relaxed_madd, "f32x4.relaxed_madd" },
            { Mnemonic.f32x4_relaxed_nmadd, "f32x4.relaxed_nmadd" },
            { Mnemonic.f64x2_relaxed_madd, "f64x2.relaxed_madd" },
            { Mnemonic.f64x2_relaxed_nmadd, "f64x2.relaxed_nmadd" },
            { Mnemonic.i8x16_relaxed_laneselect, "i8x16.relaxed_laneselect" },
            { Mnemonic.i16x8_relaxed_laneselect, "i16x8.relaxed_laneselect" },
            { Mnemonic.i32x4_relaxed_laneselect, "i32x4.relaxed_laneselect" },
            { Mnemonic.i64x2_relaxed_laneselect, "i64x2.relaxed_laneselect" },
            { Mnemonic.f32x4_relaxed_min, "f32x4.relaxed_min" },
            { Mnemonic.f32x4_relaxed_max, "f32x4.relaxed_max" },
            { Mnemonic.f64x2_relaxed_min, "f64x2.relaxed_min" },
            { Mnemonic.f64x2_relaxed_max, "f64x2.relaxed_max" },
            { Mnemonic.i16x8_relaxed_q15mulr_s, "i16x8.relaxed_q15mulr_s" },
            { Mnemonic.i16x8_relaxed_dot_i8x16_i7x16_s, "i16x8.relaxed_dot_i8x16_i7x16_s" },
            { Mnemonic.i16x8_relaxed_dot_i8x16_i7x16_add_s, "i16x8.relaxed_dot_i8x16_i7x16_add_s" }
        };

        public WasmInstruction(Mnemonic mnemonic)
        {
            this.Mnemonic = mnemonic;
        }

        public Mnemonic Mnemonic { get; set; }
        public override int MnemonicAsInteger => (int) Mnemonic;

        public override string MnemonicAsString => mpoptostring[this.Mnemonic];

        protected override void DoRender(MachineInstructionRenderer renderer, MachineInstructionRendererOptions options)
        {
            renderer.WriteMnemonic(MnemonicAsString);
            base.RenderOperands(renderer, options);
        }

        protected override void RenderOperand(MachineOperand operand, MachineInstructionRenderer renderer, MachineInstructionRendererOptions options)
        {
            switch (operand)
            {
            case ImmediateOperand imm:
                if (imm.Width.IsIntegral || imm.Width.IsWord)
                    renderer.WriteFormat("0x{0:X}", imm.Value.ToUInt64());
                else if (imm.Width.IsReal)
                    renderer.WriteString(imm.Value.ToReal64().ToString(CultureInfo.InvariantCulture));
                return;
            }
            base.RenderOperand(operand, renderer, options);
        }
    }
}
