1///! Contains all constants and types representing the wasm 2///! binary format, as specified by: 3///! https://webassembly.github.io/spec/core/ 4const std = @import("std.zig"); 5const testing = std.testing; 6 7// TODO: Add support for multi-byte ops (e.g. table operations) 8 9/// Wasm instruction opcodes 10/// 11/// All instructions are defined as per spec: 12/// https://webassembly.github.io/spec/core/appendix/index-instructions.html 13pub const Opcode = enum(u8) { 14 @"unreachable" = 0x00, 15 nop = 0x01, 16 block = 0x02, 17 loop = 0x03, 18 @"if" = 0x04, 19 @"else" = 0x05, 20 end = 0x0B, 21 br = 0x0C, 22 br_if = 0x0D, 23 br_table = 0x0E, 24 @"return" = 0x0F, 25 call = 0x10, 26 call_indirect = 0x11, 27 drop = 0x1A, 28 select = 0x1B, 29 local_get = 0x20, 30 local_set = 0x21, 31 local_tee = 0x22, 32 global_get = 0x23, 33 global_set = 0x24, 34 i32_load = 0x28, 35 i64_load = 0x29, 36 f32_load = 0x2A, 37 f64_load = 0x2B, 38 i32_load8_s = 0x2C, 39 i32_load8_u = 0x2D, 40 i32_load16_s = 0x2E, 41 i32_load16_u = 0x2F, 42 i64_load8_s = 0x30, 43 i64_load8_u = 0x31, 44 i64_load16_s = 0x32, 45 i64_load16_u = 0x33, 46 i64_load32_s = 0x34, 47 i64_load32_u = 0x35, 48 i32_store = 0x36, 49 i64_store = 0x37, 50 f32_store = 0x38, 51 f64_store = 0x39, 52 i32_store8 = 0x3A, 53 i32_store16 = 0x3B, 54 i64_store8 = 0x3C, 55 i64_store16 = 0x3D, 56 i64_store32 = 0x3E, 57 memory_size = 0x3F, 58 memory_grow = 0x40, 59 i32_const = 0x41, 60 i64_const = 0x42, 61 f32_const = 0x43, 62 f64_const = 0x44, 63 i32_eqz = 0x45, 64 i32_eq = 0x46, 65 i32_ne = 0x47, 66 i32_lt_s = 0x48, 67 i32_lt_u = 0x49, 68 i32_gt_s = 0x4A, 69 i32_gt_u = 0x4B, 70 i32_le_s = 0x4C, 71 i32_le_u = 0x4D, 72 i32_ge_s = 0x4E, 73 i32_ge_u = 0x4F, 74 i64_eqz = 0x50, 75 i64_eq = 0x51, 76 i64_ne = 0x52, 77 i64_lt_s = 0x53, 78 i64_lt_u = 0x54, 79 i64_gt_s = 0x55, 80 i64_gt_u = 0x56, 81 i64_le_s = 0x57, 82 i64_le_u = 0x58, 83 i64_ge_s = 0x59, 84 i64_ge_u = 0x5A, 85 f32_eq = 0x5B, 86 f32_ne = 0x5C, 87 f32_lt = 0x5D, 88 f32_gt = 0x5E, 89 f32_le = 0x5F, 90 f32_ge = 0x60, 91 f64_eq = 0x61, 92 f64_ne = 0x62, 93 f64_lt = 0x63, 94 f64_gt = 0x64, 95 f64_le = 0x65, 96 f64_ge = 0x66, 97 i32_clz = 0x67, 98 i32_ctz = 0x68, 99 i32_popcnt = 0x69, 100 i32_add = 0x6A, 101 i32_sub = 0x6B, 102 i32_mul = 0x6C, 103 i32_div_s = 0x6D, 104 i32_div_u = 0x6E, 105 i32_rem_s = 0x6F, 106 i32_rem_u = 0x70, 107 i32_and = 0x71, 108 i32_or = 0x72, 109 i32_xor = 0x73, 110 i32_shl = 0x74, 111 i32_shr_s = 0x75, 112 i32_shr_u = 0x76, 113 i32_rotl = 0x77, 114 i32_rotr = 0x78, 115 i64_clz = 0x79, 116 i64_ctz = 0x7A, 117 i64_popcnt = 0x7B, 118 i64_add = 0x7C, 119 i64_sub = 0x7D, 120 i64_mul = 0x7E, 121 i64_div_s = 0x7F, 122 i64_div_u = 0x80, 123 i64_rem_s = 0x81, 124 i64_rem_u = 0x82, 125 i64_and = 0x83, 126 i64_or = 0x84, 127 i64_xor = 0x85, 128 i64_shl = 0x86, 129 i64_shr_s = 0x87, 130 i64_shr_u = 0x88, 131 i64_rotl = 0x89, 132 i64_rotr = 0x8A, 133 f32_abs = 0x8B, 134 f32_neg = 0x8C, 135 f32_ceil = 0x8D, 136 f32_floor = 0x8E, 137 f32_trunc = 0x8F, 138 f32_nearest = 0x90, 139 f32_sqrt = 0x91, 140 f32_add = 0x92, 141 f32_sub = 0x93, 142 f32_mul = 0x94, 143 f32_div = 0x95, 144 f32_min = 0x96, 145 f32_max = 0x97, 146 f32_copysign = 0x98, 147 f64_abs = 0x99, 148 f64_neg = 0x9A, 149 f64_ceil = 0x9B, 150 f64_floor = 0x9C, 151 f64_trunc = 0x9D, 152 f64_nearest = 0x9E, 153 f64_sqrt = 0x9F, 154 f64_add = 0xA0, 155 f64_sub = 0xA1, 156 f64_mul = 0xA2, 157 f64_div = 0xA3, 158 f64_min = 0xA4, 159 f64_max = 0xA5, 160 f64_copysign = 0xA6, 161 i32_wrap_i64 = 0xA7, 162 i32_trunc_f32_s = 0xA8, 163 i32_trunc_f32_u = 0xA9, 164 i32_trunc_f64_s = 0xAA, 165 i32_trunc_f64_u = 0xAB, 166 i64_extend_i32_s = 0xAC, 167 i64_extend_i32_u = 0xAD, 168 i64_trunc_f32_s = 0xAE, 169 i64_trunc_f32_u = 0xAF, 170 i64_trunc_f64_s = 0xB0, 171 i64_trunc_f64_u = 0xB1, 172 f32_convert_i32_s = 0xB2, 173 f32_convert_i32_u = 0xB3, 174 f32_convert_i64_s = 0xB4, 175 f32_convert_i64_u = 0xB5, 176 f32_demote_f64 = 0xB6, 177 f64_convert_i32_s = 0xB7, 178 f64_convert_i32_u = 0xB8, 179 f64_convert_i64_s = 0xB9, 180 f64_convert_i64_u = 0xBA, 181 f64_promote_f32 = 0xBB, 182 i32_reinterpret_f32 = 0xBC, 183 i64_reinterpret_f64 = 0xBD, 184 f32_reinterpret_i32 = 0xBE, 185 f64_reinterpret_i64 = 0xBF, 186 i32_extend8_s = 0xC0, 187 i32_extend16_s = 0xC1, 188 i64_extend8_s = 0xC2, 189 i64_extend16_s = 0xC3, 190 i64_extend32_s = 0xC4, 191 _, 192}; 193 194/// Returns the integer value of an `Opcode`. Used by the Zig compiler 195/// to write instructions to the wasm binary file 196pub fn opcode(op: Opcode) u8 { 197 return @enumToInt(op); 198} 199 200test "Wasm - opcodes" { 201 // Ensure our opcodes values remain intact as certain values are skipped due to them being reserved 202 const i32_const = opcode(.i32_const); 203 const end = opcode(.end); 204 const drop = opcode(.drop); 205 const local_get = opcode(.local_get); 206 const i64_extend32_s = opcode(.i64_extend32_s); 207 208 try testing.expectEqual(@as(u16, 0x41), i32_const); 209 try testing.expectEqual(@as(u16, 0x0B), end); 210 try testing.expectEqual(@as(u16, 0x1A), drop); 211 try testing.expectEqual(@as(u16, 0x20), local_get); 212 try testing.expectEqual(@as(u16, 0xC4), i64_extend32_s); 213} 214 215/// Enum representing all Wasm value types as per spec: 216/// https://webassembly.github.io/spec/core/binary/types.html 217pub const Valtype = enum(u8) { 218 i32 = 0x7F, 219 i64 = 0x7E, 220 f32 = 0x7D, 221 f64 = 0x7C, 222}; 223 224/// Returns the integer value of a `Valtype` 225pub fn valtype(value: Valtype) u8 { 226 return @enumToInt(value); 227} 228 229/// Reference types, where the funcref references to a function regardless of its type 230/// and ref references an object from the embedder. 231pub const RefType = enum(u8) { 232 funcref = 0x70, 233 externref = 0x6F, 234}; 235 236/// Returns the integer value of a `Reftype` 237pub fn reftype(value: RefType) u8 { 238 return @enumToInt(value); 239} 240 241test "Wasm - valtypes" { 242 const _i32 = valtype(.i32); 243 const _i64 = valtype(.i64); 244 const _f32 = valtype(.f32); 245 const _f64 = valtype(.f64); 246 247 try testing.expectEqual(@as(u8, 0x7F), _i32); 248 try testing.expectEqual(@as(u8, 0x7E), _i64); 249 try testing.expectEqual(@as(u8, 0x7D), _f32); 250 try testing.expectEqual(@as(u8, 0x7C), _f64); 251} 252 253/// Limits classify the size range of resizeable storage associated with memory types and table types. 254pub const Limits = struct { 255 min: u32, 256 max: ?u32, 257}; 258 259/// Initialization expressions are used to set the initial value on an object 260/// when a wasm module is being loaded. 261pub const InitExpression = union(enum) { 262 i32_const: i32, 263 i64_const: i64, 264 f32_const: f32, 265 f64_const: f64, 266 global_get: u32, 267}; 268 269/// 270pub const Func = struct { 271 type_index: u32, 272}; 273 274/// Tables are used to hold pointers to opaque objects. 275/// This can either by any function, or an object from the host. 276pub const Table = struct { 277 limits: Limits, 278 reftype: RefType, 279}; 280 281/// Describes the layout of the memory where `min` represents 282/// the minimal amount of pages, and the optional `max` represents 283/// the max pages. When `null` will allow the host to determine the 284/// amount of pages. 285pub const Memory = struct { 286 limits: Limits, 287}; 288 289/// Represents the type of a `Global` or an imported global. 290pub const GlobalType = struct { 291 valtype: Valtype, 292 mutable: bool, 293}; 294 295pub const Global = struct { 296 global_type: GlobalType, 297 init: InitExpression, 298}; 299 300/// Notates an object to be exported from wasm 301/// to the host. 302pub const Export = struct { 303 name: []const u8, 304 kind: ExternalKind, 305 index: u32, 306}; 307 308/// Element describes the layout of the table that can 309/// be found at `table_index` 310pub const Element = struct { 311 table_index: u32, 312 offset: InitExpression, 313 func_indexes: []const u32, 314}; 315 316/// Imports are used to import objects from the host 317pub const Import = struct { 318 module_name: []const u8, 319 name: []const u8, 320 kind: Kind, 321 322 pub const Kind = union(ExternalKind) { 323 function: u32, 324 table: Table, 325 memory: Limits, 326 global: GlobalType, 327 }; 328}; 329 330/// `Type` represents a function signature type containing both 331/// a slice of parameters as well as a slice of return values. 332pub const Type = struct { 333 params: []const Valtype, 334 returns: []const Valtype, 335 336 pub fn format(self: Type, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void { 337 _ = fmt; 338 _ = opt; 339 try writer.writeByte('('); 340 for (self.params) |param, i| { 341 try writer.print("{s}", .{@tagName(param)}); 342 if (i + 1 != self.params.len) { 343 try writer.writeAll(", "); 344 } 345 } 346 try writer.writeAll(") -> "); 347 if (self.returns.len == 0) { 348 try writer.writeAll("nil"); 349 } else { 350 for (self.returns) |return_ty, i| { 351 try writer.print("{s}", .{@tagName(return_ty)}); 352 if (i + 1 != self.returns.len) { 353 try writer.writeAll(", "); 354 } 355 } 356 } 357 } 358 359 pub fn eql(self: Type, other: Type) bool { 360 return std.mem.eql(Valtype, self.params, other.params) and 361 std.mem.eql(Valtype, self.returns, other.returns); 362 } 363 364 pub fn deinit(self: *Type, gpa: std.mem.Allocator) void { 365 gpa.free(self.params); 366 gpa.free(self.returns); 367 self.* = undefined; 368 } 369}; 370 371/// Wasm module sections as per spec: 372/// https://webassembly.github.io/spec/core/binary/modules.html 373pub const Section = enum(u8) { 374 custom, 375 type, 376 import, 377 function, 378 table, 379 memory, 380 global, 381 @"export", 382 start, 383 element, 384 code, 385 data, 386 data_count, 387 _, 388}; 389 390/// Returns the integer value of a given `Section` 391pub fn section(val: Section) u8 { 392 return @enumToInt(val); 393} 394 395/// The kind of the type when importing or exporting to/from the host environment 396/// https://webassembly.github.io/spec/core/syntax/modules.html 397pub const ExternalKind = enum(u8) { 398 function, 399 table, 400 memory, 401 global, 402}; 403 404/// Returns the integer value of a given `ExternalKind` 405pub fn externalKind(val: ExternalKind) u8 { 406 return @enumToInt(val); 407} 408 409// type constants 410pub const element_type: u8 = 0x70; 411pub const function_type: u8 = 0x60; 412pub const result_type: u8 = 0x40; 413 414/// Represents a block which will not return a value 415pub const block_empty: u8 = 0x40; 416 417// binary constants 418pub const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm 419pub const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1 (MVP) 420 421// Each wasm page size is 64kB 422pub const page_size = 64 * 1024; 423