mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Merge pull request #15519 from dweiller/issue-15482
Optimize lowering of `s[start..][0..len]`
This commit is contained in:
commit
7f7bd206dc
22 changed files with 325 additions and 31 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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});
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
57
src/Sema.zig
57
src/Sema.zig
|
|
@ -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;
|
||||
|
|
|
|||
15
src/Zir.zig
15
src/Zir.zig
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue