authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-05-11 08:59:44-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-05-11 08:59:44-07:00
log7f7bd206dc8309ca767b47fb97bb9e7c2dc882c3
tree2d24acb80954ec2d33863befa9ce48ecd3075e72
parent5512455974a9dda5d2a86a81e4a5cc520cb7afa8
parent4d296debefccbd80f2007685a27386b7434464dd
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15519 from dweiller/issue-15482

Optimize lowering of `s[start..][0..len]`

22 files changed, 325 insertions(+), 31 deletions(-)

doc/langref.html.in+8
......@@ -2953,6 +2953,14 @@ test "basic slices" {
29532953 const array_ptr = array[0..array.len];
29542954 try expect(@TypeOf(array_ptr) == *[array.len]i32);
29552955
2956 // You can perform a slice-by-length by slicing twice. This allows the compiler
2957 // to perform some optimisations like recognising a comptime-known length when
2958 // the start position is only known at runtime.
2959 var runtime_start: usize = 1;
2960 const length = 2;
2961 const array_ptr_len = array[runtime_start..][0..length];
2962 try expect(@TypeOf(array_ptr_len) == *[length]i32);
2963
29562964 // Using the address-of operator on a slice gives a single-item pointer,
29572965 // while using the `ptr` field gives a many-item pointer.
29582966 try expect(@TypeOf(slice.ptr) == [*]i32);
lib/docs/main.js+17
......@@ -1097,6 +1097,23 @@ const NAV_MODES = {
10971097 payloadHtml += decl + "[" + start + ".." + end + sentinel + "]";
10981098 return payloadHtml;
10991099 }
1100 case "sliceLength": {
1101 let payloadHtml = "";
1102 const lhsExpr = zigAnalysis.exprs[expr.sliceLength.lhs];
1103 const startExpr = zigAnalysis.exprs[expr.sliceLength.start];
1104 const lenExpr = zigAnalysis.exprs[expr.sliceLength.len];
1105 let decl = exprName(lhsExpr, opts);
1106 let start = exprName(startExpr, opts);
1107 let len = exprName(lenExpr, opts);
1108 let sentinel = "";
1109 if (expr.sliceLength["sentinel"]) {
1110 const sentinelExpr = zigAnalysis.exprs[expr.sliceLength.sentinel];
1111 let sentinel_ = exprName(sentinelExpr, options);
1112 sentinel += " :" + sentinel_;
1113 }
1114 payloadHtml += decl + "[" + start + "..][0.." + len + sentinel + "]";
1115 return payloadHtml;
1116 }
11001117 case "sliceIndex": {
11011118 const sliceIndex = zigAnalysis.exprs[expr.sliceIndex];
11021119 return exprName(sliceIndex, opts, opts);
lib/std/Build/Cache/DepTokenizer.zig+1-1
......@@ -974,7 +974,7 @@ fn hexDump(out: anytype, bytes: []const u8) !void {
974974 var line: usize = 0;
975975 var offset: usize = 0;
976976 while (line < n16) : (line += 1) {
977 try hexDump16(out, offset, bytes[offset .. offset + 16]);
977 try hexDump16(out, offset, bytes[offset..][0..16]);
978978 offset += 16;
979979 }
980980
lib/std/array_list.zig+2-2
......@@ -173,7 +173,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
173173 @memcpy(self.items[i..][0..items.len], items);
174174 }
175175
176 /// Replace range of elements `list[start..start+len]` with `new_items`.
176 /// Replace range of elements `list[start..][0..len]` with `new_items`.
177177 /// Grows list if `len < new_items.len`.
178178 /// Shrinks list if `len > new_items.len`.
179179 /// Invalidates pointers if this ArrayList is resized.
......@@ -654,7 +654,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
654654 @memcpy(self.items[i..][0..items.len], items);
655655 }
656656
657 /// Replace range of elements `list[start..start+len]` with `new_items`
657 /// Replace range of elements `list[start..][0..len]` with `new_items`
658658 /// Grows list if `len < new_items.len`.
659659 /// Shrinks list if `len > new_items.len`
660660 /// Invalidates pointers if this ArrayList is resized.
lib/std/bounded_array.zig+1-1
......@@ -168,7 +168,7 @@ pub fn BoundedArrayAligned(
168168 @memcpy(self.slice()[i..][0..items.len], items);
169169 }
170170
171 /// Replace range of elements `slice[start..start+len]` with `new_items`.
171 /// Replace range of elements `slice[start..][0..len]` with `new_items`.
172172 /// Grows slice if `len < new_items.len`.
173173 /// Shrinks slice if `len > new_items.len`.
174174 pub fn replaceRange(
lib/std/compress/deflate/decompressor.zig+1-1
......@@ -591,7 +591,7 @@ pub fn Decompressor(comptime ReaderType: type) type {
591591 }
592592
593593 if (!try self.hd1.init(self.allocator, self.bits[0..nlit]) or
594 !try self.hd2.init(self.allocator, self.bits[nlit .. nlit + ndist]))
594 !try self.hd2.init(self.allocator, self.bits[nlit..][0..ndist]))
595595 {
596596 corrupt_input_error_offset = self.roffset;
597597 self.err = InflateError.CorruptInput;
lib/std/compress/deflate/huffman_bit_writer.zig+3-3
......@@ -139,7 +139,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {
139139 self.bits >>= 48;
140140 self.nbits -= 48;
141141 var n = self.nbytes;
142 var bytes = self.bytes[n .. n + 6];
142 var bytes = self.bytes[n..][0..6];
143143 bytes[0] = @truncate(u8, bits);
144144 bytes[1] = @truncate(u8, bits >> 8);
145145 bytes[2] = @truncate(u8, bits >> 16);
......@@ -344,7 +344,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {
344344 self.bits >>= 48;
345345 self.nbits -= 48;
346346 var n = self.nbytes;
347 var bytes = self.bytes[n .. n + 6];
347 var bytes = self.bytes[n..][0..6];
348348 bytes[0] = @truncate(u8, bits);
349349 bytes[1] = @truncate(u8, bits >> 8);
350350 bytes[2] = @truncate(u8, bits >> 16);
......@@ -751,7 +751,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {
751751 var bits = self.bits;
752752 self.bits >>= 48;
753753 self.nbits -= 48;
754 var bytes = self.bytes[n .. n + 6];
754 var bytes = self.bytes[n..][0..6];
755755 bytes[0] = @truncate(u8, bits);
756756 bytes[1] = @truncate(u8, bits >> 8);
757757 bytes[2] = @truncate(u8, bits >> 16);
lib/std/hash/crc.zig+1-1
......@@ -160,7 +160,7 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type {
160160 pub fn update(self: *Self, input: []const u8) void {
161161 var i: usize = 0;
162162 while (i + 8 <= input.len) : (i += 8) {
163 const p = input[i .. i + 8];
163 const p = input[i..][0..8];
164164
165165 // Unrolling this way gives ~50Mb/s increase
166166 self.crc ^= std.mem.readIntLittle(u32, p[0..4]);
lib/std/hash/wyhash.zig+1-1
......@@ -65,7 +65,7 @@ const WyhashStateless = struct {
6565
6666 var off: usize = 0;
6767 while (off < b.len) : (off += 32) {
68 @call(.always_inline, self.round, .{b[off .. off + 32]});
68 @call(.always_inline, self.round, .{b[off..][0..32]});
6969 }
7070
7171 self.msg_len += b.len;
lib/std/json.zig+3-3
......@@ -63,7 +63,7 @@ fn encodesTo(decoded: []const u8, encoded: []const u8) bool {
6363 var buf: [4]u8 = undefined;
6464 const len = std.unicode.utf8Encode(codepoint, &buf) catch unreachable;
6565 if (i + len > decoded.len) return false;
66 if (!mem.eql(u8, decoded[i .. i + len], buf[0..len])) return false;
66 if (!mem.eql(u8, decoded[i..][0..len], buf[0..len])) return false;
6767 i += len;
6868 }
6969 }
......@@ -2285,10 +2285,10 @@ pub fn encodeJsonStringChars(chars: []const u8, options: StringifyOptions, write
22852285 const ulen = std.unicode.utf8ByteSequenceLength(chars[i]) catch unreachable;
22862286 // control characters (only things left with 1 byte length) should always be printed as unicode escapes
22872287 if (ulen == 1 or options.string.String.escape_unicode) {
2288 const codepoint = std.unicode.utf8Decode(chars[i .. i + ulen]) catch unreachable;
2288 const codepoint = std.unicode.utf8Decode(chars[i..][0..ulen]) catch unreachable;
22892289 try outputUnicodeEscape(codepoint, writer);
22902290 } else {
2291 try writer.writeAll(chars[i .. i + ulen]);
2291 try writer.writeAll(chars[i..][0..ulen]);
22922292 }
22932293 i += ulen - 1;
22942294 },
lib/std/math/big/int.zig+1-1
......@@ -4035,7 +4035,7 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void {
40354035
40364036 for (x_norm, 0..) |v, i| {
40374037 // Compute and add the squares
4038 const overflow = llmulLimb(.add, r[2 * i ..], x[i .. i + 1], v);
4038 const overflow = llmulLimb(.add, r[2 * i ..], x[i..][0..1], v);
40394039 assert(!overflow);
40404040 }
40414041}
lib/std/net.zig+1-1
......@@ -1701,7 +1701,7 @@ fn dnsParse(
17011701 p += @as(usize, 1) + @boolToInt(p[0] != 0);
17021702 const len = p[8] * @as(usize, 256) + p[9];
17031703 if (@ptrToInt(p) + len > @ptrToInt(r.ptr) + r.len) return error.InvalidDnsPacket;
1704 try callback(ctx, p[1], p[10 .. 10 + len], r);
1704 try callback(ctx, p[1], p[10..][0..len], r);
17051705 p += 10 + len;
17061706 }
17071707}
lib/std/os/windows.zig+2-2
......@@ -835,14 +835,14 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
835835 const len = buf.SubstituteNameLength >> 1;
836836 const path_buf = @as([*]const u16, &buf.PathBuffer);
837837 const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
838 return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer);
838 return parseReadlinkPath(path_buf[offset..][0..len], is_relative, out_buffer);
839839 },
840840 IO_REPARSE_TAG_MOUNT_POINT => {
841841 const buf = @ptrCast(*const MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
842842 const offset = buf.SubstituteNameOffset >> 1;
843843 const len = buf.SubstituteNameLength >> 1;
844844 const path_buf = @as([*]const u16, &buf.PathBuffer);
845 return parseReadlinkPath(path_buf[offset .. offset + len], false, out_buffer);
845 return parseReadlinkPath(path_buf[offset..][0..len], false, out_buffer);
846846 },
847847 else => |value| {
848848 std.debug.print("unsupported symlink type: {}", .{value});
lib/std/testing.zig+1-1
......@@ -941,7 +941,7 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
941941fn printWithVisibleNewlines(source: []const u8) void {
942942 var i: usize = 0;
943943 while (std.mem.indexOfScalar(u8, source[i..], '\n')) |nl| : (i += nl + 1) {
944 printLine(source[i .. i + nl]);
944 printLine(source[i..][0..nl]);
945945 }
946946 print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX)
947947}
lib/std/unicode.zig+1-1
......@@ -185,7 +185,7 @@ pub fn utf8CountCodepoints(s: []const u8) !usize {
185185
186186 switch (n) {
187187 1 => {}, // ASCII, no validation needed
188 else => _ = try utf8Decode(s[i .. i + n]),
188 else => _ = try utf8Decode(s[i..][0..n]),
189189 }
190190
191191 i += n;
src/AstGen.zig+54-2
......@@ -849,10 +849,35 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
849849 return rvalue(gz, ri, result, node);
850850 },
851851 .slice => {
852 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
853 const lhs_node = node_datas[node].lhs;
854 const lhs_tag = node_tags[lhs_node];
855 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
856 const lhs_is_open_slice = lhs_tag == .slice_open or
857 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
858 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
859 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
860
861 const start = if (lhs_is_slice_sentinel) start: {
862 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
863 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
864 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
865
866 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
867 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
868 try emitDbgStmt(gz, cursor);
869 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
870 .lhs = lhs,
871 .start = start,
872 .len = len,
873 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
874 .sentinel = .none,
875 });
876 return rvalue(gz, ri, result, node);
877 }
852878 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
853879
854880 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
855 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
856881 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
857882 const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);
858883 try emitDbgStmt(gz, cursor);
......@@ -864,10 +889,36 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
864889 return rvalue(gz, ri, result, node);
865890 },
866891 .slice_sentinel => {
892 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
893 const lhs_node = node_datas[node].lhs;
894 const lhs_tag = node_tags[lhs_node];
895 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
896 const lhs_is_open_slice = lhs_tag == .slice_open or
897 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
898 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
899 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
900
901 const start = if (lhs_is_slice_sentinel) start: {
902 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
903 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
904 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
905
906 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
907 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
908 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
909 try emitDbgStmt(gz, cursor);
910 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
911 .lhs = lhs,
912 .start = start,
913 .len = len,
914 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
915 .sentinel = sentinel,
916 });
917 return rvalue(gz, ri, result, node);
918 }
867919 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
868920
869921 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
870 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
871922 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
872923 const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
873924 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
......@@ -2557,6 +2608,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
25572608 .slice_start,
25582609 .slice_end,
25592610 .slice_sentinel,
2611 .slice_length,
25602612 .import,
25612613 .switch_block,
25622614 .switch_cond,
src/Autodoc.zig+69
......@@ -758,6 +758,7 @@ const DocData = struct {
758758 string: []const u8, // direct value
759759 sliceIndex: usize,
760760 slice: Slice,
761 sliceLength: SliceLength,
761762 cmpxchgIndex: usize,
762763 cmpxchg: Cmpxchg,
763764 builtin: Builtin,
......@@ -794,6 +795,12 @@ const DocData = struct {
794795 end: ?usize = null,
795796 sentinel: ?usize = null, // index in `exprs`
796797 };
798 const SliceLength = struct {
799 lhs: usize,
800 start: usize,
801 len: usize,
802 sentinel: ?usize = null,
803 };
797804 const Cmpxchg = struct {
798805 name: []const u8,
799806 type: usize,
......@@ -1296,6 +1303,68 @@ fn walkInstruction(
12961303 .expr = .{ .sliceIndex = slice_index },
12971304 };
12981305 },
1306 .slice_length => {
1307 const pl_node = data[inst_index].pl_node;
1308 const extra = file.zir.extraData(Zir.Inst.SliceLength, pl_node.payload_index);
1309
1310 const slice_index = self.exprs.items.len;
1311 try self.exprs.append(self.arena, .{ .slice = .{ .lhs = 0, .start = 0 } });
1312
1313 var lhs: DocData.WalkResult = try self.walkRef(
1314 file,
1315 parent_scope,
1316 parent_src,
1317 extra.data.lhs,
1318 false,
1319 );
1320 var start: DocData.WalkResult = try self.walkRef(
1321 file,
1322 parent_scope,
1323 parent_src,
1324 extra.data.start,
1325 false,
1326 );
1327 var len: DocData.WalkResult = try self.walkRef(
1328 file,
1329 parent_scope,
1330 parent_src,
1331 extra.data.len,
1332 false,
1333 );
1334 var sentinel_opt: ?DocData.WalkResult = if (extra.data.sentinel != .none)
1335 try self.walkRef(
1336 file,
1337 parent_scope,
1338 parent_src,
1339 extra.data.sentinel,
1340 false,
1341 )
1342 else
1343 null;
1344
1345 const lhs_index = self.exprs.items.len;
1346 try self.exprs.append(self.arena, lhs.expr);
1347 const start_index = self.exprs.items.len;
1348 try self.exprs.append(self.arena, start.expr);
1349 const len_index = self.exprs.items.len;
1350 try self.exprs.append(self.arena, len.expr);
1351 const sentinel_index = if (sentinel_opt) |sentinel| sentinel_index: {
1352 const index = self.exprs.items.len;
1353 try self.exprs.append(self.arena, sentinel.expr);
1354 break :sentinel_index index;
1355 } else null;
1356 self.exprs.items[slice_index] = .{ .sliceLength = .{
1357 .lhs = lhs_index,
1358 .start = start_index,
1359 .len = len_index,
1360 .sentinel = sentinel_index,
1361 } };
1362
1363 return DocData.WalkResult{
1364 .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
1365 .expr = .{ .sliceIndex = slice_index },
1366 };
1367 },
12991368
13001369 // @check array_cat and array_mul
13011370 .add,
src/Sema.zig+48-9
......@@ -985,6 +985,7 @@ fn analyzeBodyInner(
985985 .slice_end => try sema.zirSliceEnd(block, inst),
986986 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
987987 .slice_start => try sema.zirSliceStart(block, inst),
988 .slice_length => try sema.zirSliceLength(block, inst),
988989 .str => try sema.zirStr(block, inst),
989990 .switch_block => try sema.zirSwitchBlock(block, inst),
990991 .switch_cond => try sema.zirSwitchCond(block, inst, false),
......@@ -9931,7 +9932,7 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
99319932 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
99329933 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
99339934
9934 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src);
9935 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src, false);
99359936}
99369937
99379938fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9948,7 +9949,7 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
99489949 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
99499950 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
99509951
9951 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src);
9952 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src, false);
99529953}
99539954
99549955fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9967,7 +9968,29 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
99679968 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
99689969 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
99699970
9970 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src);
9971 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false);
9972}
9973
9974fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9975 const tracy = trace(@src());
9976 defer tracy.end();
9977
9978 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
9979 const src = inst_data.src();
9980 const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
9981 const array_ptr = try sema.resolveInst(extra.lhs);
9982 const start = try sema.resolveInst(extra.start);
9983 const len = try sema.resolveInst(extra.len);
9984 const sentinel = try sema.resolveInst(extra.sentinel);
9985 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
9986 const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset };
9987 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
9988 const sentinel_src: LazySrcLoc = if (sentinel == .none)
9989 .unneeded
9990 else
9991 .{ .node_offset_slice_sentinel = inst_data.src_node };
9992
9993 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
99719994}
99729995
99739996fn zirSwitchCapture(
......@@ -29193,6 +29216,7 @@ fn analyzeSlice(
2919329216 ptr_src: LazySrcLoc,
2919429217 start_src: LazySrcLoc,
2919529218 end_src: LazySrcLoc,
29219 by_length: bool,
2919629220) CompileError!Air.Inst.Ref {
2919729221 // Slice expressions can operate on a variable whose type is an array. This requires
2919829222 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
......@@ -29271,7 +29295,11 @@ fn analyzeSlice(
2927129295 const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen());
2927229296
2927329297 if (!end_is_len) {
29274 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29298 const end = if (by_length) end: {
29299 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29300 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
29301 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
29302 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
2927529303 if (try sema.resolveMaybeUndefVal(end)) |end_val| {
2927629304 const len_s_val = try Value.Tag.int_u64.create(
2927729305 sema.arena,
......@@ -29308,7 +29336,11 @@ fn analyzeSlice(
2930829336 break :e try sema.addConstant(Type.usize, len_val);
2930929337 } else if (slice_ty.isSlice()) {
2931029338 if (!end_is_len) {
29311 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29339 const end = if (by_length) end: {
29340 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29341 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
29342 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
29343 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
2931229344 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
2931329345 if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| {
2931429346 if (slice_val.isUndef()) {
......@@ -29355,7 +29387,11 @@ fn analyzeSlice(
2935529387 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
2935629388 }
2935729389 if (!end_is_len) {
29358 break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29390 if (by_length) {
29391 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29392 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
29393 break :e try sema.coerce(block, Type.usize, uncasted_end, end_src);
29394 } else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
2935929395 }
2936029396 return sema.fail(block, src, "slice of pointer must include end value", .{});
2936129397 };
......@@ -29379,7 +29415,7 @@ fn analyzeSlice(
2937929415 // requirement: start <= end
2938029416 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
2938129417 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
29382 if (!(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
29418 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
2938329419 return sema.fail(
2938429420 block,
2938529421 start_src,
......@@ -29432,11 +29468,14 @@ fn analyzeSlice(
2943229468 }
2943329469 }
2943429470
29435 if (block.wantSafety() and !block.is_comptime) {
29471 if (!by_length and block.wantSafety() and !block.is_comptime) {
2943629472 // requirement: start <= end
2943729473 try sema.panicStartLargerThanEnd(block, start, end);
2943829474 }
29439 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
29475 const new_len = if (by_length)
29476 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
29477 else
29478 try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
2944029479 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
2944129480
2944229481 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
src/Zir.zig+15
......@@ -570,6 +570,10 @@ pub const Inst = struct {
570570 /// Returns a pointer to the subslice.
571571 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
572572 slice_sentinel,
573 /// Slice operation `array_ptr[start..][0..len]`. Optional sentinel.
574 /// Returns a pointer to the subslice.
575 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
576 slice_length,
573577 /// Write a value to a pointer. For loading, see `load`.
574578 /// Source location is assumed to be same as previous instruction.
575579 /// Uses the `bin` union field.
......@@ -1135,6 +1139,7 @@ pub const Inst = struct {
11351139 .slice_start,
11361140 .slice_end,
11371141 .slice_sentinel,
1142 .slice_length,
11381143 .import,
11391144 .typeof_log2_int_type,
11401145 .resolve_inferred_alloc,
......@@ -1430,6 +1435,7 @@ pub const Inst = struct {
14301435 .slice_start,
14311436 .slice_end,
14321437 .slice_sentinel,
1438 .slice_length,
14331439 .import,
14341440 .typeof_log2_int_type,
14351441 .switch_capture,
......@@ -1667,6 +1673,7 @@ pub const Inst = struct {
16671673 .slice_start = .pl_node,
16681674 .slice_end = .pl_node,
16691675 .slice_sentinel = .pl_node,
1676 .slice_length = .pl_node,
16701677 .store = .bin,
16711678 .store_node = .pl_node,
16721679 .store_to_block_ptr = .bin,
......@@ -2980,6 +2987,14 @@ pub const Inst = struct {
29802987 sentinel: Ref,
29812988 };
29822989
2990 pub const SliceLength = struct {
2991 lhs: Ref,
2992 start: Ref,
2993 len: Ref,
2994 sentinel: Ref,
2995 start_src_node_offset: i32,
2996 };
2997
29832998 /// The meaning of these operands depends on the corresponding `Tag`.
29842999 pub const Bin = struct {
29853000 lhs: Ref,
src/link/Plan9/aout.zig+1-1
......@@ -21,7 +21,7 @@ pub const ExecHdr = extern struct {
2121 var buf: [40]u8 = undefined;
2222 var i: u8 = 0;
2323 inline for (std.meta.fields(@This())) |f| {
24 std.mem.writeIntSliceBig(u32, buf[i .. i + 4], @field(self, f.name));
24 std.mem.writeIntSliceBig(u32, buf[i..][0..4], @field(self, f.name));
2525 i += 4;
2626 }
2727 return buf;
src/print_zir.zig+17
......@@ -267,6 +267,7 @@ const Writer = struct {
267267 .slice_start => try self.writeSliceStart(stream, inst),
268268 .slice_end => try self.writeSliceEnd(stream, inst),
269269 .slice_sentinel => try self.writeSliceSentinel(stream, inst),
270 .slice_length => try self.writeSliceLength(stream, inst),
270271
271272 .union_init => try self.writeUnionInit(stream, inst),
272273
......@@ -756,6 +757,22 @@ const Writer = struct {
756757 try self.writeSrc(stream, inst_data.src());
757758 }
758759
760 fn writeSliceLength(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
761 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
762 const extra = self.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
763 try self.writeInstRef(stream, extra.lhs);
764 try stream.writeAll(", ");
765 try self.writeInstRef(stream, extra.start);
766 try stream.writeAll(", ");
767 try self.writeInstRef(stream, extra.len);
768 if (extra.sentinel != .none) {
769 try stream.writeAll(", ");
770 try self.writeInstRef(stream, extra.sentinel);
771 }
772 try stream.writeAll(") ");
773 try self.writeSrc(stream, inst_data.src());
774 }
775
759776 fn writeUnionInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
760777 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
761778 const extra = self.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
test/behavior/slice.zig+77
......@@ -180,6 +180,18 @@ test "slicing zero length array" {
180180 try expect(mem.eql(u32, s2, &[_]u32{}));
181181}
182182
183test "slicing pointer by length" {
184 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
185 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
186 const ptr: [*]const u8 = @ptrCast([*]const u8, &array);
187 const slice = ptr[1..][0..5];
188 try expect(slice.len == 5);
189 var i: usize = 0;
190 while (i < slice.len) : (i += 1) {
191 try expect(slice[i] == i + 2);
192 }
193}
194
183195const x = @intToPtr([*]i32, 0x1000)[0..0x500];
184196const y = x[0x100..];
185197test "compile time slice of pointer to hard coded address" {
......@@ -355,6 +367,10 @@ test "slice syntax resulting in pointer-to-array" {
355367 try testSlice();
356368 try testSliceOpt();
357369 try testSliceAlign();
370 try testSliceLength();
371 try testSliceLengthZ();
372 try testArrayLength();
373 try testArrayLengthZ();
358374 }
359375
360376 fn testArray() !void {
......@@ -465,6 +481,67 @@ test "slice syntax resulting in pointer-to-array" {
465481 try expectEqualSlices("a"[0..] ++ "b"[0..], "ab");
466482 try expectEqualSlices("a"[0.. :0] ++ "b"[0.. :0], "ab");
467483 }
484
485 fn testSliceLength() !void {
486 var array = [5]u8{ 1, 2, 3, 4, 5 };
487 var slice: []u8 = &array;
488 comptime try expect(@TypeOf(slice[1..][0..2]) == *[2]u8);
489 comptime try expect(@TypeOf(slice[1..][0..4]) == *[4]u8);
490 comptime try expect(@TypeOf(slice[1..][0..2 :4]) == *[2:4]u8);
491 }
492
493 fn testSliceLengthZ() !void {
494 var array = [5:0]u8{ 1, 2, 3, 4, 5 };
495 var slice: [:0]u8 = &array;
496 comptime try expect(@TypeOf(slice[1..][0..2]) == *[2]u8);
497 comptime try expect(@TypeOf(slice[1..][0..2 :4]) == *[2:4]u8);
498 comptime try expect(@TypeOf(slice[1.. :0][0..2]) == *[2]u8);
499 comptime try expect(@TypeOf(slice[1.. :0][0..2 :4]) == *[2:4]u8);
500 }
501
502 fn testArrayLength() !void {
503 var array = [5]u8{ 1, 2, 3, 4, 5 };
504 comptime try expect(@TypeOf(array[1..][0..2]) == *[2]u8);
505 comptime try expect(@TypeOf(array[1..][0..4]) == *[4]u8);
506 comptime try expect(@TypeOf(array[1..][0..2 :4]) == *[2:4]u8);
507 }
508
509 fn testArrayLengthZ() !void {
510 var array = [5:0]u8{ 1, 2, 3, 4, 5 };
511 comptime try expect(@TypeOf(array[1..][0..2]) == *[2]u8);
512 comptime try expect(@TypeOf(array[1..][0..4]) == *[4:0]u8);
513 comptime try expect(@TypeOf(array[1..][0..2 :4]) == *[2:4]u8);
514 comptime try expect(@TypeOf(array[1.. :0][0..2]) == *[2]u8);
515 comptime try expect(@TypeOf(array[1.. :0][0..4]) == *[4:0]u8);
516 comptime try expect(@TypeOf(array[1.. :0][0..2 :4]) == *[2:4]u8);
517 }
518
519 fn testMultiPointer() !void {
520 var array = [5]u8{ 1, 2, 3, 4, 5 };
521 var ptr: [*]u8 = &array;
522 comptime try expect(@TypeOf(ptr[1..][0..2]) == *[2]u8);
523 comptime try expect(@TypeOf(ptr[1..][0..4]) == *[4]u8);
524 comptime try expect(@TypeOf(ptr[1..][0..2 :4]) == *[2:4]u8);
525 }
526
527 fn testMultiPointerLengthZ() !void {
528 var array = [5:0]u8{ 1, 2, 3, 4, 5 };
529 var ptr: [*]u8 = &array;
530 comptime try expect(@TypeOf(ptr[1..][0..2]) == *[2]u8);
531 comptime try expect(@TypeOf(ptr[1..][0..4]) == *[4:0]u8);
532 comptime try expect(@TypeOf(ptr[1..][0..2 :4]) == *[2:4]u8);
533 comptime try expect(@TypeOf(ptr[1.. :0][0..2]) == *[2]u8);
534 comptime try expect(@TypeOf(ptr[1.. :0][0..4]) == *[4:0]u8);
535 comptime try expect(@TypeOf(ptr[1.. :0][0..2 :4]) == *[2:4]u8);
536
537 var ptr_z: [*:0]u8 = &array;
538 comptime try expect(@TypeOf(ptr_z[1..][0..2]) == *[2]u8);
539 comptime try expect(@TypeOf(ptr_z[1..][0..4]) == *[4:0]u8);
540 comptime try expect(@TypeOf(ptr_z[1..][0..2 :4]) == *[2:4]u8);
541 comptime try expect(@TypeOf(ptr_z[1.. :0][0..2]) == *[2]u8);
542 comptime try expect(@TypeOf(ptr_z[1.. :0][0..4]) == *[4:0]u8);
543 comptime try expect(@TypeOf(ptr_z[1.. :0][0..2 :4]) == *[2:4]u8);
544 }
468545 };
469546
470547 try S.doTheTest();