| author | |
| committer | |
| log | adfcc8851b6bb47b085cfe2526f0797b1f414996 |
| tree | ab8778be360da7a005e8de4d707b7bfc70e62635 |
| parent | 7233a3324aaa5b3995606f24b2b961149219986b |
Make the behavior tests for this a little more primitive to exercise as
little extra functionality as possible.3 files changed, 152 insertions(+), 43 deletions(-)
src/Sema.zig+66-19| ... | ... | @@ -13491,30 +13491,77 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 13491 | 13491 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 13492 | 13492 | const operand = sema.resolveInst(inst_data.operand); |
| 13493 | 13493 | 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 | }); | |
| 13494 | ||
| 13495 | const scalar_ty = if (operand_ty.zigTypeTag() == .Vector) | |
| 13496 | operand_ty.elemType2() | |
| 13497 | else | |
| 13498 | operand_ty; | |
| 13499 | ||
| 13500 | switch (operand_ty.zigTypeTag()) { | |
| 13501 | .Int, .ComptimeInt => {}, | |
| 13502 | .Vector => { | |
| 13503 | switch (scalar_ty.zigTypeTag()) { | |
| 13504 | .Int, .ComptimeInt => {}, | |
| 13505 | else => return sema.fail(block, ty_src, "expected vector of integer type, found vector of '{}'", .{scalar_ty}), | |
| 13506 | } | |
| 13507 | }, | |
| 13508 | else => return sema.fail(block, ty_src, "expected integer type or vector of integer type, found '{}'", .{operand_ty}), | |
| 13499 | 13509 | } |
| 13510 | ||
| 13500 | 13511 | 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 | }); | |
| 13512 | const bits = scalar_ty.intInfo(target).bits; | |
| 13513 | if (bits % 8 != 0) { | |
| 13514 | return sema.fail( | |
| 13515 | block, | |
| 13516 | ty_src, | |
| 13517 | "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", | |
| 13518 | .{ scalar_ty, bits }, | |
| 13519 | ); | |
| 13508 | 13520 | } |
| 13509 | 13521 | |
| 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; | |
| 13522 | switch (operand_ty.zigTypeTag()) { | |
| 13523 | .Int, .ComptimeInt => { | |
| 13524 | if (bits == 0) return Air.Inst.Ref.zero; | |
| 13515 | 13525 | |
| 13516 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 13517 | return block.addTyOp(.byte_swap, operand_ty, operand); | |
| 13526 | const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | |
| 13527 | if (val.isUndef()) return sema.addConstUndef(operand_ty); | |
| 13528 | const result_val = try val.byteSwap(operand_ty, target, sema.arena); | |
| 13529 | return sema.addConstant(operand_ty, result_val); | |
| 13530 | } else operand_src; | |
| 13531 | ||
| 13532 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 13533 | return block.addTyOp(.byte_swap, operand_ty, operand); | |
| 13534 | }, | |
| 13535 | .Vector => { | |
| 13536 | if (bits == 0) { | |
| 13537 | return sema.addConstant( | |
| 13538 | operand_ty, | |
| 13539 | try Value.Tag.repeated.create(sema.arena, Value.zero), | |
| 13540 | ); | |
| 13541 | } | |
| 13542 | ||
| 13543 | const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | |
| 13544 | if (val.isUndef()) | |
| 13545 | return sema.addConstUndef(operand_ty); | |
| 13546 | ||
| 13547 | const vec_len = operand_ty.vectorLen(); | |
| 13548 | var elem_buf: Value.ElemValueBuffer = undefined; | |
| 13549 | const elems = try sema.arena.alloc(Value, vec_len); | |
| 13550 | for (elems) |*elem, i| { | |
| 13551 | const elem_val = val.elemValueBuffer(i, &elem_buf); | |
| 13552 | elem.* = try elem_val.byteSwap(operand_ty, target, sema.arena); | |
| 13553 | } | |
| 13554 | return sema.addConstant( | |
| 13555 | operand_ty, | |
| 13556 | try Value.Tag.aggregate.create(sema.arena, elems), | |
| 13557 | ); | |
| 13558 | } else operand_src; | |
| 13559 | ||
| 13560 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 13561 | return block.addTyOp(.byte_swap, operand_ty, operand); | |
| 13562 | }, | |
| 13563 | else => unreachable, | |
| 13564 | } | |
| 13518 | 13565 | } |
| 13519 | 13566 | |
| 13520 | 13567 | 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+66-21| ... | ... | @@ -52,32 +52,77 @@ 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 | // TODO: vector initialization for @Vector(x, u0) currently fails. | |
| 118 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; | |
| 119 | ||
| 120 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 121 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 122 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 123 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 124 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 125 | ||
| 126 | comptime try vector0(); | |
| 127 | try vector0(); | |
| 83 | 128 | } |