authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-02-15 10:37:52+01:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-02-25 11:22:10+01:00
log2fcb2f597549edd0b1241cebf98c11efe2f25884
treeebf6aca8e0c1d5c77874c2c05b0cccd0967d080f
parent2fdc9e6ae8b6f1ec86050011e1170d639d8c9c2c

Sema: implement vector coercions

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,9 +602,9 @@ fn testUtf8IteratorOnAscii() !void {
602 const s = Utf8View.initComptime("abc");602 const s = Utf8View.initComptime("abc");
603603
604 var it1 = s.iterator();604 var it1 = s.iterator();
605 try testing.expect(std.mem.eql(u8, "a", it1.nextCodepointSlice().?));605 try testing.expect(mem.eql(u8, "a", it1.nextCodepointSlice().?));
606 try testing.expect(std.mem.eql(u8, "b", it1.nextCodepointSlice().?));606 try testing.expect(mem.eql(u8, "b", it1.nextCodepointSlice().?));
607 try testing.expect(std.mem.eql(u8, "c", it1.nextCodepointSlice().?));607 try testing.expect(mem.eql(u8, "c", it1.nextCodepointSlice().?));
608 try testing.expect(it1.nextCodepointSlice() == null);608 try testing.expect(it1.nextCodepointSlice() == null);
609609
610 var it2 = s.iterator();610 var it2 = s.iterator();
...@@ -632,9 +632,9 @@ fn testUtf8ViewOk() !void {...@@ -632,9 +632,9 @@ fn testUtf8ViewOk() !void {
632 const s = Utf8View.initComptime("東京市");632 const s = Utf8View.initComptime("東京市");
633633
634 var it1 = s.iterator();634 var it1 = s.iterator();
635 try testing.expect(std.mem.eql(u8, "東", it1.nextCodepointSlice().?));635 try testing.expect(mem.eql(u8, "東", it1.nextCodepointSlice().?));
636 try testing.expect(std.mem.eql(u8, "京", it1.nextCodepointSlice().?));636 try testing.expect(mem.eql(u8, "京", it1.nextCodepointSlice().?));
637 try testing.expect(std.mem.eql(u8, "市", it1.nextCodepointSlice().?));637 try testing.expect(mem.eql(u8, "市", it1.nextCodepointSlice().?));
638 try testing.expect(it1.nextCodepointSlice() == null);638 try testing.expect(it1.nextCodepointSlice() == null);
639639
640 var it2 = s.iterator();640 var it2 = s.iterator();
...@@ -772,20 +772,20 @@ fn testUtf8Peeking() !void {...@@ -772,20 +772,20 @@ fn testUtf8Peeking() !void {
772 const s = Utf8View.initComptime("noël");772 const s = Utf8View.initComptime("noël");
773 var it = s.iterator();773 var it = s.iterator();
774774
775 try testing.expect(std.mem.eql(u8, "n", it.nextCodepointSlice().?));775 try testing.expect(mem.eql(u8, "n", it.nextCodepointSlice().?));
776776
777 try testing.expect(std.mem.eql(u8, "o", it.peek(1)));777 try testing.expect(mem.eql(u8, "o", it.peek(1)));
778 try testing.expect(std.mem.eql(u8, "oë", it.peek(2)));778 try testing.expect(mem.eql(u8, "oë", it.peek(2)));
779 try testing.expect(std.mem.eql(u8, "oël", it.peek(3)));779 try testing.expect(mem.eql(u8, "oël", it.peek(3)));
780 try testing.expect(std.mem.eql(u8, "oël", it.peek(4)));780 try testing.expect(mem.eql(u8, "oël", it.peek(4)));
781 try testing.expect(std.mem.eql(u8, "oël", it.peek(10)));781 try testing.expect(mem.eql(u8, "oël", it.peek(10)));
782782
783 try testing.expect(std.mem.eql(u8, "o", it.nextCodepointSlice().?));783 try testing.expect(mem.eql(u8, "o", it.nextCodepointSlice().?));
784 try testing.expect(std.mem.eql(u8, "ë", it.nextCodepointSlice().?));784 try testing.expect(mem.eql(u8, "ë", it.nextCodepointSlice().?));
785 try testing.expect(std.mem.eql(u8, "l", it.nextCodepointSlice().?));785 try testing.expect(mem.eql(u8, "l", it.nextCodepointSlice().?));
786 try testing.expect(it.nextCodepointSlice() == null);786 try testing.expect(it.nextCodepointSlice() == null);
787787
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}
790790
791fn testError(bytes: []const u8, expected_err: anyerror) !void {791fn testError(bytes: []const u8, expected_err: anyerror) !void {
...@@ -927,20 +927,16 @@ test "fmtUtf8" {...@@ -927,20 +927,16 @@ test "fmtUtf8" {
927}927}
928928
929fn utf16LeToUtf8ArrayListImpl(929fn utf16LeToUtf8ArrayListImpl(
930 array_list: *std.ArrayList(u8),930 result: *std.ArrayList(u8),
931 utf16le: []const u16,931 utf16le: []const u16,
932 comptime surrogates: Surrogates,932 comptime surrogates: Surrogates,
933) (switch (surrogates) {933) (switch (surrogates) {
934 .cannot_encode_surrogate_half => Utf16LeToUtf8AllocError,934 .cannot_encode_surrogate_half => Utf16LeToUtf8AllocError,
935 .can_encode_surrogate_half => mem.Allocator.Error,935 .can_encode_surrogate_half => mem.Allocator.Error,
936})!void {936})!void {
937 // optimistically guess that it will all be ascii.937 assert(result.capacity >= utf16le.len);
938 try array_list.ensureTotalCapacityPrecise(utf16le.len);
939938
940 var remaining = utf16le;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 vectorized: {940 vectorized: {
945 const chunk_len = std.simd.suggestVectorLength(u16) orelse break :vectorized;941 const chunk_len = std.simd.suggestVectorLength(u16) orelse break :vectorized;
946 const Chunk = @Vector(chunk_len, u16);942 const Chunk = @Vector(chunk_len, u16);
...@@ -948,41 +944,33 @@ fn utf16LeToUtf8ArrayListImpl(...@@ -948,41 +944,33 @@ fn utf16LeToUtf8ArrayListImpl(
948 // Fast path. Check for and encode ASCII characters at the start of the input.944 // Fast path. Check for and encode ASCII characters at the start of the input.
949 while (remaining.len >= chunk_len) {945 while (remaining.len >= chunk_len) {
950 const chunk: Chunk = remaining[0..chunk_len].*;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 if (@reduce(.Or, chunk | mask != mask)) {948 if (@reduce(.Or, chunk | mask != mask)) {
953 // found a non ASCII code unit949 // found a non ASCII code unit
954 break;950 break;
955 }951 }
956 const chunk_byte_len = chunk_len * 2;952 const ascii_chunk: @Vector(chunk_len, u8) = @truncate(mem.nativeToLittle(Chunk, chunk));
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];
960 // We allocated enough space to encode every UTF-16 code unit953 // We allocated enough space to encode every UTF-16 code unit
961 // as ASCII, so if the entire string is ASCII then we are954 // as ASCII, so if the entire string is ASCII then we are
962 // guaranteed to have enough space allocated955 // guaranteed to have enough space allocated
963 array_list.appendSliceAssumeCapacity(&ascii_bytes);956 result.addManyAsArrayAssumeCapacity(chunk_len).* = ascii_chunk;
964 remaining = remaining[chunk_len..];957 remaining = remaining[chunk_len..];
965 }958 }
966 }959 }
967960
968 var out_index: usize = array_list.items.len;
969 switch (surrogates) {961 switch (surrogates) {
970 .cannot_encode_surrogate_half => {962 .cannot_encode_surrogate_half => {
971 var it = Utf16LeIterator.init(remaining);963 var it = Utf16LeIterator.init(remaining);
972 while (try it.nextCodepoint()) |codepoint| {964 while (try it.nextCodepoint()) |codepoint| {
973 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;965 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
974 try array_list.resize(array_list.items.len + utf8_len);966 assert((utf8Encode(codepoint, try result.addManyAsSlice(utf8_len)) catch unreachable) == utf8_len);
975 assert((utf8Encode(codepoint, array_list.items[out_index..]) catch unreachable) == utf8_len);
976 out_index += utf8_len;
977 }967 }
978 },968 },
979 .can_encode_surrogate_half => {969 .can_encode_surrogate_half => {
980 var it = Wtf16LeIterator.init(remaining);970 var it = Wtf16LeIterator.init(remaining);
981 while (it.nextCodepoint()) |codepoint| {971 while (it.nextCodepoint()) |codepoint| {
982 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;972 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
983 try array_list.resize(array_list.items.len + utf8_len);973 assert((wtf8Encode(codepoint, try result.addManyAsSlice(utf8_len)) catch unreachable) == utf8_len);
984 assert((wtf8Encode(codepoint, array_list.items[out_index..]) catch unreachable) == utf8_len);
985 out_index += utf8_len;
986 }974 }
987 },975 },
988 }976 }
...@@ -990,8 +978,9 @@ fn utf16LeToUtf8ArrayListImpl(...@@ -990,8 +978,9 @@ fn utf16LeToUtf8ArrayListImpl(
990978
991pub const Utf16LeToUtf8AllocError = mem.Allocator.Error || Utf16LeToUtf8Error;979pub const Utf16LeToUtf8AllocError = mem.Allocator.Error || Utf16LeToUtf8Error;
992980
993pub fn utf16LeToUtf8ArrayList(array_list: *std.ArrayList(u8), utf16le: []const u16) Utf16LeToUtf8AllocError!void {981pub fn utf16LeToUtf8ArrayList(result: *std.ArrayList(u8), utf16le: []const u16) Utf16LeToUtf8AllocError!void {
994 return utf16LeToUtf8ArrayListImpl(array_list, utf16le, .cannot_encode_surrogate_half);982 try result.ensureTotalCapacityPrecise(utf16le.len);
983 return utf16LeToUtf8ArrayListImpl(result, utf16le, .cannot_encode_surrogate_half);
995}984}
996985
997/// Deprecated; renamed to utf16LeToUtf8Alloc986/// Deprecated; renamed to utf16LeToUtf8Alloc
...@@ -1003,8 +992,7 @@ pub fn utf16LeToUtf8Alloc(allocator: mem.Allocator, utf16le: []const u16) Utf16L...@@ -1003,8 +992,7 @@ pub fn utf16LeToUtf8Alloc(allocator: mem.Allocator, utf16le: []const u16) Utf16L
1003 var result = try std.ArrayList(u8).initCapacity(allocator, utf16le.len);992 var result = try std.ArrayList(u8).initCapacity(allocator, utf16le.len);
1004 errdefer result.deinit();993 errdefer result.deinit();
1005994
1006 try utf16LeToUtf8ArrayList(&result, utf16le);995 try utf16LeToUtf8ArrayListImpl(&result, utf16le, .cannot_encode_surrogate_half);
1007
1008 return result.toOwnedSlice();996 return result.toOwnedSlice();
1009}997}
1010998
...@@ -1017,8 +1005,7 @@ pub fn utf16LeToUtf8AllocZ(allocator: mem.Allocator, utf16le: []const u16) Utf16...@@ -1017,8 +1005,7 @@ pub fn utf16LeToUtf8AllocZ(allocator: mem.Allocator, utf16le: []const u16) Utf16
1017 var result = try std.ArrayList(u8).initCapacity(allocator, utf16le.len + 1);1005 var result = try std.ArrayList(u8).initCapacity(allocator, utf16le.len + 1);
1018 errdefer result.deinit();1006 errdefer result.deinit();
10191007
1020 try utf16LeToUtf8ArrayList(&result, utf16le);1008 try utf16LeToUtf8ArrayListImpl(&result, utf16le, .cannot_encode_surrogate_half);
1021
1022 return result.toOwnedSliceSentinel(0);1009 return result.toOwnedSliceSentinel(0);
1023}1010}
10241011
...@@ -1030,12 +1017,9 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr...@@ -1030,12 +1017,9 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr
1030 .cannot_encode_surrogate_half => Utf16LeToUtf8Error,1017 .cannot_encode_surrogate_half => Utf16LeToUtf8Error,
1031 .can_encode_surrogate_half => error{},1018 .can_encode_surrogate_half => error{},
1032})!usize {1019})!usize {
1033 var end_index: usize = 0;1020 var dest_index: usize = 0;
10341021
1035 var remaining = utf16le;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 vectorized: {1023 vectorized: {
1040 const chunk_len = std.simd.suggestVectorLength(u16) orelse break :vectorized;1024 const chunk_len = std.simd.suggestVectorLength(u16) orelse break :vectorized;
1041 const Chunk = @Vector(chunk_len, u16);1025 const Chunk = @Vector(chunk_len, u16);
...@@ -1043,17 +1027,14 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr...@@ -1043,17 +1027,14 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr
1043 // Fast path. Check for and encode ASCII characters at the start of the input.1027 // Fast path. Check for and encode ASCII characters at the start of the input.
1044 while (remaining.len >= chunk_len) {1028 while (remaining.len >= chunk_len) {
1045 const chunk: Chunk = remaining[0..chunk_len].*;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 if (@reduce(.Or, chunk | mask != mask)) {1031 if (@reduce(.Or, chunk | mask != mask)) {
1048 // found a non ASCII code unit1032 // found a non ASCII code unit
1049 break;1033 break;
1050 }1034 }
1051 const chunk_byte_len = chunk_len * 2;1035 const ascii_chunk: @Vector(chunk_len, u8) = @truncate(mem.nativeToLittle(Chunk, chunk));
1052 const chunk_bytes: @Vector(chunk_byte_len, u8) = (std.mem.sliceAsBytes(remaining)[0..chunk_byte_len]).*;1036 utf8[dest_index..][0..chunk_len].* = ascii_chunk;
1053 const deinterlaced_bytes = std.simd.deinterlace(2, chunk_bytes);1037 dest_index += chunk_len;
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;
1057 remaining = remaining[chunk_len..];1038 remaining = remaining[chunk_len..];
1058 }1039 }
1059 }1040 }
...@@ -1062,7 +1043,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr...@@ -1062,7 +1043,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr
1062 .cannot_encode_surrogate_half => {1043 .cannot_encode_surrogate_half => {
1063 var it = Utf16LeIterator.init(remaining);1044 var it = Utf16LeIterator.init(remaining);
1064 while (try it.nextCodepoint()) |codepoint| {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 // The maximum possible codepoint encoded by UTF-16 is U+10FFFF,1047 // The maximum possible codepoint encoded by UTF-16 is U+10FFFF,
1067 // which is within the valid codepoint range.1048 // which is within the valid codepoint range.
1068 error.CodepointTooLarge => unreachable,1049 error.CodepointTooLarge => unreachable,
...@@ -1075,7 +1056,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr...@@ -1075,7 +1056,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr
1075 .can_encode_surrogate_half => {1056 .can_encode_surrogate_half => {
1076 var it = Wtf16LeIterator.init(remaining);1057 var it = Wtf16LeIterator.init(remaining);
1077 while (it.nextCodepoint()) |codepoint| {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 // The maximum possible codepoint encoded by UTF-16 is U+10FFFF,1060 // The maximum possible codepoint encoded by UTF-16 is U+10FFFF,
1080 // which is within the valid codepoint range.1061 // which is within the valid codepoint range.
1081 error.CodepointTooLarge => unreachable,1062 error.CodepointTooLarge => unreachable,
...@@ -1083,7 +1064,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr...@@ -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}
10881069
1089/// Deprecated; renamed to utf16LeToUtf81070/// Deprecated; renamed to utf16LeToUtf8
...@@ -1156,18 +1137,12 @@ test utf16LeToUtf8 {...@@ -1156,18 +1137,12 @@ test utf16LeToUtf8 {
1156 }1137 }
1157}1138}
11581139
1159fn utf8ToUtf16LeArrayListImpl(array_list: *std.ArrayList(u16), utf8: []const u8, comptime surrogates: Surrogates) !void {1140fn utf8ToUtf16LeArrayListImpl(result: *std.ArrayList(u16), utf8: []const u8, comptime surrogates: Surrogates) !void {
1160 // optimistically guess that it will not require surrogate pairs1141 assert(result.capacity >= utf8.len);
1161 try array_list.ensureTotalCapacityPrecise(utf8.len);
11621142
1163 var remaining = utf8;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 vectorized: {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 const Chunk = @Vector(chunk_len, u8);1146 const Chunk = @Vector(chunk_len, u8);
11721147
1173 // Fast path. Check for and encode ASCII characters at the start of the input.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,9 +1153,8 @@ fn utf8ToUtf16LeArrayListImpl(array_list: *std.ArrayList(u16), utf8: []const u8,
1178 // found a non ASCII code unit1153 // found a non ASCII code unit
1179 break;1154 break;
1180 }1155 }
1181 const zeroes: Chunk = @splat(0);1156 const utf16_chunk = mem.nativeToLittle(@Vector(chunk_len, u16), chunk);
1182 const utf16_chunk: [chunk_len * 2]u8 align(@alignOf(u16)) = std.simd.interlace(.{ chunk, zeroes });1157 result.addManyAsArrayAssumeCapacity(chunk_len).* = utf16_chunk;
1183 array_list.appendSliceAssumeCapacity(std.mem.bytesAsSlice(u16, &utf16_chunk));
1184 remaining = remaining[chunk_len..];1158 remaining = remaining[chunk_len..];
1185 }1159 }
1186 }1160 }
...@@ -1192,21 +1166,18 @@ fn utf8ToUtf16LeArrayListImpl(array_list: *std.ArrayList(u16), utf8: []const u8,...@@ -1192,21 +1166,18 @@ fn utf8ToUtf16LeArrayListImpl(array_list: *std.ArrayList(u16), utf8: []const u8,
1192 var it = view.iterator();1166 var it = view.iterator();
1193 while (it.nextCodepoint()) |codepoint| {1167 while (it.nextCodepoint()) |codepoint| {
1194 if (codepoint < 0x10000) {1168 if (codepoint < 0x10000) {
1195 const short = @as(u16, @intCast(codepoint));1169 try result.append(mem.nativeToLittle(u16, @intCast(codepoint)));
1196 try array_list.append(mem.nativeToLittle(u16, short));
1197 } else {1170 } else {
1198 const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;1171 const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;
1199 const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00;1172 const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00;
1200 var out: [2]u16 = undefined;1173 try result.appendSlice(&.{ mem.nativeToLittle(u16, high), mem.nativeToLittle(u16, low) });
1201 out[0] = mem.nativeToLittle(u16, high);
1202 out[1] = mem.nativeToLittle(u16, low);
1203 try array_list.appendSlice(out[0..]);
1204 }1174 }
1205 }1175 }
1206}1176}
12071177
1208pub fn utf8ToUtf16LeArrayList(array_list: *std.ArrayList(u16), utf8: []const u8) error{ InvalidUtf8, OutOfMemory }!void {1178pub fn utf8ToUtf16LeArrayList(result: *std.ArrayList(u16), utf8: []const u8) error{ InvalidUtf8, OutOfMemory }!void {
1209 return utf8ToUtf16LeArrayListImpl(array_list, utf8, .cannot_encode_surrogate_half);1179 try result.ensureTotalCapacityPrecise(utf8.len);
1180 return utf8ToUtf16LeArrayListImpl(result, utf8, .cannot_encode_surrogate_half);
1210}1181}
12111182
1212pub fn utf8ToUtf16LeAlloc(allocator: mem.Allocator, utf8: []const u8) error{ InvalidUtf8, OutOfMemory }![]u16 {1183pub 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,7 +1186,6 @@ pub fn utf8ToUtf16LeAlloc(allocator: mem.Allocator, utf8: []const u8) error{ Inv
1215 errdefer result.deinit();1186 errdefer result.deinit();
12161187
1217 try utf8ToUtf16LeArrayListImpl(&result, utf8, .cannot_encode_surrogate_half);1188 try utf8ToUtf16LeArrayListImpl(&result, utf8, .cannot_encode_surrogate_half);
1218
1219 return result.toOwnedSlice();1189 return result.toOwnedSlice();
1220}1190}
12211191
...@@ -1228,7 +1198,6 @@ pub fn utf8ToUtf16LeAllocZ(allocator: mem.Allocator, utf8: []const u8) error{ In...@@ -1228,7 +1198,6 @@ pub fn utf8ToUtf16LeAllocZ(allocator: mem.Allocator, utf8: []const u8) error{ In
1228 errdefer result.deinit();1198 errdefer result.deinit();
12291199
1230 try utf8ToUtf16LeArrayListImpl(&result, utf8, .cannot_encode_surrogate_half);1200 try utf8ToUtf16LeArrayListImpl(&result, utf8, .cannot_encode_surrogate_half);
1231
1232 return result.toOwnedSliceSentinel(0);1201 return result.toOwnedSliceSentinel(0);
1233}1202}
12341203
...@@ -1239,16 +1208,11 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) error{InvalidUtf8}!usize...@@ -1239,16 +1208,11 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) error{InvalidUtf8}!usize
1239}1208}
12401209
1241pub fn utf8ToUtf16LeImpl(utf16le: []u16, utf8: []const u8, comptime surrogates: Surrogates) !usize {1210pub fn utf8ToUtf16LeImpl(utf16le: []u16, utf8: []const u8, comptime surrogates: Surrogates) !usize {
1242 var dest_i: usize = 0;1211 var dest_index: usize = 0;
12431212
1244 var remaining = utf8;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 vectorized: {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 const Chunk = @Vector(chunk_len, u8);1216 const Chunk = @Vector(chunk_len, u8);
12531217
1254 // Fast path. Check for and encode ASCII characters at the start of the input.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,57 +1223,60 @@ pub fn utf8ToUtf16LeImpl(utf16le: []u16, utf8: []const u8, comptime surrogates:
1259 // found a non ASCII code unit1223 // found a non ASCII code unit
1260 break;1224 break;
1261 }1225 }
1262 const zeroes: Chunk = @splat(0);1226 const utf16_chunk = mem.nativeToLittle(@Vector(chunk_len, u16), chunk);
1263 const utf16_bytes: [chunk_len * 2]u8 align(@alignOf(u16)) = std.simd.interlace(.{ chunk, zeroes });1227 utf16le[dest_index..][0..chunk_len].* = utf16_chunk;
1264 @memcpy(utf16le[dest_i..][0..chunk_len], std.mem.bytesAsSlice(u16, &utf16_bytes));1228 dest_index += chunk_len;
1265 dest_i += chunk_len;
1266 remaining = remaining[chunk_len..];1229 remaining = remaining[chunk_len..];
1267 }1230 }
1268 }1231 }
12691232
1270 var src_i: usize = 0;1233 const view = switch (surrogates) {
1271 while (src_i < remaining.len) {1234 .cannot_encode_surrogate_half => try Utf8View.init(remaining),
1272 const n = utf8ByteSequenceLength(remaining[src_i]) catch return switch (surrogates) {1235 .can_encode_surrogate_half => try Wtf8View.init(remaining),
1273 .cannot_encode_surrogate_half => error.InvalidUtf8,1236 };
1274 .can_encode_surrogate_half => error.InvalidWtf8,1237 var it = view.iterator();
1275 };1238 while (it.nextCodepoint()) |codepoint| {
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 };
1281 if (codepoint < 0x10000) {1239 if (codepoint < 0x10000) {
1282 const short = @as(u16, @intCast(codepoint));1240 utf16le[dest_index] = mem.nativeToLittle(u16, @intCast(codepoint));
1283 utf16le[dest_i] = mem.nativeToLittle(u16, short);1241 dest_index += 1;
1284 dest_i += 1;
1285 } else {1242 } else {
1286 const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;1243 const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;
1287 const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00;1244 const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00;
1288 utf16le[dest_i] = mem.nativeToLittle(u16, high);1245 utf16le[dest_index..][0..2].* = .{ mem.nativeToLittle(u16, high), mem.nativeToLittle(u16, low) };
1289 utf16le[dest_i + 1] = mem.nativeToLittle(u16, low);1246 dest_index += 2;
1290 dest_i += 2;
1291 }1247 }
1292 src_i = next_src_i;
1293 }1248 }
1294 return dest_i;1249 return dest_index;
1295}1250}
12961251
1297test "utf8ToUtf16Le" {1252test "utf8ToUtf16Le" {
1298 var utf16le: [2]u16 = [_]u16{0} ** 2;1253 var utf16le: [128]u16 = undefined;
1299 {1254 {
1300 const length = try utf8ToUtf16Le(utf16le[0..], "𐐷");1255 const length = try utf8ToUtf16Le(utf16le[0..], "𐐷");
1301 try testing.expectEqual(@as(usize, 2), length);1256 try testing.expectEqualSlices(u8, "\x01\xd8\x37\xdc", mem.sliceAsBytes(utf16le[0..length]));
1302 try testing.expectEqualSlices(u8, "\x01\xd8\x37\xdc", mem.sliceAsBytes(utf16le[0..]));
1303 }1257 }
1304 {1258 {
1305 const length = try utf8ToUtf16Le(utf16le[0..], "\u{10FFFF}");1259 const length = try utf8ToUtf16Le(utf16le[0..], "\u{10FFFF}");
1306 try testing.expectEqual(@as(usize, 2), length);1260 try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16le[0..length]));
1307 try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16le[0..]));
1308 }1261 }
1309 {1262 {
1310 const result = utf8ToUtf16Le(utf16le[0..], "\xf4\x90\x80\x80");1263 const result = utf8ToUtf16Le(utf16le[0..], "\xf4\x90\x80\x80");
1311 try testing.expectError(error.InvalidUtf8, result);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}
13141281
1315test utf8ToUtf16LeArrayList {1282test utf8ToUtf16LeArrayList {
...@@ -1354,25 +1321,40 @@ test utf8ToUtf16LeAllocZ {...@@ -1354,25 +1321,40 @@ test utf8ToUtf16LeAllocZ {
1354 {1321 {
1355 const utf16 = try utf8ToUtf16LeAllocZ(testing.allocator, "𐐷");1322 const utf16 = try utf8ToUtf16LeAllocZ(testing.allocator, "𐐷");
1356 defer testing.allocator.free(utf16);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 try testing.expect(utf16[2] == 0);1325 try testing.expect(utf16[2] == 0);
1359 }1326 }
1360 {1327 {
1361 const utf16 = try utf8ToUtf16LeAllocZ(testing.allocator, "\u{10FFFF}");1328 const utf16 = try utf8ToUtf16LeAllocZ(testing.allocator, "\u{10FFFF}");
1362 defer testing.allocator.free(utf16);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 try testing.expect(utf16[2] == 0);1331 try testing.expect(utf16[2] == 0);
1365 }1332 }
1366 {1333 {
1367 const result = utf8ToUtf16LeAllocZ(testing.allocator, "\xf4\x90\x80\x80");1334 const result = utf8ToUtf16LeAllocZ(testing.allocator, "\xf4\x90\x80\x80");
1368 try testing.expectError(error.InvalidUtf8, result);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}
13711353
1372/// Converts a UTF-8 string literal into a UTF-16LE string literal.1354/// Converts a UTF-8 string literal into a UTF-16LE string literal.
1373pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8) catch unreachable:0]u16 {1355pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8) catch |err| @compileError(err):0]u16 {
1374 return comptime blk: {1356 return comptime blk: {
1375 const len: usize = calcUtf16LeLen(utf8) catch |err| @compileError(err);1357 const len: usize = calcUtf16LeLen(utf8) catch unreachable;
1376 var utf16le: [len:0]u16 = [_:0]u16{0} ** len;1358 var utf16le: [len:0]u16 = [_:0]u16{0} ** len;
1377 const utf16le_len = utf8ToUtf16Le(&utf16le, utf8[0..]) catch |err| @compileError(err);1359 const utf16le_len = utf8ToUtf16Le(&utf16le, utf8[0..]) catch |err| @compileError(err);
1378 assert(len == utf16le_len);1360 assert(len == utf16le_len);
...@@ -1453,12 +1435,12 @@ test "fmtUtf16Le" {...@@ -1453,12 +1435,12 @@ test "fmtUtf16Le" {
1453 try expectFmt("", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))});1435 try expectFmt("", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))});
1454 try expectFmt("foo", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))});1436 try expectFmt("foo", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))});
1455 try expectFmt("𐐷", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("𐐷"))});1437 try expectFmt("𐐷", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("𐐷"))});
1456 try expectFmt("퟿", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\xff\xd7", native_endian)})});1438 try expectFmt("퟿", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xd7", native_endian)})});
1457 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\x00\xd8", native_endian)})});1439 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xd8", native_endian)})});
1458 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\xff\xdb", native_endian)})});1440 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdb", native_endian)})});
1459 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\x00\xdc", native_endian)})});1441 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xdc", native_endian)})});
1460 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\xff\xdf", native_endian)})});1442 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdf", native_endian)})});
1461 try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{std.mem.readInt(u16, "\x00\xe0", native_endian)})});1443 try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xe0", native_endian)})});
1462}1444}
14631445
1464test "utf8ToUtf16LeStringLiteral" {1446test "utf8ToUtf16LeStringLiteral" {
...@@ -1701,8 +1683,9 @@ pub const Wtf8Iterator = struct {...@@ -1701,8 +1683,9 @@ pub const Wtf8Iterator = struct {
1701 }1683 }
1702};1684};
17031685
1704pub fn wtf16LeToWtf8ArrayList(array_list: *std.ArrayList(u8), utf16le: []const u16) mem.Allocator.Error!void {1686pub fn wtf16LeToWtf8ArrayList(result: *std.ArrayList(u8), utf16le: []const u16) mem.Allocator.Error!void {
1705 return utf16LeToUtf8ArrayListImpl(array_list, utf16le, .can_encode_surrogate_half);1687 try result.ensureTotalCapacityPrecise(utf16le.len);
1688 return utf16LeToUtf8ArrayListImpl(result, utf16le, .can_encode_surrogate_half);
1706}1689}
17071690
1708/// Caller must free returned memory.1691/// Caller must free returned memory.
...@@ -1711,8 +1694,7 @@ pub fn wtf16LeToWtf8Alloc(allocator: mem.Allocator, wtf16le: []const u16) mem.Al...@@ -1711,8 +1694,7 @@ pub fn wtf16LeToWtf8Alloc(allocator: mem.Allocator, wtf16le: []const u16) mem.Al
1711 var result = try std.ArrayList(u8).initCapacity(allocator, wtf16le.len);1694 var result = try std.ArrayList(u8).initCapacity(allocator, wtf16le.len);
1712 errdefer result.deinit();1695 errdefer result.deinit();
17131696
1714 try wtf16LeToWtf8ArrayList(&result, wtf16le);1697 try utf16LeToUtf8ArrayListImpl(&result, wtf16le, .can_encode_surrogate_half);
1715
1716 return result.toOwnedSlice();1698 return result.toOwnedSlice();
1717}1699}
17181700
...@@ -1722,8 +1704,7 @@ pub fn wtf16LeToWtf8AllocZ(allocator: mem.Allocator, wtf16le: []const u16) mem.A...@@ -1722,8 +1704,7 @@ pub fn wtf16LeToWtf8AllocZ(allocator: mem.Allocator, wtf16le: []const u16) mem.A
1722 var result = try std.ArrayList(u8).initCapacity(allocator, wtf16le.len + 1);1704 var result = try std.ArrayList(u8).initCapacity(allocator, wtf16le.len + 1);
1723 errdefer result.deinit();1705 errdefer result.deinit();
17241706
1725 try wtf16LeToWtf8ArrayList(&result, wtf16le);1707 try utf16LeToUtf8ArrayListImpl(&result, wtf16le, .can_encode_surrogate_half);
1726
1727 return result.toOwnedSliceSentinel(0);1708 return result.toOwnedSliceSentinel(0);
1728}1709}
17291710
...@@ -1731,8 +1712,9 @@ pub fn wtf16LeToWtf8(wtf8: []u8, wtf16le: []const u16) usize {...@@ -1731,8 +1712,9 @@ pub fn wtf16LeToWtf8(wtf8: []u8, wtf16le: []const u16) usize {
1731 return utf16LeToUtf8Impl(wtf8, wtf16le, .can_encode_surrogate_half) catch |err| switch (err) {};1712 return utf16LeToUtf8Impl(wtf8, wtf16le, .can_encode_surrogate_half) catch |err| switch (err) {};
1732}1713}
17331714
1734pub fn wtf8ToWtf16LeArrayList(array_list: *std.ArrayList(u16), wtf8: []const u8) error{ InvalidWtf8, OutOfMemory }!void {1715pub fn wtf8ToWtf16LeArrayList(result: *std.ArrayList(u16), wtf8: []const u8) error{ InvalidWtf8, OutOfMemory }!void {
1735 return utf8ToUtf16LeArrayListImpl(array_list, wtf8, .can_encode_surrogate_half);1716 try result.ensureTotalCapacityPrecise(wtf8.len);
1717 return utf8ToUtf16LeArrayListImpl(result, wtf8, .can_encode_surrogate_half);
1736}1718}
17371719
1738pub fn wtf8ToWtf16LeAlloc(allocator: mem.Allocator, wtf8: []const u8) error{ InvalidWtf8, OutOfMemory }![]u16 {1720pub 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,7 +1723,6 @@ pub fn wtf8ToWtf16LeAlloc(allocator: mem.Allocator, wtf8: []const u8) error{ Inv
1741 errdefer result.deinit();1723 errdefer result.deinit();
17421724
1743 try utf8ToUtf16LeArrayListImpl(&result, wtf8, .can_encode_surrogate_half);1725 try utf8ToUtf16LeArrayListImpl(&result, wtf8, .can_encode_surrogate_half);
1744
1745 return result.toOwnedSlice();1726 return result.toOwnedSlice();
1746}1727}
17471728
...@@ -1751,7 +1732,6 @@ pub fn wtf8ToWtf16LeAllocZ(allocator: mem.Allocator, wtf8: []const u8) error{ In...@@ -1751,7 +1732,6 @@ pub fn wtf8ToWtf16LeAllocZ(allocator: mem.Allocator, wtf8: []const u8) error{ In
1751 errdefer result.deinit();1732 errdefer result.deinit();
17521733
1753 try utf8ToUtf16LeArrayListImpl(&result, wtf8, .can_encode_surrogate_half);1734 try utf8ToUtf16LeArrayListImpl(&result, wtf8, .can_encode_surrogate_half);
1754
1755 return result.toOwnedSliceSentinel(0);1735 return result.toOwnedSliceSentinel(0);
1756}1736}
17571737
...@@ -1910,7 +1890,7 @@ pub const Wtf16LeIterator = struct {...@@ -1910,7 +1890,7 @@ pub const Wtf16LeIterator = struct {
19101890
1911 pub fn init(s: []const u16) Wtf16LeIterator {1891 pub fn init(s: []const u16) Wtf16LeIterator {
1912 return Wtf16LeIterator{1892 return Wtf16LeIterator{
1913 .bytes = std.mem.sliceAsBytes(s),1893 .bytes = mem.sliceAsBytes(s),
1914 .i = 0,1894 .i = 0,
1915 };1895 };
1916 }1896 }
...@@ -1923,12 +1903,12 @@ pub const Wtf16LeIterator = struct {...@@ -1923,12 +1903,12 @@ pub const Wtf16LeIterator = struct {
1923 assert(it.i <= it.bytes.len);1903 assert(it.i <= it.bytes.len);
1924 if (it.i == it.bytes.len) return null;1904 if (it.i == it.bytes.len) return null;
1925 var code_units: [2]u16 = undefined;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 it.i += 2;1907 it.i += 2;
1928 surrogate_pair: {1908 surrogate_pair: {
1929 if (utf16IsHighSurrogate(code_units[0])) {1909 if (utf16IsHighSurrogate(code_units[0])) {
1930 if (it.i >= it.bytes.len) break :surrogate_pair;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 const codepoint = utf16DecodeSurrogatePair(&code_units) catch break :surrogate_pair;1912 const codepoint = utf16DecodeSurrogatePair(&code_units) catch break :surrogate_pair;
1933 it.i += 2;1913 it.i += 2;
1934 return codepoint;1914 return codepoint;
...@@ -2045,31 +2025,31 @@ fn testRoundtripWtf16(wtf16le: []const u16) !void {...@@ -2045,31 +2025,31 @@ fn testRoundtripWtf16(wtf16le: []const u16) !void {
20452025
2046test "well-formed WTF-16 roundtrips" {2026test "well-formed WTF-16 roundtrips" {
2047 try testRoundtripWtf16(&[_]u16{2027 try testRoundtripWtf16(&[_]u16{
2048 std.mem.nativeToLittle(u16, 0xD83D), // high surrogate2028 mem.nativeToLittle(u16, 0xD83D), // high surrogate
2049 std.mem.nativeToLittle(u16, 0xDCA9), // low surrogate2029 mem.nativeToLittle(u16, 0xDCA9), // low surrogate
2050 });2030 });
2051 try testRoundtripWtf16(&[_]u16{2031 try testRoundtripWtf16(&[_]u16{
2052 std.mem.nativeToLittle(u16, 0xD83D), // high surrogate2032 mem.nativeToLittle(u16, 0xD83D), // high surrogate
2053 std.mem.nativeToLittle(u16, ' '), // not surrogate2033 mem.nativeToLittle(u16, ' '), // not surrogate
2054 std.mem.nativeToLittle(u16, 0xDCA9), // low surrogate2034 mem.nativeToLittle(u16, 0xDCA9), // low surrogate
2055 });2035 });
2056 try testRoundtripWtf16(&[_]u16{2036 try testRoundtripWtf16(&[_]u16{
2057 std.mem.nativeToLittle(u16, 0xD800), // high surrogate2037 mem.nativeToLittle(u16, 0xD800), // high surrogate
2058 std.mem.nativeToLittle(u16, 0xDBFF), // high surrogate2038 mem.nativeToLittle(u16, 0xDBFF), // high surrogate
2059 });2039 });
2060 try testRoundtripWtf16(&[_]u16{2040 try testRoundtripWtf16(&[_]u16{
2061 std.mem.nativeToLittle(u16, 0xD800), // high surrogate2041 mem.nativeToLittle(u16, 0xD800), // high surrogate
2062 std.mem.nativeToLittle(u16, 0xE000), // not surrogate2042 mem.nativeToLittle(u16, 0xE000), // not surrogate
2063 });2043 });
2064 try testRoundtripWtf16(&[_]u16{2044 try testRoundtripWtf16(&[_]u16{
2065 std.mem.nativeToLittle(u16, 0xD7FF), // not surrogate2045 mem.nativeToLittle(u16, 0xD7FF), // not surrogate
2066 std.mem.nativeToLittle(u16, 0xDC00), // low surrogate2046 mem.nativeToLittle(u16, 0xDC00), // low surrogate
2067 });2047 });
2068 try testRoundtripWtf16(&[_]u16{2048 try testRoundtripWtf16(&[_]u16{
2069 std.mem.nativeToLittle(u16, 0x61), // not surrogate2049 mem.nativeToLittle(u16, 0x61), // not surrogate
2070 std.mem.nativeToLittle(u16, 0xDC00), // low surrogate2050 mem.nativeToLittle(u16, 0xDC00), // low surrogate
2071 });2051 });
2072 try testRoundtripWtf16(&[_]u16{2052 try testRoundtripWtf16(&[_]u16{
2073 std.mem.nativeToLittle(u16, 0xDC00), // low surrogate2053 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,10 +7404,14 @@ pub fn isIntegerType(ip: *const InternPool, ty: Index) bool {
7404 .c_ulong_type,7404 .c_ulong_type,
7405 .c_longlong_type,7405 .c_longlong_type,
7406 .c_ulonglong_type,7406 .c_ulonglong_type,
7407 .c_longdouble_type,
7408 .comptime_int_type,7407 .comptime_int_type,
7409 => true,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}
74137417
src/Sema.zig+31-2
...@@ -23328,7 +23328,8 @@ fn checkVectorElemType(...@@ -23328,7 +23328,8 @@ fn checkVectorElemType(
23328 const mod = sema.mod;23328 const mod = sema.mod;
23329 switch (ty.zigTypeTag(mod)) {23329 switch (ty.zigTypeTag(mod)) {
23330 .Int, .Float, .Bool => return,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 return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(mod)});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,7 +28456,7 @@ const CoerceOpts = struct {
28455 report_err: bool = true,28456 report_err: bool = true,
28456 /// Ignored if `report_err == false`.28457 /// Ignored if `report_err == false`.
28457 is_ret: bool = false,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 no_cast_to_comptime_int: bool = false,28460 no_cast_to_comptime_int: bool = false,
2846028461
28461 param_src: struct {28462 param_src: struct {
...@@ -31858,6 +31859,34 @@ fn coerceArrayLike(...@@ -31858,6 +31859,34 @@ fn coerceArrayLike(
31858 }31859 }
3185931860
31860 const dest_elem_ty = dest_ty.childType(mod);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 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);31890 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
31862 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);31891 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
31863 var runtime_src: ?LazySrcLoc = null;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,11 +2853,14 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
2853}2853}
28542854
2855fn airFpext(self: *Self, inst: Air.Inst.Index) !void {2855fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
2856 const mod = self.bin_file.comp.module.?;
2856 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;2857 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2857 const dst_ty = self.typeOfIndex(inst);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 const src_ty = self.typeOf(ty_op.operand);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.*);
28612864
2862 const result = result: {2865 const result = result: {
2863 if (switch (src_bits) {2866 if (switch (src_bits) {
...@@ -2881,94 +2884,290 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void {...@@ -2881,94 +2884,290 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
2881 },2884 },
2882 else => unreachable,2885 else => unreachable,
2883 }) {2886 }) {
2887 if (dst_ty.isVector(mod)) break :result null;
2884 var callee_buf: ["__extend?f?f2".len]u8 = undefined;2888 var callee_buf: ["__extend?f?f2".len]u8 = undefined;
2885 break :result try self.genCall(.{ .lib = .{2889 break :result try self.genCall(.{ .lib = .{
2886 .return_type = self.floatCompilerRtAbiType(dst_ty, src_ty).toIntern(),2890 .return_type = self.floatCompilerRtAbiType(dst_scalar_ty, src_scalar_ty).toIntern(),
2887 .param_types = &.{self.floatCompilerRtAbiType(src_ty, dst_ty).toIntern()},2891 .param_types = &.{self.floatCompilerRtAbiType(src_scalar_ty, dst_scalar_ty).toIntern()},
2888 .callee = std.fmt.bufPrint(&callee_buf, "__extend{c}f{c}f2", .{2892 .callee = std.fmt.bufPrint(&callee_buf, "__extend{c}f{c}f2", .{
2889 floatCompilerRtAbiName(src_bits),2893 floatCompilerRtAbiName(src_bits),
2890 floatCompilerRtAbiName(dst_bits),2894 floatCompilerRtAbiName(dst_bits),
2891 }) catch unreachable,2895 }) catch unreachable,
2892 } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }});2896 } }, &.{src_scalar_ty}, &.{.{ .air_ref = ty_op.operand }});
2893 }2897 }
28942898
2899 const src_abi_size: u32 = @intCast(src_ty.abiSize(mod));
2895 const src_mcv = try self.resolveInst(ty_op.operand);2900 const src_mcv = try self.resolveInst(ty_op.operand);
2896 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))2901 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2897 src_mcv2902 src_mcv
2898 else2903 else
2899 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv);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 const dst_lock = self.register_manager.lockReg(dst_reg);2907 const dst_lock = self.register_manager.lockReg(dst_reg);
2902 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);2908 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
29032909
2910 const vec_len = if (dst_ty.isVector(mod)) dst_ty.vectorLen(mod) else 1;
2904 if (src_bits == 16) {2911 if (src_bits == 16) {
2905 assert(self.hasFeature(.f16c));2912 assert(self.hasFeature(.f16c));
2906 const mat_src_reg = if (src_mcv.isRegister())2913 const mat_src_reg = if (src_mcv.isRegister())
2907 src_mcv.getReg().?2914 src_mcv.getReg().?
2908 else2915 else
2909 try self.copyToTmpRegister(src_ty, src_mcv);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 switch (dst_bits) {2922 switch (dst_bits) {
2912 32 => {},2923 32 => {},
2913 64 => try self.asmRegisterRegisterRegister(2924 64 => try self.asmRegisterRegisterRegister(
2914 .{ .v_sd, .cvtss2 },2925 .{ .v_sd, .cvtss2 },
2915 dst_reg,2926 dst_alias,
2916 dst_reg,2927 dst_alias,
2917 dst_reg,2928 dst_alias,
2918 ),2929 ),
2919 else => unreachable,2930 else => unreachable,
2920 }2931 }
2921 } else {2932 } else {
2922 assert(src_bits == 32 and dst_bits == 64);2933 assert(src_bits == 32 and dst_bits == 64);
2923 if (self.hasFeature(.avx)) if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory(2934 if (self.hasFeature(.avx)) switch (vec_len) {
2924 .{ .v_sd, .cvtss2 },2935 1 => if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory(
2925 dst_reg,2936 .{ .v_sd, .cvtss2 },
2926 dst_reg,2937 dst_alias,
2927 try src_mcv.mem(self, .dword),2938 dst_alias,
2928 ) else try self.asmRegisterRegisterRegister(2939 try src_mcv.mem(self, self.memSize(src_ty)),
2929 .{ .v_sd, .cvtss2 },2940 ) else try self.asmRegisterRegisterRegister(
2930 dst_reg,2941 .{ .v_sd, .cvtss2 },
2931 dst_reg,2942 dst_alias,
2932 (if (src_mcv.isRegister())2943 dst_alias,
2933 src_mcv.getReg().?2944 registerAlias(if (src_mcv.isRegister())
2934 else2945 src_mcv.getReg().?
2935 try self.copyToTmpRegister(src_ty, src_mcv)).to128(),2946 else
2936 ) else if (src_mcv.isMemory()) try self.asmRegisterMemory(2947 try self.copyToTmpRegister(src_ty, src_mcv), src_abi_size),
2937 .{ ._sd, .cvtss2 },2948 ),
2938 dst_reg,2949 2...4 => if (src_mcv.isMemory()) try self.asmRegisterMemory(
2939 try src_mcv.mem(self, .dword),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 ) else try self.asmRegisterRegister(2970 ) else try self.asmRegisterRegister(
2941 .{ ._sd, .cvtss2 },2971 switch (vec_len) {
2942 dst_reg,2972 1 => .{ ._sd, .cvtss2 },
2943 (if (src_mcv.isRegister())2973 2 => .{ ._pd, .cvtps2 },
2974 else => break :result null,
2975 },
2976 dst_alias,
2977 registerAlias(if (src_mcv.isRegister())
2944 src_mcv.getReg().?2978 src_mcv.getReg().?
2945 else2979 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 break :result dst_mcv;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 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2987 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2952}2988}
29532989
2954fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {2990fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
2955 const mod = self.bin_file.comp.module.?;2991 const mod = self.bin_file.comp.module.?;
2956 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;2992 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2957 const result: MCValue = result: {2993 const src_ty = self.typeOf(ty_op.operand);
2958 const src_ty = self.typeOf(ty_op.operand);2994 const dst_ty = self.typeOfIndex(inst);
2959 const src_int_info = src_ty.intInfo(mod);
29602995
2961 const dst_ty = self.typeOfIndex(inst);2996 const result = @as(?MCValue, result: {
2962 const dst_int_info = dst_ty.intInfo(mod);2997 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(mod));
2963 const abi_size: u32 = @intCast(dst_ty.abiSize(mod));
29642998
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 const extend = switch (src_int_info.signedness) {3001 const extend = switch (src_int_info.signedness) {
2967 .signed => dst_int_info,3002 .signed => dst_int_info,
2968 .unsigned => src_int_info,3003 .unsigned => src_int_info,
2969 }.signedness;3004 }.signedness;
29703005
2971 const src_mcv = try self.resolveInst(ty_op.operand);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 const src_storage_bits: u16 = switch (src_mcv) {3171 const src_storage_bits: u16 = switch (src_mcv) {
2973 .register, .register_offset => 64,3172 .register, .register_offset => 64,
2974 .register_pair => 128,3173 .register_pair => 128,
...@@ -2986,13 +3185,13 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {...@@ -2986,13 +3185,13 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
2986 };3185 };
29873186
2988 if (dst_int_info.bits <= src_int_info.bits) break :result if (dst_mcv.isRegister())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 else3189 else
2991 dst_mcv;3190 dst_mcv;
29923191
2993 if (dst_mcv.isRegister()) {3192 if (dst_mcv.isRegister()) {
2994 try self.truncateRegister(src_ty, dst_mcv.getReg().?);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 }
29973196
2998 const src_limbs_len = math.divCeil(u16, src_int_info.bits, 64) catch unreachable;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,7 +3239,9 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
3040 );3239 );
30413240
3042 break :result dst_mcv;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 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3245 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3045}3246}
30463247
...@@ -3063,7 +3264,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {...@@ -3063,7 +3264,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
3063 src_mcv3264 src_mcv
3064 else if (dst_abi_size <= 8)3265 else if (dst_abi_size <= 8)
3065 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv)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 const dst_regs =3268 const dst_regs =
3068 try self.register_manager.allocRegs(2, .{ inst, inst }, abi.RegisterClass.gp);3269 try self.register_manager.allocRegs(2, .{ inst, inst }, abi.RegisterClass.gp);
3069 const dst_mcv: MCValue = .{ .register_pair = dst_regs };3270 const dst_mcv: MCValue = .{ .register_pair = dst_regs };
...@@ -3080,19 +3281,22 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {...@@ -3080,19 +3281,22 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
30803281
3081 if (dst_ty.zigTypeTag(mod) == .Vector) {3282 if (dst_ty.zigTypeTag(mod) == .Vector) {
3082 assert(src_ty.zigTypeTag(mod) == .Vector and dst_ty.vectorLen(mod) == src_ty.vectorLen(mod));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);3284 const dst_elem_ty = dst_ty.childType(mod);
3084 const src_info = src_ty.childType(mod).intInfo(mod);3285 const dst_elem_abi_size: u32 = @intCast(dst_elem_ty.abiSize(mod));
3085 const mir_tag = @as(?Mir.Inst.FixedTag, switch (dst_info.bits) {3286 const src_elem_ty = src_ty.childType(mod);
3086 8 => switch (src_info.bits) {3287 const src_elem_abi_size: u32 = @intCast(src_elem_ty.abiSize(mod));
3087 16 => switch (dst_ty.vectorLen(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 1...8 => if (self.hasFeature(.avx)) .{ .vp_b, .ackusw } else .{ .p_b, .ackusw },3292 1...8 => if (self.hasFeature(.avx)) .{ .vp_b, .ackusw } else .{ .p_b, .ackusw },
3089 9...16 => if (self.hasFeature(.avx2)) .{ .vp_b, .ackusw } else null,3293 9...16 => if (self.hasFeature(.avx2)) .{ .vp_b, .ackusw } else null,
3090 else => null,3294 else => null,
3091 },3295 },
3092 else => null,3296 else => null,
3093 },3297 },
3094 16 => switch (src_info.bits) {3298 2 => switch (src_elem_abi_size) {
3095 32 => switch (dst_ty.vectorLen(mod)) {3299 4 => switch (dst_ty.vectorLen(mod)) {
3096 1...4 => if (self.hasFeature(.avx))3300 1...4 => if (self.hasFeature(.avx))
3097 .{ .vp_w, .ackusd }3301 .{ .vp_w, .ackusd }
3098 else if (self.hasFeature(.sse4_1))3302 else if (self.hasFeature(.sse4_1))
...@@ -3107,12 +3311,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {...@@ -3107,12 +3311,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
3107 else => null,3311 else => null,
3108 }) orelse return self.fail("TODO implement airTrunc for {}", .{dst_ty.fmt(mod)});3312 }) orelse return self.fail("TODO implement airTrunc for {}", .{dst_ty.fmt(mod)});
31093313
3110 const elem_ty = src_ty.childType(mod);3314 const dst_info = dst_elem_ty.intInfo(mod);
3111 const mask_val = try mod.intValue(elem_ty, @as(u64, math.maxInt(u64)) >> @intCast(64 - dst_info.bits));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));
31123318
3113 const splat_ty = try mod.vectorType(.{3319 const splat_ty = try mod.vectorType(.{
3114 .len = @intCast(@divExact(@as(u64, if (src_abi_size > 16) 256 else 128), src_info.bits)),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 const splat_abi_size: u32 = @intCast(splat_ty.abiSize(mod));3323 const splat_abi_size: u32 = @intCast(splat_ty.abiSize(mod));
31183324
...@@ -4086,7 +4292,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -4086,7 +4292,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
4086 if (dst_info.bits > 128 and dst_info.signedness == .unsigned) {4292 if (dst_info.bits > 128 and dst_info.signedness == .unsigned) {
4087 const slow_inc = self.hasFeature(.slow_incdec);4293 const slow_inc = self.hasFeature(.slow_incdec);
4088 const abi_size: u32 = @intCast(dst_ty.abiSize(mod));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;
40904296
4091 try self.spillRegisters(&.{ .rax, .rcx, .rdx });4297 try self.spillRegisters(&.{ .rax, .rcx, .rdx });
4092 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx });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,7 +7141,7 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
6935 },7141 },
6936 else => {7142 else => {
6937 const abi_size: u31 = @intCast(ty.abiSize(mod));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;
69397145
6940 const tmp_regs =7146 const tmp_regs =
6941 try self.register_manager.allocRegs(3, .{null} ** 3, abi.RegisterClass.gp);7147 try self.register_manager.allocRegs(3, .{null} ** 3, abi.RegisterClass.gp);
...@@ -8222,7 +8428,7 @@ fn genShiftBinOpMir(...@@ -8222,7 +8428,7 @@ fn genShiftBinOpMir(
8222 try self.asmRegisterImmediate(8428 try self.asmRegisterImmediate(
8223 .{ ._, .@"and" },8429 .{ ._, .@"and" },
8224 .cl,8430 .cl,
8225 Immediate.u(std.math.maxInt(u6)),8431 Immediate.u(math.maxInt(u6)),
8226 );8432 );
8227 try self.asmRegisterImmediate(8433 try self.asmRegisterImmediate(
8228 .{ ._r, .sh },8434 .{ ._r, .sh },
...@@ -8259,7 +8465,7 @@ fn genShiftBinOpMir(...@@ -8259,7 +8465,7 @@ fn genShiftBinOpMir(
8259 try self.asmRegisterImmediate(8465 try self.asmRegisterImmediate(
8260 .{ ._, .@"and" },8466 .{ ._, .@"and" },
8261 .cl,8467 .cl,
8262 Immediate.u(std.math.maxInt(u6)),8468 Immediate.u(math.maxInt(u6)),
8263 );8469 );
8264 try self.asmRegisterImmediate(8470 try self.asmRegisterImmediate(
8265 .{ ._r, .sh },8471 .{ ._r, .sh },
...@@ -8324,7 +8530,7 @@ fn genShiftBinOpMir(...@@ -8324,7 +8530,7 @@ fn genShiftBinOpMir(
8324 }, .sh },8530 }, .sh },
8325 temp_regs[2].to64(),8531 temp_regs[2].to64(),
8326 temp_regs[3].to64(),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 else => try self.asmRegisterRegisterRegister(.{ switch (tag[0]) {8535 else => try self.asmRegisterRegisterRegister(.{ switch (tag[0]) {
8330 ._l => ._ld,8536 ._l => ._ld,
...@@ -8379,7 +8585,7 @@ fn genShiftBinOpMir(...@@ -8379,7 +8585,7 @@ fn genShiftBinOpMir(
8379 .immediate => |shift_imm| try self.asmRegisterImmediate(8585 .immediate => |shift_imm| try self.asmRegisterImmediate(
8380 tag,8586 tag,
8381 temp_regs[2].to64(),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 else => try self.asmRegisterRegister(tag, temp_regs[2].to64(), .cl),8590 else => try self.asmRegisterRegister(tag, temp_regs[2].to64(), .cl),
8385 }8591 }
...@@ -8974,7 +9180,7 @@ fn genMulDivBinOp(...@@ -8974,7 +9180,7 @@ fn genMulDivBinOp(
8974 switch (tag) {9180 switch (tag) {
8975 .mul, .mul_wrap => {9181 .mul, .mul_wrap => {
8976 const slow_inc = self.hasFeature(.slow_incdec);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;
89789184
8979 try self.spillRegisters(&.{ .rax, .rcx, .rdx });9185 try self.spillRegisters(&.{ .rax, .rcx, .rdx });
8980 const reg_locks = self.register_manager.lockRegs(3, .{ .rax, .rcx, .rdx });9186 const reg_locks = self.register_manager.lockRegs(3, .{ .rax, .rcx, .rdx });
...@@ -14535,7 +14741,7 @@ fn genSetReg(...@@ -14535,7 +14741,7 @@ fn genSetReg(
14535 ty,14741 ty,
14536 dst_reg.class(),14742 dst_reg.class(),
14537 self.getFrameAddrAlignment(frame_addr).compare(.gte, Alignment.fromLog2Units(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 .lea_frame => .{ .move = .{ ._, .lea } },14747 .lea_frame => .{ .move = .{ ._, .lea } },
...@@ -16833,6 +17039,7 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {...@@ -16833,6 +17039,7 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
16833 @intCast(mask_elem_val.toSignedInt(mod));17039 @intCast(mask_elem_val.toSignedInt(mod));
16834 }17040 }
1683517041
17042 const has_avx = self.hasFeature(.avx);
16836 const result = @as(?MCValue, result: {17043 const result = @as(?MCValue, result: {
16837 for (mask_elems) |mask_elem| {17044 for (mask_elems) |mask_elem| {
16838 if (mask_elem) |_| break;17045 if (mask_elem) |_| break;
...@@ -16858,7 +17065,6 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {...@@ -16858,7 +17065,6 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
16858 break :result dst_mcv;17065 break :result dst_mcv;
16859 }17066 }
1686017067
16861 const has_avx = self.hasFeature(.avx);
16862 shufpd: {17068 shufpd: {
16863 if (elem_abi_size != 8) break :shufpd;17069 if (elem_abi_size != 8) break :shufpd;
16864 if (max_abi_size > @as(u32, if (has_avx) 32 else 16)) break :shufpd;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,6 +335,8 @@ pub const Mnemonic = enum {
335 pextrb, pextrd, pextrq,335 pextrb, pextrd, pextrq,
336 pinsrb, pinsrd, pinsrq,336 pinsrb, pinsrd, pinsrq,
337 pmaxsb, pmaxsd, pmaxud, pmaxuw, pminsb, pminsd, pminud, pminuw,337 pmaxsb, pmaxsd, pmaxud, pmaxuw, pminsb, pminsd, pminud, pminuw,
338 pmovsxbd, pmovsxbq, pmovsxbw, pmovsxdq, pmovsxwd, pmovsxwq,
339 pmovzxbd, pmovzxbq, pmovzxbw, pmovzxdq, pmovzxwd, pmovzxwq,
338 pmulld,340 pmulld,
339 roundpd, roundps, roundsd, roundss,341 roundpd, roundps, roundsd, roundss,
340 // SSE4.2342 // SSE4.2
...@@ -387,6 +389,8 @@ pub const Mnemonic = enum {...@@ -387,6 +389,8 @@ pub const Mnemonic = enum {
387 vpmaxsb, vpmaxsd, vpmaxsw, vpmaxub, vpmaxud, vpmaxuw,389 vpmaxsb, vpmaxsd, vpmaxsw, vpmaxub, vpmaxud, vpmaxuw,
388 vpminsb, vpminsd, vpminsw, vpminub, vpminud, vpminuw,390 vpminsb, vpminsd, vpminsw, vpminub, vpminud, vpminuw,
389 vpmovmskb,391 vpmovmskb,
392 vpmovsxbd, vpmovsxbq, vpmovsxbw, vpmovsxdq, vpmovsxwd, vpmovsxwq,
393 vpmovzxbd, vpmovzxbq, vpmovzxbw, vpmovzxdq, vpmovzxwd, vpmovzxwq,
390 vpmulhw, vpmulld, vpmullw,394 vpmulhw, vpmulld, vpmullw,
391 vpor,395 vpor,
392 vpshufb, vpshufd, vpshufhw, vpshuflw,396 vpshufb, vpshufd, vpshufhw, vpshuflw,
src/arch/x86_64/Mir.zig+8
...@@ -658,6 +658,14 @@ pub const Inst = struct {...@@ -658,6 +658,14 @@ pub const Inst = struct {
658 /// Insert scalar single-precision floating-point value658 /// Insert scalar single-precision floating-point value
659 /// Insert packed floating-point values659 /// Insert packed floating-point values
660 insert,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 /// Round packed single-precision floating-point values669 /// Round packed single-precision floating-point values
662 /// Round scalar single-precision floating-point value670 /// Round scalar single-precision floating-point value
663 /// Round packed double-precision floating-point values671 /// Round packed double-precision floating-point values
src/arch/x86_64/encodings.zig+42
...@@ -1235,6 +1235,20 @@ pub const table = [_]Entry{...@@ -1235,6 +1235,20 @@ pub const table = [_]Entry{
12351235
1236 .{ .pminud, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x3b }, 0, .none, .sse4_1 },1236 .{ .pminud, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x3b }, 0, .none, .sse4_1 },
12371237
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 .{ .pmulld, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x40 }, 0, .none, .sse4_1 },1252 .{ .pmulld, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x40 }, 0, .none, .sse4_1 },
12391253
1240 .{ .roundpd, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x09 }, 0, .none, .sse4_1 },1254 .{ .roundpd, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x09 }, 0, .none, .sse4_1 },
...@@ -1587,6 +1601,20 @@ pub const table = [_]Entry{...@@ -1587,6 +1601,20 @@ pub const table = [_]Entry{
1587 .{ .vpmovmskb, .rm, &.{ .r32, .xmm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_128_wig, .avx },1601 .{ .vpmovmskb, .rm, &.{ .r32, .xmm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_128_wig, .avx },
1588 .{ .vpmovmskb, .rm, &.{ .r64, .xmm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_128_wig, .avx },1602 .{ .vpmovmskb, .rm, &.{ .r64, .xmm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_128_wig, .avx },
15891603
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 .{ .vpmulhw, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0xe5 }, 0, .vex_128_wig, .avx },1618 .{ .vpmulhw, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0xe5 }, 0, .vex_128_wig, .avx },
15911619
1592 .{ .vpmulld, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x40 }, 0, .vex_128_wig, .avx },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,6 +1844,20 @@ pub const table = [_]Entry{
1816 .{ .vpmovmskb, .rm, &.{ .r32, .ymm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_256_wig, .avx2 },1844 .{ .vpmovmskb, .rm, &.{ .r32, .ymm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_256_wig, .avx2 },
1817 .{ .vpmovmskb, .rm, &.{ .r64, .ymm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_256_wig, .avx2 },1845 .{ .vpmovmskb, .rm, &.{ .r64, .ymm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_256_wig, .avx2 },
18181846
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 .{ .vpmulhw, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0xe5 }, 0, .vex_256_wig, .avx2 },1861 .{ .vpmulhw, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0xe5 }, 0, .vex_256_wig, .avx2 },
18201862
1821 .{ .vpmulld, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0x40 }, 0, .vex_256_wig, .avx2 },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,41 +6109,48 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
6109 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;6109 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
61106110
6111 const inst_ty = f.typeOfIndex(inst);6111 const inst_ty = f.typeOfIndex(inst);
6112 const inst_scalar_ty = inst_ty.scalarType(mod);
6112 const operand = try f.resolveInst(ty_op.operand);6113 const operand = try f.resolveInst(ty_op.operand);
6113 try reap(f, inst, &.{ty_op.operand});6114 try reap(f, inst, &.{ty_op.operand});
6114 const operand_ty = f.typeOf(ty_op.operand);6115 const operand_ty = f.typeOf(ty_op.operand);
6116 const scalar_ty = operand_ty.scalarType(mod);
6115 const target = f.object.dg.module.getTarget();6117 const target = f.object.dg.module.getTarget();
6116 const operation = if (inst_ty.isRuntimeFloat() and operand_ty.isRuntimeFloat())6118 const operation = if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isRuntimeFloat())
6117 if (inst_ty.floatBits(target) < operand_ty.floatBits(target)) "trunc" else "extend"6119 if (inst_scalar_ty.floatBits(target) < scalar_ty.floatBits(target)) "trunc" else "extend"
6118 else if (inst_ty.isInt(mod) and operand_ty.isRuntimeFloat())6120 else if (inst_scalar_ty.isInt(mod) and scalar_ty.isRuntimeFloat())
6119 if (inst_ty.isSignedInt(mod)) "fix" else "fixuns"6121 if (inst_scalar_ty.isSignedInt(mod)) "fix" else "fixuns"
6120 else if (inst_ty.isRuntimeFloat() and operand_ty.isInt(mod))6122 else if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isInt(mod))
6121 if (operand_ty.isSignedInt(mod)) "float" else "floatun"6123 if (scalar_ty.isSignedInt(mod)) "float" else "floatun"
6122 else6124 else
6123 unreachable;6125 unreachable;
61246126
6125 const writer = f.object.writer();6127 const writer = f.object.writer();
6126 const local = try f.allocLocal(inst, inst_ty);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 try f.writeCValue(writer, local, .Other);6131 try f.writeCValue(writer, local, .Other);
61286132 try v.elem(f, writer);
6129 try writer.writeAll(" = ");6133 try a.assign(f, writer);
6130 if (inst_ty.isInt(mod) and operand_ty.isRuntimeFloat()) {6134 if (inst_scalar_ty.isInt(mod) and scalar_ty.isRuntimeFloat()) {
6131 try writer.writeAll("zig_wrap_");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 try writer.writeByte('(');6137 try writer.writeByte('(');
6134 }6138 }
6135 try writer.writeAll("zig_");6139 try writer.writeAll("zig_");
6136 try writer.writeAll(operation);6140 try writer.writeAll(operation);
6137 try writer.writeAll(compilerRtAbbrev(operand_ty, mod));6141 try writer.writeAll(compilerRtAbbrev(scalar_ty, mod));
6138 try writer.writeAll(compilerRtAbbrev(inst_ty, mod));6142 try writer.writeAll(compilerRtAbbrev(inst_scalar_ty, mod));
6139 try writer.writeByte('(');6143 try writer.writeByte('(');
6140 try f.writeCValue(writer, operand, .FunctionArgument);6144 try f.writeCValue(writer, operand, .FunctionArgument);
6145 try v.elem(f, writer);
6141 try writer.writeByte(')');6146 try writer.writeByte(')');
6142 if (inst_ty.isInt(mod) and operand_ty.isRuntimeFloat()) {6147 if (inst_scalar_ty.isInt(mod) and scalar_ty.isRuntimeFloat()) {
6143 try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits);6148 try f.object.dg.renderBuiltinInfo(writer, inst_scalar_ty, .bits);
6144 try writer.writeByte(')');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 return local;6154 return local;
6148}6155}
61496156
src/codegen/llvm.zig+8-2
...@@ -8648,8 +8648,6 @@ pub const FuncGen = struct {...@@ -8648,8 +8648,6 @@ pub const FuncGen = struct {
8648 const operand_ty = self.typeOf(ty_op.operand);8648 const operand_ty = self.typeOf(ty_op.operand);
8649 const dest_ty = self.typeOfIndex(inst);8649 const dest_ty = self.typeOfIndex(inst);
8650 const target = mod.getTarget();8650 const target = mod.getTarget();
8651 const dest_bits = dest_ty.floatBits(target);
8652 const src_bits = operand_ty.floatBits(target);
86538651
8654 if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) {8652 if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) {
8655 return self.wip.cast(.fpext, operand, try o.lowerType(dest_ty), "");8653 return self.wip.cast(.fpext, operand, try o.lowerType(dest_ty), "");
...@@ -8657,11 +8655,19 @@ pub const FuncGen = struct {...@@ -8657,11 +8655,19 @@ pub const FuncGen = struct {
8657 const operand_llvm_ty = try o.lowerType(operand_ty);8655 const operand_llvm_ty = try o.lowerType(operand_ty);
8658 const dest_llvm_ty = try o.lowerType(dest_ty);8656 const dest_llvm_ty = try o.lowerType(dest_ty);
86598657
8658 const dest_bits = dest_ty.scalarType(mod).floatBits(target);
8659 const src_bits = operand_ty.scalarType(mod).floatBits(target);
8660 const fn_name = try o.builder.fmt("__extend{s}f{s}f2", .{8660 const fn_name = try o.builder.fmt("__extend{s}f{s}f2", .{
8661 compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits),8661 compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits),
8662 });8662 });
86638663
8664 const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty);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 return self.wip.call(8671 return self.wip.call(
8666 .normal,8672 .normal,
8667 .ccc,8673 .ccc,
src/type.zig+2-1
...@@ -2134,7 +2134,8 @@ pub const Type = struct {...@@ -2134,7 +2134,8 @@ pub const Type = struct {
21342134
2135 /// Returns true if and only if the type is a fixed-width integer.2135 /// Returns true if and only if the type is a fixed-width integer.
2136 pub fn isInt(self: Type, mod: *const Module) bool {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 }
21392140
2140 /// Returns true if and only if the type is a fixed-width, signed integer.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,25 +601,25 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
601601
602test "@intCast on vector" {602test "@intCast on vector" {
603 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO603 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
604 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
605 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO604 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
606 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO605 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
607 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO606 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
607 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
608608
609 const S = struct {609 const S = struct {
610 fn doTheTest() !void {610 fn doTheTest() !void {
611 // Upcast (implicit, equivalent to @intCast)611 // Upcast (implicit, equivalent to @intCast)
612 var up0: @Vector(2, u8) = [_]u8{ 0x55, 0xaa };612 var up0: @Vector(2, u8) = [_]u8{ 0x55, 0xaa };
613 _ = &up0;613 _ = &up0;
614 const up1 = @as(@Vector(2, u16), up0);614 const up1: @Vector(2, u16) = up0;
615 const up2 = @as(@Vector(2, u32), up0);615 const up2: @Vector(2, u32) = up0;
616 const up3 = @as(@Vector(2, u64), up0);616 const up3: @Vector(2, u64) = up0;
617 // Downcast (safety-checked)617 // Downcast (safety-checked)
618 var down0 = up3;618 var down0 = up3;
619 _ = &down0;619 _ = &down0;
620 const down1 = @as(@Vector(2, u32), @intCast(down0));620 const down1: @Vector(2, u32) = @intCast(down0);
621 const down2 = @as(@Vector(2, u16), @intCast(down0));621 const down2: @Vector(2, u16) = @intCast(down0);
622 const down3 = @as(@Vector(2, u8), @intCast(down0));622 const down3: @Vector(2, u8) = @intCast(down0);
623623
624 try expect(mem.eql(u16, &@as([2]u16, up1), &[2]u16{ 0x55, 0xaa }));624 try expect(mem.eql(u16, &@as([2]u16, up1), &[2]u16{ 0x55, 0xaa }));
625 try expect(mem.eql(u32, &@as([2]u32, up2), &[2]u32{ 0x55, 0xaa }));625 try expect(mem.eql(u32, &@as([2]u32, up2), &[2]u32{ 0x55, 0xaa }));
...@@ -629,20 +629,10 @@ test "@intCast on vector" {...@@ -629,20 +629,10 @@ test "@intCast on vector" {
629 try expect(mem.eql(u16, &@as([2]u16, down2), &[2]u16{ 0x55, 0xaa }));629 try expect(mem.eql(u16, &@as([2]u16, down2), &[2]u16{ 0x55, 0xaa }));
630 try expect(mem.eql(u8, &@as([2]u8, down3), &[2]u8{ 0x55, 0xaa }));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 };
641633
642 try S.doTheTest();634 try S.doTheTest();
643 try comptime S.doTheTest();635 try comptime S.doTheTest();
644 try S.doTheTestFloat();
645 try comptime S.doTheTestFloat();
646}636}
647637
648test "@floatCast cast down" {638test "@floatCast cast down" {
...@@ -2340,10 +2330,31 @@ test "@floatCast on vector" {...@@ -2340,10 +2330,31 @@ test "@floatCast on vector" {
23402330
2341 const S = struct {2331 const S = struct {
2342 fn doTheTest() !void {2332 fn doTheTest() !void {
2343 var a: @Vector(3, f64) = .{ 1.5, 2.5, 3.5 };2333 {
2344 _ = &a;2334 var a: @Vector(2, f64) = .{ 1.5, 2.5 };
2345 const b: @Vector(3, f32) = @floatCast(a);2335 _ = &a;
2346 try expectEqual(@Vector(3, f32){ 1.5, 2.5, 3.5 }, b);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 };
23492360