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
signaturebadge-question-mark 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) {...@@ -237,6 +237,277 @@ pub const PrefixedOpcode = enum(u8) {
237 _,237 _,
238};238};
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
240/// Enum representing all Wasm value types as per spec:511/// Enum representing all Wasm value types as per spec:
241/// https://webassembly.github.io/spec/core/binary/types.html512/// https://webassembly.github.io/spec/core/binary/types.html
242pub const Valtype = enum(u8) {513pub const Valtype = enum(u8) {
...@@ -244,6 +515,7 @@ pub const Valtype = enum(u8) {...@@ -244,6 +515,7 @@ pub const Valtype = enum(u8) {
244 i64 = 0x7E,515 i64 = 0x7E,
245 f32 = 0x7D,516 f32 = 0x7D,
246 f64 = 0x7C,517 f64 = 0x7C,
518 v128 = 0x7B,
247};519};
248520
249/// Returns the integer value of a `Valtype`521/// Returns the integer value of a `Valtype`
src/arch/wasm/CodeGen.zig+221-17
...@@ -43,6 +43,10 @@ const WValue = union(enum) {...@@ -43,6 +43,10 @@ const WValue = union(enum) {
43 imm32: u32,43 imm32: u32,
44 /// An immediate 64bit value44 /// An immediate 64bit value
45 imm64: u64,45 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,
46 /// A constant 32bit float value50 /// A constant 32bit float value
47 float32: f32,51 float32: f32,
48 /// A constant 64bit float value52 /// A constant 64bit float value
...@@ -116,6 +120,7 @@ const WValue = union(enum) {...@@ -116,6 +120,7 @@ const WValue = union(enum) {
116 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,120 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
117 .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return,121 .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return,
118 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,122 .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,
119 }124 }
120 value.* = undefined;125 value.* = undefined;
121 }126 }
...@@ -258,18 +263,18 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -258,18 +263,18 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
258 8 => switch (args.valtype1.?) {263 8 => switch (args.valtype1.?) {
259 .i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u,264 .i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u,
260 .i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u,265 .i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u,
261 .f32, .f64 => unreachable,266 .f32, .f64, .v128 => unreachable,
262 },267 },
263 16 => switch (args.valtype1.?) {268 16 => switch (args.valtype1.?) {
264 .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u,269 .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u,
265 .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u,270 .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u,
266 .f32, .f64 => unreachable,271 .f32, .f64, .v128 => unreachable,
267 },272 },
268 32 => switch (args.valtype1.?) {273 32 => switch (args.valtype1.?) {
269 .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,274 .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,
270 .i32 => return .i32_load,275 .i32 => return .i32_load,
271 .f32 => return .f32_load,276 .f32 => return .f32_load,
272 .f64 => unreachable,277 .f64, .v128 => unreachable,
273 },278 },
274 64 => switch (args.valtype1.?) {279 64 => switch (args.valtype1.?) {
275 .i64 => return .i64_load,280 .i64 => return .i64_load,
...@@ -282,24 +287,25 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -282,24 +287,25 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
282 .i64 => return .i64_load,287 .i64 => return .i64_load,
283 .f32 => return .f32_load,288 .f32 => return .f32_load,
284 .f64 => return .f64_load,289 .f64 => return .f64_load,
290 .v128 => unreachable, // handled independently
285 },291 },
286 .store => if (args.width) |width| {292 .store => if (args.width) |width| {
287 switch (width) {293 switch (width) {
288 8 => switch (args.valtype1.?) {294 8 => switch (args.valtype1.?) {
289 .i32 => return .i32_store8,295 .i32 => return .i32_store8,
290 .i64 => return .i64_store8,296 .i64 => return .i64_store8,
291 .f32, .f64 => unreachable,297 .f32, .f64, .v128 => unreachable,
292 },298 },
293 16 => switch (args.valtype1.?) {299 16 => switch (args.valtype1.?) {
294 .i32 => return .i32_store16,300 .i32 => return .i32_store16,
295 .i64 => return .i64_store16,301 .i64 => return .i64_store16,
296 .f32, .f64 => unreachable,302 .f32, .f64, .v128 => unreachable,
297 },303 },
298 32 => switch (args.valtype1.?) {304 32 => switch (args.valtype1.?) {
299 .i64 => return .i64_store32,305 .i64 => return .i64_store32,
300 .i32 => return .i32_store,306 .i32 => return .i32_store,
301 .f32 => return .f32_store,307 .f32 => return .f32_store,
302 .f64 => unreachable,308 .f64, .v128 => unreachable,
303 },309 },
304 64 => switch (args.valtype1.?) {310 64 => switch (args.valtype1.?) {
305 .i64 => return .i64_store,311 .i64 => return .i64_store,
...@@ -314,6 +320,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -314,6 +320,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
314 .i64 => return .i64_store,320 .i64 => return .i64_store,
315 .f32 => return .f32_store,321 .f32 => return .f32_store,
316 .f64 => return .f64_store,322 .f64 => return .f64_store,
323 .v128 => unreachable, // handled independently
317 }324 }
318 },325 },
319326
...@@ -325,24 +332,27 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -325,24 +332,27 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
325 .i64 => return .i64_const,332 .i64 => return .i64_const,
326 .f32 => return .f32_const,333 .f32 => return .f32_const,
327 .f64 => return .f64_const,334 .f64 => return .f64_const,
335 .v128 => unreachable, // handled independently
328 },336 },
329337
330 .eqz => switch (args.valtype1.?) {338 .eqz => switch (args.valtype1.?) {
331 .i32 => return .i32_eqz,339 .i32 => return .i32_eqz,
332 .i64 => return .i64_eqz,340 .i64 => return .i64_eqz,
333 .f32, .f64 => unreachable,341 .f32, .f64, .v128 => unreachable,
334 },342 },
335 .eq => switch (args.valtype1.?) {343 .eq => switch (args.valtype1.?) {
336 .i32 => return .i32_eq,344 .i32 => return .i32_eq,
337 .i64 => return .i64_eq,345 .i64 => return .i64_eq,
338 .f32 => return .f32_eq,346 .f32 => return .f32_eq,
339 .f64 => return .f64_eq,347 .f64 => return .f64_eq,
348 .v128 => unreachable, // handled independently
340 },349 },
341 .ne => switch (args.valtype1.?) {350 .ne => switch (args.valtype1.?) {
342 .i32 => return .i32_ne,351 .i32 => return .i32_ne,
343 .i64 => return .i64_ne,352 .i64 => return .i64_ne,
344 .f32 => return .f32_ne,353 .f32 => return .f32_ne,
345 .f64 => return .f64_ne,354 .f64 => return .f64_ne,
355 .v128 => unreachable, // handled independently
346 },356 },
347357
348 .lt => switch (args.valtype1.?) {358 .lt => switch (args.valtype1.?) {
...@@ -350,40 +360,47 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -350,40 +360,47 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
350 .i64 => if (args.signedness.? == .signed) return .i64_lt_s else return .i64_lt_u,360 .i64 => if (args.signedness.? == .signed) return .i64_lt_s else return .i64_lt_u,
351 .f32 => return .f32_lt,361 .f32 => return .f32_lt,
352 .f64 => return .f64_lt,362 .f64 => return .f64_lt,
363 .v128 => unreachable, // handled independently
353 },364 },
354 .gt => switch (args.valtype1.?) {365 .gt => switch (args.valtype1.?) {
355 .i32 => if (args.signedness.? == .signed) return .i32_gt_s else return .i32_gt_u,366 .i32 => if (args.signedness.? == .signed) return .i32_gt_s else return .i32_gt_u,
356 .i64 => if (args.signedness.? == .signed) return .i64_gt_s else return .i64_gt_u,367 .i64 => if (args.signedness.? == .signed) return .i64_gt_s else return .i64_gt_u,
357 .f32 => return .f32_gt,368 .f32 => return .f32_gt,
358 .f64 => return .f64_gt,369 .f64 => return .f64_gt,
370 .v128 => unreachable, // handled independently
359 },371 },
360 .le => switch (args.valtype1.?) {372 .le => switch (args.valtype1.?) {
361 .i32 => if (args.signedness.? == .signed) return .i32_le_s else return .i32_le_u,373 .i32 => if (args.signedness.? == .signed) return .i32_le_s else return .i32_le_u,
362 .i64 => if (args.signedness.? == .signed) return .i64_le_s else return .i64_le_u,374 .i64 => if (args.signedness.? == .signed) return .i64_le_s else return .i64_le_u,
363 .f32 => return .f32_le,375 .f32 => return .f32_le,
364 .f64 => return .f64_le,376 .f64 => return .f64_le,
377 .v128 => unreachable, // handled independently
365 },378 },
366 .ge => switch (args.valtype1.?) {379 .ge => switch (args.valtype1.?) {
367 .i32 => if (args.signedness.? == .signed) return .i32_ge_s else return .i32_ge_u,380 .i32 => if (args.signedness.? == .signed) return .i32_ge_s else return .i32_ge_u,
368 .i64 => if (args.signedness.? == .signed) return .i64_ge_s else return .i64_ge_u,381 .i64 => if (args.signedness.? == .signed) return .i64_ge_s else return .i64_ge_u,
369 .f32 => return .f32_ge,382 .f32 => return .f32_ge,
370 .f64 => return .f64_ge,383 .f64 => return .f64_ge,
384 .v128 => unreachable, // handled independently
371 },385 },
372386
373 .clz => switch (args.valtype1.?) {387 .clz => switch (args.valtype1.?) {
374 .i32 => return .i32_clz,388 .i32 => return .i32_clz,
375 .i64 => return .i64_clz,389 .i64 => return .i64_clz,
376 .f32, .f64 => unreachable,390 .f32, .f64 => unreachable,
391 .v128 => unreachable, // handled independently
377 },392 },
378 .ctz => switch (args.valtype1.?) {393 .ctz => switch (args.valtype1.?) {
379 .i32 => return .i32_ctz,394 .i32 => return .i32_ctz,
380 .i64 => return .i64_ctz,395 .i64 => return .i64_ctz,
381 .f32, .f64 => unreachable,396 .f32, .f64 => unreachable,
397 .v128 => unreachable, // handled independently
382 },398 },
383 .popcnt => switch (args.valtype1.?) {399 .popcnt => switch (args.valtype1.?) {
384 .i32 => return .i32_popcnt,400 .i32 => return .i32_popcnt,
385 .i64 => return .i64_popcnt,401 .i64 => return .i64_popcnt,
386 .f32, .f64 => unreachable,402 .f32, .f64 => unreachable,
403 .v128 => unreachable, // handled independently
387 },404 },
388405
389 .add => switch (args.valtype1.?) {406 .add => switch (args.valtype1.?) {
...@@ -391,18 +408,21 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -391,18 +408,21 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
391 .i64 => return .i64_add,408 .i64 => return .i64_add,
392 .f32 => return .f32_add,409 .f32 => return .f32_add,
393 .f64 => return .f64_add,410 .f64 => return .f64_add,
411 .v128 => unreachable, // handled independently
394 },412 },
395 .sub => switch (args.valtype1.?) {413 .sub => switch (args.valtype1.?) {
396 .i32 => return .i32_sub,414 .i32 => return .i32_sub,
397 .i64 => return .i64_sub,415 .i64 => return .i64_sub,
398 .f32 => return .f32_sub,416 .f32 => return .f32_sub,
399 .f64 => return .f64_sub,417 .f64 => return .f64_sub,
418 .v128 => unreachable, // handled independently
400 },419 },
401 .mul => switch (args.valtype1.?) {420 .mul => switch (args.valtype1.?) {
402 .i32 => return .i32_mul,421 .i32 => return .i32_mul,
403 .i64 => return .i64_mul,422 .i64 => return .i64_mul,
404 .f32 => return .f32_mul,423 .f32 => return .f32_mul,
405 .f64 => return .f64_mul,424 .f64 => return .f64_mul,
425 .v128 => unreachable, // handled independently
406 },426 },
407427
408 .div => switch (args.valtype1.?) {428 .div => switch (args.valtype1.?) {
...@@ -410,71 +430,84 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -410,71 +430,84 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
410 .i64 => if (args.signedness.? == .signed) return .i64_div_s else return .i64_div_u,430 .i64 => if (args.signedness.? == .signed) return .i64_div_s else return .i64_div_u,
411 .f32 => return .f32_div,431 .f32 => return .f32_div,
412 .f64 => return .f64_div,432 .f64 => return .f64_div,
433 .v128 => unreachable, // handled independently
413 },434 },
414 .rem => switch (args.valtype1.?) {435 .rem => switch (args.valtype1.?) {
415 .i32 => if (args.signedness.? == .signed) return .i32_rem_s else return .i32_rem_u,436 .i32 => if (args.signedness.? == .signed) return .i32_rem_s else return .i32_rem_u,
416 .i64 => if (args.signedness.? == .signed) return .i64_rem_s else return .i64_rem_u,437 .i64 => if (args.signedness.? == .signed) return .i64_rem_s else return .i64_rem_u,
417 .f32, .f64 => unreachable,438 .f32, .f64 => unreachable,
439 .v128 => unreachable, // handled independently
418 },440 },
419441
420 .@"and" => switch (args.valtype1.?) {442 .@"and" => switch (args.valtype1.?) {
421 .i32 => return .i32_and,443 .i32 => return .i32_and,
422 .i64 => return .i64_and,444 .i64 => return .i64_and,
423 .f32, .f64 => unreachable,445 .f32, .f64 => unreachable,
446 .v128 => unreachable, // handled independently
424 },447 },
425 .@"or" => switch (args.valtype1.?) {448 .@"or" => switch (args.valtype1.?) {
426 .i32 => return .i32_or,449 .i32 => return .i32_or,
427 .i64 => return .i64_or,450 .i64 => return .i64_or,
428 .f32, .f64 => unreachable,451 .f32, .f64 => unreachable,
452 .v128 => unreachable, // handled independently
429 },453 },
430 .xor => switch (args.valtype1.?) {454 .xor => switch (args.valtype1.?) {
431 .i32 => return .i32_xor,455 .i32 => return .i32_xor,
432 .i64 => return .i64_xor,456 .i64 => return .i64_xor,
433 .f32, .f64 => unreachable,457 .f32, .f64 => unreachable,
458 .v128 => unreachable, // handled independently
434 },459 },
435460
436 .shl => switch (args.valtype1.?) {461 .shl => switch (args.valtype1.?) {
437 .i32 => return .i32_shl,462 .i32 => return .i32_shl,
438 .i64 => return .i64_shl,463 .i64 => return .i64_shl,
439 .f32, .f64 => unreachable,464 .f32, .f64 => unreachable,
465 .v128 => unreachable, // handled independently
440 },466 },
441 .shr => switch (args.valtype1.?) {467 .shr => switch (args.valtype1.?) {
442 .i32 => if (args.signedness.? == .signed) return .i32_shr_s else return .i32_shr_u,468 .i32 => if (args.signedness.? == .signed) return .i32_shr_s else return .i32_shr_u,
443 .i64 => if (args.signedness.? == .signed) return .i64_shr_s else return .i64_shr_u,469 .i64 => if (args.signedness.? == .signed) return .i64_shr_s else return .i64_shr_u,
444 .f32, .f64 => unreachable,470 .f32, .f64 => unreachable,
471 .v128 => unreachable, // handled independently
445 },472 },
446 .rotl => switch (args.valtype1.?) {473 .rotl => switch (args.valtype1.?) {
447 .i32 => return .i32_rotl,474 .i32 => return .i32_rotl,
448 .i64 => return .i64_rotl,475 .i64 => return .i64_rotl,
449 .f32, .f64 => unreachable,476 .f32, .f64 => unreachable,
477 .v128 => unreachable, // handled independently
450 },478 },
451 .rotr => switch (args.valtype1.?) {479 .rotr => switch (args.valtype1.?) {
452 .i32 => return .i32_rotr,480 .i32 => return .i32_rotr,
453 .i64 => return .i64_rotr,481 .i64 => return .i64_rotr,
454 .f32, .f64 => unreachable,482 .f32, .f64 => unreachable,
483 .v128 => unreachable, // handled independently
455 },484 },
456485
457 .abs => switch (args.valtype1.?) {486 .abs => switch (args.valtype1.?) {
458 .i32, .i64 => unreachable,487 .i32, .i64 => unreachable,
459 .f32 => return .f32_abs,488 .f32 => return .f32_abs,
460 .f64 => return .f64_abs,489 .f64 => return .f64_abs,
490 .v128 => unreachable, // handled independently
461 },491 },
462 .neg => switch (args.valtype1.?) {492 .neg => switch (args.valtype1.?) {
463 .i32, .i64 => unreachable,493 .i32, .i64 => unreachable,
464 .f32 => return .f32_neg,494 .f32 => return .f32_neg,
465 .f64 => return .f64_neg,495 .f64 => return .f64_neg,
496 .v128 => unreachable, // handled independently
466 },497 },
467 .ceil => switch (args.valtype1.?) {498 .ceil => switch (args.valtype1.?) {
468 .i64 => unreachable,499 .i64 => unreachable,
469 .i32 => return .f32_ceil, // when valtype is f16, we store it in i32.500 .i32 => return .f32_ceil, // when valtype is f16, we store it in i32.
470 .f32 => return .f32_ceil,501 .f32 => return .f32_ceil,
471 .f64 => return .f64_ceil,502 .f64 => return .f64_ceil,
503 .v128 => unreachable, // handled independently
472 },504 },
473 .floor => switch (args.valtype1.?) {505 .floor => switch (args.valtype1.?) {
474 .i64 => unreachable,506 .i64 => unreachable,
475 .i32 => return .f32_floor, // when valtype is f16, we store it in i32.507 .i32 => return .f32_floor, // when valtype is f16, we store it in i32.
476 .f32 => return .f32_floor,508 .f32 => return .f32_floor,
477 .f64 => return .f64_floor,509 .f64 => return .f64_floor,
510 .v128 => unreachable, // handled independently
478 },511 },
479 .trunc => switch (args.valtype1.?) {512 .trunc => switch (args.valtype1.?) {
480 .i32 => if (args.valtype2) |valty| switch (valty) {513 .i32 => if (args.valtype2) |valty| switch (valty) {
...@@ -482,40 +515,48 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -482,40 +515,48 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
482 .i64 => unreachable,515 .i64 => unreachable,
483 .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u,516 .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u,
484 .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u,517 .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u,
518 .v128 => unreachable, // handled independently
485 } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32.519 } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32.
486 .i64 => switch (args.valtype2.?) {520 .i64 => switch (args.valtype2.?) {
487 .i32 => unreachable,521 .i32 => unreachable,
488 .i64 => unreachable,522 .i64 => unreachable,
489 .f32 => if (args.signedness.? == .signed) return .i64_trunc_f32_s else return .i64_trunc_f32_u,523 .f32 => if (args.signedness.? == .signed) return .i64_trunc_f32_s else return .i64_trunc_f32_u,
490 .f64 => if (args.signedness.? == .signed) return .i64_trunc_f64_s else return .i64_trunc_f64_u,524 .f64 => if (args.signedness.? == .signed) return .i64_trunc_f64_s else return .i64_trunc_f64_u,
525 .v128 => unreachable, // handled independently
491 },526 },
492 .f32 => return .f32_trunc,527 .f32 => return .f32_trunc,
493 .f64 => return .f64_trunc,528 .f64 => return .f64_trunc,
529 .v128 => unreachable, // handled independently
494 },530 },
495 .nearest => switch (args.valtype1.?) {531 .nearest => switch (args.valtype1.?) {
496 .i32, .i64 => unreachable,532 .i32, .i64 => unreachable,
497 .f32 => return .f32_nearest,533 .f32 => return .f32_nearest,
498 .f64 => return .f64_nearest,534 .f64 => return .f64_nearest,
535 .v128 => unreachable, // handled independently
499 },536 },
500 .sqrt => switch (args.valtype1.?) {537 .sqrt => switch (args.valtype1.?) {
501 .i32, .i64 => unreachable,538 .i32, .i64 => unreachable,
502 .f32 => return .f32_sqrt,539 .f32 => return .f32_sqrt,
503 .f64 => return .f64_sqrt,540 .f64 => return .f64_sqrt,
541 .v128 => unreachable, // handled independently
504 },542 },
505 .min => switch (args.valtype1.?) {543 .min => switch (args.valtype1.?) {
506 .i32, .i64 => unreachable,544 .i32, .i64 => unreachable,
507 .f32 => return .f32_min,545 .f32 => return .f32_min,
508 .f64 => return .f64_min,546 .f64 => return .f64_min,
547 .v128 => unreachable, // handled independently
509 },548 },
510 .max => switch (args.valtype1.?) {549 .max => switch (args.valtype1.?) {
511 .i32, .i64 => unreachable,550 .i32, .i64 => unreachable,
512 .f32 => return .f32_max,551 .f32 => return .f32_max,
513 .f64 => return .f64_max,552 .f64 => return .f64_max,
553 .v128 => unreachable, // handled independently
514 },554 },
515 .copysign => switch (args.valtype1.?) {555 .copysign => switch (args.valtype1.?) {
516 .i32, .i64 => unreachable,556 .i32, .i64 => unreachable,
517 .f32 => return .f32_copysign,557 .f32 => return .f32_copysign,
518 .f64 => return .f64_copysign,558 .f64 => return .f64_copysign,
559 .v128 => unreachable, // handled independently
519 },560 },
520561
521 .wrap => switch (args.valtype1.?) {562 .wrap => switch (args.valtype1.?) {
...@@ -523,8 +564,10 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -523,8 +564,10 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
523 .i32 => unreachable,564 .i32 => unreachable,
524 .i64 => return .i32_wrap_i64,565 .i64 => return .i32_wrap_i64,
525 .f32, .f64 => unreachable,566 .f32, .f64 => unreachable,
567 .v128 => unreachable, // handled independently
526 },568 },
527 .i64, .f32, .f64 => unreachable,569 .i64, .f32, .f64 => unreachable,
570 .v128 => unreachable, // handled independently
528 },571 },
529 .convert => switch (args.valtype1.?) {572 .convert => switch (args.valtype1.?) {
530 .i32, .i64 => unreachable,573 .i32, .i64 => unreachable,
...@@ -532,12 +575,15 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -532,12 +575,15 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
532 .i32 => if (args.signedness.? == .signed) return .f32_convert_i32_s else return .f32_convert_i32_u,575 .i32 => if (args.signedness.? == .signed) return .f32_convert_i32_s else return .f32_convert_i32_u,
533 .i64 => if (args.signedness.? == .signed) return .f32_convert_i64_s else return .f32_convert_i64_u,576 .i64 => if (args.signedness.? == .signed) return .f32_convert_i64_s else return .f32_convert_i64_u,
534 .f32, .f64 => unreachable,577 .f32, .f64 => unreachable,
578 .v128 => unreachable, // handled independently
535 },579 },
536 .f64 => switch (args.valtype2.?) {580 .f64 => switch (args.valtype2.?) {
537 .i32 => if (args.signedness.? == .signed) return .f64_convert_i32_s else return .f64_convert_i32_u,581 .i32 => if (args.signedness.? == .signed) return .f64_convert_i32_s else return .f64_convert_i32_u,
538 .i64 => if (args.signedness.? == .signed) return .f64_convert_i64_s else return .f64_convert_i64_u,582 .i64 => if (args.signedness.? == .signed) return .f64_convert_i64_s else return .f64_convert_i64_u,
539 .f32, .f64 => unreachable,583 .f32, .f64 => unreachable,
584 .v128 => unreachable, // handled independently
540 },585 },
586 .v128 => unreachable, // handled independently
541 },587 },
542 .demote => if (args.valtype1.? == .f32 and args.valtype2.? == .f64) return .f32_demote_f64 else unreachable,588 .demote => if (args.valtype1.? == .f32 and args.valtype2.? == .f64) return .f32_demote_f64 else unreachable,
543 .promote => if (args.valtype1.? == .f64 and args.valtype2.? == .f32) return .f64_promote_f32 else unreachable,589 .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 {...@@ -546,6 +592,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
546 .i64 => if (args.valtype2.? == .f64) return .i64_reinterpret_f64 else unreachable,592 .i64 => if (args.valtype2.? == .f64) return .i64_reinterpret_f64 else unreachable,
547 .f32 => if (args.valtype2.? == .i32) return .f32_reinterpret_i32 else unreachable,593 .f32 => if (args.valtype2.? == .i32) return .f32_reinterpret_i32 else unreachable,
548 .f64 => if (args.valtype2.? == .i64) return .f64_reinterpret_i64 else unreachable,594 .f64 => if (args.valtype2.? == .i64) return .f64_reinterpret_i64 else unreachable,
595 .v128 => unreachable, // handled independently
549 },596 },
550 .extend => switch (args.valtype1.?) {597 .extend => switch (args.valtype1.?) {
551 .i32 => switch (args.width.?) {598 .i32 => switch (args.width.?) {
...@@ -560,6 +607,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -560,6 +607,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
560 else => unreachable,607 else => unreachable,
561 },608 },
562 .f32, .f64 => unreachable,609 .f32, .f64 => unreachable,
610 .v128 => unreachable, // handled independently
563 },611 },
564 }612 }
565}613}
...@@ -629,6 +677,10 @@ err_msg: *Module.ErrorMsg,...@@ -629,6 +677,10 @@ err_msg: *Module.ErrorMsg,
629/// List of all locals' types generated throughout this declaration677/// List of all locals' types generated throughout this declaration
630/// used to emit locals count at start of 'code' section.678/// used to emit locals count at start of 'code' section.
631locals: std.ArrayListUnmanaged(u8),679locals: 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) = .{},
632/// The Target we're emitting (used to call intInfo)684/// The Target we're emitting (used to call intInfo)
633target: std.Target,685target: std.Target,
634/// Represents the wasm binary file that is being linked.686/// Represents the wasm binary file that is being linked.
...@@ -665,14 +717,17 @@ stack_alignment: u32 = 16,...@@ -665,14 +717,17 @@ stack_alignment: u32 = 16,
665/// It is illegal to store a non-i32 valtype in this list.717/// It is illegal to store a non-i32 valtype in this list.
666free_locals_i32: std.ArrayListUnmanaged(u32) = .{},718free_locals_i32: std.ArrayListUnmanaged(u32) = .{},
667/// A list of indexes which represents a local of valtype `i64`.719/// 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.
669free_locals_i64: std.ArrayListUnmanaged(u32) = .{},721free_locals_i64: std.ArrayListUnmanaged(u32) = .{},
670/// A list of indexes which represents a local of valtype `f32`.722/// 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.
672free_locals_f32: std.ArrayListUnmanaged(u32) = .{},724free_locals_f32: std.ArrayListUnmanaged(u32) = .{},
673/// A list of indexes which represents a local of valtype `f64`.725/// 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.
675free_locals_f64: std.ArrayListUnmanaged(u32) = .{},727free_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
677/// When in debug mode, this tracks if no `finishAir` was missed.732/// When in debug mode, this tracks if no `finishAir` was missed.
678/// Forgetting to call `finishAir` will cause the result to not be733/// Forgetting to call `finishAir` will cause the result to not be
...@@ -699,12 +754,14 @@ pub fn deinit(func: *CodeGen) void {...@@ -699,12 +754,14 @@ pub fn deinit(func: *CodeGen) void {
699 func.branches.deinit(func.gpa);754 func.branches.deinit(func.gpa);
700 func.blocks.deinit(func.gpa);755 func.blocks.deinit(func.gpa);
701 func.locals.deinit(func.gpa);756 func.locals.deinit(func.gpa);
757 func.simd_immediates.deinit(func.gpa);
702 func.mir_instructions.deinit(func.gpa);758 func.mir_instructions.deinit(func.gpa);
703 func.mir_extra.deinit(func.gpa);759 func.mir_extra.deinit(func.gpa);
704 func.free_locals_i32.deinit(func.gpa);760 func.free_locals_i32.deinit(func.gpa);
705 func.free_locals_i64.deinit(func.gpa);761 func.free_locals_i64.deinit(func.gpa);
706 func.free_locals_f32.deinit(func.gpa);762 func.free_locals_f32.deinit(func.gpa);
707 func.free_locals_f64.deinit(func.gpa);763 func.free_locals_f64.deinit(func.gpa);
764 func.free_locals_v128.deinit(func.gpa);
708 func.* = undefined;765 func.* = undefined;
709}766}
710767
...@@ -867,6 +924,17 @@ fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void {...@@ -867,6 +924,17 @@ fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void {
867 try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });924 try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });
868}925}
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
870fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {938fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {
871 const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float));939 const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float));
872 try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });940 try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
...@@ -924,6 +992,10 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {...@@ -924,6 +992,10 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {
924 },992 },
925 else => wasm.Valtype.i32,993 else => wasm.Valtype.i32,
926 },994 },
995 .Vector => switch (determineSimdStoreStrategy(ty, target)) {
996 .direct => wasm.Valtype.v128,
997 .unrolled => wasm.Valtype.i32,
998 },
927 else => wasm.Valtype.i32, // all represented as reference/immediate999 else => wasm.Valtype.i32, // all represented as reference/immediate
928 };1000 };
929}1001}
...@@ -950,6 +1022,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {...@@ -950,6 +1022,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
950 .local => |idx| try func.addLabel(.local_get, idx.value),1022 .local => |idx| try func.addLabel(.local_get, idx.value),
951 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),1023 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),
952 .imm64 => |val| try func.addImm64(val),1024 .imm64 => |val| try func.addImm64(val),
1025 .imm128 => |val| try func.addImm128(val),
953 .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),1026 .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
954 .float64 => |val| try func.addFloat64(val),1027 .float64 => |val| try func.addFloat64(val),
955 .memory => |ptr| {1028 .memory => |ptr| {
...@@ -1016,6 +1089,10 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {...@@ -1016,6 +1089,10 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
1016 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });1089 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1017 return WValue{ .local = .{ .value = index, .references = 1 } };1090 return WValue{ .local = .{ .value = index, .references = 1 } };
1018 },1091 },
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 },
1019 }1096 }
1020 log.debug("new local of type {}\n", .{valtype});1097 log.debug("new local of type {}\n", .{valtype});
1021 // no local was free to be re-used, so allocate a new local instead1098 // no local was free to be re-used, so allocate a new local instead
...@@ -1098,7 +1175,6 @@ pub fn generate(...@@ -1098,7 +1175,6 @@ pub fn generate(
1098 .gpa = bin_file.allocator,1175 .gpa = bin_file.allocator,
1099 .air = air,1176 .air = air,
1100 .liveness = liveness,1177 .liveness = liveness,
1101 // .values = .{},
1102 .code = code,1178 .code = code,
1103 .decl_index = func.owner_decl,1179 .decl_index = func.owner_decl,
1104 .decl = bin_file.options.module.?.declPtr(func.owner_decl),1180 .decl = bin_file.options.module.?.declPtr(func.owner_decl),
...@@ -1481,9 +1557,9 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {...@@ -1481,9 +1557,9 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1481 .imm64 => |val| val,1557 .imm64 => |val| val,
1482 else => unreachable,1558 else => unreachable,
1483 };1559 };
1484 // if the size (length) is more than 1024 bytes, we use a runtime loop instead to prevent1560 // if the size (length) is more than 32 bytes, we use a runtime loop instead to prevent
1485 // binary size bloat.1561 // binary size bloat.
1486 if (length > 1024) break :blk;1562 if (length > 32) break :blk;
1487 var offset: u32 = 0;1563 var offset: u32 = 0;
1488 const lhs_base = dst.offset();1564 const lhs_base = dst.offset();
1489 const rhs_base = src.offset();1565 const rhs_base = src.offset();
...@@ -1612,7 +1688,6 @@ fn isByRef(ty: Type, target: std.Target) bool {...@@ -1612,7 +1688,6 @@ fn isByRef(ty: Type, target: std.Target) bool {
1612 => return false,1688 => return false,
16131689
1614 .Array,1690 .Array,
1615 .Vector,
1616 .Frame,1691 .Frame,
1617 .Union,1692 .Union,
1618 => return ty.hasRuntimeBitsIgnoreComptime(),1693 => return ty.hasRuntimeBitsIgnoreComptime(),
...@@ -1625,6 +1700,7 @@ fn isByRef(ty: Type, target: std.Target) bool {...@@ -1625,6 +1700,7 @@ fn isByRef(ty: Type, target: std.Target) bool {
1625 }1700 }
1626 return ty.hasRuntimeBitsIgnoreComptime();1701 return ty.hasRuntimeBitsIgnoreComptime();
1627 },1702 },
1703 .Vector => return determineSimdStoreStrategy(ty, target) == .unrolled,
1628 .Int => return ty.intInfo(target).bits > 64,1704 .Int => return ty.intInfo(target).bits > 64,
1629 .Float => return ty.floatBits(target) > 64,1705 .Float => return ty.floatBits(target) > 64,
1630 .ErrorUnion => {1706 .ErrorUnion => {
...@@ -1647,6 +1723,26 @@ fn isByRef(ty: Type, target: std.Target) bool {...@@ -1647,6 +1723,26 @@ fn isByRef(ty: Type, target: std.Target) bool {
1647 }1723 }
1648}1724}
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
1650/// Creates a new local for a pointer that points to memory with given offset.1746/// Creates a new local for a pointer that points to memory with given offset.
1651/// This can be used to get a pointer to a struct field, error payload, etc.1747/// This can be used to get a pointer to a struct field, error payload, etc.
1652/// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new1748/// 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...@@ -2187,10 +2283,29 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
2187 const len = @intCast(u32, ty.abiSize(func.target));2283 const len = @intCast(u32, ty.abiSize(func.target));
2188 return func.memcpy(lhs, rhs, .{ .imm32 = len });2284 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2189 },2285 },
2190 .Struct, .Array, .Union, .Vector => if (isByRef(ty, func.target)) {2286 .Struct, .Array, .Union => if (isByRef(ty, func.target)) {
2191 const len = @intCast(u32, ty.abiSize(func.target));2287 const len = @intCast(u32, ty.abiSize(func.target));
2192 return func.memcpy(lhs, rhs, .{ .imm32 = len });2288 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2193 },2289 },
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 },
2194 .Pointer => {2309 .Pointer => {
2195 if (ty.isSlice()) {2310 if (ty.isSlice()) {
2196 // store pointer first2311 // store pointer first
...@@ -2289,6 +2404,19 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu...@@ -2289,6 +2404,19 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
2289 // load local's value from memory by its stack position2404 // load local's value from memory by its stack position
2290 try func.emitWValue(operand);2405 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
2292 const abi_size = @intCast(u8, ty.abiSize(func.target));2420 const abi_size = @intCast(u8, ty.abiSize(func.target));
2293 const opcode = buildOpcode(.{2421 const opcode = buildOpcode(.{
2294 .valtype1 = typeToValtype(ty, func.target),2422 .valtype1 = typeToValtype(ty, func.target),
...@@ -2766,10 +2894,24 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {...@@ -2766,10 +2894,24 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
2766 const int_val = Value.initPayload(&payload.base);2894 const int_val = Value.initPayload(&payload.base);
2767 return func.lowerConstant(int_val, struct_obj.backing_int_ty);2895 return func.lowerConstant(int_val, struct_obj.backing_int_ty);
2768 },2896 },
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 },
2769 else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),2903 else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),
2770 }2904 }
2771}2905}
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
2773fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {2915fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
2774 switch (ty.zigTypeTag()) {2916 switch (ty.zigTypeTag()) {
2775 .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa },2917 .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa },
...@@ -4288,9 +4430,71 @@ fn airIntToFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4288,9 +4430,71 @@ fn airIntToFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4288fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4430fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4289 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4431 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4290 const operand = try func.resolveInst(ty_op.operand);4432 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;4488 const result = try func.allocStack(ty);
4293 return func.fail("TODO: Implement wasm airSplat", .{});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});
4294}4498}
42954499
4296fn airSelect(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4500fn 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 {...@@ -240,6 +240,7 @@ pub fn emitMir(emit: *Emit) InnerError!void {
240 .i64_ctz => try emit.emitTag(tag),240 .i64_ctz => try emit.emitTag(tag),
241241
242 .extended => try emit.emitExtended(inst),242 .extended => try emit.emitExtended(inst),
243 .simd => try emit.emitSimd(inst),
243 }244 }
244 }245 }
245}246}
...@@ -341,11 +342,14 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {...@@ -341,11 +342,14 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
341 const extra_index = emit.mir.instructions.items(.data)[inst].payload;342 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
342 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;343 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;
343 try emit.code.append(@enumToInt(tag));344 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 {
345 // wasm encodes alignment as power of 2, rather than natural alignment349 // wasm encodes alignment as power of 2, rather than natural alignment
346 const encoded_alignment = @ctz(mem_arg.alignment);350 const encoded_alignment = @ctz(mem_arg.alignment);
347 try leb128.writeULEB128(emit.code.writer(), encoded_alignment);351 try leb128.writeULEB128(writer, encoded_alignment);
348 try leb128.writeULEB128(emit.code.writer(), mem_arg.offset);352 try leb128.writeULEB128(writer, mem_arg.offset);
349}353}
350354
351fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {355fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
...@@ -426,6 +430,38 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -426,6 +430,38 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
426 }430 }
427}431}
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
429fn emitMemFill(emit: *Emit) !void {465fn emitMemFill(emit: *Emit) !void {
430 try emit.code.append(0xFC);466 try emit.code.append(0xFC);
431 try emit.code.append(0x0B);467 try emit.code.append(0x0B);
src/arch/wasm/Mir.zig+7-1
...@@ -518,6 +518,12 @@ pub const Inst = struct {...@@ -518,6 +518,12 @@ pub const Inst = struct {
518 ///518 ///
519 /// The `data` field depends on the extension instruction519 /// The `data` field depends on the extension instruction
520 extended = 0xFC,520 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,
521 /// Contains a symbol to a function pointer527 /// Contains a symbol to a function pointer
522 /// uses `label`528 /// uses `label`
523 ///529 ///
...@@ -578,7 +584,7 @@ pub fn deinit(self: *Mir, gpa: std.mem.Allocator) void {...@@ -578,7 +584,7 @@ pub fn deinit(self: *Mir, gpa: std.mem.Allocator) void {
578 self.* = undefined;584 self.* = undefined;
579}585}
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 } {
582 const fields = std.meta.fields(T);588 const fields = std.meta.fields(T);
583 var i: usize = index;589 var i: usize = index;
584 var result: T = undefined;590 var result: T = undefined;
src/codegen.zig+48
...@@ -808,6 +808,54 @@ pub fn generateSymbol(...@@ -808,6 +808,54 @@ pub fn generateSymbol(
808 }808 }
809 return Result{ .appended = {} };809 return Result{ .appended = {} };
810 },810 },
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 },
811 else => |t| {859 else => |t| {
812 return Result{860 return Result{
813 .fail = try ErrorMsg.create(861 .fail = try ErrorMsg.create(
test/behavior/vector.zig-8
...@@ -138,7 +138,6 @@ test "vector bit operators" {...@@ -138,7 +138,6 @@ test "vector bit operators" {
138}138}
139139
140test "implicit cast vector to array" {140test "implicit cast vector to array" {
141 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
142 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO141 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
143 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO142 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
144 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO143 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -157,7 +156,6 @@ test "implicit cast vector to array" {...@@ -157,7 +156,6 @@ test "implicit cast vector to array" {
157}156}
158157
159test "array to vector" {158test "array to vector" {
160 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
161 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO159 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
162 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO160 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
163 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO161 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -235,7 +233,6 @@ test "vector casts of sizes not divisible by 8" {...@@ -235,7 +233,6 @@ test "vector casts of sizes not divisible by 8" {
235}233}
236234
237test "vector @splat" {235test "vector @splat" {
238 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
239 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO236 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
240 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO237 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
241 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO238 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -284,7 +281,6 @@ test "vector @splat" {...@@ -284,7 +281,6 @@ test "vector @splat" {
284}281}
285282
286test "load vector elements via comptime index" {283test "load vector elements via comptime index" {
287 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
288 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO284 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
289 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO285 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
290 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO286 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -307,7 +303,6 @@ test "load vector elements via comptime index" {...@@ -307,7 +303,6 @@ test "load vector elements via comptime index" {
307}303}
308304
309test "store vector elements via comptime index" {305test "store vector elements via comptime index" {
310 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
311 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO306 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
312 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO307 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
313 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO308 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -336,7 +331,6 @@ test "store vector elements via comptime index" {...@@ -336,7 +331,6 @@ test "store vector elements via comptime index" {
336}331}
337332
338test "load vector elements via runtime index" {333test "load vector elements via runtime index" {
339 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
340 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO334 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
341 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO335 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
342 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO336 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -359,7 +353,6 @@ test "load vector elements via runtime index" {...@@ -359,7 +353,6 @@ test "load vector elements via runtime index" {
359}353}
360354
361test "store vector elements via runtime index" {355test "store vector elements via runtime index" {
362 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
363 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO356 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
364 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO357 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
365 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO358 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -383,7 +376,6 @@ test "store vector elements via runtime index" {...@@ -383,7 +376,6 @@ test "store vector elements via runtime index" {
383}376}
384377
385test "initialize vector which is a struct field" {378test "initialize vector which is a struct field" {
386 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
387 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO379 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
388 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO380 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
389 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO381 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO