authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-17 18:04:46-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-17 18:04:46-07:00
logff21cb42a0201501d0bb762c9e496997fbccfdf3
tree045f08ea0530a9f1bdbecd7895d06469618a1a20
parent7233a3324aaa5b3995606f24b2b961149219986b
parent4fa506063373b38bed842af53e6405dec7107534
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11206 from schmee/vector-byteswap

Implement `@byteSwap` for vectors

3 files changed, 140 insertions(+), 52 deletions(-)

src/Sema.zig+57-28
...@@ -3205,14 +3205,11 @@ fn zirValidateArrayInit(...@@ -3205,14 +3205,11 @@ fn zirValidateArrayInit(
3205 // instruction after it within the same block.3205 // instruction after it within the same block.
3206 // Possible performance enhancement: save the `block_index` between iterations3206 // Possible performance enhancement: save the `block_index` between iterations
3207 // of the for loop.3207 // of the for loop.
3208 const next_air_inst = inst: {3208 var block_index = block.instructions.items.len - 1;
3209 var block_index = block.instructions.items.len - 1;3209 while (block.instructions.items[block_index] != elem_ptr_air_inst) {
3210 while (block.instructions.items[block_index] != elem_ptr_air_inst) {3210 block_index -= 1;
3211 block_index -= 1;3211 }
3212 }3212 first_block_index = @minimum(first_block_index, block_index);
3213 first_block_index = @minimum(first_block_index, block_index);
3214 break :inst block.instructions.items[block_index + 1];
3215 };
32163213
3217 // Array has one possible value, so value is always comptime-known3214 // Array has one possible value, so value is always comptime-known
3218 if (opt_opv) |opv| {3215 if (opt_opv) |opv| {
...@@ -3222,6 +3219,7 @@ fn zirValidateArrayInit(...@@ -3222,6 +3219,7 @@ fn zirValidateArrayInit(
32223219
3223 // If the next instructon is a store with a comptime operand, this element3220 // If the next instructon is a store with a comptime operand, this element
3224 // is comptime.3221 // is comptime.
3222 const next_air_inst = block.instructions.items[block_index + 1];
3225 switch (air_tags[next_air_inst]) {3223 switch (air_tags[next_air_inst]) {
3226 .store => {3224 .store => {
3227 const bin_op = air_datas[next_air_inst].bin_op;3225 const bin_op = air_datas[next_air_inst].bin_op;
...@@ -13491,30 +13489,61 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -13491,30 +13489,61 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
13491 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };13489 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
13492 const operand = sema.resolveInst(inst_data.operand);13490 const operand = sema.resolveInst(inst_data.operand);
13493 const operand_ty = sema.typeOf(operand);13491 const operand_ty = sema.typeOf(operand);
13494 // TODO implement support for vectors13492 const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src);
13495 if (operand_ty.zigTypeTag() != .Int) {
13496 return sema.fail(block, ty_src, "expected integer type, found '{}'", .{
13497 operand_ty,
13498 });
13499 }
13500 const target = sema.mod.getTarget();13493 const target = sema.mod.getTarget();
13501 const bits = operand_ty.intInfo(target).bits;13494 const bits = scalar_ty.intInfo(target).bits;
13502 if (bits == 0) return Air.Inst.Ref.zero;13495 if (bits % 8 != 0) {
13503 if (operand_ty.intInfo(target).bits % 8 != 0) {13496 return sema.fail(
13504 return sema.fail(block, ty_src, "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", .{13497 block,
13505 operand_ty,13498 ty_src,
13506 operand_ty.intInfo(target).bits,13499 "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits",
13507 });13500 .{ scalar_ty, bits },
13501 );
13508 }13502 }
1350913503
13510 const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {13504 switch (operand_ty.zigTypeTag()) {
13511 if (val.isUndef()) return sema.addConstUndef(operand_ty);13505 .Int, .ComptimeInt => {
13512 const result_val = try val.byteSwap(operand_ty, target, sema.arena);13506 if (bits == 0) return Air.Inst.Ref.zero;
13513 return sema.addConstant(operand_ty, result_val);
13514 } else operand_src;
1351513507
13516 try sema.requireRuntimeBlock(block, runtime_src);13508 const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
13517 return block.addTyOp(.byte_swap, operand_ty, operand);13509 if (val.isUndef()) return sema.addConstUndef(operand_ty);
13510 const result_val = try val.byteSwap(operand_ty, target, sema.arena);
13511 return sema.addConstant(operand_ty, result_val);
13512 } else operand_src;
13513
13514 try sema.requireRuntimeBlock(block, runtime_src);
13515 return block.addTyOp(.byte_swap, operand_ty, operand);
13516 },
13517 .Vector => {
13518 if (bits == 0) {
13519 return sema.addConstant(
13520 operand_ty,
13521 try Value.Tag.repeated.create(sema.arena, Value.zero),
13522 );
13523 }
13524
13525 const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
13526 if (val.isUndef())
13527 return sema.addConstUndef(operand_ty);
13528
13529 const vec_len = operand_ty.vectorLen();
13530 var elem_buf: Value.ElemValueBuffer = undefined;
13531 const elems = try sema.arena.alloc(Value, vec_len);
13532 for (elems) |*elem, i| {
13533 const elem_val = val.elemValueBuffer(i, &elem_buf);
13534 elem.* = try elem_val.byteSwap(operand_ty, target, sema.arena);
13535 }
13536 return sema.addConstant(
13537 operand_ty,
13538 try Value.Tag.aggregate.create(sema.arena, elems),
13539 );
13540 } else operand_src;
13541
13542 try sema.requireRuntimeBlock(block, runtime_src);
13543 return block.addTyOp(.byte_swap, operand_ty, operand);
13544 },
13545 else => unreachable,
13546 }
13518}13547}
1351913548
13520fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {13549fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/codegen/llvm.zig+20-3
...@@ -6078,9 +6078,26 @@ pub const FuncGen = struct {...@@ -6078,9 +6078,26 @@ pub const FuncGen = struct {
6078 if (bits % 16 == 8) {6078 if (bits % 16 == 8) {
6079 // If not an even byte-multiple, we need zero-extend + shift-left 1 byte6079 // If not an even byte-multiple, we need zero-extend + shift-left 1 byte
6080 // The truncated result at the end will be the correct bswap6080 // The truncated result at the end will be the correct bswap
6081 operand_llvm_ty = self.context.intType(bits + 8);6081 const scalar_llvm_ty = self.context.intType(bits + 8);
6082 const extended = self.builder.buildZExt(operand, operand_llvm_ty, "");6082 if (operand_ty.zigTypeTag() == .Vector) {
6083 operand = self.builder.buildShl(extended, operand_llvm_ty.constInt(8, .False), "");6083 const vec_len = operand_ty.vectorLen();
6084 operand_llvm_ty = scalar_llvm_ty.vectorType(vec_len);
6085
6086 const shifts = try self.gpa.alloc(*const llvm.Value, vec_len);
6087 defer self.gpa.free(shifts);
6088
6089 for (shifts) |*elem| {
6090 elem.* = scalar_llvm_ty.constInt(8, .False);
6091 }
6092 const shift_vec = llvm.constVector(shifts.ptr, vec_len);
6093
6094 const extended = self.builder.buildZExt(operand, operand_llvm_ty, "");
6095 operand = self.builder.buildShl(extended, shift_vec, "");
6096 } else {
6097 const extended = self.builder.buildZExt(operand, scalar_llvm_ty, "");
6098 operand = self.builder.buildShl(extended, scalar_llvm_ty.constInt(8, .False), "");
6099 operand_llvm_ty = scalar_llvm_ty;
6100 }
6084 bits = bits + 8;6101 bits = bits + 8;
6085 }6102 }
60866103
test/behavior/byteswap.zig+63-21
...@@ -52,32 +52,74 @@ test "@byteSwap integers" {...@@ -52,32 +52,74 @@ test "@byteSwap integers" {
52 try ByteSwapIntTest.run();52 try ByteSwapIntTest.run();
53}53}
5454
55test "@byteSwap vectors" {55fn vector8() !void {
56 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;56 var v = @Vector(2, u8){ 0x12, 0x13 };
57 var result = @byteSwap(u8, v);
58 try expect(result[0] == 0x12);
59 try expect(result[1] == 0x13);
60}
61
62test "@byteSwap vectors u8" {
57 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;63 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
58 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;64 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
59 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;65 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
60 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;66 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
61 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;67 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
6268
63 const ByteSwapVectorTest = struct {69 comptime try vector8();
64 fn run() !void {70 try vector8();
65 try t(u8, 2, [_]u8{ 0x12, 0x13 }, [_]u8{ 0x12, 0x13 });71}
66 try t(u16, 2, [_]u16{ 0x1234, 0x2345 }, [_]u16{ 0x3412, 0x4523 });
67 try t(u24, 2, [_]u24{ 0x123456, 0x234567 }, [_]u24{ 0x563412, 0x674523 });
68 }
6972
70 fn t(73fn vector16() !void {
71 comptime I: type,74 var v = @Vector(2, u16){ 0x1234, 0x2345 };
72 comptime n: comptime_int,75 var result = @byteSwap(u16, v);
73 input: std.meta.Vector(n, I),76 try expect(result[0] == 0x3412);
74 expected_vector: std.meta.Vector(n, I),77 try expect(result[1] == 0x4523);
75 ) !void {78}
76 const actual_output: [n]I = @byteSwap(I, input);79
77 const expected_output: [n]I = expected_vector;80test "@byteSwap vectors u16" {
78 try std.testing.expectEqual(expected_output, actual_output);81 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
79 }82 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
80 };83 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
81 comptime try ByteSwapVectorTest.run();84 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
82 try ByteSwapVectorTest.run();85 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
86
87 comptime try vector16();
88 try vector16();
89}
90
91fn vector24() !void {
92 var v = @Vector(2, u24){ 0x123456, 0x234567 };
93 var result = @byteSwap(u24, v);
94 try expect(result[0] == 0x563412);
95 try expect(result[1] == 0x674523);
96}
97
98test "@byteSwap vectors u24" {
99 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
100 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
101 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
103 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
104
105 comptime try vector24();
106 try vector24();
107}
108
109fn vector0() !void {
110 var v = @Vector(2, u0){ 0, 0 };
111 var result = @byteSwap(u0, v);
112 try expect(result[0] == 0);
113 try expect(result[1] == 0);
114}
115
116test "@byteSwap vectors u0" {
117 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
118 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
119 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
120 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
121 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
122
123 comptime try vector0();
124 try vector0();
83}125}