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 {
602602 const s = Utf8View.initComptime("abc");
603603
604604 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().?));
608608 try testing.expect(it1.nextCodepointSlice() == null);
609609
610610 var it2 = s.iterator();
......@@ -632,9 +632,9 @@ fn testUtf8ViewOk() !void {
632632 const s = Utf8View.initComptime("東京市");
633633
634634 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().?));
638638 try testing.expect(it1.nextCodepointSlice() == null);
639639
640640 var it2 = s.iterator();
......@@ -772,20 +772,20 @@ fn testUtf8Peeking() !void {
772772 const s = Utf8View.initComptime("noël");
773773 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)));
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)));
782782
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().?));
786786 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)));
789789}
790790
791791fn testError(bytes: []const u8, expected_err: anyerror) !void {
......@@ -927,20 +927,16 @@ test "fmtUtf8" {
927927}
928928
929929fn utf16LeToUtf8ArrayListImpl(
930 array_list: *std.ArrayList(u8),
930 result: *std.ArrayList(u8),
931931 utf16le: []const u16,
932932 comptime surrogates: Surrogates,
933933) (switch (surrogates) {
934934 .cannot_encode_surrogate_half => Utf16LeToUtf8AllocError,
935935 .can_encode_surrogate_half => mem.Allocator.Error,
936936})!void {
937 // optimistically guess that it will all be ascii.
938 try array_list.ensureTotalCapacityPrecise(utf16le.len);
937 assert(result.capacity >= utf16le.len);
939938
940939 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 })))
944940 vectorized: {
945941 const chunk_len = std.simd.suggestVectorLength(u16) orelse break :vectorized;
946942 const Chunk = @Vector(chunk_len, u16);
......@@ -948,41 +944,33 @@ fn utf16LeToUtf8ArrayListImpl(
948944 // Fast path. Check for and encode ASCII characters at the start of the input.
949945 while (remaining.len >= chunk_len) {
950946 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));
952948 if (@reduce(.Or, chunk | mask != mask)) {
953949 // found a non ASCII code unit
954950 break;
955951 }
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));
960953 // We allocated enough space to encode every UTF-16 code unit
961954 // as ASCII, so if the entire string is ASCII then we are
962955 // guaranteed to have enough space allocated
963 array_list.appendSliceAssumeCapacity(&ascii_bytes);
956 result.addManyAsArrayAssumeCapacity(chunk_len).* = ascii_chunk;
964957 remaining = remaining[chunk_len..];
965958 }
966959 }
967960
968 var out_index: usize = array_list.items.len;
969961 switch (surrogates) {
970962 .cannot_encode_surrogate_half => {
971963 var it = Utf16LeIterator.init(remaining);
972964 while (try it.nextCodepoint()) |codepoint| {
973965 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);
977967 }
978968 },
979969 .can_encode_surrogate_half => {
980970 var it = Wtf16LeIterator.init(remaining);
981971 while (it.nextCodepoint()) |codepoint| {
982972 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);
986974 }
987975 },
988976 }
......@@ -990,8 +978,9 @@ fn utf16LeToUtf8ArrayListImpl(
990978
991979pub const Utf16LeToUtf8AllocError = mem.Allocator.Error || Utf16LeToUtf8Error;
992980
993pub fn utf16LeToUtf8ArrayList(array_list: *std.ArrayList(u8), utf16le: []const u16) Utf16LeToUtf8AllocError!void {
994 return utf16LeToUtf8ArrayListImpl(array_list, utf16le, .cannot_encode_surrogate_half);
981pub 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);
995984}
996985
997986/// Deprecated; renamed to utf16LeToUtf8Alloc
......@@ -1003,8 +992,7 @@ pub fn utf16LeToUtf8Alloc(allocator: mem.Allocator, utf16le: []const u16) Utf16L
1003992 var result = try std.ArrayList(u8).initCapacity(allocator, utf16le.len);
1004993 errdefer result.deinit();
1005994
1006 try utf16LeToUtf8ArrayList(&result, utf16le);
1007
995 try utf16LeToUtf8ArrayListImpl(&result, utf16le, .cannot_encode_surrogate_half);
1008996 return result.toOwnedSlice();
1009997}
1010998
......@@ -1017,8 +1005,7 @@ pub fn utf16LeToUtf8AllocZ(allocator: mem.Allocator, utf16le: []const u16) Utf16
10171005 var result = try std.ArrayList(u8).initCapacity(allocator, utf16le.len + 1);
10181006 errdefer result.deinit();
10191007
1020 try utf16LeToUtf8ArrayList(&result, utf16le);
1021
1008 try utf16LeToUtf8ArrayListImpl(&result, utf16le, .cannot_encode_surrogate_half);
10221009 return result.toOwnedSliceSentinel(0);
10231010}
10241011
......@@ -1030,12 +1017,9 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr
10301017 .cannot_encode_surrogate_half => Utf16LeToUtf8Error,
10311018 .can_encode_surrogate_half => error{},
10321019})!usize {
1033 var end_index: usize = 0;
1020 var dest_index: usize = 0;
10341021
10351022 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 })))
10391023 vectorized: {
10401024 const chunk_len = std.simd.suggestVectorLength(u16) orelse break :vectorized;
10411025 const Chunk = @Vector(chunk_len, u16);
......@@ -1043,17 +1027,14 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr
10431027 // Fast path. Check for and encode ASCII characters at the start of the input.
10441028 while (remaining.len >= chunk_len) {
10451029 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));
10471031 if (@reduce(.Or, chunk | mask != mask)) {
10481032 // found a non ASCII code unit
10491033 break;
10501034 }
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;
10571038 remaining = remaining[chunk_len..];
10581039 }
10591040 }
......@@ -1062,7 +1043,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr
10621043 .cannot_encode_surrogate_half => {
10631044 var it = Utf16LeIterator.init(remaining);
10641045 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) {
10661047 // The maximum possible codepoint encoded by UTF-16 is U+10FFFF,
10671048 // which is within the valid codepoint range.
10681049 error.CodepointTooLarge => unreachable,
......@@ -1075,7 +1056,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr
10751056 .can_encode_surrogate_half => {
10761057 var it = Wtf16LeIterator.init(remaining);
10771058 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) {
10791060 // The maximum possible codepoint encoded by UTF-16 is U+10FFFF,
10801061 // which is within the valid codepoint range.
10811062 error.CodepointTooLarge => unreachable,
......@@ -1083,7 +1064,7 @@ fn utf16LeToUtf8Impl(utf8: []u8, utf16le: []const u16, comptime surrogates: Surr
10831064 }
10841065 },
10851066 }
1086 return end_index;
1067 return dest_index;
10871068}
10881069
10891070/// Deprecated; renamed to utf16LeToUtf8
......@@ -1156,18 +1137,12 @@ test utf16LeToUtf8 {
11561137 }
11571138}
11581139
1159fn 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);
1140fn utf8ToUtf16LeArrayListImpl(result: *std.ArrayList(u16), utf8: []const u8, comptime surrogates: Surrogates) !void {
1141 assert(result.capacity >= utf8.len);
11621142
11631143 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())
11691144 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;
11711146 const Chunk = @Vector(chunk_len, u8);
11721147
11731148 // 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,
11781153 // found a non ASCII code unit
11791154 break;
11801155 }
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;
11841158 remaining = remaining[chunk_len..];
11851159 }
11861160 }
......@@ -1192,21 +1166,18 @@ fn utf8ToUtf16LeArrayListImpl(array_list: *std.ArrayList(u16), utf8: []const u8,
11921166 var it = view.iterator();
11931167 while (it.nextCodepoint()) |codepoint| {
11941168 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)));
11971170 } else {
11981171 const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;
11991172 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) });
12041174 }
12051175 }
12061176}
12071177
1208pub fn utf8ToUtf16LeArrayList(array_list: *std.ArrayList(u16), utf8: []const u8) error{ InvalidUtf8, OutOfMemory }!void {
1209 return utf8ToUtf16LeArrayListImpl(array_list, utf8, .cannot_encode_surrogate_half);
1178pub 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);
12101181}
12111182
12121183pub 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
12151186 errdefer result.deinit();
12161187
12171188 try utf8ToUtf16LeArrayListImpl(&result, utf8, .cannot_encode_surrogate_half);
1218
12191189 return result.toOwnedSlice();
12201190}
12211191
......@@ -1228,7 +1198,6 @@ pub fn utf8ToUtf16LeAllocZ(allocator: mem.Allocator, utf8: []const u8) error{ In
12281198 errdefer result.deinit();
12291199
12301200 try utf8ToUtf16LeArrayListImpl(&result, utf8, .cannot_encode_surrogate_half);
1231
12321201 return result.toOwnedSliceSentinel(0);
12331202}
12341203
......@@ -1239,16 +1208,11 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) error{InvalidUtf8}!usize
12391208}
12401209
12411210pub fn utf8ToUtf16LeImpl(utf16le: []u16, utf8: []const u8, comptime surrogates: Surrogates) !usize {
1242 var dest_i: usize = 0;
1211 var dest_index: usize = 0;
12431212
12441213 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())
12501214 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;
12521216 const Chunk = @Vector(chunk_len, u8);
12531217
12541218 // 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:
12591223 // found a non ASCII code unit
12601224 break;
12611225 }
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;
12661229 remaining = remaining[chunk_len..];
12671230 }
12681231 }
12691232
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| {
12811239 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;
12851242 } else {
12861243 const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;
12871244 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;
12911247 }
1292 src_i = next_src_i;
12931248 }
1294 return dest_i;
1249 return dest_index;
12951250}
12961251
12971252test "utf8ToUtf16Le" {
1298 var utf16le: [2]u16 = [_]u16{0} ** 2;
1253 var utf16le: [128]u16 = undefined;
12991254 {
13001255 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]));
13031257 }
13041258 {
13051259 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]));
13081261 }
13091262 {
13101263 const result = utf8ToUtf16Le(utf16le[0..], "\xf4\x90\x80\x80");
13111264 try testing.expectError(error.InvalidUtf8, result);
13121265 }
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 }
13131280}
13141281
13151282test utf8ToUtf16LeArrayList {
......@@ -1354,25 +1321,40 @@ test utf8ToUtf16LeAllocZ {
13541321 {
13551322 const utf16 = try utf8ToUtf16LeAllocZ(testing.allocator, "𐐷");
13561323 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));
13581325 try testing.expect(utf16[2] == 0);
13591326 }
13601327 {
13611328 const utf16 = try utf8ToUtf16LeAllocZ(testing.allocator, "\u{10FFFF}");
13621329 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));
13641331 try testing.expect(utf16[2] == 0);
13651332 }
13661333 {
13671334 const result = utf8ToUtf16LeAllocZ(testing.allocator, "\xf4\x90\x80\x80");
13681335 try testing.expectError(error.InvalidUtf8, result);
13691336 }
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 }
13701352}
13711353
13721354/// 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 {
13741356 return comptime blk: {
1375 const len: usize = calcUtf16LeLen(utf8) catch |err| @compileError(err);
1357 const len: usize = calcUtf16LeLen(utf8) catch unreachable;
13761358 var utf16le: [len:0]u16 = [_:0]u16{0} ** len;
13771359 const utf16le_len = utf8ToUtf16Le(&utf16le, utf8[0..]) catch |err| @compileError(err);
13781360 assert(len == utf16le_len);
......@@ -1453,12 +1435,12 @@ test "fmtUtf16Le" {
14531435 try expectFmt("", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))});
14541436 try expectFmt("foo", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))});
14551437 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)})});
14621444}
14631445
14641446test "utf8ToUtf16LeStringLiteral" {
......@@ -1701,8 +1683,9 @@ pub const Wtf8Iterator = struct {
17011683 }
17021684};
17031685
1704pub fn wtf16LeToWtf8ArrayList(array_list: *std.ArrayList(u8), utf16le: []const u16) mem.Allocator.Error!void {
1705 return utf16LeToUtf8ArrayListImpl(array_list, utf16le, .can_encode_surrogate_half);
1686pub 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);
17061689}
17071690
17081691/// Caller must free returned memory.
......@@ -1711,8 +1694,7 @@ pub fn wtf16LeToWtf8Alloc(allocator: mem.Allocator, wtf16le: []const u16) mem.Al
17111694 var result = try std.ArrayList(u8).initCapacity(allocator, wtf16le.len);
17121695 errdefer result.deinit();
17131696
1714 try wtf16LeToWtf8ArrayList(&result, wtf16le);
1715
1697 try utf16LeToUtf8ArrayListImpl(&result, wtf16le, .can_encode_surrogate_half);
17161698 return result.toOwnedSlice();
17171699}
17181700
......@@ -1722,8 +1704,7 @@ pub fn wtf16LeToWtf8AllocZ(allocator: mem.Allocator, wtf16le: []const u16) mem.A
17221704 var result = try std.ArrayList(u8).initCapacity(allocator, wtf16le.len + 1);
17231705 errdefer result.deinit();
17241706
1725 try wtf16LeToWtf8ArrayList(&result, wtf16le);
1726
1707 try utf16LeToUtf8ArrayListImpl(&result, wtf16le, .can_encode_surrogate_half);
17271708 return result.toOwnedSliceSentinel(0);
17281709}
17291710
......@@ -1731,8 +1712,9 @@ pub fn wtf16LeToWtf8(wtf8: []u8, wtf16le: []const u16) usize {
17311712 return utf16LeToUtf8Impl(wtf8, wtf16le, .can_encode_surrogate_half) catch |err| switch (err) {};
17321713}
17331714
1734pub fn wtf8ToWtf16LeArrayList(array_list: *std.ArrayList(u16), wtf8: []const u8) error{ InvalidWtf8, OutOfMemory }!void {
1735 return utf8ToUtf16LeArrayListImpl(array_list, wtf8, .can_encode_surrogate_half);
1715pub 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);
17361718}
17371719
17381720pub 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
17411723 errdefer result.deinit();
17421724
17431725 try utf8ToUtf16LeArrayListImpl(&result, wtf8, .can_encode_surrogate_half);
1744
17451726 return result.toOwnedSlice();
17461727}
17471728
......@@ -1751,7 +1732,6 @@ pub fn wtf8ToWtf16LeAllocZ(allocator: mem.Allocator, wtf8: []const u8) error{ In
17511732 errdefer result.deinit();
17521733
17531734 try utf8ToUtf16LeArrayListImpl(&result, wtf8, .can_encode_surrogate_half);
1754
17551735 return result.toOwnedSliceSentinel(0);
17561736}
17571737
......@@ -1910,7 +1890,7 @@ pub const Wtf16LeIterator = struct {
19101890
19111891 pub fn init(s: []const u16) Wtf16LeIterator {
19121892 return Wtf16LeIterator{
1913 .bytes = std.mem.sliceAsBytes(s),
1893 .bytes = mem.sliceAsBytes(s),
19141894 .i = 0,
19151895 };
19161896 }
......@@ -1923,12 +1903,12 @@ pub const Wtf16LeIterator = struct {
19231903 assert(it.i <= it.bytes.len);
19241904 if (it.i == it.bytes.len) return null;
19251905 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);
19271907 it.i += 2;
19281908 surrogate_pair: {
19291909 if (utf16IsHighSurrogate(code_units[0])) {
19301910 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);
19321912 const codepoint = utf16DecodeSurrogatePair(&code_units) catch break :surrogate_pair;
19331913 it.i += 2;
19341914 return codepoint;
......@@ -2045,31 +2025,31 @@ fn testRoundtripWtf16(wtf16le: []const u16) !void {
20452025
20462026test "well-formed WTF-16 roundtrips" {
20472027 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
20502030 });
20512031 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
20552035 });
20562036 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
20592039 });
20602040 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
20632043 });
20642044 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
20672047 });
20682048 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
20712051 });
20722052 try testRoundtripWtf16(&[_]u16{
2073 std.mem.nativeToLittle(u16, 0xDC00), // low surrogate
2053 mem.nativeToLittle(u16, 0xDC00), // low surrogate
20742054 });
20752055}
src/InternPool.zig+6-2
......@@ -7404,10 +7404,14 @@ pub fn isIntegerType(ip: *const InternPool, ty: Index) bool {
74047404 .c_ulong_type,
74057405 .c_longlong_type,
74067406 .c_ulonglong_type,
7407 .c_longdouble_type,
74087407 .comptime_int_type,
74097408 => 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 },
74117415 };
74127416}
74137417
src/Sema.zig+31-2
......@@ -23328,7 +23328,8 @@ fn checkVectorElemType(
2332823328 const mod = sema.mod;
2332923329 switch (ty.zigTypeTag(mod)) {
2333023330 .Int, .Float, .Bool => return,
23331 else => if (ty.isPtrAtRuntime(mod)) return,
23331 .Optional, .Pointer => if (ty.isPtrAtRuntime(mod)) return,
23332 else => {},
2333223333 }
2333323334 return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(mod)});
2333423335}
......@@ -28455,7 +28456,7 @@ const CoerceOpts = struct {
2845528456 report_err: bool = true,
2845628457 /// Ignored if `report_err == false`.
2845728458 is_ret: bool = false,
28458 /// Should coercion to comptime_int ermit an error message.
28459 /// Should coercion to comptime_int emit an error message.
2845928460 no_cast_to_comptime_int: bool = false,
2846028461
2846128462 param_src: struct {
......@@ -31858,6 +31859,34 @@ fn coerceArrayLike(
3185831859 }
3185931860
3186031861 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
3186131890 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
3186231891 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
3186331892 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 {
28532853}
28542854
28552855fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
2856 const mod = self.bin_file.comp.module.?;
28562857 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
28572858 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.*);
28592861 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
28622865 const result = result: {
28632866 if (switch (src_bits) {
......@@ -2881,94 +2884,290 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
28812884 },
28822885 else => unreachable,
28832886 }) {
2887 if (dst_ty.isVector(mod)) break :result null;
28842888 var callee_buf: ["__extend?f?f2".len]u8 = undefined;
28852889 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()},
28882892 .callee = std.fmt.bufPrint(&callee_buf, "__extend{c}f{c}f2", .{
28892893 floatCompilerRtAbiName(src_bits),
28902894 floatCompilerRtAbiName(dst_bits),
28912895 }) catch unreachable,
2892 } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }});
2896 } }, &.{src_scalar_ty}, &.{.{ .air_ref = ty_op.operand }});
28932897 }
28942898
2899 const src_abi_size: u32 = @intCast(src_ty.abiSize(mod));
28952900 const src_mcv = try self.resolveInst(ty_op.operand);
28962901 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
28972902 src_mcv
28982903 else
28992904 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)));
29012907 const dst_lock = self.register_manager.lockReg(dst_reg);
29022908 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;
29042911 if (src_bits == 16) {
29052912 assert(self.hasFeature(.f16c));
29062913 const mat_src_reg = if (src_mcv.isRegister())
29072914 src_mcv.getReg().?
29082915 else
29092916 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 );
29112922 switch (dst_bits) {
29122923 32 => {},
29132924 64 => try self.asmRegisterRegisterRegister(
29142925 .{ .v_sd, .cvtss2 },
2915 dst_reg,
2916 dst_reg,
2917 dst_reg,
2926 dst_alias,
2927 dst_alias,
2928 dst_alias,
29182929 ),
29192930 else => unreachable,
29202931 }
29212932 } else {
29222933 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)),
29402970 ) 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())
29442978 src_mcv.getReg().?
29452979 else
2946 try self.copyToTmpRegister(src_ty, src_mcv)).to128(),
2980 try self.copyToTmpRegister(src_ty, src_mcv), src_abi_size),
29472981 );
29482982 }
29492983 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 });
29512987 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
29522988}
29532989
29542990fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
29552991 const mod = self.bin_file.comp.module.?;
29562992 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);
29602995
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));
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);
29663001 const extend = switch (src_int_info.signedness) {
29673002 .signed => dst_int_info,
29683003 .unsigned => src_int_info,
29693004 }.signedness;
29703005
29713006 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
29723171 const src_storage_bits: u16 = switch (src_mcv) {
29733172 .register, .register_offset => 64,
29743173 .register_pair => 128,
......@@ -2986,13 +3185,13 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
29863185 };
29873186
29883187 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) }
29903189 else
29913190 dst_mcv;
29923191
29933192 if (dst_mcv.isRegister()) {
29943193 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) };
29963195 }
29973196
29983197 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 {
30403239 );
30413240
30423241 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 });
30443245 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
30453246}
30463247
......@@ -3063,7 +3264,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
30633264 src_mcv
30643265 else if (dst_abi_size <= 8)
30653266 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: {
30673268 const dst_regs =
30683269 try self.register_manager.allocRegs(2, .{ inst, inst }, abi.RegisterClass.gp);
30693270 const dst_mcv: MCValue = .{ .register_pair = dst_regs };
......@@ -3080,19 +3281,22 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
30803281
30813282 if (dst_ty.zigTypeTag(mod) == .Vector) {
30823283 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)) {
30883292 1...8 => if (self.hasFeature(.avx)) .{ .vp_b, .ackusw } else .{ .p_b, .ackusw },
30893293 9...16 => if (self.hasFeature(.avx2)) .{ .vp_b, .ackusw } else null,
30903294 else => null,
30913295 },
30923296 else => null,
30933297 },
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)) {
30963300 1...4 => if (self.hasFeature(.avx))
30973301 .{ .vp_w, .ackusd }
30983302 else if (self.hasFeature(.sse4_1))
......@@ -3107,12 +3311,14 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
31073311 else => null,
31083312 }) orelse return self.fail("TODO implement airTrunc for {}", .{dst_ty.fmt(mod)});
31093313
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));
31123318
31133319 const splat_ty = try mod.vectorType(.{
31143320 .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,
31163322 });
31173323 const splat_abi_size: u32 = @intCast(splat_ty.abiSize(mod));
31183324
......@@ -4086,7 +4292,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
40864292 if (dst_info.bits > 128 and dst_info.signedness == .unsigned) {
40874293 const slow_inc = self.hasFeature(.slow_incdec);
40884294 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
40914297 try self.spillRegisters(&.{ .rax, .rcx, .rdx });
40924298 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx });
......@@ -6935,7 +7141,7 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
69357141 },
69367142 else => {
69377143 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
69407146 const tmp_regs =
69417147 try self.register_manager.allocRegs(3, .{null} ** 3, abi.RegisterClass.gp);
......@@ -8222,7 +8428,7 @@ fn genShiftBinOpMir(
82228428 try self.asmRegisterImmediate(
82238429 .{ ._, .@"and" },
82248430 .cl,
8225 Immediate.u(std.math.maxInt(u6)),
8431 Immediate.u(math.maxInt(u6)),
82268432 );
82278433 try self.asmRegisterImmediate(
82288434 .{ ._r, .sh },
......@@ -8259,7 +8465,7 @@ fn genShiftBinOpMir(
82598465 try self.asmRegisterImmediate(
82608466 .{ ._, .@"and" },
82618467 .cl,
8262 Immediate.u(std.math.maxInt(u6)),
8468 Immediate.u(math.maxInt(u6)),
82638469 );
82648470 try self.asmRegisterImmediate(
82658471 .{ ._r, .sh },
......@@ -8324,7 +8530,7 @@ fn genShiftBinOpMir(
83248530 }, .sh },
83258531 temp_regs[2].to64(),
83268532 temp_regs[3].to64(),
8327 Immediate.u(shift_imm & std.math.maxInt(u6)),
8533 Immediate.u(shift_imm & math.maxInt(u6)),
83288534 ),
83298535 else => try self.asmRegisterRegisterRegister(.{ switch (tag[0]) {
83308536 ._l => ._ld,
......@@ -8379,7 +8585,7 @@ fn genShiftBinOpMir(
83798585 .immediate => |shift_imm| try self.asmRegisterImmediate(
83808586 tag,
83818587 temp_regs[2].to64(),
8382 Immediate.u(shift_imm & std.math.maxInt(u6)),
8588 Immediate.u(shift_imm & math.maxInt(u6)),
83838589 ),
83848590 else => try self.asmRegisterRegister(tag, temp_regs[2].to64(), .cl),
83858591 }
......@@ -8974,7 +9180,7 @@ fn genMulDivBinOp(
89749180 switch (tag) {
89759181 .mul, .mul_wrap => {
89769182 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
89799185 try self.spillRegisters(&.{ .rax, .rcx, .rdx });
89809186 const reg_locks = self.register_manager.lockRegs(3, .{ .rax, .rcx, .rdx });
......@@ -14535,7 +14741,7 @@ fn genSetReg(
1453514741 ty,
1453614742 dst_reg.class(),
1453714743 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)),
1453914745 )),
1454014746 ),
1454114747 .lea_frame => .{ .move = .{ ._, .lea } },
......@@ -16833,6 +17039,7 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
1683317039 @intCast(mask_elem_val.toSignedInt(mod));
1683417040 }
1683517041
17042 const has_avx = self.hasFeature(.avx);
1683617043 const result = @as(?MCValue, result: {
1683717044 for (mask_elems) |mask_elem| {
1683817045 if (mask_elem) |_| break;
......@@ -16858,7 +17065,6 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
1685817065 break :result dst_mcv;
1685917066 }
1686017067
16861 const has_avx = self.hasFeature(.avx);
1686217068 shufpd: {
1686317069 if (elem_abi_size != 8) break :shufpd;
1686417070 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 {
335335 pextrb, pextrd, pextrq,
336336 pinsrb, pinsrd, pinsrq,
337337 pmaxsb, pmaxsd, pmaxud, pmaxuw, pminsb, pminsd, pminud, pminuw,
338 pmovsxbd, pmovsxbq, pmovsxbw, pmovsxdq, pmovsxwd, pmovsxwq,
339 pmovzxbd, pmovzxbq, pmovzxbw, pmovzxdq, pmovzxwd, pmovzxwq,
338340 pmulld,
339341 roundpd, roundps, roundsd, roundss,
340342 // SSE4.2
......@@ -387,6 +389,8 @@ pub const Mnemonic = enum {
387389 vpmaxsb, vpmaxsd, vpmaxsw, vpmaxub, vpmaxud, vpmaxuw,
388390 vpminsb, vpminsd, vpminsw, vpminub, vpminud, vpminuw,
389391 vpmovmskb,
392 vpmovsxbd, vpmovsxbq, vpmovsxbw, vpmovsxdq, vpmovsxwd, vpmovsxwq,
393 vpmovzxbd, vpmovzxbq, vpmovzxbw, vpmovzxdq, vpmovzxwd, vpmovzxwq,
390394 vpmulhw, vpmulld, vpmullw,
391395 vpor,
392396 vpshufb, vpshufd, vpshufhw, vpshuflw,
src/arch/x86_64/Mir.zig+8
......@@ -658,6 +658,14 @@ pub const Inst = struct {
658658 /// Insert scalar single-precision floating-point value
659659 /// Insert packed floating-point values
660660 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,
661669 /// Round packed single-precision floating-point values
662670 /// Round scalar single-precision floating-point value
663671 /// Round packed double-precision floating-point values
src/arch/x86_64/encodings.zig+42
......@@ -1235,6 +1235,20 @@ pub const table = [_]Entry{
12351235
12361236 .{ .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
12381252 .{ .pmulld, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x40 }, 0, .none, .sse4_1 },
12391253
12401254 .{ .roundpd, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x09 }, 0, .none, .sse4_1 },
......@@ -1587,6 +1601,20 @@ pub const table = [_]Entry{
15871601 .{ .vpmovmskb, .rm, &.{ .r32, .xmm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_128_wig, .avx },
15881602 .{ .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
15901618 .{ .vpmulhw, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0xe5 }, 0, .vex_128_wig, .avx },
15911619
15921620 .{ .vpmulld, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x40 }, 0, .vex_128_wig, .avx },
......@@ -1816,6 +1844,20 @@ pub const table = [_]Entry{
18161844 .{ .vpmovmskb, .rm, &.{ .r32, .ymm }, &.{ 0x66, 0x0f, 0xd7 }, 0, .vex_256_wig, .avx2 },
18171845 .{ .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
18191861 .{ .vpmulhw, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0xe5 }, 0, .vex_256_wig, .avx2 },
18201862
18211863 .{ .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 {
61096109 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
61106110
61116111 const inst_ty = f.typeOfIndex(inst);
6112 const inst_scalar_ty = inst_ty.scalarType(mod);
61126113 const operand = try f.resolveInst(ty_op.operand);
61136114 try reap(f, inst, &.{ty_op.operand});
61146115 const operand_ty = f.typeOf(ty_op.operand);
6116 const scalar_ty = operand_ty.scalarType(mod);
61156117 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"
61226124 else
61236125 unreachable;
61246126
61256127 const writer = f.object.writer();
61266128 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);
61276131 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()) {
61316135 try writer.writeAll("zig_wrap_");
6132 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty);
6136 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty);
61336137 try writer.writeByte('(');
61346138 }
61356139 try writer.writeAll("zig_");
61366140 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));
61396143 try writer.writeByte('(');
61406144 try f.writeCValue(writer, operand, .FunctionArgument);
6145 try v.elem(f, writer);
61416146 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);
61446149 try writer.writeByte(')');
61456150 }
6146 try writer.writeAll(";\n");
6151 try a.end(f, writer);
6152 try v.end(f, inst, writer);
6153
61476154 return local;
61486155}
61496156
src/codegen/llvm.zig+8-2
......@@ -8648,8 +8648,6 @@ pub const FuncGen = struct {
86488648 const operand_ty = self.typeOf(ty_op.operand);
86498649 const dest_ty = self.typeOfIndex(inst);
86508650 const target = mod.getTarget();
8651 const dest_bits = dest_ty.floatBits(target);
8652 const src_bits = operand_ty.floatBits(target);
86538651
86548652 if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) {
86558653 return self.wip.cast(.fpext, operand, try o.lowerType(dest_ty), "");
......@@ -8657,11 +8655,19 @@ pub const FuncGen = struct {
86578655 const operand_llvm_ty = try o.lowerType(operand_ty);
86588656 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);
86608660 const fn_name = try o.builder.fmt("__extend{s}f{s}f2", .{
86618661 compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits),
86628662 });
86638663
86648664 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 );
86658671 return self.wip.call(
86668672 .normal,
86678673 .ccc,
src/type.zig+2-1
......@@ -2134,7 +2134,8 @@ pub const Type = struct {
21342134
21352135 /// Returns true if and only if the type is a fixed-width integer.
21362136 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());
21382139 }
21392140
21402141 /// 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" {
601601
602602test "@intCast on vector" {
603603 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
604 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
605604 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
606605 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
607606 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
607 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
608608
609609 const S = struct {
610610 fn doTheTest() !void {
611611 // Upcast (implicit, equivalent to @intCast)
612612 var up0: @Vector(2, u8) = [_]u8{ 0x55, 0xaa };
613613 _ = &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;
617617 // Downcast (safety-checked)
618618 var down0 = up3;
619619 _ = &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);
623623
624624 try expect(mem.eql(u16, &@as([2]u16, up1), &[2]u16{ 0x55, 0xaa }));
625625 try expect(mem.eql(u32, &@as([2]u32, up2), &[2]u32{ 0x55, 0xaa }));
......@@ -629,20 +629,10 @@ test "@intCast on vector" {
629629 try expect(mem.eql(u16, &@as([2]u16, down2), &[2]u16{ 0x55, 0xaa }));
630630 try expect(mem.eql(u8, &@as([2]u8, down3), &[2]u8{ 0x55, 0xaa }));
631631 }
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 }
640632 };
641633
642634 try S.doTheTest();
643635 try comptime S.doTheTest();
644 try S.doTheTestFloat();
645 try comptime S.doTheTestFloat();
646636}
647637
648638test "@floatCast cast down" {
......@@ -2340,10 +2330,31 @@ test "@floatCast on vector" {
23402330
23412331 const S = struct {
23422332 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 }
23472358 }
23482359 };
23492360