authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-13 14:11:22-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-12-13 14:11:22-05:00
log2e66b3be6ea6a5be758f8bc855807b4af1020ec8
tree01b67ba0e122b53cfbb66d864355303b3722fe31
parent3318611618665e6d09ddad866efdabf54eb498ce
parent37561a920b6dad1231f0b9e5a69eb0978af6f5d0
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13910 from Luukdegram/wasm-simd


6 files changed, 586 insertions(+), 28 deletions(-)

lib/std/wasm.zig+272
......@@ -237,6 +237,277 @@ pub const PrefixedOpcode = enum(u8) {
237237 _,
238238};
239239
240/// Simd opcodes that require a prefix `0xFD`.
241/// Each opcode represents a varuint32, meaning
242/// they are encoded as leb128 in binary.
243pub const SimdOpcode = enum(u32) {
244 v128_load = 0x00,
245 v128_load8x8_s = 0x01,
246 v128_load8x8_u = 0x02,
247 v128_load16x4_s = 0x03,
248 v128_load16x4_u = 0x04,
249 v128_load32x2_s = 0x05,
250 v128_load32x2_u = 0x06,
251 v128_load8_splat = 0x07,
252 v128_load16_splat = 0x08,
253 v128_load32_splat = 0x09,
254 v128_load64_splat = 0x0A,
255 v128_store = 0x0B,
256 v128_const = 0x0C,
257 i8x16_shuffle = 0x0D,
258 i8x16_swizzle = 0x0E,
259 i8x16_splat = 0x0F,
260 i16x8_splat = 0x10,
261 i32x4_splat = 0x11,
262 i64x2_splat = 0x12,
263 f32x4_splat = 0x13,
264 f64x2_splat = 0x14,
265 i8x16_extract_lane_s = 0x15,
266 i8x16_extract_lane_u = 0x16,
267 i8x16_replace_lane = 0x17,
268 i16x8_extract_lane_s = 0x18,
269 i16x8_extract_lane_u = 0x19,
270 i16x8_replace_lane = 0x1A,
271 i32x4_extract_lane = 0x1B,
272 i32x4_replace_lane = 0x1C,
273 i64x2_extract_lane = 0x1D,
274 i64x2_replace_lane = 0x1E,
275 f32x4_extract_lane = 0x1F,
276 f32x4_replace_lane = 0x20,
277 f64x2_extract_lane = 0x21,
278 f64x2_replace_lane = 0x22,
279 i8x16_eq = 0x23,
280 i16x8_eq = 0x2D,
281 i32x4_eq = 0x37,
282 i8x16_ne = 0x24,
283 i16x8_ne = 0x2E,
284 i32x4_ne = 0x38,
285 i8x16_lt_s = 0x25,
286 i16x8_lt_s = 0x2F,
287 i32x4_lt_s = 0x39,
288 i8x16_lt_u = 0x26,
289 i16x8_lt_u = 0x30,
290 i32x4_lt_u = 0x3A,
291 i8x16_gt_s = 0x27,
292 i16x8_gt_s = 0x31,
293 i32x4_gt_s = 0x3B,
294 i8x16_gt_u = 0x28,
295 i16x8_gt_u = 0x32,
296 i32x4_gt_u = 0x3C,
297 i8x16_le_s = 0x29,
298 i16x8_le_s = 0x33,
299 i32x4_le_s = 0x3D,
300 i8x16_le_u = 0x2A,
301 i16x8_le_u = 0x34,
302 i32x4_le_u = 0x3E,
303 i8x16_ge_s = 0x2B,
304 i16x8_ge_s = 0x35,
305 i32x4_ge_s = 0x3F,
306 i8x16_ge_u = 0x2C,
307 i16x8_ge_u = 0x36,
308 i32x4_ge_u = 0x40,
309 f32x4_eq = 0x41,
310 f64x2_eq = 0x47,
311 f32x4_ne = 0x42,
312 f64x2_ne = 0x48,
313 f32x4_lt = 0x43,
314 f64x2_lt = 0x49,
315 f32x4_gt = 0x44,
316 f64x2_gt = 0x4A,
317 f32x4_le = 0x45,
318 f64x2_le = 0x4B,
319 f32x4_ge = 0x46,
320 f64x2_ge = 0x4C,
321 v128_not = 0x4D,
322 v128_and = 0x4E,
323 v128_andnot = 0x4F,
324 v128_or = 0x50,
325 v128_xor = 0x51,
326 v128_bitselect = 0x52,
327 v128_any_true = 0x53,
328 v128_load8_lane = 0x54,
329 v128_load16_lane = 0x55,
330 v128_load32_lane = 0x56,
331 v128_load64_lane = 0x57,
332 v128_store8_lane = 0x58,
333 v128_store16_lane = 0x59,
334 v128_store32_lane = 0x5A,
335 v128_store64_lane = 0x5B,
336 v128_load32_zero = 0x5C,
337 v128_load64_zero = 0x5D,
338 f32x4_demote_f64x2_zero = 0x5E,
339 f64x2_promote_low_f32x4 = 0x5F,
340 i8x16_abs = 0x60,
341 i16x8_abs = 0x80,
342 i32x4_abs = 0xA0,
343 i64x2_abs = 0xC0,
344 i8x16_neg = 0x61,
345 i16x8_neg = 0x81,
346 i32x4_neg = 0xA1,
347 i64x2_neg = 0xC1,
348 i8x16_popcnt = 0x62,
349 i16x8_q15mulr_sat_s = 0x82,
350 i8x16_all_true = 0x63,
351 i16x8_all_true = 0x83,
352 i32x4_all_true = 0xA3,
353 i64x2_all_true = 0xC3,
354 i8x16_bitmask = 0x64,
355 i16x8_bitmask = 0x84,
356 i32x4_bitmask = 0xA4,
357 i64x2_bitmask = 0xC4,
358 i8x16_narrow_i16x8_s = 0x65,
359 i16x8_narrow_i32x4_s = 0x85,
360 i8x16_narrow_i16x8_u = 0x66,
361 i16x8_narrow_i32x4_u = 0x86,
362 f32x4_ceil = 0x67,
363 i16x8_extend_low_i8x16_s = 0x87,
364 i32x4_extend_low_i16x8_s = 0xA7,
365 i64x2_extend_low_i32x4_s = 0xC7,
366 f32x4_floor = 0x68,
367 i16x8_extend_high_i8x16_s = 0x88,
368 i32x4_extend_high_i16x8_s = 0xA8,
369 i64x2_extend_high_i32x4_s = 0xC8,
370 f32x4_trunc = 0x69,
371 i16x8_extend_low_i8x16_u = 0x89,
372 i32x4_extend_low_i16x8_u = 0xA9,
373 i64x2_extend_low_i32x4_u = 0xC9,
374 f32x4_nearest = 0x6A,
375 i16x8_extend_high_i8x16_u = 0x8A,
376 i32x4_extend_high_i16x8_u = 0xAA,
377 i64x2_extend_high_i32x4_u = 0xCA,
378 i8x16_shl = 0x6B,
379 i16x8_shl = 0x8B,
380 i32x4_shl = 0xAB,
381 i64x2_shl = 0xCB,
382 i8x16_shr_s = 0x6C,
383 i16x8_shr_s = 0x8C,
384 i32x4_shr_s = 0xAC,
385 i64x2_shr_s = 0xCC,
386 i8x16_shr_u = 0x6D,
387 i16x8_shr_u = 0x8D,
388 i32x4_shr_u = 0xAD,
389 i64x2_shr_u = 0xCD,
390 i8x16_add = 0x6E,
391 i16x8_add = 0x8E,
392 i32x4_add = 0xAE,
393 i64x2_add = 0xCE,
394 i8x16_add_sat_s = 0x6F,
395 i16x8_add_sat_s = 0x8F,
396 i8x16_add_sat_u = 0x70,
397 i16x8_add_sat_u = 0x90,
398 i8x16_sub = 0x71,
399 i16x8_sub = 0x91,
400 i32x4_sub = 0xB1,
401 i64x2_sub = 0xD1,
402 i8x16_sub_sat_s = 0x72,
403 i16x8_sub_sat_s = 0x92,
404 i8x16_sub_sat_u = 0x73,
405 i16x8_sub_sat_u = 0x93,
406 f64x2_ceil = 0x74,
407 f64x2_nearest = 0x94,
408 f64x2_floor = 0x75,
409 i16x8_mul = 0x95,
410 i32x4_mul = 0xB5,
411 i64x2_mul = 0xD5,
412 i8x16_min_s = 0x76,
413 i16x8_min_s = 0x96,
414 i32x4_min_s = 0xB6,
415 i64x2_eq = 0xD6,
416 i8x16_min_u = 0x77,
417 i16x8_min_u = 0x97,
418 i32x4_min_u = 0xB7,
419 i64x2_ne = 0xD7,
420 i8x16_max_s = 0x78,
421 i16x8_max_s = 0x98,
422 i32x4_max_s = 0xB8,
423 i64x2_lt_s = 0xD8,
424 i8x16_max_u = 0x79,
425 i16x8_max_u = 0x99,
426 i32x4_max_u = 0xB9,
427 i64x2_gt_s = 0xD9,
428 f64x2_trunc = 0x7A,
429 i32x4_dot_i16x8_s = 0xBA,
430 i64x2_le_s = 0xDA,
431 i8x16_avgr_u = 0x7B,
432 i16x8_avgr_u = 0x9B,
433 i64x2_ge_s = 0xDB,
434 i16x8_extadd_pairwise_i8x16_s = 0x7C,
435 i16x8_extmul_low_i8x16_s = 0x9C,
436 i32x4_extmul_low_i16x8_s = 0xBC,
437 i64x2_extmul_low_i32x4_s = 0xDC,
438 i16x8_extadd_pairwise_i8x16_u = 0x7D,
439 i16x8_extmul_high_i8x16_s = 0x9D,
440 i32x4_extmul_high_i16x8_s = 0xBD,
441 i64x2_extmul_high_i32x4_s = 0xDD,
442 i32x4_extadd_pairwise_i16x8_s = 0x7E,
443 i16x8_extmul_low_i8x16_u = 0x9E,
444 i32x4_extmul_low_i16x8_u = 0xBE,
445 i64x2_extmul_low_i32x4_u = 0xDE,
446 i32x4_extadd_pairwise_i16x8_u = 0x7F,
447 i16x8_extmul_high_i8x16_u = 0x9F,
448 i32x4_extmul_high_i16x8_u = 0xBF,
449 i64x2_extmul_high_i32x4_u = 0xDF,
450 f32x4_abs = 0xE0,
451 f64x2_abs = 0xEC,
452 f32x4_neg = 0xE1,
453 f64x2_neg = 0xED,
454 f32x4_sqrt = 0xE3,
455 f64x2_sqrt = 0xEF,
456 f32x4_add = 0xE4,
457 f64x2_add = 0xF0,
458 f32x4_sub = 0xE5,
459 f64x2_sub = 0xF1,
460 f32x4_mul = 0xE6,
461 f64x2_mul = 0xF2,
462 f32x4_div = 0xE7,
463 f64x2_div = 0xF3,
464 f32x4_min = 0xE8,
465 f64x2_min = 0xF4,
466 f32x4_max = 0xE9,
467 f64x2_max = 0xF5,
468 f32x4_pmin = 0xEA,
469 f64x2_pmin = 0xF6,
470 f32x4_pmax = 0xEB,
471 f64x2_pmax = 0xF7,
472 i32x4_trunc_sat_f32x4_s = 0xF8,
473 i32x4_trunc_sat_f32x4_u = 0xF9,
474 f32x4_convert_i32x4_s = 0xFA,
475 f32x4_convert_i32x4_u = 0xFB,
476 i32x4_trunc_sat_f64x2_s_zero = 0xFC,
477 i32x4_trunc_sat_f64x2_u_zero = 0xFD,
478 f64x2_convert_low_i32x4_s = 0xFE,
479 f64x2_convert_low_i32x4_u = 0xFF,
480
481 // relaxed-simd opcodes
482 i8x16_relaxed_swizzle = 0x100,
483 i32x4_relaxed_trunc_f32x4_s = 0x101,
484 i32x4_relaxed_trunc_f32x4_u = 0x102,
485 i32x4_relaxed_trunc_f64x2_s_zero = 0x103,
486 i32x4_relaxed_trunc_f64x2_u_zero = 0x104,
487 f32x4_relaxed_madd = 0x105,
488 f32x4_relaxed_nmadd = 0x106,
489 f64x2_relaxed_madd = 0x107,
490 f64x2_relaxed_nmadd = 0x108,
491 i8x16_relaxed_laneselect = 0x109,
492 i16x8_relaxed_laneselect = 0x10a,
493 i32x4_relaxed_laneselect = 0x10b,
494 i64x2_relaxed_laneselect = 0x10c,
495 f32x4_relaxed_min = 0x10d,
496 f32x4_relaxed_max = 0x10e,
497 f64x2_relaxed_min = 0x10f,
498 f64x2_relaxed_max = 0x110,
499 i16x8_relaxed_q15mulr_s = 0x111,
500 i16x8_relaxed_dot_i8x16_i7x16_s = 0x112,
501 i32x4_relaxed_dot_i8x16_i7x16_add_s = 0x113,
502 f32x4_relaxed_dot_bf16x8_add_f32x4 = 0x114,
503};
504
505/// Returns the integer value of an `SimdOpcode`. Used by the Zig compiler
506/// to write instructions to the wasm binary file
507pub fn simdOpcode(op: SimdOpcode) u32 {
508 return @enumToInt(op);
509}
510
240511/// Enum representing all Wasm value types as per spec:
241512/// https://webassembly.github.io/spec/core/binary/types.html
242513pub const Valtype = enum(u8) {
......@@ -244,6 +515,7 @@ pub const Valtype = enum(u8) {
244515 i64 = 0x7E,
245516 f32 = 0x7D,
246517 f64 = 0x7C,
518 v128 = 0x7B,
247519};
248520
249521/// Returns the integer value of a `Valtype`
src/arch/wasm/CodeGen.zig+221-17
......@@ -43,6 +43,10 @@ const WValue = union(enum) {
4343 imm32: u32,
4444 /// An immediate 64bit value
4545 imm64: u64,
46 /// Index into the list of simd128 immediates. This `WValue` is
47 /// only possible in very rare cases, therefore it would be
48 /// a waste of memory to store the value in a 128 bit integer.
49 imm128: u32,
4650 /// A constant 32bit float value
4751 float32: f32,
4852 /// A constant 64bit float value
......@@ -116,6 +120,7 @@ const WValue = union(enum) {
116120 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
117121 .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return,
118122 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,
123 .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return,
119124 }
120125 value.* = undefined;
121126 }
......@@ -258,18 +263,18 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
258263 8 => switch (args.valtype1.?) {
259264 .i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u,
260265 .i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u,
261 .f32, .f64 => unreachable,
266 .f32, .f64, .v128 => unreachable,
262267 },
263268 16 => switch (args.valtype1.?) {
264269 .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u,
265270 .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u,
266 .f32, .f64 => unreachable,
271 .f32, .f64, .v128 => unreachable,
267272 },
268273 32 => switch (args.valtype1.?) {
269274 .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,
270275 .i32 => return .i32_load,
271276 .f32 => return .f32_load,
272 .f64 => unreachable,
277 .f64, .v128 => unreachable,
273278 },
274279 64 => switch (args.valtype1.?) {
275280 .i64 => return .i64_load,
......@@ -282,24 +287,25 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
282287 .i64 => return .i64_load,
283288 .f32 => return .f32_load,
284289 .f64 => return .f64_load,
290 .v128 => unreachable, // handled independently
285291 },
286292 .store => if (args.width) |width| {
287293 switch (width) {
288294 8 => switch (args.valtype1.?) {
289295 .i32 => return .i32_store8,
290296 .i64 => return .i64_store8,
291 .f32, .f64 => unreachable,
297 .f32, .f64, .v128 => unreachable,
292298 },
293299 16 => switch (args.valtype1.?) {
294300 .i32 => return .i32_store16,
295301 .i64 => return .i64_store16,
296 .f32, .f64 => unreachable,
302 .f32, .f64, .v128 => unreachable,
297303 },
298304 32 => switch (args.valtype1.?) {
299305 .i64 => return .i64_store32,
300306 .i32 => return .i32_store,
301307 .f32 => return .f32_store,
302 .f64 => unreachable,
308 .f64, .v128 => unreachable,
303309 },
304310 64 => switch (args.valtype1.?) {
305311 .i64 => return .i64_store,
......@@ -314,6 +320,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
314320 .i64 => return .i64_store,
315321 .f32 => return .f32_store,
316322 .f64 => return .f64_store,
323 .v128 => unreachable, // handled independently
317324 }
318325 },
319326
......@@ -325,24 +332,27 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
325332 .i64 => return .i64_const,
326333 .f32 => return .f32_const,
327334 .f64 => return .f64_const,
335 .v128 => unreachable, // handled independently
328336 },
329337
330338 .eqz => switch (args.valtype1.?) {
331339 .i32 => return .i32_eqz,
332340 .i64 => return .i64_eqz,
333 .f32, .f64 => unreachable,
341 .f32, .f64, .v128 => unreachable,
334342 },
335343 .eq => switch (args.valtype1.?) {
336344 .i32 => return .i32_eq,
337345 .i64 => return .i64_eq,
338346 .f32 => return .f32_eq,
339347 .f64 => return .f64_eq,
348 .v128 => unreachable, // handled independently
340349 },
341350 .ne => switch (args.valtype1.?) {
342351 .i32 => return .i32_ne,
343352 .i64 => return .i64_ne,
344353 .f32 => return .f32_ne,
345354 .f64 => return .f64_ne,
355 .v128 => unreachable, // handled independently
346356 },
347357
348358 .lt => switch (args.valtype1.?) {
......@@ -350,40 +360,47 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
350360 .i64 => if (args.signedness.? == .signed) return .i64_lt_s else return .i64_lt_u,
351361 .f32 => return .f32_lt,
352362 .f64 => return .f64_lt,
363 .v128 => unreachable, // handled independently
353364 },
354365 .gt => switch (args.valtype1.?) {
355366 .i32 => if (args.signedness.? == .signed) return .i32_gt_s else return .i32_gt_u,
356367 .i64 => if (args.signedness.? == .signed) return .i64_gt_s else return .i64_gt_u,
357368 .f32 => return .f32_gt,
358369 .f64 => return .f64_gt,
370 .v128 => unreachable, // handled independently
359371 },
360372 .le => switch (args.valtype1.?) {
361373 .i32 => if (args.signedness.? == .signed) return .i32_le_s else return .i32_le_u,
362374 .i64 => if (args.signedness.? == .signed) return .i64_le_s else return .i64_le_u,
363375 .f32 => return .f32_le,
364376 .f64 => return .f64_le,
377 .v128 => unreachable, // handled independently
365378 },
366379 .ge => switch (args.valtype1.?) {
367380 .i32 => if (args.signedness.? == .signed) return .i32_ge_s else return .i32_ge_u,
368381 .i64 => if (args.signedness.? == .signed) return .i64_ge_s else return .i64_ge_u,
369382 .f32 => return .f32_ge,
370383 .f64 => return .f64_ge,
384 .v128 => unreachable, // handled independently
371385 },
372386
373387 .clz => switch (args.valtype1.?) {
374388 .i32 => return .i32_clz,
375389 .i64 => return .i64_clz,
376390 .f32, .f64 => unreachable,
391 .v128 => unreachable, // handled independently
377392 },
378393 .ctz => switch (args.valtype1.?) {
379394 .i32 => return .i32_ctz,
380395 .i64 => return .i64_ctz,
381396 .f32, .f64 => unreachable,
397 .v128 => unreachable, // handled independently
382398 },
383399 .popcnt => switch (args.valtype1.?) {
384400 .i32 => return .i32_popcnt,
385401 .i64 => return .i64_popcnt,
386402 .f32, .f64 => unreachable,
403 .v128 => unreachable, // handled independently
387404 },
388405
389406 .add => switch (args.valtype1.?) {
......@@ -391,18 +408,21 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
391408 .i64 => return .i64_add,
392409 .f32 => return .f32_add,
393410 .f64 => return .f64_add,
411 .v128 => unreachable, // handled independently
394412 },
395413 .sub => switch (args.valtype1.?) {
396414 .i32 => return .i32_sub,
397415 .i64 => return .i64_sub,
398416 .f32 => return .f32_sub,
399417 .f64 => return .f64_sub,
418 .v128 => unreachable, // handled independently
400419 },
401420 .mul => switch (args.valtype1.?) {
402421 .i32 => return .i32_mul,
403422 .i64 => return .i64_mul,
404423 .f32 => return .f32_mul,
405424 .f64 => return .f64_mul,
425 .v128 => unreachable, // handled independently
406426 },
407427
408428 .div => switch (args.valtype1.?) {
......@@ -410,71 +430,84 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
410430 .i64 => if (args.signedness.? == .signed) return .i64_div_s else return .i64_div_u,
411431 .f32 => return .f32_div,
412432 .f64 => return .f64_div,
433 .v128 => unreachable, // handled independently
413434 },
414435 .rem => switch (args.valtype1.?) {
415436 .i32 => if (args.signedness.? == .signed) return .i32_rem_s else return .i32_rem_u,
416437 .i64 => if (args.signedness.? == .signed) return .i64_rem_s else return .i64_rem_u,
417438 .f32, .f64 => unreachable,
439 .v128 => unreachable, // handled independently
418440 },
419441
420442 .@"and" => switch (args.valtype1.?) {
421443 .i32 => return .i32_and,
422444 .i64 => return .i64_and,
423445 .f32, .f64 => unreachable,
446 .v128 => unreachable, // handled independently
424447 },
425448 .@"or" => switch (args.valtype1.?) {
426449 .i32 => return .i32_or,
427450 .i64 => return .i64_or,
428451 .f32, .f64 => unreachable,
452 .v128 => unreachable, // handled independently
429453 },
430454 .xor => switch (args.valtype1.?) {
431455 .i32 => return .i32_xor,
432456 .i64 => return .i64_xor,
433457 .f32, .f64 => unreachable,
458 .v128 => unreachable, // handled independently
434459 },
435460
436461 .shl => switch (args.valtype1.?) {
437462 .i32 => return .i32_shl,
438463 .i64 => return .i64_shl,
439464 .f32, .f64 => unreachable,
465 .v128 => unreachable, // handled independently
440466 },
441467 .shr => switch (args.valtype1.?) {
442468 .i32 => if (args.signedness.? == .signed) return .i32_shr_s else return .i32_shr_u,
443469 .i64 => if (args.signedness.? == .signed) return .i64_shr_s else return .i64_shr_u,
444470 .f32, .f64 => unreachable,
471 .v128 => unreachable, // handled independently
445472 },
446473 .rotl => switch (args.valtype1.?) {
447474 .i32 => return .i32_rotl,
448475 .i64 => return .i64_rotl,
449476 .f32, .f64 => unreachable,
477 .v128 => unreachable, // handled independently
450478 },
451479 .rotr => switch (args.valtype1.?) {
452480 .i32 => return .i32_rotr,
453481 .i64 => return .i64_rotr,
454482 .f32, .f64 => unreachable,
483 .v128 => unreachable, // handled independently
455484 },
456485
457486 .abs => switch (args.valtype1.?) {
458487 .i32, .i64 => unreachable,
459488 .f32 => return .f32_abs,
460489 .f64 => return .f64_abs,
490 .v128 => unreachable, // handled independently
461491 },
462492 .neg => switch (args.valtype1.?) {
463493 .i32, .i64 => unreachable,
464494 .f32 => return .f32_neg,
465495 .f64 => return .f64_neg,
496 .v128 => unreachable, // handled independently
466497 },
467498 .ceil => switch (args.valtype1.?) {
468499 .i64 => unreachable,
469500 .i32 => return .f32_ceil, // when valtype is f16, we store it in i32.
470501 .f32 => return .f32_ceil,
471502 .f64 => return .f64_ceil,
503 .v128 => unreachable, // handled independently
472504 },
473505 .floor => switch (args.valtype1.?) {
474506 .i64 => unreachable,
475507 .i32 => return .f32_floor, // when valtype is f16, we store it in i32.
476508 .f32 => return .f32_floor,
477509 .f64 => return .f64_floor,
510 .v128 => unreachable, // handled independently
478511 },
479512 .trunc => switch (args.valtype1.?) {
480513 .i32 => if (args.valtype2) |valty| switch (valty) {
......@@ -482,40 +515,48 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
482515 .i64 => unreachable,
483516 .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u,
484517 .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u,
518 .v128 => unreachable, // handled independently
485519 } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32.
486520 .i64 => switch (args.valtype2.?) {
487521 .i32 => unreachable,
488522 .i64 => unreachable,
489523 .f32 => if (args.signedness.? == .signed) return .i64_trunc_f32_s else return .i64_trunc_f32_u,
490524 .f64 => if (args.signedness.? == .signed) return .i64_trunc_f64_s else return .i64_trunc_f64_u,
525 .v128 => unreachable, // handled independently
491526 },
492527 .f32 => return .f32_trunc,
493528 .f64 => return .f64_trunc,
529 .v128 => unreachable, // handled independently
494530 },
495531 .nearest => switch (args.valtype1.?) {
496532 .i32, .i64 => unreachable,
497533 .f32 => return .f32_nearest,
498534 .f64 => return .f64_nearest,
535 .v128 => unreachable, // handled independently
499536 },
500537 .sqrt => switch (args.valtype1.?) {
501538 .i32, .i64 => unreachable,
502539 .f32 => return .f32_sqrt,
503540 .f64 => return .f64_sqrt,
541 .v128 => unreachable, // handled independently
504542 },
505543 .min => switch (args.valtype1.?) {
506544 .i32, .i64 => unreachable,
507545 .f32 => return .f32_min,
508546 .f64 => return .f64_min,
547 .v128 => unreachable, // handled independently
509548 },
510549 .max => switch (args.valtype1.?) {
511550 .i32, .i64 => unreachable,
512551 .f32 => return .f32_max,
513552 .f64 => return .f64_max,
553 .v128 => unreachable, // handled independently
514554 },
515555 .copysign => switch (args.valtype1.?) {
516556 .i32, .i64 => unreachable,
517557 .f32 => return .f32_copysign,
518558 .f64 => return .f64_copysign,
559 .v128 => unreachable, // handled independently
519560 },
520561
521562 .wrap => switch (args.valtype1.?) {
......@@ -523,8 +564,10 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
523564 .i32 => unreachable,
524565 .i64 => return .i32_wrap_i64,
525566 .f32, .f64 => unreachable,
567 .v128 => unreachable, // handled independently
526568 },
527569 .i64, .f32, .f64 => unreachable,
570 .v128 => unreachable, // handled independently
528571 },
529572 .convert => switch (args.valtype1.?) {
530573 .i32, .i64 => unreachable,
......@@ -532,12 +575,15 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
532575 .i32 => if (args.signedness.? == .signed) return .f32_convert_i32_s else return .f32_convert_i32_u,
533576 .i64 => if (args.signedness.? == .signed) return .f32_convert_i64_s else return .f32_convert_i64_u,
534577 .f32, .f64 => unreachable,
578 .v128 => unreachable, // handled independently
535579 },
536580 .f64 => switch (args.valtype2.?) {
537581 .i32 => if (args.signedness.? == .signed) return .f64_convert_i32_s else return .f64_convert_i32_u,
538582 .i64 => if (args.signedness.? == .signed) return .f64_convert_i64_s else return .f64_convert_i64_u,
539583 .f32, .f64 => unreachable,
584 .v128 => unreachable, // handled independently
540585 },
586 .v128 => unreachable, // handled independently
541587 },
542588 .demote => if (args.valtype1.? == .f32 and args.valtype2.? == .f64) return .f32_demote_f64 else unreachable,
543589 .promote => if (args.valtype1.? == .f64 and args.valtype2.? == .f32) return .f64_promote_f32 else unreachable,
......@@ -546,6 +592,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
546592 .i64 => if (args.valtype2.? == .f64) return .i64_reinterpret_f64 else unreachable,
547593 .f32 => if (args.valtype2.? == .i32) return .f32_reinterpret_i32 else unreachable,
548594 .f64 => if (args.valtype2.? == .i64) return .f64_reinterpret_i64 else unreachable,
595 .v128 => unreachable, // handled independently
549596 },
550597 .extend => switch (args.valtype1.?) {
551598 .i32 => switch (args.width.?) {
......@@ -560,6 +607,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
560607 else => unreachable,
561608 },
562609 .f32, .f64 => unreachable,
610 .v128 => unreachable, // handled independently
563611 },
564612 }
565613}
......@@ -629,6 +677,10 @@ err_msg: *Module.ErrorMsg,
629677/// List of all locals' types generated throughout this declaration
630678/// used to emit locals count at start of 'code' section.
631679locals: std.ArrayListUnmanaged(u8),
680/// List of simd128 immediates. Each value is stored as an array of bytes.
681/// This list will only be populated for 128bit-simd values when the target features
682/// are enabled also.
683simd_immediates: std.ArrayListUnmanaged([16]u8) = .{},
632684/// The Target we're emitting (used to call intInfo)
633685target: std.Target,
634686/// Represents the wasm binary file that is being linked.
......@@ -665,14 +717,17 @@ stack_alignment: u32 = 16,
665717/// It is illegal to store a non-i32 valtype in this list.
666718free_locals_i32: std.ArrayListUnmanaged(u32) = .{},
667719/// A list of indexes which represents a local of valtype `i64`.
668/// It is illegal to store a non-i32 valtype in this list.
720/// It is illegal to store a non-i64 valtype in this list.
669721free_locals_i64: std.ArrayListUnmanaged(u32) = .{},
670722/// A list of indexes which represents a local of valtype `f32`.
671/// It is illegal to store a non-i32 valtype in this list.
723/// It is illegal to store a non-f32 valtype in this list.
672724free_locals_f32: std.ArrayListUnmanaged(u32) = .{},
673725/// A list of indexes which represents a local of valtype `f64`.
674/// It is illegal to store a non-i32 valtype in this list.
726/// It is illegal to store a non-f64 valtype in this list.
675727free_locals_f64: std.ArrayListUnmanaged(u32) = .{},
728/// A list of indexes which represents a local of valtype `v127`.
729/// It is illegal to store a non-v128 valtype in this list.
730free_locals_v128: std.ArrayListUnmanaged(u32) = .{},
676731
677732/// When in debug mode, this tracks if no `finishAir` was missed.
678733/// Forgetting to call `finishAir` will cause the result to not be
......@@ -699,12 +754,14 @@ pub fn deinit(func: *CodeGen) void {
699754 func.branches.deinit(func.gpa);
700755 func.blocks.deinit(func.gpa);
701756 func.locals.deinit(func.gpa);
757 func.simd_immediates.deinit(func.gpa);
702758 func.mir_instructions.deinit(func.gpa);
703759 func.mir_extra.deinit(func.gpa);
704760 func.free_locals_i32.deinit(func.gpa);
705761 func.free_locals_i64.deinit(func.gpa);
706762 func.free_locals_f32.deinit(func.gpa);
707763 func.free_locals_f64.deinit(func.gpa);
764 func.free_locals_v128.deinit(func.gpa);
708765 func.* = undefined;
709766}
710767
......@@ -867,6 +924,17 @@ fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void {
867924 try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });
868925}
869926
927/// Accepts the index into the list of 128bit-immediates
928fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {
929 const simd_values = func.simd_immediates.items[index];
930 const extra_index = @intCast(u32, func.mir_extra.items.len);
931 // tag + 128bit value
932 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);
933 func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));
934 func.mir_extra.appendSliceAssumeCapacity(@alignCast(4, mem.bytesAsSlice(u32, &simd_values)));
935 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
936}
937
870938fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {
871939 const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float));
872940 try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
......@@ -924,6 +992,10 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {
924992 },
925993 else => wasm.Valtype.i32,
926994 },
995 .Vector => switch (determineSimdStoreStrategy(ty, target)) {
996 .direct => wasm.Valtype.v128,
997 .unrolled => wasm.Valtype.i32,
998 },
927999 else => wasm.Valtype.i32, // all represented as reference/immediate
9281000 };
9291001}
......@@ -950,6 +1022,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
9501022 .local => |idx| try func.addLabel(.local_get, idx.value),
9511023 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),
9521024 .imm64 => |val| try func.addImm64(val),
1025 .imm128 => |val| try func.addImm128(val),
9531026 .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
9541027 .float64 => |val| try func.addFloat64(val),
9551028 .memory => |ptr| {
......@@ -1016,6 +1089,10 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
10161089 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
10171090 return WValue{ .local = .{ .value = index, .references = 1 } };
10181091 },
1092 .v128 => if (func.free_locals_v128.popOrNull()) |index| {
1093 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1094 return WValue{ .local = .{ .value = index, .references = 1 } };
1095 },
10191096 }
10201097 log.debug("new local of type {}\n", .{valtype});
10211098 // no local was free to be re-used, so allocate a new local instead
......@@ -1098,7 +1175,6 @@ pub fn generate(
10981175 .gpa = bin_file.allocator,
10991176 .air = air,
11001177 .liveness = liveness,
1101 // .values = .{},
11021178 .code = code,
11031179 .decl_index = func.owner_decl,
11041180 .decl = bin_file.options.module.?.declPtr(func.owner_decl),
......@@ -1481,9 +1557,9 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
14811557 .imm64 => |val| val,
14821558 else => unreachable,
14831559 };
1484 // if the size (length) is more than 1024 bytes, we use a runtime loop instead to prevent
1560 // if the size (length) is more than 32 bytes, we use a runtime loop instead to prevent
14851561 // binary size bloat.
1486 if (length > 1024) break :blk;
1562 if (length > 32) break :blk;
14871563 var offset: u32 = 0;
14881564 const lhs_base = dst.offset();
14891565 const rhs_base = src.offset();
......@@ -1612,7 +1688,6 @@ fn isByRef(ty: Type, target: std.Target) bool {
16121688 => return false,
16131689
16141690 .Array,
1615 .Vector,
16161691 .Frame,
16171692 .Union,
16181693 => return ty.hasRuntimeBitsIgnoreComptime(),
......@@ -1625,6 +1700,7 @@ fn isByRef(ty: Type, target: std.Target) bool {
16251700 }
16261701 return ty.hasRuntimeBitsIgnoreComptime();
16271702 },
1703 .Vector => return determineSimdStoreStrategy(ty, target) == .unrolled,
16281704 .Int => return ty.intInfo(target).bits > 64,
16291705 .Float => return ty.floatBits(target) > 64,
16301706 .ErrorUnion => {
......@@ -1647,6 +1723,26 @@ fn isByRef(ty: Type, target: std.Target) bool {
16471723 }
16481724}
16491725
1726const SimdStoreStrategy = enum {
1727 direct,
1728 unrolled,
1729};
1730
1731/// For a given vector type, returns the `SimdStoreStrategy`.
1732/// This means when a given type is 128 bits and either the simd128 or relaxed-simd
1733/// features are enabled, the function will return `.direct`. This would allow to store
1734/// it using a instruction, rather than an unrolled version.
1735fn determineSimdStoreStrategy(ty: Type, target: std.Target) SimdStoreStrategy {
1736 std.debug.assert(ty.zigTypeTag() == .Vector);
1737 if (ty.bitSize(target) != 128) return .unrolled;
1738 const hasFeature = std.Target.wasm.featureSetHas;
1739 const features = target.cpu.features;
1740 if (hasFeature(features, .relaxed_simd) or hasFeature(features, .simd128)) {
1741 return .direct;
1742 }
1743 return .unrolled;
1744}
1745
16501746/// Creates a new local for a pointer that points to memory with given offset.
16511747/// This can be used to get a pointer to a struct field, error payload, etc.
16521748/// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new
......@@ -2187,10 +2283,29 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
21872283 const len = @intCast(u32, ty.abiSize(func.target));
21882284 return func.memcpy(lhs, rhs, .{ .imm32 = len });
21892285 },
2190 .Struct, .Array, .Union, .Vector => if (isByRef(ty, func.target)) {
2286 .Struct, .Array, .Union => if (isByRef(ty, func.target)) {
21912287 const len = @intCast(u32, ty.abiSize(func.target));
21922288 return func.memcpy(lhs, rhs, .{ .imm32 = len });
21932289 },
2290 .Vector => switch (determineSimdStoreStrategy(ty, func.target)) {
2291 .unrolled => {
2292 const len = @intCast(u32, ty.abiSize(func.target));
2293 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2294 },
2295 .direct => {
2296 try func.emitWValue(lhs);
2297 try func.lowerToStack(rhs);
2298 // TODO: Add helper functions for simd opcodes
2299 const extra_index = @intCast(u32, func.mir_extra.items.len);
2300 // stores as := opcode, offset, alignment (opcode::memarg)
2301 try func.mir_extra.appendSlice(func.gpa, &[_]u32{
2302 std.wasm.simdOpcode(.v128_store),
2303 offset + lhs.offset(),
2304 ty.abiAlignment(func.target),
2305 });
2306 return func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
2307 },
2308 },
21942309 .Pointer => {
21952310 if (ty.isSlice()) {
21962311 // store pointer first
......@@ -2289,6 +2404,19 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
22892404 // load local's value from memory by its stack position
22902405 try func.emitWValue(operand);
22912406
2407 if (ty.zigTypeTag() == .Vector) {
2408 // TODO: Add helper functions for simd opcodes
2409 const extra_index = @intCast(u32, func.mir_extra.items.len);
2410 // stores as := opcode, offset, alignment (opcode::memarg)
2411 try func.mir_extra.appendSlice(func.gpa, &[_]u32{
2412 std.wasm.simdOpcode(.v128_load),
2413 offset + operand.offset(),
2414 ty.abiAlignment(func.target),
2415 });
2416 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
2417 return WValue{ .stack = {} };
2418 }
2419
22922420 const abi_size = @intCast(u8, ty.abiSize(func.target));
22932421 const opcode = buildOpcode(.{
22942422 .valtype1 = typeToValtype(ty, func.target),
......@@ -2766,10 +2894,24 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
27662894 const int_val = Value.initPayload(&payload.base);
27672895 return func.lowerConstant(int_val, struct_obj.backing_int_ty);
27682896 },
2897 .Vector => {
2898 assert(determineSimdStoreStrategy(ty, target) == .direct);
2899 var buf: [16]u8 = undefined;
2900 val.writeToMemory(ty, func.bin_file.base.options.module.?, &buf);
2901 return func.storeSimdImmd(buf);
2902 },
27692903 else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),
27702904 }
27712905}
27722906
2907/// Stores the value as a 128bit-immediate value by storing it inside
2908/// the list and returning the index into this list as `WValue`.
2909fn storeSimdImmd(func: *CodeGen, value: [16]u8) !WValue {
2910 const index = @intCast(u32, func.simd_immediates.items.len);
2911 try func.simd_immediates.append(func.gpa, value);
2912 return WValue{ .imm128 = index };
2913}
2914
27732915fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
27742916 switch (ty.zigTypeTag()) {
27752917 .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa },
......@@ -4288,9 +4430,71 @@ fn airIntToFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42884430fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42894431 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
42904432 const operand = try func.resolveInst(ty_op.operand);
4433 const ty = func.air.typeOfIndex(inst);
4434 const elem_ty = ty.childType();
4435
4436 if (determineSimdStoreStrategy(ty, func.target) == .direct) blk: {
4437 switch (operand) {
4438 // when the operand lives in the linear memory section, we can directly
4439 // load and splat the value at once. Meaning we do not first have to load
4440 // the scalar value onto the stack.
4441 .stack_offset, .memory, .memory_offset => {
4442 const opcode = switch (elem_ty.bitSize(func.target)) {
4443 8 => std.wasm.simdOpcode(.v128_load8_splat),
4444 16 => std.wasm.simdOpcode(.v128_load16_splat),
4445 32 => std.wasm.simdOpcode(.v128_load32_splat),
4446 64 => std.wasm.simdOpcode(.v128_load64_splat),
4447 else => break :blk, // Cannot make use of simd-instructions
4448 };
4449 const result = try func.allocLocal(ty);
4450 try func.emitWValue(operand);
4451 // TODO: Add helper functions for simd opcodes
4452 const extra_index = @intCast(u32, func.mir_extra.items.len);
4453 // stores as := opcode, offset, alignment (opcode::memarg)
4454 try func.mir_extra.appendSlice(func.gpa, &[_]u32{
4455 opcode,
4456 operand.offset(),
4457 elem_ty.abiAlignment(func.target),
4458 });
4459 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
4460 try func.addLabel(.local_set, result.local.value);
4461 return func.finishAir(inst, result, &.{ty_op.operand});
4462 },
4463 .local => {
4464 const opcode = switch (elem_ty.bitSize(func.target)) {
4465 8 => std.wasm.simdOpcode(.i8x16_splat),
4466 16 => std.wasm.simdOpcode(.i16x8_splat),
4467 32 => if (elem_ty.isInt()) std.wasm.simdOpcode(.i32x4_splat) else std.wasm.simdOpcode(.f32x4_splat),
4468 64 => if (elem_ty.isInt()) std.wasm.simdOpcode(.i64x2_splat) else std.wasm.simdOpcode(.f64x2_splat),
4469 else => break :blk, // Cannot make use of simd-instructions
4470 };
4471 const result = try func.allocLocal(ty);
4472 try func.emitWValue(operand);
4473 const extra_index = @intCast(u32, func.mir_extra.items.len);
4474 try func.mir_extra.append(func.gpa, opcode);
4475 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
4476 try func.addLabel(.local_set, result.local.value);
4477 return func.finishAir(inst, result, &.{ty_op.operand});
4478 },
4479 else => unreachable,
4480 }
4481 }
4482 const elem_size = elem_ty.bitSize(func.target);
4483 const vector_len = @intCast(usize, ty.vectorLen());
4484 if ((!std.math.isPowerOfTwo(elem_size) or elem_size % 8 != 0) and vector_len > 1) {
4485 return func.fail("TODO: WebAssembly `@splat` for arbitrary element bitsize {d}", .{elem_size});
4486 }
42914487
4292 _ = operand;
4293 return func.fail("TODO: Implement wasm airSplat", .{});
4488 const result = try func.allocStack(ty);
4489 const elem_byte_size = @intCast(u32, elem_ty.abiSize(func.target));
4490 var index: usize = 0;
4491 var offset: u32 = 0;
4492 while (index < vector_len) : (index += 1) {
4493 try func.store(result, operand, elem_ty, offset);
4494 offset += elem_byte_size;
4495 }
4496
4497 return func.finishAir(inst, result, &.{ty_op.operand});
42944498}
42954499
42964500fn airSelect(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
src/arch/wasm/Emit.zig+38-2
......@@ -240,6 +240,7 @@ pub fn emitMir(emit: *Emit) InnerError!void {
240240 .i64_ctz => try emit.emitTag(tag),
241241
242242 .extended => try emit.emitExtended(inst),
243 .simd => try emit.emitSimd(inst),
243244 }
244245 }
245246}
......@@ -341,11 +342,14 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
341342 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
342343 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;
343344 try emit.code.append(@enumToInt(tag));
345 try encodeMemArg(mem_arg, emit.code.writer());
346}
344347
348fn encodeMemArg(mem_arg: Mir.MemArg, writer: anytype) !void {
345349 // wasm encodes alignment as power of 2, rather than natural alignment
346350 const encoded_alignment = @ctz(mem_arg.alignment);
347 try leb128.writeULEB128(emit.code.writer(), encoded_alignment);
348 try leb128.writeULEB128(emit.code.writer(), mem_arg.offset);
351 try leb128.writeULEB128(writer, encoded_alignment);
352 try leb128.writeULEB128(writer, mem_arg.offset);
349353}
350354
351355fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
......@@ -426,6 +430,38 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
426430 }
427431}
428432
433fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
434 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
435 const opcode = emit.mir.extra[extra_index];
436 const writer = emit.code.writer();
437 try emit.code.append(0xFD);
438 try leb128.writeULEB128(writer, opcode);
439 switch (@intToEnum(std.wasm.SimdOpcode, opcode)) {
440 .v128_store,
441 .v128_load,
442 .v128_load8_splat,
443 .v128_load16_splat,
444 .v128_load32_splat,
445 .v128_load64_splat,
446 => {
447 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;
448 try encodeMemArg(mem_arg, writer);
449 },
450 .v128_const => {
451 const simd_value = emit.mir.extra[extra_index + 1 ..][0..4];
452 try writer.writeAll(std.mem.asBytes(simd_value));
453 },
454 .i8x16_splat,
455 .i16x8_splat,
456 .i32x4_splat,
457 .i64x2_splat,
458 .f32x4_splat,
459 .f64x2_splat,
460 => {}, // opcode already written
461 else => |tag| return emit.fail("TODO: Implement simd instruction: {s}\n", .{@tagName(tag)}),
462 }
463}
464
429465fn emitMemFill(emit: *Emit) !void {
430466 try emit.code.append(0xFC);
431467 try emit.code.append(0x0B);
src/arch/wasm/Mir.zig+7-1
......@@ -518,6 +518,12 @@ pub const Inst = struct {
518518 ///
519519 /// The `data` field depends on the extension instruction
520520 extended = 0xFC,
521 /// The instruction consists of a simd opcode.
522 /// The actual simd-opcode is found at payload's index.
523 ///
524 /// The `data` field depends on the simd instruction and
525 /// may contain additional data.
526 simd = 0xFD,
521527 /// Contains a symbol to a function pointer
522528 /// uses `label`
523529 ///
......@@ -578,7 +584,7 @@ pub fn deinit(self: *Mir, gpa: std.mem.Allocator) void {
578584 self.* = undefined;
579585}
580586
581pub fn extraData(self: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
587pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
582588 const fields = std.meta.fields(T);
583589 var i: usize = index;
584590 var result: T = undefined;
src/codegen.zig+48
......@@ -808,6 +808,54 @@ pub fn generateSymbol(
808808 }
809809 return Result{ .appended = {} };
810810 },
811 .Vector => switch (typed_value.val.tag()) {
812 .bytes => {
813 const bytes = typed_value.val.castTag(.bytes).?.data;
814 const len = @intCast(usize, typed_value.ty.arrayLen());
815 try code.ensureUnusedCapacity(len);
816 code.appendSliceAssumeCapacity(bytes[0..len]);
817 return Result{ .appended = {} };
818 },
819 .aggregate => {
820 const elem_vals = typed_value.val.castTag(.aggregate).?.data;
821 const elem_ty = typed_value.ty.elemType();
822 const len = @intCast(usize, typed_value.ty.arrayLen());
823 for (elem_vals[0..len]) |elem_val| {
824 switch (try generateSymbol(bin_file, src_loc, .{
825 .ty = elem_ty,
826 .val = elem_val,
827 }, code, debug_output, reloc_info)) {
828 .appended => {},
829 .externally_managed => |slice| {
830 code.appendSliceAssumeCapacity(slice);
831 },
832 .fail => |em| return Result{ .fail = em },
833 }
834 }
835 return Result{ .appended = {} };
836 },
837 .repeated => {
838 const array = typed_value.val.castTag(.repeated).?.data;
839 const elem_ty = typed_value.ty.childType();
840 const len = typed_value.ty.arrayLen();
841
842 var index: u64 = 0;
843 while (index < len) : (index += 1) {
844 switch (try generateSymbol(bin_file, src_loc, .{
845 .ty = elem_ty,
846 .val = array,
847 }, code, debug_output, reloc_info)) {
848 .appended => {},
849 .externally_managed => |slice| {
850 code.appendSliceAssumeCapacity(slice);
851 },
852 .fail => |em| return Result{ .fail = em },
853 }
854 }
855 return Result{ .appended = {} };
856 },
857 else => unreachable,
858 },
811859 else => |t| {
812860 return Result{
813861 .fail = try ErrorMsg.create(
test/behavior/vector.zig-8
......@@ -138,7 +138,6 @@ test "vector bit operators" {
138138}
139139
140140test "implicit cast vector to array" {
141 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
142141 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
143142 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
144143 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -157,7 +156,6 @@ test "implicit cast vector to array" {
157156}
158157
159158test "array to vector" {
160 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
161159 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
162160 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
163161 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -235,7 +233,6 @@ test "vector casts of sizes not divisible by 8" {
235233}
236234
237235test "vector @splat" {
238 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
239236 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
240237 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
241238 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -284,7 +281,6 @@ test "vector @splat" {
284281}
285282
286283test "load vector elements via comptime index" {
287 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
288284 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
289285 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
290286 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -307,7 +303,6 @@ test "load vector elements via comptime index" {
307303}
308304
309305test "store vector elements via comptime index" {
310 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
311306 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
312307 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
313308 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -336,7 +331,6 @@ test "store vector elements via comptime index" {
336331}
337332
338333test "load vector elements via runtime index" {
339 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
340334 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
341335 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
342336 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -359,7 +353,6 @@ test "load vector elements via runtime index" {
359353}
360354
361355test "store vector elements via runtime index" {
362 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
363356 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
364357 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
365358 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -383,7 +376,6 @@ test "store vector elements via runtime index" {
383376}
384377
385378test "initialize vector which is a struct field" {
386 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
387379 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
388380 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
389381 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO