| author | |
| committer | |
| log | 2fcb2f597549edd0b1241cebf98c11efe2f25884 |
| tree | ebf6aca8e0c1d5c77874c2c05b0cccd0967d080f |
| parent | 2fdc9e6ae8b6f1ec86050011e1170d639d8c9c2c |
These used to be lowered elementwise in air, and now are a single air
instruction that can be lowered elementwise in the backend if necessary.11 files changed, 552 insertions(+), 254 deletions(-)
lib/std/unicode.zig+129-149| ... | ... | @@ -602,9 +602,9 @@ fn testUtf8IteratorOnAscii() !void { |
| 602 | 602 | const s = Utf8View.initComptime("abc"); |
| 603 | 603 | |
| 604 | 604 | var it1 = s.iterator(); |
| 605 | try testing.expect(std.mem.eql(u8, "a", it1.nextCodepointSlice().?)); | |
| 606 | try testing.expect(std.mem.eql(u8, "b", it1.nextCodepointSlice().?)); | |
| 607 | try testing.expect(std.mem.eql(u8, "c", it1.nextCodepointSlice().?)); | |
| 605 | try testing.expect(mem.eql(u8, "a", it1.nextCodepointSlice().?)); | |
| 606 | try testing.expect(mem.eql(u8, "b", it1.nextCodepointSlice().?)); | |
| 607 | try testing.expect(mem.eql(u8, "c", it1.nextCodepointSlice().?)); | |
| 608 | 608 | try testing.expect(it1.nextCodepointSlice() == null); |
| 609 | 609 | |
| 610 | 610 | var it2 = s.iterator(); |
| ... | ... | @@ -632,9 +632,9 @@ fn testUtf8ViewOk() !void { |
| 632 | 632 | const s = Utf8View.initComptime("東京市"); |
| 633 | 633 | |
| 634 | 634 | var it1 = s.iterator(); |
| 635 | try testing.expect(std.mem.eql(u8, "東", it1.nextCodepointSlice().?)); | |
| 636 | try testing.expect(std.mem.eql(u8, "京", it1.nextCodepointSlice().?)); | |
| 637 | try testing.expect(std.mem.eql(u8, "市", it1.nextCodepointSlice().?)); | |
| 635 | try testing.expect(mem.eql(u8, "東", it1.nextCodepointSlice().?)); | |
| 636 | try testing.expect(mem.eql(u8, "京", it1.nextCodepointSlice().?)); | |
| 637 | try testing.expect(mem.eql(u8, "市", it1.nextCodepointSlice().?)); | |
| 638 | 638 | try testing.expect(it1.nextCodepointSlice() == null); |
| 639 | 639 | |
| 640 | 640 | var it2 = s.iterator(); |
| ... | ... | @@ -772,20 +772,20 @@ fn testUtf8Peeking() !void { |
| 772 | 772 | const s = Utf8View.initComptime("noël"); |
| 773 | 773 | var it = s.iterator(); |
| 774 | 774 | |
| 775 | try testing.expect(std.mem.eql(u8, "n", it.nextCodepointSlice().?)); | |
| 775 | try testing.expect(mem.eql(u8, "n", it.nextCodepointSlice().?)); | |
| 776 | 776 | |
| 777 | try testing.expect(std.mem.eql(u8, "o", it.peek(1))); | |
| 778 | try testing.expect(std.mem.eql(u8, "oë", it.peek(2))); | |
| 779 | try testing.expect(std.mem.eql(u8, "oël", it.peek(3))); | |
| 780 | try testing.expect(std.mem.eql(u8, "oël", it.peek(4))); | |
| 781 | try testing.expect(std.mem.eql(u8, "oël", it.peek(10))); | |
| 777 | try testing.expect(mem.eql(u8, "o", it.peek(1))); | |
| 778 | try testing.expect(mem.eql(u8, "oë", it.peek(2))); | |
| 779 | try testing.expect(mem.eql(u8, "oël", it.peek(3))); | |
| 780 | try testing.expect(mem.eql(u8, "oël", it.peek(4))); | |
| 781 | try testing.expect(mem.eql(u8, "oël", it.peek(10))); | |
| 782 | 782 | |
| 783 | try testing.expect(std.mem.eql(u8, "o", it.nextCodepointSlice().?)); | |
| 784 | try testing.expect(std.mem.eql(u8, "ë", it.nextCodepointSlice().?)); | |
| 785 | try testing.expect(std.mem.eql(u8, "l", it.nextCodepointSlice().?)); | |
| 783 | try testing.expect(mem.eql(u8, "o", it.nextCodepointSlice().?)); | |
| 784 | try testing.expect(mem.eql(u8, "ë", it.nextCodepointSlice().?)); | |
| 785 | try testing.expect(mem.eql(u8, "l", it.nextCodepointSlice().?)); | |
| 786 | 786 | try testing.expect(it.nextCodepointSlice() == null); |
| 787 | 787 | |
| 788 | try testing.expect(std.mem.eql(u8, &[_]u8{}, it.peek(1))); | |
| 788 | try testing.expect(mem.eql(u8, &[_]u8{}, it.peek(1))); | |
| 789 | 789 | } |
| 790 | 790 | |
| 791 | 791 | fn testError(bytes: []const u8, expected_err: anyerror) !void { |
| ... | ... | @@ -927,20 +927,16 @@ test "fmtUtf8" { |
| 927 | 927 | } |
| 928 | 928 | |
| 929 | 929 | fn utf16LeToUtf8ArrayListImpl( |
| 930 | array_list: *std.ArrayList(u8), | |
| 930 | result: *std.ArrayList(u8), | |
| 931 | 931 | utf16le: []const u16, |
| 932 | 932 | comptime surrogates: Surrogates, |
| 933 | 933 | ) (switch (surrogates) { |
| 934 | 934 | .cannot_encode_surrogate_half => Utf16LeToUtf8AllocError, |
| 935 | 935 | .can_encode_surrogate_half => mem.Allocator.Error, |
| 936 | 936 | })!void { |
| 937 | // optimistically guess that it will all be ascii. | |
| 938 | try array_list.ensureTotalCapacityPrecise(utf16le.len); | |
| 937 | assert(result.capacity >= utf16le.len); | |
| 939 | 938 | |
| 940 | 939 | var remaining = utf16le; |
| 941 | if (builtin.zig_backend != .stage2_x86_64 or | |
| 942 | comptime (std.Target.x86.featureSetHas(builtin.cpu.features, .ssse3) and | |
| 943 | !std.Target.x86.featureSetHasAny(builtin.cpu.features, .{ .prefer_256_bit, .avx }))) | |
| 944 | 940 | vectorized: { |
| 945 | 941 | const chunk_len = std.simd.suggestVectorLength(u16) orelse break :vectorized; |
| 946 | 942 | const Chunk = @Vector(chunk_len, u16); |
| ... | ... | @@ -948,41 +944,33 @@ fn utf16LeToUtf8ArrayListImpl( |
| 948 | 944 | // Fast path. Check for and encode ASCII characters at the start of the input. |
| 949 | 945 | while (remaining.len >= chunk_len) { |
| 950 | 946 | const chunk: Chunk = remaining[0..chunk_len].*; |
| 951 | const mask: Chunk = @splat(std.mem.nativeToLittle(u16, 0x7F)); | |
| 947 | const mask: Chunk = @splat(mem.nativeToLittle(u16, 0x7F)); | |
| 952 | 948 | if (@reduce(.Or, chunk | mask != mask)) { |
| 953 | 949 | // found a non ASCII code unit |
| 954 | 950 | break; |
| 955 | 951 | } |
| 956 | const chunk_byte_len = chunk_len * 2; | |
| 957 | const chunk_bytes: @Vector(chunk_byte_len, u8) = (std.mem.sliceAsBytes(remaining)[0..chunk_byte_len]).*; | |
| 958 | const deinterlaced_bytes = std.simd.deinterlace(2, chunk_bytes); | |
| 959 | const ascii_bytes: [chunk_len]u8 = deinterlaced_bytes[0]; | |
| 952 | const ascii_chunk: @Vector(chunk_len, u8) = @truncate(mem.nativeToLittle(Chunk, chunk)); | |
| 960 | 953 | // We allocated enough space to encode every UTF-16 code unit |
| 961 | 954 | // as ASCII, so if the entire string is ASCII then we are |
| 962 | 955 | // guaranteed to have enough space allocated |
| 963 | array_list.appendSliceAssumeCapacity(&ascii_bytes); | |
| 956 | result.addManyAsArrayAssumeCapacity(chunk_len).* = ascii_chunk; | |
| 964 | 957 | remaining = remaining[chunk_len..]; |
| 965 | 958 | } |
| 966 | 959 | } |
| 967 | 960 | |
| 968 | var out_index: usize = array_list.items.len; | |
| 969 | 961 | switch (surrogates) { |
| 970 | 962 | .cannot_encode_surrogate_half => { |
| 971 | 963 | var it = Utf16LeIterator.init(remaining); |
| 972 | 964 | while (try it.nextCodepoint()) |codepoint| { |
| 973 | 965 | const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable; |
| 974 | try array_list.resize(array_list.items.len + utf8_len); | |
| 975 | assert((utf8Encode(codepoint, array_list.items[out_index..]) catch unreachable) == utf8_len); | |
| 976 | out_index += utf8_len; | |
| 966 | assert((utf8Encode(codepoint, try result.addManyAsSlice(utf8_len)) catch unreachable) == utf8_len); | |
| 977 | 967 | } |
| 978 | 968 | }, |
| 979 | 969 | .can_encode_surrogate_half => { |
| 980 | 970 | var it = Wtf16LeIterator.init(remaining); |
| 981 | 971 | while (it.nextCodepoint()) |codepoint| { |
| 982 | 972 | const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable; |
| 983 | try array_list.resize(array_list.items.len + utf8_len); | |
| 984 | assert((wtf8Encode(codepoint, array_list.items[out_index..]) catch unreachable) == utf8_len); | |
| 985 | out_index += utf8_len; | |
| 973 | assert((wtf8Encode(codepoint, try result.addManyAsSlice(utf8_len)) catch unreachable) == utf8_len); | |
| 986 | 974 | } |
| 987 | 975 | }, |
| 988 | 976 | } |
| ... | ... | @@ -990,8 +978,9 @@ fn utf16LeToUtf8ArrayListImpl( |
| 990 | 978 | |
| 991 | 979 | pub const Utf16LeToUtf8AllocError = mem.Allocator.Error || Utf16LeToUtf8Error; |
| 992 | 980 | |
| 993 | pub fn utf16LeToUtf8ArrayList(array_list: *std.ArrayList(u8), utf16le: []const u16) Utf16LeToUtf8AllocError!void { | |
| 994 | return utf16LeToUtf8ArrayListImpl(array_list, utf16le, .cannot_encode_surrogate_half); | |
| 981 | pub fn utf16LeToUtf8ArrayList(result: *std.ArrayList(u8), utf16le: []const u16) Utf16LeToUtf8AllocError!void { | |
| 982 | try result.ensureTotalCapacityPrecise(utf16le.len); | |
| 983 | return utf16LeToUtf8ArrayListImpl(result, utf16le, .cannot_encode_surrogate_half); | |
| 995 | 984 | } |
| 996 | 985 | |
| 997 | 986 | /// Deprecated; renamed to utf16LeToUtf8Alloc |
| ... | ... | @@ -1003,8 +992,7 @@ pub fn utf16LeToUtf8Alloc(allocator: mem.Allocator, utf16le: []const u16) Utf16L |
| 1003 | 992 | var result = try std.ArrayList(u8).initCapacity(allocator, utf16le.len); |
| 1004 | 993 | errdefer result.deinit(); |
| 1005 | 994 | |
| 1006 | try utf16LeToUtf8ArrayList(&result, utf16le); | |
| 1007 | ||
| 995 | try utf16LeToUtf8ArrayListImpl(&result, utf16le, .cannot_encode_surrogate_half); | |
| 1008 | 996 | return result.toOwnedSlice(); |
| 1009 | 997 | } |
| 1010 | 998 | |
| ... | ... | @@ -1017,8 +1005,7 @@ pub fn utf16LeToUtf8AllocZ(allocator: mem.Allocator, utf16le: []const u16) Utf16 |
| 1017 | 1005 | var result = try std.ArrayList(u8).initCapacity(allocator, utf16le.len + 1); |
| 1018 | 1006 | errdefer result.deinit(); |
| 1019 | 1007 | |
| 1020 | try utf16LeToUtf8ArrayList(&result, utf16le); | |
| 1021 | ||
| 1008 | try utf16LeToUtf8ArrayListImpl(&result, utf16le, .cannot_encode_surrogate_half); | |
| 1022 | 1009 | return result.toOwnedSliceSentinel(0); |
| 1023 | 1010 | } |
| 1024 | 1011 | |
| ... | ... | @@ -1030,12 +1017,9 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr |
| 1030 | 1017 | .cannot_encode_surrogate_half => Utf16LeToUtf8Error, |
| 1031 | 1018 | .can_encode_surrogate_half => error{}, |
| 1032 | 1019 | })!usize { |
| 1033 | var end_index: usize = 0; | |
| 1020 | var dest_index: usize = 0; | |
| 1034 | 1021 | |
| 1035 | 1022 | var remaining = utf16le; |
| 1036 | if (builtin.zig_backend != .stage2_x86_64 or | |
| 1037 | comptime (std.Target.x86.featureSetHas(builtin.cpu.features, .ssse3) and | |
| 1038 | !std.Target.x86.featureSetHasAny(builtin.cpu.features, .{ .prefer_256_bit, .avx }))) | |
| 1039 | 1023 | vectorized: { |
| 1040 | 1024 | const chunk_len = std.simd.suggestVectorLength(u16) orelse break :vectorized; |
| 1041 | 1025 | const Chunk = @Vector(chunk_len, u16); |
| ... | ... | @@ -1043,17 +1027,14 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr |
| 1043 | 1027 | // Fast path. Check for and encode ASCII characters at the start of the input. |
| 1044 | 1028 | while (remaining.len >= chunk_len) { |
| 1045 | 1029 | const chunk: Chunk = remaining[0..chunk_len].*; |
| 1046 | const mask: Chunk = @splat(std.mem.nativeToLittle(u16, 0x7F)); | |
| 1030 | const mask: Chunk = @splat(mem.nativeToLittle(u16, 0x7F)); | |
| 1047 | 1031 | if (@reduce(.Or, chunk | mask != mask)) { |
| 1048 | 1032 | // found a non ASCII code unit |
| 1049 | 1033 | break; |
| 1050 | 1034 | } |
| 1051 | const chunk_byte_len = chunk_len * 2; | |
| 1052 | const chunk_bytes: @Vector(chunk_byte_len, u8) = (std.mem.sliceAsBytes(remaining)[0..chunk_byte_len]).*; | |
| 1053 | const deinterlaced_bytes = std.simd.deinterlace(2, chunk_bytes); | |
| 1054 | const ascii_bytes: [chunk_len]u8 = deinterlaced_bytes[0]; | |
| 1055 | @memcpy(utf8[end_index .. end_index + chunk_len], &ascii_bytes); | |
| 1056 | end_index += chunk_len; | |
| 1035 | const ascii_chunk: @Vector(chunk_len, u8) = @truncate(mem.nativeToLittle(Chunk, chunk)); | |
| 1036 | utf8[dest_index..][0..chunk_len].* = ascii_chunk; | |
| 1037 | dest_index += chunk_len; | |
| 1057 | 1038 | remaining = remaining[chunk_len..]; |
| 1058 | 1039 | } |
| 1059 | 1040 | } |
| ... | ... | @@ -1062,7 +1043,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr |
| 1062 | 1043 | .cannot_encode_surrogate_half => { |
| 1063 | 1044 | var it = Utf16LeIterator.init(remaining); |
| 1064 | 1045 | while (try it.nextCodepoint()) |codepoint| { |
| 1065 | end_index += utf8Encode(codepoint, utf8[end_index..]) catch |err| switch (err) { | |
| 1046 | dest_index += utf8Encode(codepoint, utf8[dest_index..]) catch |err| switch (err) { | |
| 1066 | 1047 | // The maximum possible codepoint encoded by UTF-16 is U+10FFFF, |
| 1067 | 1048 | // which is within the valid codepoint range. |
| 1068 | 1049 | error.CodepointTooLarge => unreachable, |
| ... | ... | @@ -1075,7 +1056,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr |
| 1075 | 1056 | .can_encode_surrogate_half => { |
| 1076 | 1057 | var it = Wtf16LeIterator.init(remaining); |
| 1077 | 1058 | while (it.nextCodepoint()) |codepoint| { |
| 1078 | end_index += wtf8Encode(codepoint, utf8[end_index..]) catch |err| switch (err) { | |
| 1059 | dest_index += wtf8Encode(codepoint, utf8[dest_index..]) catch |err| switch (err) { | |
| 1079 | 1060 | // The maximum possible codepoint encoded by UTF-16 is U+10FFFF, |
| 1080 | 1061 | // which is within the valid codepoint range. |
| 1081 | 1062 | error.CodepointTooLarge => unreachable, |
| ... | ... | @@ -1083,7 +1064,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr |
| 1083 | 1064 | } |
| 1084 | 1065 | }, |
| 1085 | 1066 | } |
| 1086 | return end_index; | |
| 1067 | return dest_index; | |
| 1087 | 1068 | } |
| 1088 | 1069 | |
| 1089 | 1070 | /// Deprecated; renamed to utf16LeToUtf8 |
| ... | ... | @@ -1156,18 +1137,12 @@ test utf16LeToUtf8 { |
| 1156 | 1137 | } |
| 1157 | 1138 | } |
| 1158 | 1139 | |
| 1159 | fn utf8ToUtf16LeArrayListImpl(array_list: *std.ArrayList(u16), utf8: []const u8, comptime surrogates: Surrogates) !void { | |
| 1160 | // optimistically guess that it will not require surrogate pairs | |
| 1161 | try array_list.ensureTotalCapacityPrecise(utf8.len); | |
| 1140 | fn utf8ToUtf16LeArrayListImpl(result: *std.ArrayList(u16), utf8: []const u8, comptime surrogates: Surrogates) !void { | |
| 1141 | assert(result.capacity >= utf8.len); | |
| 1162 | 1142 | |
| 1163 | 1143 | var remaining = utf8; |
| 1164 | // Need support for std.simd.interlace | |
| 1165 | if ((builtin.zig_backend != .stage2_x86_64 or | |
| 1166 | comptime (std.Target.x86.featureSetHas(builtin.cpu.features, .ssse3) and | |
| 1167 | !std.Target.x86.featureSetHasAny(builtin.cpu.features, .{ .prefer_256_bit, .avx }))) and | |
| 1168 | comptime !builtin.cpu.arch.isMIPS()) | |
| 1169 | 1144 | vectorized: { |
| 1170 | const chunk_len = @divExact(std.simd.suggestVectorLength(u8) orelse break :vectorized, 2); | |
| 1145 | const chunk_len = std.simd.suggestVectorLength(u16) orelse break :vectorized; | |
| 1171 | 1146 | const Chunk = @Vector(chunk_len, u8); |
| 1172 | 1147 | |
| 1173 | 1148 | // Fast path. Check for and encode ASCII characters at the start of the input. |
| ... | ... | @@ -1178,9 +1153,8 @@ fn utf8ToUtf16LeArrayListImpl(array_list: *std.ArrayList(u16), utf8: []const u8, |
| 1178 | 1153 | // found a non ASCII code unit |
| 1179 | 1154 | break; |
| 1180 | 1155 | } |
| 1181 | const zeroes: Chunk = @splat(0); | |
| 1182 | const utf16_chunk: [chunk_len * 2]u8 align(@alignOf(u16)) = std.simd.interlace(.{ chunk, zeroes }); | |
| 1183 | array_list.appendSliceAssumeCapacity(std.mem.bytesAsSlice(u16, &utf16_chunk)); | |
| 1156 | const utf16_chunk = mem.nativeToLittle(@Vector(chunk_len, u16), chunk); | |
| 1157 | result.addManyAsArrayAssumeCapacity(chunk_len).* = utf16_chunk; | |
| 1184 | 1158 | remaining = remaining[chunk_len..]; |
| 1185 | 1159 | } |
| 1186 | 1160 | } |
| ... | ... | @@ -1192,21 +1166,18 @@ fn utf8ToUtf16LeArrayListImpl(array_list: *std.ArrayList(u16), utf8: []const u8, |
| 1192 | 1166 | var it = view.iterator(); |
| 1193 | 1167 | while (it.nextCodepoint()) |codepoint| { |
| 1194 | 1168 | if (codepoint < 0x10000) { |
| 1195 | const short = @as(u16, @intCast(codepoint)); | |
| 1196 | try array_list.append(mem.nativeToLittle(u16, short)); | |
| 1169 | try result.append(mem.nativeToLittle(u16, @intCast(codepoint))); | |
| 1197 | 1170 | } else { |
| 1198 | 1171 | const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800; |
| 1199 | 1172 | const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00; |
| 1200 | var out: [2]u16 = undefined; | |
| 1201 | out[0] = mem.nativeToLittle(u16, high); | |
| 1202 | out[1] = mem.nativeToLittle(u16, low); | |
| 1203 | try array_list.appendSlice(out[0..]); | |
| 1173 | try result.appendSlice(&.{ mem.nativeToLittle(u16, high), mem.nativeToLittle(u16, low) }); | |
| 1204 | 1174 | } |
| 1205 | 1175 | } |
| 1206 | 1176 | } |
| 1207 | 1177 | |
| 1208 | pub fn utf8ToUtf16LeArrayList(array_list: *std.ArrayList(u16), utf8: []const u8) error{ InvalidUtf8, OutOfMemory }!void { | |
| 1209 | return utf8ToUtf16LeArrayListImpl(array_list, utf8, .cannot_encode_surrogate_half); | |
| 1178 | pub fn utf8ToUtf16LeArrayList(result: *std.ArrayList(u16), utf8: []const u8) error{ InvalidUtf8, OutOfMemory }!void { | |
| 1179 | try result.ensureTotalCapacityPrecise(utf8.len); | |
| 1180 | return utf8ToUtf16LeArrayListImpl(result, utf8, .cannot_encode_surrogate_half); | |
| 1210 | 1181 | } |
| 1211 | 1182 | |
| 1212 | 1183 | pub fn utf8ToUtf16LeAlloc(allocator: mem.Allocator, utf8: []const u8) error{ InvalidUtf8, OutOfMemory }![]u16 { |
| ... | ... | @@ -1215,7 +1186,6 @@ pub fn utf8ToUtf16LeAlloc(allocator: mem.Allocator, utf8: []const u8) error{ Inv |
| 1215 | 1186 | errdefer result.deinit(); |
| 1216 | 1187 | |
| 1217 | 1188 | try utf8ToUtf16LeArrayListImpl(&result, utf8, .cannot_encode_surrogate_half); |
| 1218 | ||
| 1219 | 1189 | return result.toOwnedSlice(); |
| 1220 | 1190 | } |
| 1221 | 1191 | |
| ... | ... | @@ -1228,7 +1198,6 @@ pub fn utf8ToUtf16LeAllocZ(allocator: mem.Allocator, utf8: []const u8) error{ In |
| 1228 | 1198 | errdefer result.deinit(); |
| 1229 | 1199 | |
| 1230 | 1200 | try utf8ToUtf16LeArrayListImpl(&result, utf8, .cannot_encode_surrogate_half); |
| 1231 | ||
| 1232 | 1201 | return result.toOwnedSliceSentinel(0); |
| 1233 | 1202 | } |
| 1234 | 1203 | |
| ... | ... | @@ -1239,16 +1208,11 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) error{InvalidUtf8}!usize |
| 1239 | 1208 | } |
| 1240 | 1209 | |
| 1241 | 1210 | pub fn utf8ToUtf16LeImpl(utf16le: []u16, utf8: []const u8, comptime surrogates: Surrogates) !usize { |
| 1242 | var dest_i: usize = 0; | |
| 1211 | var dest_index: usize = 0; | |
| 1243 | 1212 | |
| 1244 | 1213 | var remaining = utf8; |
| 1245 | // Need support for std.simd.interlace | |
| 1246 | if ((builtin.zig_backend != .stage2_x86_64 or | |
| 1247 | comptime (std.Target.x86.featureSetHas(builtin.cpu.features, .ssse3) and | |
| 1248 | !std.Target.x86.featureSetHasAny(builtin.cpu.features, .{ .prefer_256_bit, .avx }))) and | |
| 1249 | comptime !builtin.cpu.arch.isMIPS()) | |
| 1250 | 1214 | vectorized: { |
| 1251 | const chunk_len = @divExact(std.simd.suggestVectorLength(u8) orelse break :vectorized, 2); | |
| 1215 | const chunk_len = std.simd.suggestVectorLength(u16) orelse break :vectorized; | |
| 1252 | 1216 | const Chunk = @Vector(chunk_len, u8); |
| 1253 | 1217 | |
| 1254 | 1218 | // Fast path. Check for and encode ASCII characters at the start of the input. |
| ... | ... | @@ -1259,57 +1223,60 @@ pub fn utf8ToUtf16LeImpl(utf16le: []u16, utf8: []const u8, comptime surrogates: |
| 1259 | 1223 | // found a non ASCII code unit |
| 1260 | 1224 | break; |
| 1261 | 1225 | } |
| 1262 | const zeroes: Chunk = @splat(0); | |
| 1263 | const utf16_bytes: [chunk_len * 2]u8 align(@alignOf(u16)) = std.simd.interlace(.{ chunk, zeroes }); | |
| 1264 | @memcpy(utf16le[dest_i..][0..chunk_len], std.mem.bytesAsSlice(u16, &utf16_bytes)); | |
| 1265 | dest_i += chunk_len; | |
| 1226 | const utf16_chunk = mem.nativeToLittle(@Vector(chunk_len, u16), chunk); | |
| 1227 | utf16le[dest_index..][0..chunk_len].* = utf16_chunk; | |
| 1228 | dest_index += chunk_len; | |
| 1266 | 1229 | remaining = remaining[chunk_len..]; |
| 1267 | 1230 | } |
| 1268 | 1231 | } |
| 1269 | 1232 | |
| 1270 | var src_i: usize = 0; | |
| 1271 | while (src_i < remaining.len) { | |
| 1272 | const n = utf8ByteSequenceLength(remaining[src_i]) catch return switch (surrogates) { | |
| 1273 | .cannot_encode_surrogate_half => error.InvalidUtf8, | |
| 1274 | .can_encode_surrogate_half => error.InvalidWtf8, | |
| 1275 | }; | |
| 1276 | const next_src_i = src_i + n; | |
| 1277 | const codepoint = switch (surrogates) { | |
| 1278 | .cannot_encode_surrogate_half => utf8Decode(remaining[src_i..next_src_i]) catch return error.InvalidUtf8, | |
| 1279 | .can_encode_surrogate_half => wtf8Decode(remaining[src_i..next_src_i]) catch return error.InvalidWtf8, | |
| 1280 | }; | |
| 1233 | const view = switch (surrogates) { | |
| 1234 | .cannot_encode_surrogate_half => try Utf8View.init(remaining), | |
| 1235 | .can_encode_surrogate_half => try Wtf8View.init(remaining), | |
| 1236 | }; | |
| 1237 | var it = view.iterator(); | |
| 1238 | while (it.nextCodepoint()) |codepoint| { | |
| 1281 | 1239 | if (codepoint < 0x10000) { |
| 1282 | const short = @as(u16, @intCast(codepoint)); | |
| 1283 | utf16le[dest_i] = mem.nativeToLittle(u16, short); | |
| 1284 | dest_i += 1; | |
| 1240 | utf16le[dest_index] = mem.nativeToLittle(u16, @intCast(codepoint)); | |
| 1241 | dest_index += 1; | |
| 1285 | 1242 | } else { |
| 1286 | 1243 | const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800; |
| 1287 | 1244 | const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00; |
| 1288 | utf16le[dest_i] = mem.nativeToLittle(u16, high); | |
| 1289 | utf16le[dest_i + 1] = mem.nativeToLittle(u16, low); | |
| 1290 | dest_i += 2; | |
| 1245 | utf16le[dest_index..][0..2].* = .{ mem.nativeToLittle(u16, high), mem.nativeToLittle(u16, low) }; | |
| 1246 | dest_index += 2; | |
| 1291 | 1247 | } |
| 1292 | src_i = next_src_i; | |
| 1293 | 1248 | } |
| 1294 | return dest_i; | |
| 1249 | return dest_index; | |
| 1295 | 1250 | } |
| 1296 | 1251 | |
| 1297 | 1252 | test "utf8ToUtf16Le" { |
| 1298 | var utf16le: [2]u16 = [_]u16{0} ** 2; | |
| 1253 | var utf16le: [128]u16 = undefined; | |
| 1299 | 1254 | { |
| 1300 | 1255 | const length = try utf8ToUtf16Le(utf16le[0..], "𐐷"); |
| 1301 | try testing.expectEqual(@as(usize, 2), length); | |
| 1302 | try testing.expectEqualSlices(u8, "\x01\xd8\x37\xdc", mem.sliceAsBytes(utf16le[0..])); | |
| 1256 | try testing.expectEqualSlices(u8, "\x01\xd8\x37\xdc", mem.sliceAsBytes(utf16le[0..length])); | |
| 1303 | 1257 | } |
| 1304 | 1258 | { |
| 1305 | 1259 | const length = try utf8ToUtf16Le(utf16le[0..], "\u{10FFFF}"); |
| 1306 | try testing.expectEqual(@as(usize, 2), length); | |
| 1307 | try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16le[0..])); | |
| 1260 | try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16le[0..length])); | |
| 1308 | 1261 | } |
| 1309 | 1262 | { |
| 1310 | 1263 | const result = utf8ToUtf16Le(utf16le[0..], "\xf4\x90\x80\x80"); |
| 1311 | 1264 | try testing.expectError(error.InvalidUtf8, result); |
| 1312 | 1265 | } |
| 1266 | { | |
| 1267 | const length = try utf8ToUtf16Le(utf16le[0..], "This string has been designed to test the vectorized implementat" ++ | |
| 1268 | "ion by beginning with one hundred twenty-seven ASCII characters¡"); | |
| 1269 | try testing.expectEqualSlices(u8, &.{ | |
| 1270 | 'T', 0, 'h', 0, 'i', 0, 's', 0, ' ', 0, 's', 0, 't', 0, 'r', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'h', 0, 'a', 0, 's', 0, ' ', 0, | |
| 1271 | 'b', 0, 'e', 0, 'e', 0, 'n', 0, ' ', 0, 'd', 0, 'e', 0, 's', 0, 'i', 0, 'g', 0, 'n', 0, 'e', 0, 'd', 0, ' ', 0, 't', 0, 'o', 0, | |
| 1272 | ' ', 0, 't', 0, 'e', 0, 's', 0, 't', 0, ' ', 0, 't', 0, 'h', 0, 'e', 0, ' ', 0, 'v', 0, 'e', 0, 'c', 0, 't', 0, 'o', 0, 'r', 0, | |
| 1273 | 'i', 0, 'z', 0, 'e', 0, 'd', 0, ' ', 0, 'i', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 'a', 0, 't', 0, | |
| 1274 | 'i', 0, 'o', 0, 'n', 0, ' ', 0, 'b', 0, 'y', 0, ' ', 0, 'b', 0, 'e', 0, 'g', 0, 'i', 0, 'n', 0, 'n', 0, 'i', 0, 'n', 0, 'g', 0, | |
| 1275 | ' ', 0, 'w', 0, 'i', 0, 't', 0, 'h', 0, ' ', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'h', 0, 'u', 0, 'n', 0, 'd', 0, 'r', 0, 'e', 0, | |
| 1276 | 'd', 0, ' ', 0, 't', 0, 'w', 0, 'e', 0, 'n', 0, 't', 0, 'y', 0, '-', 0, 's', 0, 'e', 0, 'v', 0, 'e', 0, 'n', 0, ' ', 0, 'A', 0, | |
| 1277 | 'S', 0, 'C', 0, 'I', 0, 'I', 0, ' ', 0, 'c', 0, 'h', 0, 'a', 0, 'r', 0, 'a', 0, 'c', 0, 't', 0, 'e', 0, 'r', 0, 's', 0, '¡', 0, | |
| 1278 | }, mem.sliceAsBytes(utf16le[0..length])); | |
| 1279 | } | |
| 1313 | 1280 | } |
| 1314 | 1281 | |
| 1315 | 1282 | test utf8ToUtf16LeArrayList { |
| ... | ... | @@ -1354,25 +1321,40 @@ test utf8ToUtf16LeAllocZ { |
| 1354 | 1321 | { |
| 1355 | 1322 | const utf16 = try utf8ToUtf16LeAllocZ(testing.allocator, "𐐷"); |
| 1356 | 1323 | defer testing.allocator.free(utf16); |
| 1357 | try testing.expectEqualSlices(u8, "\x01\xd8\x37\xdc", mem.sliceAsBytes(utf16[0..])); | |
| 1324 | try testing.expectEqualSlices(u8, "\x01\xd8\x37\xdc", mem.sliceAsBytes(utf16)); | |
| 1358 | 1325 | try testing.expect(utf16[2] == 0); |
| 1359 | 1326 | } |
| 1360 | 1327 | { |
| 1361 | 1328 | const utf16 = try utf8ToUtf16LeAllocZ(testing.allocator, "\u{10FFFF}"); |
| 1362 | 1329 | defer testing.allocator.free(utf16); |
| 1363 | try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16[0..])); | |
| 1330 | try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16)); | |
| 1364 | 1331 | try testing.expect(utf16[2] == 0); |
| 1365 | 1332 | } |
| 1366 | 1333 | { |
| 1367 | 1334 | const result = utf8ToUtf16LeAllocZ(testing.allocator, "\xf4\x90\x80\x80"); |
| 1368 | 1335 | try testing.expectError(error.InvalidUtf8, result); |
| 1369 | 1336 | } |
| 1337 | { | |
| 1338 | const utf16 = try utf8ToUtf16LeWithNull(testing.allocator, "This string has been designed to test the vectorized implementat" ++ | |
| 1339 | "ion by beginning with one hundred twenty-seven ASCII characters¡"); | |
| 1340 | defer testing.allocator.free(utf16); | |
| 1341 | try testing.expectEqualSlices(u8, &.{ | |
| 1342 | 'T', 0, 'h', 0, 'i', 0, 's', 0, ' ', 0, 's', 0, 't', 0, 'r', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'h', 0, 'a', 0, 's', 0, ' ', 0, | |
| 1343 | 'b', 0, 'e', 0, 'e', 0, 'n', 0, ' ', 0, 'd', 0, 'e', 0, 's', 0, 'i', 0, 'g', 0, 'n', 0, 'e', 0, 'd', 0, ' ', 0, 't', 0, 'o', 0, | |
| 1344 | ' ', 0, 't', 0, 'e', 0, 's', 0, 't', 0, ' ', 0, 't', 0, 'h', 0, 'e', 0, ' ', 0, 'v', 0, 'e', 0, 'c', 0, 't', 0, 'o', 0, 'r', 0, | |
| 1345 | 'i', 0, 'z', 0, 'e', 0, 'd', 0, ' ', 0, 'i', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 'a', 0, 't', 0, | |
| 1346 | 'i', 0, 'o', 0, 'n', 0, ' ', 0, 'b', 0, 'y', 0, ' ', 0, 'b', 0, 'e', 0, 'g', 0, 'i', 0, 'n', 0, 'n', 0, 'i', 0, 'n', 0, 'g', 0, | |
| 1347 | ' ', 0, 'w', 0, 'i', 0, 't', 0, 'h', 0, ' ', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'h', 0, 'u', 0, 'n', 0, 'd', 0, 'r', 0, 'e', 0, | |
| 1348 | 'd', 0, ' ', 0, 't', 0, 'w', 0, 'e', 0, 'n', 0, 't', 0, 'y', 0, '-', 0, 's', 0, 'e', 0, 'v', 0, 'e', 0, 'n', 0, ' ', 0, 'A', 0, | |
| 1349 | 'S', 0, 'C', 0, 'I', 0, 'I', 0, ' ', 0, 'c', 0, 'h', 0, 'a', 0, 'r', 0, 'a', 0, 'c', 0, 't', 0, 'e', 0, 'r', 0, 's', 0, '¡', 0, | |
| 1350 | }, mem.sliceAsBytes(utf16)); | |
| 1351 | } | |
| 1370 | 1352 | } |
| 1371 | 1353 | |
| 1372 | 1354 | /// Converts a UTF-8 string literal into a UTF-16LE string literal. |
| 1373 | pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8) catch unreachable:0]u16 { | |
| 1355 | pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8) catch |err| @compileError(err):0]u16 { | |
| 1374 | 1356 | return comptime blk: { |
| 1375 | const len: usize = calcUtf16LeLen(utf8) catch |err| @compileError(err); | |
| 1357 | const len: usize = calcUtf16LeLen(utf8) catch unreachable; | |
| 1376 | 1358 | var utf16le: [len:0]u16 = [_:0]u16{0} ** len; |
| 1377 | 1359 | const utf16le_len = utf8ToUtf16Le(&utf16le, utf8[0..]) catch |err| @compileError(err); |
| 1378 | 1360 | assert(len == utf16le_len); |
| ... | ... | @@ -1453,12 +1435,12 @@ test "fmtUtf16Le" { |
| 1453 | 1435 | try expectFmt("", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))}); |
| 1454 | 1436 | try expectFmt("foo", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))}); |
| 1455 | 1437 | try expectFmt("𐐷", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("𐐷"))}); |
| 1456 | try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\xff\xd7", native_endian)})}); | |
| 1457 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\x00\xd8", native_endian)})}); | |
| 1458 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\xff\xdb", native_endian)})}); | |
| 1459 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\x00\xdc", native_endian)})}); | |
| 1460 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\xff\xdf", native_endian)})}); | |
| 1461 | try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\x00\xe0", native_endian)})}); | |
| 1438 | try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xd7", native_endian)})}); | |
| 1439 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xd8", native_endian)})}); | |
| 1440 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdb", native_endian)})}); | |
| 1441 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xdc", native_endian)})}); | |
| 1442 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdf", native_endian)})}); | |
| 1443 | try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xe0", native_endian)})}); | |
| 1462 | 1444 | } |
| 1463 | 1445 | |
| 1464 | 1446 | test "utf8ToUtf16LeStringLiteral" { |
| ... | ... | @@ -1701,8 +1683,9 @@ pub const Wtf8Iterator = struct { |
| 1701 | 1683 | } |
| 1702 | 1684 | }; |
| 1703 | 1685 | |
| 1704 | pub fn wtf16LeToWtf8ArrayList(array_list: *std.ArrayList(u8), utf16le: []const u16) mem.Allocator.Error!void { | |
| 1705 | return utf16LeToUtf8ArrayListImpl(array_list, utf16le, .can_encode_surrogate_half); | |
| 1686 | pub fn wtf16LeToWtf8ArrayList(result: *std.ArrayList(u8), utf16le: []const u16) mem.Allocator.Error!void { | |
| 1687 | try result.ensureTotalCapacityPrecise(utf16le.len); | |
| 1688 | return utf16LeToUtf8ArrayListImpl(result, utf16le, .can_encode_surrogate_half); | |
| 1706 | 1689 | } |
| 1707 | 1690 | |
| 1708 | 1691 | /// Caller must free returned memory. |
| ... | ... | @@ -1711,8 +1694,7 @@ pub fn wtf16LeToWtf8Alloc(allocator: mem.Allocator, wtf16le: []const u16) mem.Al |
| 1711 | 1694 | var result = try std.ArrayList(u8).initCapacity(allocator, wtf16le.len); |
| 1712 | 1695 | errdefer result.deinit(); |
| 1713 | 1696 | |
| 1714 | try wtf16LeToWtf8ArrayList(&result, wtf16le); | |
| 1715 | ||
| 1697 | try utf16LeToUtf8ArrayListImpl(&result, wtf16le, .can_encode_surrogate_half); | |
| 1716 | 1698 | return result.toOwnedSlice(); |
| 1717 | 1699 | } |
| 1718 | 1700 | |
| ... | ... | @@ -1722,8 +1704,7 @@ pub fn wtf16LeToWtf8AllocZ(allocator: mem.Allocator, wtf16le: []const u16) mem.A |
| 1722 | 1704 | var result = try std.ArrayList(u8).initCapacity(allocator, wtf16le.len + 1); |
| 1723 | 1705 | errdefer result.deinit(); |
| 1724 | 1706 | |
| 1725 | try wtf16LeToWtf8ArrayList(&result, wtf16le); | |
| 1726 | ||
| 1707 | try utf16LeToUtf8ArrayListImpl(&result, wtf16le, .can_encode_surrogate_half); | |
| 1727 | 1708 | return result.toOwnedSliceSentinel(0); |
| 1728 | 1709 | } |
| 1729 | 1710 | |
| ... | ... | @@ -1731,8 +1712,9 @@ pub fn wtf16LeToWtf8(wtf8: []u8, wtf16le: []const u16) usize { |
| 1731 | 1712 | return utf16LeToUtf8Impl(wtf8, wtf16le, .can_encode_surrogate_half) catch |err| switch (err) {}; |
| 1732 | 1713 | } |
| 1733 | 1714 | |
| 1734 | pub fn wtf8ToWtf16LeArrayList(array_list: *std.ArrayList(u16), wtf8: []const u8) error{ InvalidWtf8, OutOfMemory }!void { | |
| 1735 | return utf8ToUtf16LeArrayListImpl(array_list, wtf8, .can_encode_surrogate_half); | |
| 1715 | pub fn wtf8ToWtf16LeArrayList(result: *std.ArrayList(u16), wtf8: []const u8) error{ InvalidWtf8, OutOfMemory }!void { | |
| 1716 | try result.ensureTotalCapacityPrecise(wtf8.len); | |
| 1717 | return utf8ToUtf16LeArrayListImpl(result, wtf8, .can_encode_surrogate_half); | |
| 1736 | 1718 | } |
| 1737 | 1719 | |
| 1738 | 1720 | pub fn wtf8ToWtf16LeAlloc(allocator: mem.Allocator, wtf8: []const u8) error{ InvalidWtf8, OutOfMemory }![]u16 { |
| ... | ... | @@ -1741,7 +1723,6 @@ pub fn wtf8ToWtf16LeAlloc(allocator: mem.Allocator, wtf8: []const u8) error{ Inv |
| 1741 | 1723 | errdefer result.deinit(); |
| 1742 | 1724 | |
| 1743 | 1725 | try utf8ToUtf16LeArrayListImpl(&result, wtf8, .can_encode_surrogate_half); |
| 1744 | ||
| 1745 | 1726 | return result.toOwnedSlice(); |
| 1746 | 1727 | } |
| 1747 | 1728 | |
| ... | ... | @@ -1751,7 +1732,6 @@ pub fn wtf8ToWtf16LeAllocZ(allocator: mem.Allocator, wtf8: []const u8) error{ In |
| 1751 | 1732 | errdefer result.deinit(); |
| 1752 | 1733 | |
| 1753 | 1734 | try utf8ToUtf16LeArrayListImpl(&result, wtf8, .can_encode_surrogate_half); |
| 1754 | ||
| 1755 | 1735 | return result.toOwnedSliceSentinel(0); |
| 1756 | 1736 | } |
| 1757 | 1737 | |
| ... | ... | @@ -1910,7 +1890,7 @@ pub const Wtf16LeIterator = struct { |
| 1910 | 1890 | |
| 1911 | 1891 | pub fn init(s: []const u16) Wtf16LeIterator { |
| 1912 | 1892 | return Wtf16LeIterator{ |
| 1913 | .bytes = std.mem.sliceAsBytes(s), | |
| 1893 | .bytes = mem.sliceAsBytes(s), | |
| 1914 | 1894 | .i = 0, |
| 1915 | 1895 | }; |
| 1916 | 1896 | } |
| ... | ... | @@ -1923,12 +1903,12 @@ pub const Wtf16LeIterator = struct { |
| 1923 | 1903 | assert(it.i <= it.bytes.len); |
| 1924 | 1904 | if (it.i == it.bytes.len) return null; |
| 1925 | 1905 | var code_units: [2]u16 = undefined; |
| 1926 | code_units[0] = std.mem.readInt(u16, it.bytes[it.i..][0..2], .little); | |
| 1906 | code_units[0] = mem.readInt(u16, it.bytes[it.i..][0..2], .little); | |
| 1927 | 1907 | it.i += 2; |
| 1928 | 1908 | surrogate_pair: { |
| 1929 | 1909 | if (utf16IsHighSurrogate(code_units[0])) { |
| 1930 | 1910 | if (it.i >= it.bytes.len) break :surrogate_pair; |
| 1931 | code_units[1] = std.mem.readInt(u16, it.bytes[it.i..][0..2], .little); | |
| 1911 | code_units[1] = mem.readInt(u16, it.bytes[it.i..][0..2], .little); | |
| 1932 | 1912 | const codepoint = utf16DecodeSurrogatePair(&code_units) catch break :surrogate_pair; |
| 1933 | 1913 | it.i += 2; |
| 1934 | 1914 | return codepoint; |
| ... | ... | @@ -2045,31 +2025,31 @@ fn testRoundtripWtf16(wtf16le: []const u16) !void { |
| 2045 | 2025 | |
| 2046 | 2026 | test "well-formed WTF-16 roundtrips" { |
| 2047 | 2027 | try testRoundtripWtf16(&[_]u16{ |
| 2048 | std.mem.nativeToLittle(u16, 0xD83D), // high surrogate | |
| 2049 | std.mem.nativeToLittle(u16, 0xDCA9), // low surrogate | |
| 2028 | mem.nativeToLittle(u16, 0xD83D), // high surrogate | |
| 2029 | mem.nativeToLittle(u16, 0xDCA9), // low surrogate | |
| 2050 | 2030 | }); |
| 2051 | 2031 | try testRoundtripWtf16(&[_]u16{ |
| 2052 | std.mem.nativeToLittle(u16, 0xD83D), // high surrogate | |
| 2053 | std.mem.nativeToLittle(u16, ' '), // not surrogate | |
| 2054 | std.mem.nativeToLittle(u16, 0xDCA9), // low surrogate | |
| 2032 | mem.nativeToLittle(u16, 0xD83D), // high surrogate | |
| 2033 | mem.nativeToLittle(u16, ' '), // not surrogate | |
| 2034 | mem.nativeToLittle(u16, 0xDCA9), // low surrogate | |
| 2055 | 2035 | }); |
| 2056 | 2036 | try testRoundtripWtf16(&[_]u16{ |
| 2057 | std.mem.nativeToLittle(u16, 0xD800), // high surrogate | |
| 2058 | std.mem.nativeToLittle(u16, 0xDBFF), // high surrogate | |
| 2037 | mem.nativeToLittle(u16, 0xD800), // high surrogate | |
| 2038 | mem.nativeToLittle(u16, 0xDBFF), // high surrogate | |
| 2059 | 2039 | }); |
| 2060 | 2040 | try testRoundtripWtf16(&[_]u16{ |
| 2061 | std.mem.nativeToLittle(u16, 0xD800), // high surrogate | |
| 2062 | std.mem.nativeToLittle(u16, 0xE000), // not surrogate | |
| 2041 | mem.nativeToLittle(u16, 0xD800), // high surrogate | |
| 2042 | mem.nativeToLittle(u16, 0xE000), // not surrogate | |
| 2063 | 2043 | }); |
| 2064 | 2044 | try testRoundtripWtf16(&[_]u16{ |
| 2065 | std.mem.nativeToLittle(u16, 0xD7FF), // not surrogate | |
| 2066 | std.mem.nativeToLittle(u16, 0xDC00), // low surrogate | |
| 2045 | mem.nativeToLittle(u16, 0xD7FF), // not surrogate | |
| 2046 | mem.nativeToLittle(u16, 0xDC00), // low surrogate | |
| 2067 | 2047 | }); |
| 2068 | 2048 | try testRoundtripWtf16(&[_]u16{ |
| 2069 | std.mem.nativeToLittle(u16, 0x61), // not surrogate | |
| 2070 | std.mem.nativeToLittle(u16, 0xDC00), // low surrogate | |
| 2049 | mem.nativeToLittle(u16, 0x61), // not surrogate | |
| 2050 | mem.nativeToLittle(u16, 0xDC00), // low surrogate | |
| 2071 | 2051 | }); |
| 2072 | 2052 | try testRoundtripWtf16(&[_]u16{ |
| 2073 | std.mem.nativeToLittle(u16, 0xDC00), // low surrogate | |
| 2053 | mem.nativeToLittle(u16, 0xDC00), // low surrogate | |
| 2074 | 2054 | }); |
| 2075 | 2055 | } |
src/InternPool.zig+6-2| ... | ... | @@ -7404,10 +7404,14 @@ pub fn isIntegerType(ip: *const InternPool, ty: Index) bool { |
| 7404 | 7404 | .c_ulong_type, |
| 7405 | 7405 | .c_longlong_type, |
| 7406 | 7406 | .c_ulonglong_type, |
| 7407 | .c_longdouble_type, | |
| 7408 | 7407 | .comptime_int_type, |
| 7409 | 7408 | => true, |
| 7410 | else => ip.indexToKey(ty) == .int_type, | |
| 7409 | else => switch (ip.items.items(.tag)[@intFromEnum(ty)]) { | |
| 7410 | .type_int_signed, | |
| 7411 | .type_int_unsigned, | |
| 7412 | => true, | |
| 7413 | else => false, | |
| 7414 | }, | |
| 7411 | 7415 | }; |
| 7412 | 7416 | } |
| 7413 | 7417 |
src/Sema.zig+31-2| ... | ... | @@ -23328,7 +23328,8 @@ fn checkVectorElemType( |
| 23328 | 23328 | const mod = sema.mod; |
| 23329 | 23329 | switch (ty.zigTypeTag(mod)) { |
| 23330 | 23330 | .Int, .Float, .Bool => return, |
| 23331 | else => if (ty.isPtrAtRuntime(mod)) return, | |
| 23331 | .Optional, .Pointer => if (ty.isPtrAtRuntime(mod)) return, | |
| 23332 | else => {}, | |
| 23332 | 23333 | } |
| 23333 | 23334 | return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(mod)}); |
| 23334 | 23335 | } |
| ... | ... | @@ -28455,7 +28456,7 @@ const CoerceOpts = struct { |
| 28455 | 28456 | report_err: bool = true, |
| 28456 | 28457 | /// Ignored if `report_err == false`. |
| 28457 | 28458 | is_ret: bool = false, |
| 28458 | /// Should coercion to comptime_int ermit an error message. | |
| 28459 | /// Should coercion to comptime_int emit an error message. | |
| 28459 | 28460 | no_cast_to_comptime_int: bool = false, |
| 28460 | 28461 | |
| 28461 | 28462 | param_src: struct { |
| ... | ... | @@ -31858,6 +31859,34 @@ fn coerceArrayLike( |
| 31858 | 31859 | } |
| 31859 | 31860 | |
| 31860 | 31861 | const dest_elem_ty = dest_ty.childType(mod); |
| 31862 | if (dest_ty.isVector(mod) and inst_ty.isVector(mod) and (try sema.resolveValue(inst)) == null) { | |
| 31863 | const inst_elem_ty = inst_ty.childType(mod); | |
| 31864 | switch (dest_elem_ty.zigTypeTag(mod)) { | |
| 31865 | .Int => if (inst_elem_ty.isInt(mod)) { | |
| 31866 | // integer widening | |
| 31867 | const dst_info = dest_elem_ty.intInfo(mod); | |
| 31868 | const src_info = inst_elem_ty.intInfo(mod); | |
| 31869 | if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or | |
| 31870 | // small enough unsigned ints can get casted to large enough signed ints | |
| 31871 | (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) | |
| 31872 | { | |
| 31873 | try sema.requireRuntimeBlock(block, inst_src, null); | |
| 31874 | return block.addTyOp(.intcast, dest_ty, inst); | |
| 31875 | } | |
| 31876 | }, | |
| 31877 | .Float => if (inst_elem_ty.isRuntimeFloat()) { | |
| 31878 | // float widening | |
| 31879 | const src_bits = inst_elem_ty.floatBits(target); | |
| 31880 | const dst_bits = dest_elem_ty.floatBits(target); | |
| 31881 | if (dst_bits >= src_bits) { | |
| 31882 | try sema.requireRuntimeBlock(block, inst_src, null); | |
| 31883 | return block.addTyOp(.fpext, dest_ty, inst); | |
| 31884 | } | |
| 31885 | }, | |
| 31886 | else => {}, | |
| 31887 | } | |
| 31888 | } | |
| 31889 | ||
| 31861 | 31890 | const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); |
| 31862 | 31891 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); |
| 31863 | 31892 | var runtime_src: ?LazySrcLoc = null; |
src/arch/x86_64/CodeGen.zig+268-62| ... | ... | @@ -2853,11 +2853,14 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 2853 | 2853 | } |
| 2854 | 2854 | |
| 2855 | 2855 | fn airFpext(self: *Self, inst: Air.Inst.Index) !void { |
| 2856 | const mod = self.bin_file.comp.module.?; | |
| 2856 | 2857 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 2857 | 2858 | const dst_ty = self.typeOfIndex(inst); |
| 2858 | const dst_bits = dst_ty.floatBits(self.target.*); | |
| 2859 | const dst_scalar_ty = dst_ty.scalarType(mod); | |
| 2860 | const dst_bits = dst_scalar_ty.floatBits(self.target.*); | |
| 2859 | 2861 | const src_ty = self.typeOf(ty_op.operand); |
| 2860 | const src_bits = src_ty.floatBits(self.target.*); | |
| 2862 | const src_scalar_ty = src_ty.scalarType(mod); | |
| 2863 | const src_bits = src_scalar_ty.floatBits(self.target.*); | |
| 2861 | 2864 | |
| 2862 | 2865 | const result = result: { |
| 2863 | 2866 | if (switch (src_bits) { |
| ... | ... | @@ -2881,94 +2884,290 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void { |
| 2881 | 2884 | }, |
| 2882 | 2885 | else => unreachable, |
| 2883 | 2886 | }) { |
| 2887 | if (dst_ty.isVector(mod)) break :result null; | |
| 2884 | 2888 | var callee_buf: ["__extend?f?f2".len]u8 = undefined; |
| 2885 | 2889 | break :result try self.genCall(.{ .lib = .{ |
| 2886 | .return_type = self.floatCompilerRtAbiType(dst_ty, src_ty).toIntern(), | |
| 2887 | .param_types = &.{self.floatCompilerRtAbiType(src_ty, dst_ty).toIntern()}, | |
| 2890 | .return_type = self.floatCompilerRtAbiType(dst_scalar_ty, src_scalar_ty).toIntern(), | |
| 2891 | .param_types = &.{self.floatCompilerRtAbiType(src_scalar_ty, dst_scalar_ty).toIntern()}, | |
| 2888 | 2892 | .callee = std.fmt.bufPrint(&callee_buf, "__extend{c}f{c}f2", .{ |
| 2889 | 2893 | floatCompilerRtAbiName(src_bits), |
| 2890 | 2894 | floatCompilerRtAbiName(dst_bits), |
| 2891 | 2895 | }) catch unreachable, |
| 2892 | } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }}); | |
| 2896 | } }, &.{src_scalar_ty}, &.{.{ .air_ref = ty_op.operand }}); | |
| 2893 | 2897 | } |
| 2894 | 2898 | |
| 2899 | const src_abi_size: u32 = @intCast(src_ty.abiSize(mod)); | |
| 2895 | 2900 | const src_mcv = try self.resolveInst(ty_op.operand); |
| 2896 | 2901 | const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) |
| 2897 | 2902 | src_mcv |
| 2898 | 2903 | else |
| 2899 | 2904 | try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); |
| 2900 | const dst_reg = dst_mcv.getReg().?.to128(); | |
| 2905 | const dst_reg = dst_mcv.getReg().?; | |
| 2906 | const dst_alias = registerAlias(dst_reg, @intCast(@max(dst_ty.abiSize(mod), 16))); | |
| 2901 | 2907 | const dst_lock = self.register_manager.lockReg(dst_reg); |
| 2902 | 2908 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 2903 | 2909 | |
| 2910 | const vec_len = if (dst_ty.isVector(mod)) dst_ty.vectorLen(mod) else 1; | |
| 2904 | 2911 | if (src_bits == 16) { |
| 2905 | 2912 | assert(self.hasFeature(.f16c)); |
| 2906 | 2913 | const mat_src_reg = if (src_mcv.isRegister()) |
| 2907 | 2914 | src_mcv.getReg().? |
| 2908 | 2915 | else |
| 2909 | 2916 | try self.copyToTmpRegister(src_ty, src_mcv); |
| 2910 | try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, mat_src_reg.to128()); | |
| 2917 | try self.asmRegisterRegister( | |
| 2918 | .{ .v_ps, .cvtph2 }, | |
| 2919 | dst_alias, | |
| 2920 | registerAlias(mat_src_reg, src_abi_size), | |
| 2921 | ); | |
| 2911 | 2922 | switch (dst_bits) { |
| 2912 | 2923 | 32 => {}, |
| 2913 | 2924 | 64 => try self.asmRegisterRegisterRegister( |
| 2914 | 2925 | .{ .v_sd, .cvtss2 }, |
| 2915 | dst_reg, | |
| 2916 | dst_reg, | |
| 2917 | dst_reg, | |
| 2926 | dst_alias, | |
| 2927 | dst_alias, | |
| 2928 | dst_alias, | |
| 2918 | 2929 | ), |
| 2919 | 2930 | else => unreachable, |
| 2920 | 2931 | } |
| 2921 | 2932 | } else { |
| 2922 | 2933 | assert(src_bits == 32 and dst_bits == 64); |
| 2923 | if (self.hasFeature(.avx)) if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory( | |
| 2924 | .{ .v_sd, .cvtss2 }, | |
| 2925 | dst_reg, | |
| 2926 | dst_reg, | |
| 2927 | try src_mcv.mem(self, .dword), | |
| 2928 | ) else try self.asmRegisterRegisterRegister( | |
| 2929 | .{ .v_sd, .cvtss2 }, | |
| 2930 | dst_reg, | |
| 2931 | dst_reg, | |
| 2932 | (if (src_mcv.isRegister()) | |
| 2933 | src_mcv.getReg().? | |
| 2934 | else | |
| 2935 | try self.copyToTmpRegister(src_ty, src_mcv)).to128(), | |
| 2936 | ) else if (src_mcv.isMemory()) try self.asmRegisterMemory( | |
| 2937 | .{ ._sd, .cvtss2 }, | |
| 2938 | dst_reg, | |
| 2939 | try src_mcv.mem(self, .dword), | |
| 2934 | if (self.hasFeature(.avx)) switch (vec_len) { | |
| 2935 | 1 => if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory( | |
| 2936 | .{ .v_sd, .cvtss2 }, | |
| 2937 | dst_alias, | |
| 2938 | dst_alias, | |
| 2939 | try src_mcv.mem(self, self.memSize(src_ty)), | |
| 2940 | ) else try self.asmRegisterRegisterRegister( | |
| 2941 | .{ .v_sd, .cvtss2 }, | |
| 2942 | dst_alias, | |
| 2943 | dst_alias, | |
| 2944 | registerAlias(if (src_mcv.isRegister()) | |
| 2945 | src_mcv.getReg().? | |
| 2946 | else | |
| 2947 | try self.copyToTmpRegister(src_ty, src_mcv), src_abi_size), | |
| 2948 | ), | |
| 2949 | 2...4 => if (src_mcv.isMemory()) try self.asmRegisterMemory( | |
| 2950 | .{ .v_pd, .cvtps2 }, | |
| 2951 | dst_alias, | |
| 2952 | try src_mcv.mem(self, self.memSize(src_ty)), | |
| 2953 | ) else try self.asmRegisterRegister( | |
| 2954 | .{ .v_pd, .cvtps2 }, | |
| 2955 | dst_alias, | |
| 2956 | registerAlias(if (src_mcv.isRegister()) | |
| 2957 | src_mcv.getReg().? | |
| 2958 | else | |
| 2959 | try self.copyToTmpRegister(src_ty, src_mcv), src_abi_size), | |
| 2960 | ), | |
| 2961 | else => break :result null, | |
| 2962 | } else if (src_mcv.isMemory()) try self.asmRegisterMemory( | |
| 2963 | switch (vec_len) { | |
| 2964 | 1 => .{ ._sd, .cvtss2 }, | |
| 2965 | 2 => .{ ._pd, .cvtps2 }, | |
| 2966 | else => break :result null, | |
| 2967 | }, | |
| 2968 | dst_alias, | |
| 2969 | try src_mcv.mem(self, self.memSize(src_ty)), | |
| 2940 | 2970 | ) else try self.asmRegisterRegister( |
| 2941 | .{ ._sd, .cvtss2 }, | |
| 2942 | dst_reg, | |
| 2943 | (if (src_mcv.isRegister()) | |
| 2971 | switch (vec_len) { | |
| 2972 | 1 => .{ ._sd, .cvtss2 }, | |
| 2973 | 2 => .{ ._pd, .cvtps2 }, | |
| 2974 | else => break :result null, | |
| 2975 | }, | |
| 2976 | dst_alias, | |
| 2977 | registerAlias(if (src_mcv.isRegister()) | |
| 2944 | 2978 | src_mcv.getReg().? |
| 2945 | 2979 | else |
| 2946 | try self.copyToTmpRegister(src_ty, src_mcv)).to128(), | |
| 2980 | try self.copyToTmpRegister(src_ty, src_mcv), src_abi_size), | |
| 2947 | 2981 | ); |
| 2948 | 2982 | } |
| 2949 | 2983 | break :result dst_mcv; |
| 2950 | }; | |
| 2984 | } orelse return self.fail("TODO implement airFpext from {} to {}", .{ | |
| 2985 | src_ty.fmt(mod), dst_ty.fmt(mod), | |
| 2986 | }); | |
| 2951 | 2987 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2952 | 2988 | } |
| 2953 | 2989 | |
| 2954 | 2990 | fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 2955 | 2991 | const mod = self.bin_file.comp.module.?; |
| 2956 | 2992 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 2957 | const result: MCValue = result: { | |
| 2958 | const src_ty = self.typeOf(ty_op.operand); | |
| 2959 | const src_int_info = src_ty.intInfo(mod); | |
| 2993 | const src_ty = self.typeOf(ty_op.operand); | |
| 2994 | const dst_ty = self.typeOfIndex(inst); | |
| 2960 | 2995 | |
| 2961 | const dst_ty = self.typeOfIndex(inst); | |
| 2962 | const dst_int_info = dst_ty.intInfo(mod); | |
| 2963 | const abi_size: u32 = @intCast(dst_ty.abiSize(mod)); | |
| 2996 | const result = @as(?MCValue, result: { | |
| 2997 | const dst_abi_size: u32 = @intCast(dst_ty.abiSize(mod)); | |
| 2964 | 2998 | |
| 2965 | const min_ty = if (dst_int_info.bits < src_int_info.bits) dst_ty else src_ty; | |
| 2999 | const src_int_info = src_ty.intInfo(mod); | |
| 3000 | const dst_int_info = dst_ty.intInfo(mod); | |
| 2966 | 3001 | const extend = switch (src_int_info.signedness) { |
| 2967 | 3002 | .signed => dst_int_info, |
| 2968 | 3003 | .unsigned => src_int_info, |
| 2969 | 3004 | }.signedness; |
| 2970 | 3005 | |
| 2971 | 3006 | const src_mcv = try self.resolveInst(ty_op.operand); |
| 3007 | if (dst_ty.isVector(mod)) { | |
| 3008 | const src_abi_size: u32 = @intCast(src_ty.abiSize(mod)); | |
| 3009 | const max_abi_size = @max(dst_abi_size, src_abi_size); | |
| 3010 | if (max_abi_size > @as(u32, if (self.hasFeature(.avx2)) 32 else 16)) break :result null; | |
| 3011 | const has_avx = self.hasFeature(.avx); | |
| 3012 | ||
| 3013 | const dst_elem_abi_size = dst_ty.childType(mod).abiSize(mod); | |
| 3014 | const src_elem_abi_size = src_ty.childType(mod).abiSize(mod); | |
| 3015 | switch (math.order(dst_elem_abi_size, src_elem_abi_size)) { | |
| 3016 | .lt => { | |
| 3017 | const mir_tag: Mir.Inst.FixedTag = switch (dst_elem_abi_size) { | |
| 3018 | else => break :result null, | |
| 3019 | 1 => switch (src_elem_abi_size) { | |
| 3020 | else => break :result null, | |
| 3021 | 2 => switch (dst_int_info.signedness) { | |
| 3022 | .signed => if (has_avx) .{ .vp_b, .ackssw } else .{ .p_b, .ackssw }, | |
| 3023 | .unsigned => if (has_avx) .{ .vp_b, .ackusw } else .{ .p_b, .ackusw }, | |
| 3024 | }, | |
| 3025 | }, | |
| 3026 | 2 => switch (src_elem_abi_size) { | |
| 3027 | else => break :result null, | |
| 3028 | 4 => switch (dst_int_info.signedness) { | |
| 3029 | .signed => if (has_avx) .{ .vp_w, .ackssd } else .{ .p_w, .ackssd }, | |
| 3030 | .unsigned => if (has_avx) | |
| 3031 | .{ .vp_w, .ackusd } | |
| 3032 | else if (self.hasFeature(.sse4_1)) | |
| 3033 | .{ .p_w, .ackusd } | |
| 3034 | else | |
| 3035 | break :result null, | |
| 3036 | }, | |
| 3037 | }, | |
| 3038 | }; | |
| 3039 | ||
| 3040 | const dst_mcv: MCValue = if (src_mcv.isRegister() and | |
| 3041 | self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) | |
| 3042 | src_mcv | |
| 3043 | else if (has_avx and src_mcv.isRegister()) | |
| 3044 | .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } | |
| 3045 | else | |
| 3046 | try self.copyToRegisterWithInstTracking(inst, src_ty, src_mcv); | |
| 3047 | const dst_reg = dst_mcv.getReg().?; | |
| 3048 | const dst_alias = registerAlias(dst_reg, dst_abi_size); | |
| 3049 | ||
| 3050 | if (has_avx) try self.asmRegisterRegisterRegister( | |
| 3051 | mir_tag, | |
| 3052 | dst_alias, | |
| 3053 | registerAlias(if (src_mcv.isRegister()) | |
| 3054 | src_mcv.getReg().? | |
| 3055 | else | |
| 3056 | dst_reg, src_abi_size), | |
| 3057 | dst_alias, | |
| 3058 | ) else try self.asmRegisterRegister( | |
| 3059 | mir_tag, | |
| 3060 | dst_alias, | |
| 3061 | dst_alias, | |
| 3062 | ); | |
| 3063 | break :result dst_mcv; | |
| 3064 | }, | |
| 3065 | .eq => if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) | |
| 3066 | break :result src_mcv | |
| 3067 | else { | |
| 3068 | const dst_mcv = try self.allocRegOrMem(inst, true); | |
| 3069 | try self.genCopy(dst_ty, dst_mcv, src_mcv, .{}); | |
| 3070 | break :result dst_mcv; | |
| 3071 | }, | |
| 3072 | .gt => if (self.hasFeature(.sse4_1)) { | |
| 3073 | const mir_tag: Mir.Inst.FixedTag = .{ switch (dst_elem_abi_size) { | |
| 3074 | else => break :result null, | |
| 3075 | 2 => if (has_avx) .vp_w else .p_w, | |
| 3076 | 4 => if (has_avx) .vp_d else .p_d, | |
| 3077 | 8 => if (has_avx) .vp_q else .p_q, | |
| 3078 | }, switch (src_elem_abi_size) { | |
| 3079 | else => break :result null, | |
| 3080 | 1 => switch (extend) { | |
| 3081 | .signed => .movsxb, | |
| 3082 | .unsigned => .movzxb, | |
| 3083 | }, | |
| 3084 | 2 => switch (extend) { | |
| 3085 | .signed => .movsxw, | |
| 3086 | .unsigned => .movzxw, | |
| 3087 | }, | |
| 3088 | 4 => switch (extend) { | |
| 3089 | .signed => .movsxd, | |
| 3090 | .unsigned => .movzxd, | |
| 3091 | }, | |
| 3092 | } }; | |
| 3093 | ||
| 3094 | const dst_mcv: MCValue = if (src_mcv.isRegister() and | |
| 3095 | self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) | |
| 3096 | src_mcv | |
| 3097 | else | |
| 3098 | .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) }; | |
| 3099 | const dst_reg = dst_mcv.getReg().?; | |
| 3100 | const dst_alias = registerAlias(dst_reg, dst_abi_size); | |
| 3101 | ||
| 3102 | if (src_mcv.isMemory()) try self.asmRegisterMemory( | |
| 3103 | mir_tag, | |
| 3104 | dst_alias, | |
| 3105 | try src_mcv.mem(self, self.memSize(src_ty)), | |
| 3106 | ) else try self.asmRegisterRegister( | |
| 3107 | mir_tag, | |
| 3108 | dst_alias, | |
| 3109 | registerAlias(if (src_mcv.isRegister()) | |
| 3110 | src_mcv.getReg().? | |
| 3111 | else | |
| 3112 | try self.copyToTmpRegister(src_ty, src_mcv), src_abi_size), | |
| 3113 | ); | |
| 3114 | break :result dst_mcv; | |
| 3115 | } else { | |
| 3116 | const mir_tag: Mir.Inst.FixedTag = switch (dst_elem_abi_size) { | |
| 3117 | else => break :result null, | |
| 3118 | 2 => switch (src_elem_abi_size) { | |
| 3119 | else => break :result null, | |
| 3120 | 1 => .{ .p_, .unpcklbw }, | |
| 3121 | }, | |
| 3122 | 4 => switch (src_elem_abi_size) { | |
| 3123 | else => break :result null, | |
| 3124 | 2 => .{ .p_, .unpcklwd }, | |
| 3125 | }, | |
| 3126 | 8 => switch (src_elem_abi_size) { | |
| 3127 | else => break :result null, | |
| 3128 | 2 => .{ .p_, .unpckldq }, | |
| 3129 | }, | |
| 3130 | }; | |
| 3131 | ||
| 3132 | const dst_mcv: MCValue = if (src_mcv.isRegister() and | |
| 3133 | self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) | |
| 3134 | src_mcv | |
| 3135 | else | |
| 3136 | try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); | |
| 3137 | const dst_reg = dst_mcv.getReg().?; | |
| 3138 | ||
| 3139 | const ext_reg = try self.register_manager.allocReg(null, abi.RegisterClass.sse); | |
| 3140 | const ext_alias = registerAlias(ext_reg, src_abi_size); | |
| 3141 | const ext_lock = self.register_manager.lockRegAssumeUnused(ext_reg); | |
| 3142 | defer self.register_manager.unlockReg(ext_lock); | |
| 3143 | ||
| 3144 | try self.asmRegisterRegister(.{ .p_, .xor }, ext_alias, ext_alias); | |
| 3145 | switch (extend) { | |
| 3146 | .signed => try self.asmRegisterRegister( | |
| 3147 | .{ switch (src_elem_abi_size) { | |
| 3148 | else => unreachable, | |
| 3149 | 1 => .p_b, | |
| 3150 | 2 => .p_w, | |
| 3151 | 4 => .p_d, | |
| 3152 | }, .cmpgt }, | |
| 3153 | ext_alias, | |
| 3154 | registerAlias(dst_reg, src_abi_size), | |
| 3155 | ), | |
| 3156 | .unsigned => {}, | |
| 3157 | } | |
| 3158 | try self.asmRegisterRegister( | |
| 3159 | mir_tag, | |
| 3160 | registerAlias(dst_reg, dst_abi_size), | |
| 3161 | registerAlias(ext_reg, dst_abi_size), | |
| 3162 | ); | |
| 3163 | break :result dst_mcv; | |
| 3164 | }, | |
| 3165 | } | |
| 3166 | @compileError("unreachable"); | |
| 3167 | } | |
| 3168 | ||
| 3169 | const min_ty = if (dst_int_info.bits < src_int_info.bits) dst_ty else src_ty; | |
| 3170 | ||
| 2972 | 3171 | const src_storage_bits: u16 = switch (src_mcv) { |
| 2973 | 3172 | .register, .register_offset => 64, |
| 2974 | 3173 | .register_pair => 128, |
| ... | ... | @@ -2986,13 +3185,13 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 2986 | 3185 | }; |
| 2987 | 3186 | |
| 2988 | 3187 | if (dst_int_info.bits <= src_int_info.bits) break :result if (dst_mcv.isRegister()) |
| 2989 | .{ .register = registerAlias(dst_mcv.getReg().?, abi_size) } | |
| 3188 | .{ .register = registerAlias(dst_mcv.getReg().?, dst_abi_size) } | |
| 2990 | 3189 | else |
| 2991 | 3190 | dst_mcv; |
| 2992 | 3191 | |
| 2993 | 3192 | if (dst_mcv.isRegister()) { |
| 2994 | 3193 | try self.truncateRegister(src_ty, dst_mcv.getReg().?); |
| 2995 | break :result .{ .register = registerAlias(dst_mcv.getReg().?, abi_size) }; | |
| 3194 | break :result .{ .register = registerAlias(dst_mcv.getReg().?, dst_abi_size) }; | |
| 2996 | 3195 | } |
| 2997 | 3196 | |
| 2998 | 3197 | const src_limbs_len = math.divCeil(u16, src_int_info.bits, 64) catch unreachable; |
| ... | ... | @@ -3040,7 +3239,9 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 3040 | 3239 | ); |
| 3041 | 3240 | |
| 3042 | 3241 | break :result dst_mcv; |
| 3043 | }; | |
| 3242 | }) orelse return self.fail("TODO implement airIntCast from {} to {}", .{ | |
| 3243 | src_ty.fmt(mod), dst_ty.fmt(mod), | |
| 3244 | }); | |
| 3044 | 3245 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3045 | 3246 | } |
| 3046 | 3247 | |
| ... | ... | @@ -3063,7 +3264,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 3063 | 3264 | src_mcv |
| 3064 | 3265 | else if (dst_abi_size <= 8) |
| 3065 | 3266 | try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv) |
| 3066 | else if (dst_abi_size <= 16) dst: { | |
| 3267 | else if (dst_abi_size <= 16 and !dst_ty.isVector(mod)) dst: { | |
| 3067 | 3268 | const dst_regs = |
| 3068 | 3269 | try self.register_manager.allocRegs(2, .{ inst, inst }, abi.RegisterClass.gp); |
| 3069 | 3270 | const dst_mcv: MCValue = .{ .register_pair = dst_regs }; |
| ... | ... | @@ -3080,19 +3281,22 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 3080 | 3281 | |
| 3081 | 3282 | if (dst_ty.zigTypeTag(mod) == .Vector) { |
| 3082 | 3283 | assert(src_ty.zigTypeTag(mod) == .Vector and dst_ty.vectorLen(mod) == src_ty.vectorLen(mod)); |
| 3083 | const dst_info = dst_ty.childType(mod).intInfo(mod); | |
| 3084 | const src_info = src_ty.childType(mod).intInfo(mod); | |
| 3085 | const mir_tag = @as(?Mir.Inst.FixedTag, switch (dst_info.bits) { | |
| 3086 | 8 => switch (src_info.bits) { | |
| 3087 | 16 => switch (dst_ty.vectorLen(mod)) { | |
| 3284 | const dst_elem_ty = dst_ty.childType(mod); | |
| 3285 | const dst_elem_abi_size: u32 = @intCast(dst_elem_ty.abiSize(mod)); | |
| 3286 | const src_elem_ty = src_ty.childType(mod); | |
| 3287 | const src_elem_abi_size: u32 = @intCast(src_elem_ty.abiSize(mod)); | |
| 3288 | ||
| 3289 | const mir_tag = @as(?Mir.Inst.FixedTag, switch (dst_elem_abi_size) { | |
| 3290 | 1 => switch (src_elem_abi_size) { | |
| 3291 | 2 => switch (dst_ty.vectorLen(mod)) { | |
| 3088 | 3292 | 1...8 => if (self.hasFeature(.avx)) .{ .vp_b, .ackusw } else .{ .p_b, .ackusw }, |
| 3089 | 3293 | 9...16 => if (self.hasFeature(.avx2)) .{ .vp_b, .ackusw } else null, |
| 3090 | 3294 | else => null, |
| 3091 | 3295 | }, |
| 3092 | 3296 | else => null, |
| 3093 | 3297 | }, |
| 3094 | 16 => switch (src_info.bits) { | |
| 3095 | 32 => switch (dst_ty.vectorLen(mod)) { | |
| 3298 | 2 => switch (src_elem_abi_size) { | |
| 3299 | 4 => switch (dst_ty.vectorLen(mod)) { | |
| 3096 | 3300 | 1...4 => if (self.hasFeature(.avx)) |
| 3097 | 3301 | .{ .vp_w, .ackusd } |
| 3098 | 3302 | else if (self.hasFeature(.sse4_1)) |
| ... | ... | @@ -3107,12 +3311,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 3107 | 3311 | else => null, |
| 3108 | 3312 | }) orelse return self.fail("TODO implement airTrunc for {}", .{dst_ty.fmt(mod)}); |
| 3109 | 3313 | |
| 3110 | const elem_ty = src_ty.childType(mod); | |
| 3111 | const mask_val = try mod.intValue(elem_ty, @as(u64, math.maxInt(u64)) >> @intCast(64 - dst_info.bits)); | |
| 3314 | const dst_info = dst_elem_ty.intInfo(mod); | |
| 3315 | const src_info = src_elem_ty.intInfo(mod); | |
| 3316 | ||
| 3317 | const mask_val = try mod.intValue(src_elem_ty, @as(u64, math.maxInt(u64)) >> @intCast(64 - dst_info.bits)); | |
| 3112 | 3318 | |
| 3113 | 3319 | const splat_ty = try mod.vectorType(.{ |
| 3114 | 3320 | .len = @intCast(@divExact(@as(u64, if (src_abi_size > 16) 256 else 128), src_info.bits)), |
| 3115 | .child = elem_ty.ip_index, | |
| 3321 | .child = src_elem_ty.ip_index, | |
| 3116 | 3322 | }); |
| 3117 | 3323 | const splat_abi_size: u32 = @intCast(splat_ty.abiSize(mod)); |
| 3118 | 3324 | |
| ... | ... | @@ -4086,7 +4292,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 4086 | 4292 | if (dst_info.bits > 128 and dst_info.signedness == .unsigned) { |
| 4087 | 4293 | const slow_inc = self.hasFeature(.slow_incdec); |
| 4088 | 4294 | const abi_size: u32 = @intCast(dst_ty.abiSize(mod)); |
| 4089 | const limb_len = std.math.divCeil(u32, abi_size, 8) catch unreachable; | |
| 4295 | const limb_len = math.divCeil(u32, abi_size, 8) catch unreachable; | |
| 4090 | 4296 | |
| 4091 | 4297 | try self.spillRegisters(&.{ .rax, .rcx, .rdx }); |
| 4092 | 4298 | const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx }); |
| ... | ... | @@ -6935,7 +7141,7 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void { |
| 6935 | 7141 | }, |
| 6936 | 7142 | else => { |
| 6937 | 7143 | const abi_size: u31 = @intCast(ty.abiSize(mod)); |
| 6938 | const limb_len = std.math.divCeil(u31, abi_size, 8) catch unreachable; | |
| 7144 | const limb_len = math.divCeil(u31, abi_size, 8) catch unreachable; | |
| 6939 | 7145 | |
| 6940 | 7146 | const tmp_regs = |
| 6941 | 7147 | try self.register_manager.allocRegs(3, .{null} ** 3, abi.RegisterClass.gp); |
| ... | ... | @@ -8222,7 +8428,7 @@ fn genShiftBinOpMir( |
| 8222 | 8428 | try self.asmRegisterImmediate( |
| 8223 | 8429 | .{ ._, .@"and" }, |
| 8224 | 8430 | .cl, |
| 8225 | Immediate.u(std.math.maxInt(u6)), | |
| 8431 | Immediate.u(math.maxInt(u6)), | |
| 8226 | 8432 | ); |
| 8227 | 8433 | try self.asmRegisterImmediate( |
| 8228 | 8434 | .{ ._r, .sh }, |
| ... | ... | @@ -8259,7 +8465,7 @@ fn genShiftBinOpMir( |
| 8259 | 8465 | try self.asmRegisterImmediate( |
| 8260 | 8466 | .{ ._, .@"and" }, |
| 8261 | 8467 | .cl, |
| 8262 | Immediate.u(std.math.maxInt(u6)), | |
| 8468 | Immediate.u(math.maxInt(u6)), | |
| 8263 | 8469 | ); |
| 8264 | 8470 | try self.asmRegisterImmediate( |
| 8265 | 8471 | .{ ._r, .sh }, |
| ... | ... | @@ -8324,7 +8530,7 @@ fn genShiftBinOpMir( |
| 8324 | 8530 | }, .sh }, |
| 8325 | 8531 | temp_regs[2].to64(), |
| 8326 | 8532 | temp_regs[3].to64(), |
| 8327 | Immediate.u(shift_imm & std.math.maxInt(u6)), | |
| 8533 | Immediate.u(shift_imm & math.maxInt(u6)), | |
| 8328 | 8534 | ), |
| 8329 | 8535 | else => try self.asmRegisterRegisterRegister(.{ switch (tag[0]) { |
| 8330 | 8536 | ._l => ._ld, |
| ... | ... | @@ -8379,7 +8585,7 @@ fn genShiftBinOpMir( |
| 8379 | 8585 | .immediate => |shift_imm| try self.asmRegisterImmediate( |
| 8380 | 8586 | tag, |
| 8381 | 8587 | temp_regs[2].to64(), |
| 8382 | Immediate.u(shift_imm & std.math.maxInt(u6)), | |
| 8588 | Immediate.u(shift_imm & math.maxInt(u6)), | |
| 8383 | 8589 | ), |
| 8384 | 8590 | else => try self.asmRegisterRegister(tag, temp_regs[2].to64(), .cl), |
| 8385 | 8591 | } |
| ... | ... | @@ -8974,7 +9180,7 @@ fn genMulDivBinOp( |
| 8974 | 9180 | switch (tag) { |
| 8975 | 9181 | .mul, .mul_wrap => { |
| 8976 | 9182 | const slow_inc = self.hasFeature(.slow_incdec); |
| 8977 | const limb_len = std.math.divCeil(u32, src_abi_size, 8) catch unreachable; | |
| 9183 | const limb_len = math.divCeil(u32, src_abi_size, 8) catch unreachable; | |
| 8978 | 9184 | |
| 8979 | 9185 | try self.spillRegisters(&.{ .rax, .rcx, .rdx }); |
| 8980 | 9186 | const reg_locks = self.register_manager.lockRegs(3, .{ .rax, .rcx, .rdx }); |
| ... | ... | @@ -14535,7 +14741,7 @@ fn genSetReg( |
| 14535 | 14741 | ty, |
| 14536 | 14742 | dst_reg.class(), |
| 14537 | 14743 | self.getFrameAddrAlignment(frame_addr).compare(.gte, Alignment.fromLog2Units( |
| 14538 | std.math.log2_int_ceil(u10, @divExact(dst_reg.bitSize(), 8)), | |
| 14744 | math.log2_int_ceil(u10, @divExact(dst_reg.bitSize(), 8)), | |
| 14539 | 14745 | )), |
| 14540 | 14746 | ), |
| 14541 | 14747 | .lea_frame => .{ .move = .{ ._, .lea } }, |
| ... | ... | @@ -16833,6 +17039,7 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 16833 | 17039 | @intCast(mask_elem_val.toSignedInt(mod)); |
| 16834 | 17040 | } |
| 16835 | 17041 | |
| 17042 | const has_avx = self.hasFeature(.avx); | |
| 16836 | 17043 | const result = @as(?MCValue, result: { |
| 16837 | 17044 | for (mask_elems) |mask_elem| { |
| 16838 | 17045 | if (mask_elem) |_| break; |
| ... | ... | @@ -16858,7 +17065,6 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 16858 | 17065 | break :result dst_mcv; |
| 16859 | 17066 | } |
| 16860 | 17067 | |
| 16861 | const has_avx = self.hasFeature(.avx); | |
| 16862 | 17068 | shufpd: { |
| 16863 | 17069 | if (elem_abi_size != 8) break :shufpd; |
| 16864 | 17070 | if (max_abi_size > @as(u32, if (has_avx) 32 else 16)) break :shufpd; |
src/arch/x86_64/Encoding.zig+4| ... | ... | @@ -335,6 +335,8 @@ pub const Mnemonic = enum { |
| 335 | 335 | pextrb, pextrd, pextrq, |
| 336 | 336 | pinsrb, pinsrd, pinsrq, |
| 337 | 337 | pmaxsb, pmaxsd, pmaxud, pmaxuw, pminsb, pminsd, pminud, pminuw, |
| 338 | pmovsxbd, pmovsxbq, pmovsxbw, pmovsxdq, pmovsxwd, pmovsxwq, | |
| 339 | pmovzxbd, pmovzxbq, pmovzxbw, pmovzxdq, pmovzxwd, pmovzxwq, | |
| 338 | 340 | pmulld, |
| 339 | 341 | roundpd, roundps, roundsd, roundss, |
| 340 | 342 | // SSE4.2 |
| ... | ... | @@ -387,6 +389,8 @@ pub const Mnemonic = enum { |
| 387 | 389 | vpmaxsb, vpmaxsd, vpmaxsw, vpmaxub, vpmaxud, vpmaxuw, |
| 388 | 390 | vpminsb, vpminsd, vpminsw, vpminub, vpminud, vpminuw, |
| 389 | 391 | vpmovmskb, |
| 392 | vpmovsxbd, vpmovsxbq, vpmovsxbw, vpmovsxdq, vpmovsxwd, vpmovsxwq, | |
| 393 | vpmovzxbd, vpmovzxbq, vpmovzxbw, vpmovzxdq, vpmovzxwd, vpmovzxwq, | |
| 390 | 394 | vpmulhw, vpmulld, vpmullw, |
| 391 | 395 | vpor, |
| 392 | 396 | vpshufb, vpshufd, vpshufhw, vpshuflw, |
src/arch/x86_64/Mir.zig+8| ... | ... | @@ -658,6 +658,14 @@ pub const Inst = struct { |
| 658 | 658 | /// Insert scalar single-precision floating-point value |
| 659 | 659 | /// Insert packed floating-point values |
| 660 | 660 | insert, |
| 661 | /// Packed move with sign extend | |
| 662 | movsxb, | |
| 663 | movsxd, | |
| 664 | movsxw, | |
| 665 | /// Packed move with zero extend | |
| 666 | movzxb, | |
| 667 | movzxd, | |
| 668 | movzxw, | |
| 661 | 669 | /// Round packed single-precision floating-point values |
| 662 | 670 | /// Round scalar single-precision floating-point value |
| 663 | 671 | /// Round packed double-precision floating-point values |
src/arch/x86_64/encodings.zig+42| ... | ... | @@ -1235,6 +1235,20 @@ pub const table = [_]Entry{ |
| 1235 | 1235 | |
| 1236 | 1236 | .{ .pminud, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x3b }, 0, .none, .sse4_1 }, |
| 1237 | 1237 | |
| 1238 | .{ .pmovsxbw, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x20 }, 0, .none, .sse4_1 }, | |
| 1239 | .{ .pmovsxbd, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x21 }, 0, .none, .sse4_1 }, | |
| 1240 | .{ .pmovsxbq, .rm, &.{ .xmm, .xmm_m16 }, &.{ 0x66, 0x0f, 0x38, 0x22 }, 0, .none, .sse4_1 }, | |
| 1241 | .{ .pmovsxwd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x23 }, 0, .none, .sse4_1 }, | |
| 1242 | .{ .pmovsxwq, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x24 }, 0, .none, .sse4_1 }, | |
| 1243 | .{ .pmovsxdq, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x25 }, 0, .none, .sse4_1 }, | |
| 1244 | ||
| 1245 | .{ .pmovzxbw, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x30 }, 0, .none, .sse4_1 }, | |
| 1246 | .{ .pmovzxbd, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x31 }, 0, .none, .sse4_1 }, | |
| 1247 | .{ .pmovzxbq, .rm, &.{ .xmm, .xmm_m16 }, &.{ 0x66, 0x0f, 0x38, 0x32 }, 0, .none, .sse4_1 }, | |
| 1248 | .{ .pmovzxwd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x33 }, 0, .none, .sse4_1 }, | |
| 1249 | .{ .pmovzxwq, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x34 }, 0, .none, .sse4_1 }, | |
| 1250 | .{ .pmovzxdq, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x35 }, 0, .none, .sse4_1 }, | |
| 1251 | ||
| 1238 | 1252 | .{ .pmulld, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x40 }, 0, .none, .sse4_1 }, |
| 1239 | 1253 | |
| 1240 | 1254 | .{ .roundpd, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x09 }, 0, .none, .sse4_1 }, |
| ... | ... | @@ -1587,6 +1601,20 @@ pub const table = [_]Entry{ |
| 1587 | 1601 | .{ .vpmovmskb, .rm, &.{ .r32, .xmm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_128_wig, .avx }, |
| 1588 | 1602 | .{ .vpmovmskb, .rm, &.{ .r64, .xmm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_128_wig, .avx }, |
| 1589 | 1603 | |
| 1604 | .{ .vpmovsxbw, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x20 }, 0, .vex_128_wig, .avx }, | |
| 1605 | .{ .vpmovsxbd, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x21 }, 0, .vex_128_wig, .avx }, | |
| 1606 | .{ .vpmovsxbq, .rm, &.{ .xmm, .xmm_m16 }, &.{ 0x66, 0x0f, 0x38, 0x22 }, 0, .vex_128_wig, .avx }, | |
| 1607 | .{ .vpmovsxwd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x23 }, 0, .vex_128_wig, .avx }, | |
| 1608 | .{ .vpmovsxwq, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x24 }, 0, .vex_128_wig, .avx }, | |
| 1609 | .{ .vpmovsxdq, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x25 }, 0, .vex_128_wig, .avx }, | |
| 1610 | ||
| 1611 | .{ .vpmovzxbw, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x30 }, 0, .vex_128_wig, .avx }, | |
| 1612 | .{ .vpmovzxbd, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x31 }, 0, .vex_128_wig, .avx }, | |
| 1613 | .{ .vpmovzxbq, .rm, &.{ .xmm, .xmm_m16 }, &.{ 0x66, 0x0f, 0x38, 0x32 }, 0, .vex_128_wig, .avx }, | |
| 1614 | .{ .vpmovzxwd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x33 }, 0, .vex_128_wig, .avx }, | |
| 1615 | .{ .vpmovzxwq, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x34 }, 0, .vex_128_wig, .avx }, | |
| 1616 | .{ .vpmovzxdq, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x35 }, 0, .vex_128_wig, .avx }, | |
| 1617 | ||
| 1590 | 1618 | .{ .vpmulhw, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0xe5 }, 0, .vex_128_wig, .avx }, |
| 1591 | 1619 | |
| 1592 | 1620 | .{ .vpmulld, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x40 }, 0, .vex_128_wig, .avx }, |
| ... | ... | @@ -1816,6 +1844,20 @@ pub const table = [_]Entry{ |
| 1816 | 1844 | .{ .vpmovmskb, .rm, &.{ .r32, .ymm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_256_wig, .avx2 }, |
| 1817 | 1845 | .{ .vpmovmskb, .rm, &.{ .r64, .ymm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_256_wig, .avx2 }, |
| 1818 | 1846 | |
| 1847 | .{ .vpmovsxbw, .rm, &.{ .ymm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x20 }, 0, .vex_256_wig, .avx2 }, | |
| 1848 | .{ .vpmovsxbd, .rm, &.{ .ymm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x21 }, 0, .vex_256_wig, .avx2 }, | |
| 1849 | .{ .vpmovsxbq, .rm, &.{ .ymm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x22 }, 0, .vex_256_wig, .avx2 }, | |
| 1850 | .{ .vpmovsxwd, .rm, &.{ .ymm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x23 }, 0, .vex_256_wig, .avx2 }, | |
| 1851 | .{ .vpmovsxwq, .rm, &.{ .ymm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x24 }, 0, .vex_256_wig, .avx2 }, | |
| 1852 | .{ .vpmovsxdq, .rm, &.{ .ymm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x25 }, 0, .vex_256_wig, .avx2 }, | |
| 1853 | ||
| 1854 | .{ .vpmovzxbw, .rm, &.{ .ymm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x30 }, 0, .vex_256_wig, .avx2 }, | |
| 1855 | .{ .vpmovzxbd, .rm, &.{ .ymm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x31 }, 0, .vex_256_wig, .avx2 }, | |
| 1856 | .{ .vpmovzxbq, .rm, &.{ .ymm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x32 }, 0, .vex_256_wig, .avx2 }, | |
| 1857 | .{ .vpmovzxwd, .rm, &.{ .ymm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x33 }, 0, .vex_256_wig, .avx2 }, | |
| 1858 | .{ .vpmovzxwq, .rm, &.{ .ymm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x34 }, 0, .vex_256_wig, .avx2 }, | |
| 1859 | .{ .vpmovzxdq, .rm, &.{ .ymm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x35 }, 0, .vex_256_wig, .avx2 }, | |
| 1860 | ||
| 1819 | 1861 | .{ .vpmulhw, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0xe5 }, 0, .vex_256_wig, .avx2 }, |
| 1820 | 1862 | |
| 1821 | 1863 | .{ .vpmulld, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0x40 }, 0, .vex_256_wig, .avx2 }, |
src/codegen/c.zig+22-15| ... | ... | @@ -6109,41 +6109,48 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6109 | 6109 | const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 6110 | 6110 | |
| 6111 | 6111 | const inst_ty = f.typeOfIndex(inst); |
| 6112 | const inst_scalar_ty = inst_ty.scalarType(mod); | |
| 6112 | 6113 | const operand = try f.resolveInst(ty_op.operand); |
| 6113 | 6114 | try reap(f, inst, &.{ty_op.operand}); |
| 6114 | 6115 | const operand_ty = f.typeOf(ty_op.operand); |
| 6116 | const scalar_ty = operand_ty.scalarType(mod); | |
| 6115 | 6117 | const target = f.object.dg.module.getTarget(); |
| 6116 | const operation = if (inst_ty.isRuntimeFloat() and operand_ty.isRuntimeFloat()) | |
| 6117 | if (inst_ty.floatBits(target) < operand_ty.floatBits(target)) "trunc" else "extend" | |
| 6118 | else if (inst_ty.isInt(mod) and operand_ty.isRuntimeFloat()) | |
| 6119 | if (inst_ty.isSignedInt(mod)) "fix" else "fixuns" | |
| 6120 | else if (inst_ty.isRuntimeFloat() and operand_ty.isInt(mod)) | |
| 6121 | if (operand_ty.isSignedInt(mod)) "float" else "floatun" | |
| 6118 | const operation = if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isRuntimeFloat()) | |
| 6119 | if (inst_scalar_ty.floatBits(target) < scalar_ty.floatBits(target)) "trunc" else "extend" | |
| 6120 | else if (inst_scalar_ty.isInt(mod) and scalar_ty.isRuntimeFloat()) | |
| 6121 | if (inst_scalar_ty.isSignedInt(mod)) "fix" else "fixuns" | |
| 6122 | else if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isInt(mod)) | |
| 6123 | if (scalar_ty.isSignedInt(mod)) "float" else "floatun" | |
| 6122 | 6124 | else |
| 6123 | 6125 | unreachable; |
| 6124 | 6126 | |
| 6125 | 6127 | const writer = f.object.writer(); |
| 6126 | 6128 | const local = try f.allocLocal(inst, inst_ty); |
| 6129 | const v = try Vectorize.start(f, inst, writer, operand_ty); | |
| 6130 | const a = try Assignment.start(f, writer, scalar_ty); | |
| 6127 | 6131 | try f.writeCValue(writer, local, .Other); |
| 6128 | ||
| 6129 | try writer.writeAll(" = "); | |
| 6130 | if (inst_ty.isInt(mod) and operand_ty.isRuntimeFloat()) { | |
| 6132 | try v.elem(f, writer); | |
| 6133 | try a.assign(f, writer); | |
| 6134 | if (inst_scalar_ty.isInt(mod) and scalar_ty.isRuntimeFloat()) { | |
| 6131 | 6135 | try writer.writeAll("zig_wrap_"); |
| 6132 | try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); | |
| 6136 | try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); | |
| 6133 | 6137 | try writer.writeByte('('); |
| 6134 | 6138 | } |
| 6135 | 6139 | try writer.writeAll("zig_"); |
| 6136 | 6140 | try writer.writeAll(operation); |
| 6137 | try writer.writeAll(compilerRtAbbrev(operand_ty, mod)); | |
| 6138 | try writer.writeAll(compilerRtAbbrev(inst_ty, mod)); | |
| 6141 | try writer.writeAll(compilerRtAbbrev(scalar_ty, mod)); | |
| 6142 | try writer.writeAll(compilerRtAbbrev(inst_scalar_ty, mod)); | |
| 6139 | 6143 | try writer.writeByte('('); |
| 6140 | 6144 | try f.writeCValue(writer, operand, .FunctionArgument); |
| 6145 | try v.elem(f, writer); | |
| 6141 | 6146 | try writer.writeByte(')'); |
| 6142 | if (inst_ty.isInt(mod) and operand_ty.isRuntimeFloat()) { | |
| 6143 | try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits); | |
| 6147 | if (inst_scalar_ty.isInt(mod) and scalar_ty.isRuntimeFloat()) { | |
| 6148 | try f.object.dg.renderBuiltinInfo(writer, inst_scalar_ty, .bits); | |
| 6144 | 6149 | try writer.writeByte(')'); |
| 6145 | 6150 | } |
| 6146 | try writer.writeAll(";\n"); | |
| 6151 | try a.end(f, writer); | |
| 6152 | try v.end(f, inst, writer); | |
| 6153 | ||
| 6147 | 6154 | return local; |
| 6148 | 6155 | } |
| 6149 | 6156 |
src/codegen/llvm.zig+8-2| ... | ... | @@ -8648,8 +8648,6 @@ pub const FuncGen = struct { |
| 8648 | 8648 | const operand_ty = self.typeOf(ty_op.operand); |
| 8649 | 8649 | const dest_ty = self.typeOfIndex(inst); |
| 8650 | 8650 | const target = mod.getTarget(); |
| 8651 | const dest_bits = dest_ty.floatBits(target); | |
| 8652 | const src_bits = operand_ty.floatBits(target); | |
| 8653 | 8651 | |
| 8654 | 8652 | if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) { |
| 8655 | 8653 | return self.wip.cast(.fpext, operand, try o.lowerType(dest_ty), ""); |
| ... | ... | @@ -8657,11 +8655,19 @@ pub const FuncGen = struct { |
| 8657 | 8655 | const operand_llvm_ty = try o.lowerType(operand_ty); |
| 8658 | 8656 | const dest_llvm_ty = try o.lowerType(dest_ty); |
| 8659 | 8657 | |
| 8658 | const dest_bits = dest_ty.scalarType(mod).floatBits(target); | |
| 8659 | const src_bits = operand_ty.scalarType(mod).floatBits(target); | |
| 8660 | 8660 | const fn_name = try o.builder.fmt("__extend{s}f{s}f2", .{ |
| 8661 | 8661 | compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), |
| 8662 | 8662 | }); |
| 8663 | 8663 | |
| 8664 | 8664 | const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); |
| 8665 | if (dest_ty.isVector(mod)) return self.buildElementwiseCall( | |
| 8666 | libc_fn, | |
| 8667 | &.{operand}, | |
| 8668 | try o.builder.poisonValue(dest_llvm_ty), | |
| 8669 | dest_ty.vectorLen(mod), | |
| 8670 | ); | |
| 8665 | 8671 | return self.wip.call( |
| 8666 | 8672 | .normal, |
| 8667 | 8673 | .ccc, |
src/type.zig+2-1| ... | ... | @@ -2134,7 +2134,8 @@ pub const Type = struct { |
| 2134 | 2134 | |
| 2135 | 2135 | /// Returns true if and only if the type is a fixed-width integer. |
| 2136 | 2136 | pub fn isInt(self: Type, mod: *const Module) bool { |
| 2137 | return self.isSignedInt(mod) or self.isUnsignedInt(mod); | |
| 2137 | return self.toIntern() != .comptime_int_type and | |
| 2138 | mod.intern_pool.isIntegerType(self.toIntern()); | |
| 2138 | 2139 | } |
| 2139 | 2140 | |
| 2140 | 2141 | /// Returns true if and only if the type is a fixed-width, signed integer. |
test/behavior/cast.zig+32-21| ... | ... | @@ -601,25 +601,25 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { |
| 601 | 601 | |
| 602 | 602 | test "@intCast on vector" { |
| 603 | 603 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 604 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 605 | 604 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 606 | 605 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 607 | 606 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 607 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 608 | 608 | |
| 609 | 609 | const S = struct { |
| 610 | 610 | fn doTheTest() !void { |
| 611 | 611 | // Upcast (implicit, equivalent to @intCast) |
| 612 | 612 | var up0: @Vector(2, u8) = [_]u8{ 0x55, 0xaa }; |
| 613 | 613 | _ = &up0; |
| 614 | const up1 = @as(@Vector(2, u16), up0); | |
| 615 | const up2 = @as(@Vector(2, u32), up0); | |
| 616 | const up3 = @as(@Vector(2, u64), up0); | |
| 614 | const up1: @Vector(2, u16) = up0; | |
| 615 | const up2: @Vector(2, u32) = up0; | |
| 616 | const up3: @Vector(2, u64) = up0; | |
| 617 | 617 | // Downcast (safety-checked) |
| 618 | 618 | var down0 = up3; |
| 619 | 619 | _ = &down0; |
| 620 | const down1 = @as(@Vector(2, u32), @intCast(down0)); | |
| 621 | const down2 = @as(@Vector(2, u16), @intCast(down0)); | |
| 622 | const down3 = @as(@Vector(2, u8), @intCast(down0)); | |
| 620 | const down1: @Vector(2, u32) = @intCast(down0); | |
| 621 | const down2: @Vector(2, u16) = @intCast(down0); | |
| 622 | const down3: @Vector(2, u8) = @intCast(down0); | |
| 623 | 623 | |
| 624 | 624 | try expect(mem.eql(u16, &@as([2]u16, up1), &[2]u16{ 0x55, 0xaa })); |
| 625 | 625 | try expect(mem.eql(u32, &@as([2]u32, up2), &[2]u32{ 0x55, 0xaa })); |
| ... | ... | @@ -629,20 +629,10 @@ test "@intCast on vector" { |
| 629 | 629 | try expect(mem.eql(u16, &@as([2]u16, down2), &[2]u16{ 0x55, 0xaa })); |
| 630 | 630 | try expect(mem.eql(u8, &@as([2]u8, down3), &[2]u8{ 0x55, 0xaa })); |
| 631 | 631 | } |
| 632 | ||
| 633 | fn doTheTestFloat() !void { | |
| 634 | var vec: @Vector(2, f32) = @splat(1234.0); | |
| 635 | _ = &vec; | |
| 636 | const wider: @Vector(2, f64) = vec; | |
| 637 | try expect(wider[0] == 1234.0); | |
| 638 | try expect(wider[1] == 1234.0); | |
| 639 | } | |
| 640 | 632 | }; |
| 641 | 633 | |
| 642 | 634 | try S.doTheTest(); |
| 643 | 635 | try comptime S.doTheTest(); |
| 644 | try S.doTheTestFloat(); | |
| 645 | try comptime S.doTheTestFloat(); | |
| 646 | 636 | } |
| 647 | 637 | |
| 648 | 638 | test "@floatCast cast down" { |
| ... | ... | @@ -2340,10 +2330,31 @@ test "@floatCast on vector" { |
| 2340 | 2330 | |
| 2341 | 2331 | const S = struct { |
| 2342 | 2332 | fn doTheTest() !void { |
| 2343 | var a: @Vector(3, f64) = .{ 1.5, 2.5, 3.5 }; | |
| 2344 | _ = &a; | |
| 2345 | const b: @Vector(3, f32) = @floatCast(a); | |
| 2346 | try expectEqual(@Vector(3, f32){ 1.5, 2.5, 3.5 }, b); | |
| 2333 | { | |
| 2334 | var a: @Vector(2, f64) = .{ 1.5, 2.5 }; | |
| 2335 | _ = &a; | |
| 2336 | const b: @Vector(2, f32) = @floatCast(a); | |
| 2337 | try expectEqual(@Vector(2, f32){ 1.5, 2.5 }, b); | |
| 2338 | } | |
| 2339 | { | |
| 2340 | var a: @Vector(2, f32) = .{ 3.25, 4.25 }; | |
| 2341 | _ = &a; | |
| 2342 | const b: @Vector(2, f64) = @floatCast(a); | |
| 2343 | try expectEqual(@Vector(2, f64){ 3.25, 4.25 }, b); | |
| 2344 | } | |
| 2345 | { | |
| 2346 | var a: @Vector(2, f32) = .{ 5.75, 6.75 }; | |
| 2347 | _ = &a; | |
| 2348 | const b: @Vector(2, f64) = a; | |
| 2349 | try expectEqual(@Vector(2, f64){ 5.75, 6.75 }, b); | |
| 2350 | } | |
| 2351 | { | |
| 2352 | var vec: @Vector(2, f32) = @splat(1234.0); | |
| 2353 | _ = &vec; | |
| 2354 | const wider: @Vector(2, f64) = vec; | |
| 2355 | try expect(wider[0] == 1234.0); | |
| 2356 | try expect(wider[1] == 1234.0); | |
| 2357 | } | |
| 2347 | 2358 | } |
| 2348 | 2359 | }; |
| 2349 | 2360 |