Merge pull request #15519 from dweiller/issue-15482

Optimize lowering of `s[start..][0..len]`
This commit is contained in:
Andrew Kelley 2023-05-11 08:59:44 -07:00 committed by GitHub
commit 7f7bd206dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 325 additions and 31 deletions

View file

@ -2953,6 +2953,14 @@ test "basic slices" {
const array_ptr = array[0..array.len];
try expect(@TypeOf(array_ptr) == *[array.len]i32);
// You can perform a slice-by-length by slicing twice. This allows the compiler
// to perform some optimisations like recognising a comptime-known length when
// the start position is only known at runtime.
var runtime_start: usize = 1;
const length = 2;
const array_ptr_len = array[runtime_start..][0..length];
try expect(@TypeOf(array_ptr_len) == *[length]i32);
// Using the address-of operator on a slice gives a single-item pointer,
// while using the `ptr` field gives a many-item pointer.
try expect(@TypeOf(slice.ptr) == [*]i32);

View file

@ -1097,6 +1097,23 @@ const NAV_MODES = {
payloadHtml += decl + "[" + start + ".." + end + sentinel + "]";
return payloadHtml;
}
case "sliceLength": {
let payloadHtml = "";
const lhsExpr = zigAnalysis.exprs[expr.sliceLength.lhs];
const startExpr = zigAnalysis.exprs[expr.sliceLength.start];
const lenExpr = zigAnalysis.exprs[expr.sliceLength.len];
let decl = exprName(lhsExpr, opts);
let start = exprName(startExpr, opts);
let len = exprName(lenExpr, opts);
let sentinel = "";
if (expr.sliceLength["sentinel"]) {
const sentinelExpr = zigAnalysis.exprs[expr.sliceLength.sentinel];
let sentinel_ = exprName(sentinelExpr, options);
sentinel += " :" + sentinel_;
}
payloadHtml += decl + "[" + start + "..][0.." + len + sentinel + "]";
return payloadHtml;
}
case "sliceIndex": {
const sliceIndex = zigAnalysis.exprs[expr.sliceIndex];
return exprName(sliceIndex, opts, opts);

View file

@ -974,7 +974,7 @@ fn hexDump(out: anytype, bytes: []const u8) !void {
var line: usize = 0;
var offset: usize = 0;
while (line < n16) : (line += 1) {
try hexDump16(out, offset, bytes[offset .. offset + 16]);
try hexDump16(out, offset, bytes[offset..][0..16]);
offset += 16;
}

View file

@ -173,7 +173,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
@memcpy(self.items[i..][0..items.len], items);
}
/// Replace range of elements `list[start..start+len]` with `new_items`.
/// Replace range of elements `list[start..][0..len]` with `new_items`.
/// Grows list if `len < new_items.len`.
/// Shrinks list if `len > new_items.len`.
/// Invalidates pointers if this ArrayList is resized.
@ -654,7 +654,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
@memcpy(self.items[i..][0..items.len], items);
}
/// Replace range of elements `list[start..start+len]` with `new_items`
/// Replace range of elements `list[start..][0..len]` with `new_items`
/// Grows list if `len < new_items.len`.
/// Shrinks list if `len > new_items.len`
/// Invalidates pointers if this ArrayList is resized.

View file

@ -168,7 +168,7 @@ pub fn BoundedArrayAligned(
@memcpy(self.slice()[i..][0..items.len], items);
}
/// Replace range of elements `slice[start..start+len]` with `new_items`.
/// Replace range of elements `slice[start..][0..len]` with `new_items`.
/// Grows slice if `len < new_items.len`.
/// Shrinks slice if `len > new_items.len`.
pub fn replaceRange(

View file

@ -591,7 +591,7 @@ pub fn Decompressor(comptime ReaderType: type) type {
}
if (!try self.hd1.init(self.allocator, self.bits[0..nlit]) or
!try self.hd2.init(self.allocator, self.bits[nlit .. nlit + ndist]))
!try self.hd2.init(self.allocator, self.bits[nlit..][0..ndist]))
{
corrupt_input_error_offset = self.roffset;
self.err = InflateError.CorruptInput;

View file

@ -139,7 +139,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {
self.bits >>= 48;
self.nbits -= 48;
var n = self.nbytes;
var bytes = self.bytes[n .. n + 6];
var bytes = self.bytes[n..][0..6];
bytes[0] = @truncate(u8, bits);
bytes[1] = @truncate(u8, bits >> 8);
bytes[2] = @truncate(u8, bits >> 16);
@ -344,7 +344,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {
self.bits >>= 48;
self.nbits -= 48;
var n = self.nbytes;
var bytes = self.bytes[n .. n + 6];
var bytes = self.bytes[n..][0..6];
bytes[0] = @truncate(u8, bits);
bytes[1] = @truncate(u8, bits >> 8);
bytes[2] = @truncate(u8, bits >> 16);
@ -751,7 +751,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {
var bits = self.bits;
self.bits >>= 48;
self.nbits -= 48;
var bytes = self.bytes[n .. n + 6];
var bytes = self.bytes[n..][0..6];
bytes[0] = @truncate(u8, bits);
bytes[1] = @truncate(u8, bits >> 8);
bytes[2] = @truncate(u8, bits >> 16);

View file

@ -160,7 +160,7 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type {
pub fn update(self: *Self, input: []const u8) void {
var i: usize = 0;
while (i + 8 <= input.len) : (i += 8) {
const p = input[i .. i + 8];
const p = input[i..][0..8];
// Unrolling this way gives ~50Mb/s increase
self.crc ^= std.mem.readIntLittle(u32, p[0..4]);

View file

@ -65,7 +65,7 @@ const WyhashStateless = struct {
var off: usize = 0;
while (off < b.len) : (off += 32) {
@call(.always_inline, self.round, .{b[off .. off + 32]});
@call(.always_inline, self.round, .{b[off..][0..32]});
}
self.msg_len += b.len;

View file

@ -63,7 +63,7 @@ fn encodesTo(decoded: []const u8, encoded: []const u8) bool {
var buf: [4]u8 = undefined;
const len = std.unicode.utf8Encode(codepoint, &buf) catch unreachable;
if (i + len > decoded.len) return false;
if (!mem.eql(u8, decoded[i .. i + len], buf[0..len])) return false;
if (!mem.eql(u8, decoded[i..][0..len], buf[0..len])) return false;
i += len;
}
}
@ -2285,10 +2285,10 @@ pub fn encodeJsonStringChars(chars: []const u8, options: StringifyOptions, write
const ulen = std.unicode.utf8ByteSequenceLength(chars[i]) catch unreachable;
// control characters (only things left with 1 byte length) should always be printed as unicode escapes
if (ulen == 1 or options.string.String.escape_unicode) {
const codepoint = std.unicode.utf8Decode(chars[i .. i + ulen]) catch unreachable;
const codepoint = std.unicode.utf8Decode(chars[i..][0..ulen]) catch unreachable;
try outputUnicodeEscape(codepoint, writer);
} else {
try writer.writeAll(chars[i .. i + ulen]);
try writer.writeAll(chars[i..][0..ulen]);
}
i += ulen - 1;
},

View file

@ -4035,7 +4035,7 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void {
for (x_norm, 0..) |v, i| {
// Compute and add the squares
const overflow = llmulLimb(.add, r[2 * i ..], x[i .. i + 1], v);
const overflow = llmulLimb(.add, r[2 * i ..], x[i..][0..1], v);
assert(!overflow);
}
}

View file

@ -1701,7 +1701,7 @@ fn dnsParse(
p += @as(usize, 1) + @boolToInt(p[0] != 0);
const len = p[8] * @as(usize, 256) + p[9];
if (@ptrToInt(p) + len > @ptrToInt(r.ptr) + r.len) return error.InvalidDnsPacket;
try callback(ctx, p[1], p[10 .. 10 + len], r);
try callback(ctx, p[1], p[10..][0..len], r);
p += 10 + len;
}
}

View file

@ -835,14 +835,14 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer);
return parseReadlinkPath(path_buf[offset..][0..len], is_relative, out_buffer);
},
IO_REPARSE_TAG_MOUNT_POINT => {
const buf = @ptrCast(*const MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
const offset = buf.SubstituteNameOffset >> 1;
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
return parseReadlinkPath(path_buf[offset .. offset + len], false, out_buffer);
return parseReadlinkPath(path_buf[offset..][0..len], false, out_buffer);
},
else => |value| {
std.debug.print("unsupported symlink type: {}", .{value});

View file

@ -941,7 +941,7 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
fn printWithVisibleNewlines(source: []const u8) void {
var i: usize = 0;
while (std.mem.indexOfScalar(u8, source[i..], '\n')) |nl| : (i += nl + 1) {
printLine(source[i .. i + nl]);
printLine(source[i..][0..nl]);
}
print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX)
}

View file

@ -185,7 +185,7 @@ pub fn utf8CountCodepoints(s: []const u8) !usize {
switch (n) {
1 => {}, // ASCII, no validation needed
else => _ = try utf8Decode(s[i .. i + n]),
else => _ = try utf8Decode(s[i..][0..n]),
}
i += n;

View file

@ -849,10 +849,35 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
return rvalue(gz, ri, result, node);
},
.slice => {
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
const lhs_node = node_datas[node].lhs;
const lhs_tag = node_tags[lhs_node];
const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
const lhs_is_open_slice = lhs_tag == .slice_open or
(lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
const start = if (lhs_is_slice_sentinel) start: {
const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
} else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
try emitDbgStmt(gz, cursor);
const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
.lhs = lhs,
.start = start,
.len = len,
.start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
.sentinel = .none,
});
return rvalue(gz, ri, result, node);
}
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);
try emitDbgStmt(gz, cursor);
@ -864,10 +889,36 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
return rvalue(gz, ri, result, node);
},
.slice_sentinel => {
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
const lhs_node = node_datas[node].lhs;
const lhs_tag = node_tags[lhs_node];
const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
const lhs_is_open_slice = lhs_tag == .slice_open or
(lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
const start = if (lhs_is_slice_sentinel) start: {
const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
} else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
try emitDbgStmt(gz, cursor);
const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
.lhs = lhs,
.start = start,
.len = len,
.start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
.sentinel = sentinel,
});
return rvalue(gz, ri, result, node);
}
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
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
.slice_start,
.slice_end,
.slice_sentinel,
.slice_length,
.import,
.switch_block,
.switch_cond,

View file

@ -758,6 +758,7 @@ const DocData = struct {
string: []const u8, // direct value
sliceIndex: usize,
slice: Slice,
sliceLength: SliceLength,
cmpxchgIndex: usize,
cmpxchg: Cmpxchg,
builtin: Builtin,
@ -794,6 +795,12 @@ const DocData = struct {
end: ?usize = null,
sentinel: ?usize = null, // index in `exprs`
};
const SliceLength = struct {
lhs: usize,
start: usize,
len: usize,
sentinel: ?usize = null,
};
const Cmpxchg = struct {
name: []const u8,
type: usize,
@ -1296,6 +1303,68 @@ fn walkInstruction(
.expr = .{ .sliceIndex = slice_index },
};
},
.slice_length => {
const pl_node = data[inst_index].pl_node;
const extra = file.zir.extraData(Zir.Inst.SliceLength, pl_node.payload_index);
const slice_index = self.exprs.items.len;
try self.exprs.append(self.arena, .{ .slice = .{ .lhs = 0, .start = 0 } });
var lhs: DocData.WalkResult = try self.walkRef(
file,
parent_scope,
parent_src,
extra.data.lhs,
false,
);
var start: DocData.WalkResult = try self.walkRef(
file,
parent_scope,
parent_src,
extra.data.start,
false,
);
var len: DocData.WalkResult = try self.walkRef(
file,
parent_scope,
parent_src,
extra.data.len,
false,
);
var sentinel_opt: ?DocData.WalkResult = if (extra.data.sentinel != .none)
try self.walkRef(
file,
parent_scope,
parent_src,
extra.data.sentinel,
false,
)
else
null;
const lhs_index = self.exprs.items.len;
try self.exprs.append(self.arena, lhs.expr);
const start_index = self.exprs.items.len;
try self.exprs.append(self.arena, start.expr);
const len_index = self.exprs.items.len;
try self.exprs.append(self.arena, len.expr);
const sentinel_index = if (sentinel_opt) |sentinel| sentinel_index: {
const index = self.exprs.items.len;
try self.exprs.append(self.arena, sentinel.expr);
break :sentinel_index index;
} else null;
self.exprs.items[slice_index] = .{ .sliceLength = .{
.lhs = lhs_index,
.start = start_index,
.len = len_index,
.sentinel = sentinel_index,
} };
return DocData.WalkResult{
.typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
.expr = .{ .sliceIndex = slice_index },
};
},
// @check array_cat and array_mul
.add,

View file

@ -985,6 +985,7 @@ fn analyzeBodyInner(
.slice_end => try sema.zirSliceEnd(block, inst),
.slice_sentinel => try sema.zirSliceSentinel(block, inst),
.slice_start => try sema.zirSliceStart(block, inst),
.slice_length => try sema.zirSliceLength(block, inst),
.str => try sema.zirStr(block, inst),
.switch_block => try sema.zirSwitchBlock(block, inst),
.switch_cond => try sema.zirSwitchCond(block, inst, false),
@ -9931,7 +9932,7 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src);
return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src, false);
}
fn 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
const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src);
return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src, false);
}
fn 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
const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src);
return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false);
}
fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
const array_ptr = try sema.resolveInst(extra.lhs);
const start = try sema.resolveInst(extra.start);
const len = try sema.resolveInst(extra.len);
const sentinel = try sema.resolveInst(extra.sentinel);
const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset };
const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
const sentinel_src: LazySrcLoc = if (sentinel == .none)
.unneeded
else
.{ .node_offset_slice_sentinel = inst_data.src_node };
return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
}
fn zirSwitchCapture(
@ -29193,6 +29216,7 @@ fn analyzeSlice(
ptr_src: LazySrcLoc,
start_src: LazySrcLoc,
end_src: LazySrcLoc,
by_length: bool,
) CompileError!Air.Inst.Ref {
// Slice expressions can operate on a variable whose type is an array. This requires
// 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(
const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen());
if (!end_is_len) {
const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
const end = if (by_length) end: {
const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
} else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
if (try sema.resolveMaybeUndefVal(end)) |end_val| {
const len_s_val = try Value.Tag.int_u64.create(
sema.arena,
@ -29308,7 +29336,11 @@ fn analyzeSlice(
break :e try sema.addConstant(Type.usize, len_val);
} else if (slice_ty.isSlice()) {
if (!end_is_len) {
const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
const end = if (by_length) end: {
const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
} else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| {
if (slice_val.isUndef()) {
@ -29355,7 +29387,11 @@ fn analyzeSlice(
break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
}
if (!end_is_len) {
break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
if (by_length) {
const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
break :e try sema.coerce(block, Type.usize, uncasted_end, end_src);
} else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
}
return sema.fail(block, src, "slice of pointer must include end value", .{});
};
@ -29379,7 +29415,7 @@ fn analyzeSlice(
// requirement: start <= end
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
if (!(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
return sema.fail(
block,
start_src,
@ -29432,11 +29468,14 @@ fn analyzeSlice(
}
}
if (block.wantSafety() and !block.is_comptime) {
if (!by_length and block.wantSafety() and !block.is_comptime) {
// requirement: start <= end
try sema.panicStartLargerThanEnd(block, start, end);
}
const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
const new_len = if (by_length)
try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
else
try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;

View file

@ -570,6 +570,10 @@ pub const Inst = struct {
/// Returns a pointer to the subslice.
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
slice_sentinel,
/// Slice operation `array_ptr[start..][0..len]`. Optional sentinel.
/// Returns a pointer to the subslice.
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
slice_length,
/// Write a value to a pointer. For loading, see `load`.
/// Source location is assumed to be same as previous instruction.
/// Uses the `bin` union field.
@ -1135,6 +1139,7 @@ pub const Inst = struct {
.slice_start,
.slice_end,
.slice_sentinel,
.slice_length,
.import,
.typeof_log2_int_type,
.resolve_inferred_alloc,
@ -1430,6 +1435,7 @@ pub const Inst = struct {
.slice_start,
.slice_end,
.slice_sentinel,
.slice_length,
.import,
.typeof_log2_int_type,
.switch_capture,
@ -1667,6 +1673,7 @@ pub const Inst = struct {
.slice_start = .pl_node,
.slice_end = .pl_node,
.slice_sentinel = .pl_node,
.slice_length = .pl_node,
.store = .bin,
.store_node = .pl_node,
.store_to_block_ptr = .bin,
@ -2980,6 +2987,14 @@ pub const Inst = struct {
sentinel: Ref,
};
pub const SliceLength = struct {
lhs: Ref,
start: Ref,
len: Ref,
sentinel: Ref,
start_src_node_offset: i32,
};
/// The meaning of these operands depends on the corresponding `Tag`.
pub const Bin = struct {
lhs: Ref,

View file

@ -21,7 +21,7 @@ pub const ExecHdr = extern struct {
var buf: [40]u8 = undefined;
var i: u8 = 0;
inline for (std.meta.fields(@This())) |f| {
std.mem.writeIntSliceBig(u32, buf[i .. i + 4], @field(self, f.name));
std.mem.writeIntSliceBig(u32, buf[i..][0..4], @field(self, f.name));
i += 4;
}
return buf;

View file

@ -267,6 +267,7 @@ const Writer = struct {
.slice_start => try self.writeSliceStart(stream, inst),
.slice_end => try self.writeSliceEnd(stream, inst),
.slice_sentinel => try self.writeSliceSentinel(stream, inst),
.slice_length => try self.writeSliceLength(stream, inst),
.union_init => try self.writeUnionInit(stream, inst),
@ -756,6 +757,22 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
fn writeSliceLength(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
const extra = self.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
try self.writeInstRef(stream, extra.lhs);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.start);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.len);
if (extra.sentinel != .none) {
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.sentinel);
}
try stream.writeAll(") ");
try self.writeSrc(stream, inst_data.src());
}
fn writeUnionInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
const extra = self.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;

View file

@ -180,6 +180,18 @@ test "slicing zero length array" {
try expect(mem.eql(u32, s2, &[_]u32{}));
}
test "slicing pointer by length" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
const ptr: [*]const u8 = @ptrCast([*]const u8, &array);
const slice = ptr[1..][0..5];
try expect(slice.len == 5);
var i: usize = 0;
while (i < slice.len) : (i += 1) {
try expect(slice[i] == i + 2);
}
}
const x = @intToPtr([*]i32, 0x1000)[0..0x500];
const y = x[0x100..];
test "compile time slice of pointer to hard coded address" {
@ -355,6 +367,10 @@ test "slice syntax resulting in pointer-to-array" {
try testSlice();
try testSliceOpt();
try testSliceAlign();
try testSliceLength();
try testSliceLengthZ();
try testArrayLength();
try testArrayLengthZ();
}
fn testArray() !void {
@ -465,6 +481,67 @@ test "slice syntax resulting in pointer-to-array" {
try expectEqualSlices("a"[0..] ++ "b"[0..], "ab");
try expectEqualSlices("a"[0.. :0] ++ "b"[0.. :0], "ab");
}
fn testSliceLength() !void {
var array = [5]u8{ 1, 2, 3, 4, 5 };
var slice: []u8 = &array;
comptime try expect(@TypeOf(slice[1..][0..2]) == *[2]u8);
comptime try expect(@TypeOf(slice[1..][0..4]) == *[4]u8);
comptime try expect(@TypeOf(slice[1..][0..2 :4]) == *[2:4]u8);
}
fn testSliceLengthZ() !void {
var array = [5:0]u8{ 1, 2, 3, 4, 5 };
var slice: [:0]u8 = &array;
comptime try expect(@TypeOf(slice[1..][0..2]) == *[2]u8);
comptime try expect(@TypeOf(slice[1..][0..2 :4]) == *[2:4]u8);
comptime try expect(@TypeOf(slice[1.. :0][0..2]) == *[2]u8);
comptime try expect(@TypeOf(slice[1.. :0][0..2 :4]) == *[2:4]u8);
}
fn testArrayLength() !void {
var array = [5]u8{ 1, 2, 3, 4, 5 };
comptime try expect(@TypeOf(array[1..][0..2]) == *[2]u8);
comptime try expect(@TypeOf(array[1..][0..4]) == *[4]u8);
comptime try expect(@TypeOf(array[1..][0..2 :4]) == *[2:4]u8);
}
fn testArrayLengthZ() !void {
var array = [5:0]u8{ 1, 2, 3, 4, 5 };
comptime try expect(@TypeOf(array[1..][0..2]) == *[2]u8);
comptime try expect(@TypeOf(array[1..][0..4]) == *[4:0]u8);
comptime try expect(@TypeOf(array[1..][0..2 :4]) == *[2:4]u8);
comptime try expect(@TypeOf(array[1.. :0][0..2]) == *[2]u8);
comptime try expect(@TypeOf(array[1.. :0][0..4]) == *[4:0]u8);
comptime try expect(@TypeOf(array[1.. :0][0..2 :4]) == *[2:4]u8);
}
fn testMultiPointer() !void {
var array = [5]u8{ 1, 2, 3, 4, 5 };
var ptr: [*]u8 = &array;
comptime try expect(@TypeOf(ptr[1..][0..2]) == *[2]u8);
comptime try expect(@TypeOf(ptr[1..][0..4]) == *[4]u8);
comptime try expect(@TypeOf(ptr[1..][0..2 :4]) == *[2:4]u8);
}
fn testMultiPointerLengthZ() !void {
var array = [5:0]u8{ 1, 2, 3, 4, 5 };
var ptr: [*]u8 = &array;
comptime try expect(@TypeOf(ptr[1..][0..2]) == *[2]u8);
comptime try expect(@TypeOf(ptr[1..][0..4]) == *[4:0]u8);
comptime try expect(@TypeOf(ptr[1..][0..2 :4]) == *[2:4]u8);
comptime try expect(@TypeOf(ptr[1.. :0][0..2]) == *[2]u8);
comptime try expect(@TypeOf(ptr[1.. :0][0..4]) == *[4:0]u8);
comptime try expect(@TypeOf(ptr[1.. :0][0..2 :4]) == *[2:4]u8);
var ptr_z: [*:0]u8 = &array;
comptime try expect(@TypeOf(ptr_z[1..][0..2]) == *[2]u8);
comptime try expect(@TypeOf(ptr_z[1..][0..4]) == *[4:0]u8);
comptime try expect(@TypeOf(ptr_z[1..][0..2 :4]) == *[2:4]u8);
comptime try expect(@TypeOf(ptr_z[1.. :0][0..2]) == *[2]u8);
comptime try expect(@TypeOf(ptr_z[1.. :0][0..4]) == *[4:0]u8);
comptime try expect(@TypeOf(ptr_z[1.. :0][0..2 :4]) == *[2:4]u8);
}
};
try S.doTheTest();