authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-04-08 17:13:58+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-04-08 17:13:58+02:00
log58bab660b5080f36c29ce1e870cc8aacec64eb70
treea05a59c8254cf49339ff06c7dbc36e6e234122f8
parentfde05b10b3c29b914e4d2ef034dcd8a78800ef6e
parent09fda086186ca4d19ed516efe52de0f47fd0a095
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15202 from SuperAuguste/misc-wasm

Implement `@shuffle`, fix vector element accesses for WASM backend

4 files changed, 131 insertions(+), 13 deletions(-)

src/arch/wasm/CodeGen.zig+111-10
......@@ -4518,11 +4518,48 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
45184518 const elem_ty = array_ty.childType();
45194519 const elem_size = elem_ty.abiSize(func.target);
45204520
4521 try func.lowerToStack(array);
4522 try func.emitWValue(index);
4523 try func.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
4524 try func.addTag(.i32_mul);
4525 try func.addTag(.i32_add);
4521 if (isByRef(array_ty, func.target)) {
4522 try func.lowerToStack(array);
4523 try func.emitWValue(index);
4524 try func.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
4525 try func.addTag(.i32_mul);
4526 try func.addTag(.i32_add);
4527 } else {
4528 std.debug.assert(array_ty.zigTypeTag() == .Vector);
4529
4530 switch (index) {
4531 inline .imm32, .imm64 => |lane| {
4532 const opcode: wasm.SimdOpcode = switch (elem_ty.bitSize(func.target)) {
4533 8 => if (elem_ty.isSignedInt()) .i8x16_extract_lane_s else .i8x16_extract_lane_u,
4534 16 => if (elem_ty.isSignedInt()) .i16x8_extract_lane_s else .i16x8_extract_lane_u,
4535 32 => if (elem_ty.isInt()) .i32x4_extract_lane else .f32x4_extract_lane,
4536 64 => if (elem_ty.isInt()) .i64x2_extract_lane else .f64x2_extract_lane,
4537 else => unreachable,
4538 };
4539
4540 var operands = [_]u32{ std.wasm.simdOpcode(opcode), @intCast(u8, lane) };
4541
4542 try func.emitWValue(array);
4543
4544 const extra_index = @intCast(u32, func.mir_extra.items.len);
4545 try func.mir_extra.appendSlice(func.gpa, &operands);
4546 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
4547
4548 return func.finishAir(inst, try WValue.toLocal(.stack, func, elem_ty), &.{ bin_op.lhs, bin_op.rhs });
4549 },
4550 else => {
4551 var stack_vec = try func.allocStack(array_ty);
4552 try func.store(stack_vec, array, array_ty, 0);
4553
4554 // Is a non-unrolled vector (v128)
4555 try func.lowerToStack(stack_vec);
4556 try func.emitWValue(index);
4557 try func.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
4558 try func.addTag(.i32_mul);
4559 try func.addTag(.i32_add);
4560 },
4561 }
4562 }
45264563
45274564 const elem_result = val: {
45284565 var result = try func.allocLocal(Type.usize);
......@@ -4670,11 +4707,70 @@ fn airSelect(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
46704707}
46714708
46724709fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4673 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4674 const operand = try func.resolveInst(ty_op.operand);
4710 const inst_ty = func.air.typeOfIndex(inst);
4711 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4712 const extra = func.air.extraData(Air.Shuffle, ty_pl.payload).data;
46754713
4676 _ = operand;
4677 return func.fail("TODO: Implement wasm airShuffle", .{});
4714 const a = try func.resolveInst(extra.a);
4715 const b = try func.resolveInst(extra.b);
4716 const mask = func.air.values[extra.mask];
4717 const mask_len = extra.mask_len;
4718
4719 const child_ty = inst_ty.childType();
4720 const elem_size = child_ty.abiSize(func.target);
4721
4722 if (func.liveness.isUnused(inst)) {
4723 return func.finishAir(inst, .none, &.{ extra.a, extra.b });
4724 }
4725
4726 const module = func.bin_file.base.options.module.?;
4727 // TODO: One of them could be by ref; handle in loop
4728 if (isByRef(func.air.typeOf(extra.a), func.target) or isByRef(inst_ty, func.target)) {
4729 const result = try func.allocStack(inst_ty);
4730
4731 for (0..mask_len) |index| {
4732 var buf: Value.ElemValueBuffer = undefined;
4733 const value = mask.elemValueBuffer(module, index, &buf).toSignedInt(func.target);
4734
4735 try func.emitWValue(result);
4736
4737 const loaded = if (value >= 0)
4738 try func.load(a, child_ty, @intCast(u32, @intCast(i64, elem_size) * value))
4739 else
4740 try func.load(b, child_ty, @intCast(u32, @intCast(i64, elem_size) * ~value));
4741
4742 try func.store(.stack, loaded, child_ty, result.stack_offset.value + @intCast(u32, elem_size) * @intCast(u32, index));
4743 }
4744
4745 return func.finishAir(inst, result, &.{ extra.a, extra.b });
4746 } else {
4747 var operands = [_]u32{
4748 std.wasm.simdOpcode(.i8x16_shuffle),
4749 } ++ [1]u32{undefined} ** 4;
4750
4751 var lanes = std.mem.asBytes(operands[1..]);
4752 for (0..@intCast(usize, mask_len)) |index| {
4753 var buf: Value.ElemValueBuffer = undefined;
4754 const mask_elem = mask.elemValueBuffer(module, index, &buf).toSignedInt(func.target);
4755 const base_index = if (mask_elem >= 0)
4756 @intCast(u8, @intCast(i64, elem_size) * mask_elem)
4757 else
4758 16 + @intCast(u8, @intCast(i64, elem_size) * ~mask_elem);
4759
4760 for (0..@intCast(usize, elem_size)) |byte_offset| {
4761 lanes[index * @intCast(usize, elem_size) + byte_offset] = base_index + @intCast(u8, byte_offset);
4762 }
4763 }
4764
4765 try func.emitWValue(a);
4766 try func.emitWValue(b);
4767
4768 const extra_index = @intCast(u32, func.mir_extra.items.len);
4769 try func.mir_extra.appendSlice(func.gpa, &operands);
4770 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
4771
4772 return func.finishAir(inst, try WValue.toLocal(.stack, func, inst_ty), &.{ extra.a, extra.b });
4773 }
46784774}
46794775
46804776fn airReduce(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5125,7 +5221,12 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
51255221}
51265222
51275223fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5128 func.finishAir(inst, .{ .imm32 = 0 }, &.{});
5224 // TODO: Implement this properly once stack serialization is solved
5225 func.finishAir(inst, switch (func.arch()) {
5226 .wasm32 => .{ .imm32 = 0 },
5227 .wasm64 => .{ .imm64 = 0 },
5228 else => unreachable,
5229 }, &.{});
51295230}
51305231
51315232fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
src/arch/wasm/Emit.zig+20-1
......@@ -486,10 +486,29 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
486486 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;
487487 try encodeMemArg(mem_arg, writer);
488488 },
489 .v128_const => {
489 .v128_const,
490 .i8x16_shuffle,
491 => {
490492 const simd_value = emit.mir.extra[extra_index + 1 ..][0..4];
491493 try writer.writeAll(std.mem.asBytes(simd_value));
492494 },
495 .i8x16_extract_lane_s,
496 .i8x16_extract_lane_u,
497 .i8x16_replace_lane,
498 .i16x8_extract_lane_s,
499 .i16x8_extract_lane_u,
500 .i16x8_replace_lane,
501 .i32x4_extract_lane,
502 .i32x4_replace_lane,
503 .i64x2_extract_lane,
504 .i64x2_replace_lane,
505 .f32x4_extract_lane,
506 .f32x4_replace_lane,
507 .f64x2_extract_lane,
508 .f64x2_replace_lane,
509 => {
510 try writer.writeByte(@intCast(u8, emit.mir.extra[extra_index + 1]));
511 },
493512 .i8x16_splat,
494513 .i16x8_splat,
495514 .i32x4_splat,
test/behavior/shuffle.zig-1
......@@ -4,7 +4,6 @@ const mem = std.mem;
44const expect = std.testing.expect;
55
66test "@shuffle int" {
7 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
87 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/vector.zig-1
......@@ -807,7 +807,6 @@ test "vector @reduce comptime" {
807807}
808808
809809test "mask parameter of @shuffle is comptime scope" {
810 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
811810 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
812811 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
813812 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO