| author | |
| committer | |
| log | ff21cb42a0201501d0bb762c9e496997fbccfdf3 |
| tree | 045f08ea0530a9f1bdbecd7895d06469618a1a20 |
| parent | 7233a3324aaa5b3995606f24b2b961149219986b |
| parent | 4fa506063373b38bed842af53e6405dec7107534 |
| signature |
Implement `@byteSwap` for vectors3 files changed, 140 insertions(+), 52 deletions(-)
src/Sema.zig+57-28| ... | ... | @@ -3205,14 +3205,11 @@ fn zirValidateArrayInit( |
| 3205 | 3205 | // instruction after it within the same block. |
| 3206 | 3206 | // Possible performance enhancement: save the `block_index` between iterations |
| 3207 | 3207 | // of the for loop. |
| 3208 | const next_air_inst = inst: { | |
| 3209 | var block_index = block.instructions.items.len - 1; | |
| 3210 | while (block.instructions.items[block_index] != elem_ptr_air_inst) { | |
| 3211 | block_index -= 1; | |
| 3212 | } | |
| 3213 | first_block_index = @minimum(first_block_index, block_index); | |
| 3214 | break :inst block.instructions.items[block_index + 1]; | |
| 3215 | }; | |
| 3208 | var block_index = block.instructions.items.len - 1; | |
| 3209 | while (block.instructions.items[block_index] != elem_ptr_air_inst) { | |
| 3210 | block_index -= 1; | |
| 3211 | } | |
| 3212 | first_block_index = @minimum(first_block_index, block_index); | |
| 3216 | 3213 | |
| 3217 | 3214 | // Array has one possible value, so value is always comptime-known |
| 3218 | 3215 | if (opt_opv) |opv| { |
| ... | ... | @@ -3222,6 +3219,7 @@ fn zirValidateArrayInit( |
| 3222 | 3219 | |
| 3223 | 3220 | // If the next instructon is a store with a comptime operand, this element |
| 3224 | 3221 | // is comptime. |
| 3222 | const next_air_inst = block.instructions.items[block_index + 1]; | |
| 3225 | 3223 | switch (air_tags[next_air_inst]) { |
| 3226 | 3224 | .store => { |
| 3227 | 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 | 13489 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 13492 | 13490 | const operand = sema.resolveInst(inst_data.operand); |
| 13493 | 13491 | const operand_ty = sema.typeOf(operand); |
| 13494 | // TODO implement support for vectors | |
| 13495 | if (operand_ty.zigTypeTag() != .Int) { | |
| 13496 | return sema.fail(block, ty_src, "expected integer type, found '{}'", .{ | |
| 13497 | operand_ty, | |
| 13498 | }); | |
| 13499 | } | |
| 13492 | const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src); | |
| 13500 | 13493 | const target = sema.mod.getTarget(); |
| 13501 | const bits = operand_ty.intInfo(target).bits; | |
| 13502 | if (bits == 0) return Air.Inst.Ref.zero; | |
| 13503 | if (operand_ty.intInfo(target).bits % 8 != 0) { | |
| 13504 | return sema.fail(block, ty_src, "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", .{ | |
| 13505 | operand_ty, | |
| 13506 | operand_ty.intInfo(target).bits, | |
| 13507 | }); | |
| 13494 | const bits = scalar_ty.intInfo(target).bits; | |
| 13495 | if (bits % 8 != 0) { | |
| 13496 | return sema.fail( | |
| 13497 | block, | |
| 13498 | ty_src, | |
| 13499 | "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", | |
| 13500 | .{ scalar_ty, bits }, | |
| 13501 | ); | |
| 13508 | 13502 | } |
| 13509 | 13503 | |
| 13510 | const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | |
| 13511 | if (val.isUndef()) return sema.addConstUndef(operand_ty); | |
| 13512 | const result_val = try val.byteSwap(operand_ty, target, sema.arena); | |
| 13513 | return sema.addConstant(operand_ty, result_val); | |
| 13514 | } else operand_src; | |
| 13504 | switch (operand_ty.zigTypeTag()) { | |
| 13505 | .Int, .ComptimeInt => { | |
| 13506 | if (bits == 0) return Air.Inst.Ref.zero; | |
| 13515 | 13507 | |
| 13516 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 13517 | return block.addTyOp(.byte_swap, operand_ty, operand); | |
| 13508 | const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | |
| 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 | } |
| 13519 | 13548 | |
| 13520 | 13549 | fn 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 | 6078 | if (bits % 16 == 8) { |
| 6079 | 6079 | // If not an even byte-multiple, we need zero-extend + shift-left 1 byte |
| 6080 | 6080 | // The truncated result at the end will be the correct bswap |
| 6081 | operand_llvm_ty = self.context.intType(bits + 8); | |
| 6082 | const extended = self.builder.buildZExt(operand, operand_llvm_ty, ""); | |
| 6083 | operand = self.builder.buildShl(extended, operand_llvm_ty.constInt(8, .False), ""); | |
| 6081 | const scalar_llvm_ty = self.context.intType(bits + 8); | |
| 6082 | if (operand_ty.zigTypeTag() == .Vector) { | |
| 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 | 6101 | bits = bits + 8; |
| 6085 | 6102 | } |
| 6086 | 6103 |
test/behavior/byteswap.zig+63-21| ... | ... | @@ -52,32 +52,74 @@ test "@byteSwap integers" { |
| 52 | 52 | try ByteSwapIntTest.run(); |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | test "@byteSwap vectors" { | |
| 56 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; | |
| 55 | fn vector8() !void { | |
| 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 | ||
| 62 | test "@byteSwap vectors u8" { | |
| 57 | 63 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 58 | 64 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 59 | 65 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 60 | 66 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 61 | 67 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 62 | 68 | |
| 63 | const ByteSwapVectorTest = struct { | |
| 64 | fn run() !void { | |
| 65 | try t(u8, 2, [_]u8{ 0x12, 0x13 }, [_]u8{ 0x12, 0x13 }); | |
| 66 | try t(u16, 2, [_]u16{ 0x1234, 0x2345 }, [_]u16{ 0x3412, 0x4523 }); | |
| 67 | try t(u24, 2, [_]u24{ 0x123456, 0x234567 }, [_]u24{ 0x563412, 0x674523 }); | |
| 68 | } | |
| 69 | comptime try vector8(); | |
| 70 | try vector8(); | |
| 71 | } | |
| 69 | 72 | |
| 70 | fn t( | |
| 71 | comptime I: type, | |
| 72 | comptime n: comptime_int, | |
| 73 | input: std.meta.Vector(n, I), | |
| 74 | expected_vector: std.meta.Vector(n, I), | |
| 75 | ) !void { | |
| 76 | const actual_output: [n]I = @byteSwap(I, input); | |
| 77 | const expected_output: [n]I = expected_vector; | |
| 78 | try std.testing.expectEqual(expected_output, actual_output); | |
| 79 | } | |
| 80 | }; | |
| 81 | comptime try ByteSwapVectorTest.run(); | |
| 82 | try ByteSwapVectorTest.run(); | |
| 73 | fn vector16() !void { | |
| 74 | var v = @Vector(2, u16){ 0x1234, 0x2345 }; | |
| 75 | var result = @byteSwap(u16, v); | |
| 76 | try expect(result[0] == 0x3412); | |
| 77 | try expect(result[1] == 0x4523); | |
| 78 | } | |
| 79 | ||
| 80 | test "@byteSwap vectors u16" { | |
| 81 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 82 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 83 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 84 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 85 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 86 | ||
| 87 | comptime try vector16(); | |
| 88 | try vector16(); | |
| 89 | } | |
| 90 | ||
| 91 | fn 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 | ||
| 98 | test "@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 | ||
| 109 | fn 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 | ||
| 116 | test "@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 | } |