authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-09-25 16:20:19-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-09-26 12:35:14-07:00
logf2c8940aa6b1bd6ca20730e13b8148af002e1b91
tree295c72daba87d055c3f6dd0a8ca8e33a54847741
parent76f0b6e7d8dfa527f8776a4ffe61400fd84d871a

reintroduce the std.builtin safety panic helpers

motivated by performance

3 files changed, 126 insertions(+), 117 deletions(-)

lib/std/builtin.zig+67-6
...@@ -803,6 +803,7 @@ pub const PanicCause = union(enum) {...@@ -803,6 +803,7 @@ pub const PanicCause = union(enum) {
803 memcpy_alias,803 memcpy_alias,
804 noreturn_returned,804 noreturn_returned,
805 explicit_call: []const u8,805 explicit_call: []const u8,
806 sentinel_mismatch_isize: SentinelMismatchIsize,
806807
807 pub const IndexOutOfBounds = struct {808 pub const IndexOutOfBounds = struct {
808 index: usize,809 index: usize,
...@@ -819,22 +820,82 @@ pub const PanicCause = union(enum) {...@@ -819,22 +820,82 @@ pub const PanicCause = union(enum) {
819 found: usize,820 found: usize,
820 };821 };
821822
823 pub const SentinelMismatchIsize = struct {
824 expected: isize,
825 found: isize,
826 };
827
822 pub const InactiveUnionField = struct {828 pub const InactiveUnionField = struct {
823 active: []const u8,829 active: []const u8,
824 accessed: []const u8,830 accessed: []const u8,
825 };831 };
826};832};
827833
828pub noinline fn returnError(st: *StackTrace) void {834pub fn panicSentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn {
829 @branchHint(.cold);835 @branchHint(.cold);
830 @setRuntimeSafety(false);836 switch (@typeInfo(@TypeOf(expected))) {
831 addErrRetTraceAddr(st, @returnAddress());837 .int => |int| switch (int.signedness) {
838 .unsigned => if (int.bits <= @bitSizeOf(usize)) panic(.{ .sentinel_mismatch_usize = .{
839 .expected = expected,
840 .found = found,
841 } }, null, @returnAddress()),
842 .signed => if (int.bits <= @bitSizeOf(isize)) panic(.{ .sentinel_mismatch_isize = .{
843 .expected = expected,
844 .found = found,
845 } }, null, @returnAddress()),
846 },
847 .@"enum" => |info| switch (@typeInfo(info.tag_type)) {
848 .int => |int| switch (int.signedness) {
849 .unsigned => if (int.bits <= @bitSizeOf(usize)) panic(.{ .sentinel_mismatch_usize = .{
850 .expected = @intFromEnum(expected),
851 .found = @intFromEnum(found),
852 } }, null, @returnAddress()),
853 .signed => if (int.bits <= @bitSizeOf(isize)) panic(.{ .sentinel_mismatch_isize = .{
854 .expected = @intFromEnum(expected),
855 .found = @intFromEnum(found),
856 } }, null, @returnAddress()),
857 },
858 else => comptime unreachable,
859 },
860 else => {},
861 }
862 panic(.sentinel_mismatch_other, null, @returnAddress());
832}863}
833864
834pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {865pub fn panicUnwrapError(ert: ?*StackTrace, err: anyerror) noreturn {
835 if (st.index < st.instruction_addresses.len)866 @branchHint(.cold);
836 st.instruction_addresses[st.index] = addr;867 panic(.{ .unwrap_error = err }, ert, @returnAddress());
868}
869
870pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
871 @branchHint(.cold);
872 panic(.{ .index_out_of_bounds = .{
873 .index = index,
874 .len = len,
875 } }, null, @returnAddress());
876}
877
878pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn {
879 @branchHint(.cold);
880 panic(.{ .start_index_greater_than_end = .{
881 .start = start,
882 .end = end,
883 } }, null, @returnAddress());
884}
837885
886pub fn panicInactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn {
887 @branchHint(.cold);
888 panic(.{ .inactive_union_field = .{
889 .active = @tagName(active),
890 .accessed = @tagName(accessed),
891 } }, null, @returnAddress());
892}
893
894pub noinline fn returnError(st: *StackTrace) void {
895 @branchHint(.unlikely);
896 @setRuntimeSafety(false);
897 if (st.index < st.instruction_addresses.len)
898 st.instruction_addresses[st.index] = @returnAddress();
838 st.index += 1;899 st.index += 1;
839}900}
840901
lib/std/debug.zig+17-2
...@@ -453,7 +453,7 @@ threadlocal var panic_stage: usize = 0;...@@ -453,7 +453,7 @@ threadlocal var panic_stage: usize = 0;
453// This function avoids a dependency on formatted printing.453// This function avoids a dependency on formatted printing.
454pub fn defaultPanic(454pub fn defaultPanic(
455 cause: std.builtin.PanicCause,455 cause: std.builtin.PanicCause,
456 trace: ?*const std.builtin.StackTrace,456 error_return_trace: ?*const std.builtin.StackTrace,
457 first_trace_addr: ?usize,457 first_trace_addr: ?usize,
458) noreturn {458) noreturn {
459 @branchHint(.cold);459 @branchHint(.cold);
...@@ -568,7 +568,7 @@ pub fn defaultPanic(...@@ -568,7 +568,7 @@ pub fn defaultPanic(
568 defer unlockStdErr();568 defer unlockStdErr();
569569
570 io.getStdErr().writeAll(msg) catch posix.abort();570 io.getStdErr().writeAll(msg) catch posix.abort();
571 if (trace) |t| dumpStackTrace(t.*);571 if (error_return_trace) |t| dumpStackTrace(t.*);
572 dumpCurrentStackTrace(first_trace_addr orelse @returnAddress());572 dumpCurrentStackTrace(first_trace_addr orelse @returnAddress());
573 }573 }
574574
...@@ -621,6 +621,12 @@ pub fn fmtPanicCause(buffer: []u8, cause: std.builtin.PanicCause) usize {...@@ -621,6 +621,12 @@ pub fn fmtPanicCause(buffer: []u8, cause: std.builtin.PanicCause) usize {
621 i += fmtBuf(buffer[i..], ", found ");621 i += fmtBuf(buffer[i..], ", found ");
622 i += fmtInt10(buffer[i..], mm.found);622 i += fmtInt10(buffer[i..], mm.found);
623 },623 },
624 .sentinel_mismatch_isize => |mm| {
625 i += fmtBuf(buffer[i..], "sentinel mismatch: expected ");
626 i += fmtInt10s(buffer[i..], mm.expected);
627 i += fmtBuf(buffer[i..], ", found ");
628 i += fmtInt10s(buffer[i..], mm.found);
629 },
624 .sentinel_mismatch_other => i += fmtBuf(buffer[i..], "sentinel mismatch"),630 .sentinel_mismatch_other => i += fmtBuf(buffer[i..], "sentinel mismatch"),
625 .unwrap_error => |err| {631 .unwrap_error => |err| {
626 i += fmtBuf(buffer[i..], "attempt to unwrap error: ");632 i += fmtBuf(buffer[i..], "attempt to unwrap error: ");
...@@ -653,6 +659,15 @@ fn fmtBuf(out_buf: []u8, s: []const u8) usize {...@@ -653,6 +659,15 @@ fn fmtBuf(out_buf: []u8, s: []const u8) usize {
653 return s.len;659 return s.len;
654}660}
655661
662fn fmtInt10s(out_buf: []u8, integer_value: isize) usize {
663 if (integer_value < 0) {
664 out_buf[0] = '-';
665 return 1 + fmtInt10(out_buf[1..], @abs(integer_value));
666 } else {
667 return fmtInt10(out_buf, @abs(integer_value));
668 }
669}
670
656fn fmtInt10(out_buf: []u8, integer_value: usize) usize {671fn fmtInt10(out_buf: []u8, integer_value: usize) usize {
657 var tmp_buf: [50]u8 = undefined;672 var tmp_buf: [50]u8 = undefined;
658 var i: usize = tmp_buf.len;673 var i: usize = tmp_buf.len;
src/Sema.zig+42-109
...@@ -14249,7 +14249,7 @@ fn maybeErrorUnwrap(...@@ -14249,7 +14249,7 @@ fn maybeErrorUnwrap(
14249 .as_node => try sema.zirAsNode(block, inst),14249 .as_node => try sema.zirAsNode(block, inst),
14250 .field_val => try sema.zirFieldVal(block, inst),14250 .field_val => try sema.zirFieldVal(block, inst),
14251 .@"unreachable" => {14251 .@"unreachable" => {
14252 try callPanic(sema, block, operand_src, .unwrap_error, operand, .@"safety check");14252 try safetyPanicUnwrapError(sema, block, operand_src, operand);
14253 return true;14253 return true;
14254 },14254 },
14255 .panic => {14255 .panic => {
...@@ -27827,11 +27827,24 @@ fn addSafetyCheckUnwrapError(...@@ -27827,11 +27827,24 @@ fn addSafetyCheckUnwrapError(
27827 defer fail_block.instructions.deinit(gpa);27827 defer fail_block.instructions.deinit(gpa);
2782827828
27829 const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);27829 const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
27830 try callPanic(sema, &fail_block, src, .unwrap_error, err, .@"safety check");27830 try safetyPanicUnwrapError(sema, &fail_block, src, err);
2783127831
27832 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);27832 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
27833}27833}
2783427834
27835fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air.Inst.Ref) !void {
27836 const pt = sema.pt;
27837 const zcu = pt.zcu;
27838 if (!zcu.backendSupportsFeature(.panic_fn)) {
27839 _ = try block.addNoOp(.trap);
27840 } else {
27841 const panic_fn = try pt.getBuiltin("panicUnwrapError");
27842 const err_return_trace = try sema.getErrorReturnTrace(block);
27843 const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
27844 try sema.callBuiltin(block, src, panic_fn, .auto, &args, .@"safety check");
27845 }
27846}
27847
27835fn addSafetyCheckIndexOob(27848fn addSafetyCheckIndexOob(
27836 sema: *Sema,27849 sema: *Sema,
27837 parent_block: *Block,27850 parent_block: *Block,
...@@ -27841,68 +27854,8 @@ fn addSafetyCheckIndexOob(...@@ -27841,68 +27854,8 @@ fn addSafetyCheckIndexOob(
27841 cmp_op: Air.Inst.Tag,27854 cmp_op: Air.Inst.Tag,
27842) !void {27855) !void {
27843 assert(!parent_block.is_comptime);27856 assert(!parent_block.is_comptime);
27844 const gpa = sema.gpa;
27845 const ok = try parent_block.addBinOp(cmp_op, index, len);27857 const ok = try parent_block.addBinOp(cmp_op, index, len);
2784627858 return addSafetyCheckCall(sema, parent_block, src, ok, "panicOutOfBounds", &.{ index, len });
27847 var fail_block: Block = .{
27848 .parent = parent_block,
27849 .sema = sema,
27850 .namespace = parent_block.namespace,
27851 .instructions = .{},
27852 .inlining = parent_block.inlining,
27853 .is_comptime = false,
27854 .src_base_inst = parent_block.src_base_inst,
27855 .type_name_ctx = parent_block.type_name_ctx,
27856 };
27857
27858 defer fail_block.instructions.deinit(gpa);
27859
27860 const oob_ty = try getBuiltinInnerType(sema, &fail_block, src, "PanicCause", "IndexOutOfBounds");
27861 comptime {
27862 const fields = @typeInfo(std.builtin.PanicCause.IndexOutOfBounds).@"struct".fields;
27863 assert(std.mem.eql(u8, fields[0].name, "index"));
27864 assert(std.mem.eql(u8, fields[1].name, "len"));
27865 assert(fields.len == 2);
27866 }
27867 const panic_cause_payload = try fail_block.addAggregateInit(oob_ty, &.{ index, len });
27868 try callPanic(sema, &fail_block, src, .index_out_of_bounds, panic_cause_payload, .@"safety check");
27869 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
27870}
27871
27872fn addSafetyCheckStartGreaterThanEnd(
27873 sema: *Sema,
27874 parent_block: *Block,
27875 src: LazySrcLoc,
27876 start: Air.Inst.Ref,
27877 end: Air.Inst.Ref,
27878) !void {
27879 assert(!parent_block.is_comptime);
27880 const gpa = sema.gpa;
27881 const ok = try parent_block.addBinOp(.cmp_lte, start, end);
27882
27883 var fail_block: Block = .{
27884 .parent = parent_block,
27885 .sema = sema,
27886 .namespace = parent_block.namespace,
27887 .instructions = .{},
27888 .inlining = parent_block.inlining,
27889 .is_comptime = false,
27890 .src_base_inst = parent_block.src_base_inst,
27891 .type_name_ctx = parent_block.type_name_ctx,
27892 };
27893
27894 defer fail_block.instructions.deinit(gpa);
27895
27896 const oob_ty = try getBuiltinInnerType(sema, &fail_block, src, "PanicCause", "StartIndexGreaterThanEnd");
27897 comptime {
27898 const fields = @typeInfo(std.builtin.PanicCause.StartIndexGreaterThanEnd).@"struct".fields;
27899 assert(std.mem.eql(u8, fields[0].name, "start"));
27900 assert(std.mem.eql(u8, fields[1].name, "end"));
27901 assert(fields.len == 2);
27902 }
27903 const panic_cause_payload = try fail_block.addAggregateInit(oob_ty, &.{ start, end });
27904 try callPanic(sema, &fail_block, src, .start_index_greater_than_end, panic_cause_payload, .@"safety check");
27905 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
27906}27859}
2790727860
27908fn addSafetyCheckInactiveUnionField(27861fn addSafetyCheckInactiveUnionField(
...@@ -27913,36 +27866,8 @@ fn addSafetyCheckInactiveUnionField(...@@ -27913,36 +27866,8 @@ fn addSafetyCheckInactiveUnionField(
27913 wanted_tag: Air.Inst.Ref,27866 wanted_tag: Air.Inst.Ref,
27914) !void {27867) !void {
27915 assert(!parent_block.is_comptime);27868 assert(!parent_block.is_comptime);
27916 const gpa = sema.gpa;
27917 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);27869 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
2791827870 return addSafetyCheckCall(sema, parent_block, src, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
27919 var fail_block: Block = .{
27920 .parent = parent_block,
27921 .sema = sema,
27922 .namespace = parent_block.namespace,
27923 .instructions = .{},
27924 .inlining = parent_block.inlining,
27925 .is_comptime = false,
27926 .src_base_inst = parent_block.src_base_inst,
27927 .type_name_ctx = parent_block.type_name_ctx,
27928 };
27929
27930 defer fail_block.instructions.deinit(gpa);
27931
27932 const payload_ty = try getBuiltinInnerType(sema, &fail_block, src, "PanicCause", "InactiveUnionField");
27933 comptime {
27934 const fields = @typeInfo(std.builtin.PanicCause.InactiveUnionField).@"struct".fields;
27935 assert(std.mem.eql(u8, fields[0].name, "active"));
27936 assert(std.mem.eql(u8, fields[1].name, "accessed"));
27937 assert(fields.len == 2);
27938 }
27939 // TODO: before merging the branch, check how many safety checks end up being emitted
27940 // for union field accesses and avoid extraneous ones.
27941 const active_str = try analyzeTagName(sema, &fail_block, src, src, active_tag);
27942 const accessed_str = try analyzeTagName(sema, &fail_block, src, src, wanted_tag);
27943 const panic_cause_payload = try fail_block.addAggregateInit(payload_ty, &.{ active_str, accessed_str });
27944 try callPanic(sema, &fail_block, src, .inactive_union_field, panic_cause_payload, .@"safety check");
27945 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
27946}27871}
2794727872
27948fn addSafetyCheckSentinelMismatch(27873fn addSafetyCheckSentinelMismatch(
...@@ -27955,7 +27880,6 @@ fn addSafetyCheckSentinelMismatch(...@@ -27955,7 +27880,6 @@ fn addSafetyCheckSentinelMismatch(
27955 sentinel_index: Air.Inst.Ref,27880 sentinel_index: Air.Inst.Ref,
27956) !void {27881) !void {
27957 assert(!parent_block.is_comptime);27882 assert(!parent_block.is_comptime);
27958 const gpa = sema.gpa;
27959 const pt = sema.pt;27883 const pt = sema.pt;
27960 const zcu = pt.zcu;27884 const zcu = pt.zcu;
27961 const expected_sentinel_val = maybe_sentinel orelse return;27885 const expected_sentinel_val = maybe_sentinel orelse return;
...@@ -27984,6 +27908,24 @@ fn addSafetyCheckSentinelMismatch(...@@ -27984,6 +27908,24 @@ fn addSafetyCheckSentinelMismatch(
27984 break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);27908 break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
27985 };27909 };
2798627910
27911 return addSafetyCheckCall(sema, parent_block, src, ok, "panicSentinelMismatch", &.{
27912 expected_sentinel, actual_sentinel,
27913 });
27914}
27915
27916fn addSafetyCheckCall(
27917 sema: *Sema,
27918 parent_block: *Block,
27919 src: LazySrcLoc,
27920 ok: Air.Inst.Ref,
27921 func_name: []const u8,
27922 args: []const Air.Inst.Ref,
27923) !void {
27924 assert(!parent_block.is_comptime);
27925 const gpa = sema.gpa;
27926 const pt = sema.pt;
27927 const zcu = pt.zcu;
27928
27987 var fail_block: Block = .{27929 var fail_block: Block = .{
27988 .parent = parent_block,27930 .parent = parent_block,
27989 .sema = sema,27931 .sema = sema,
...@@ -27997,23 +27939,13 @@ fn addSafetyCheckSentinelMismatch(...@@ -27997,23 +27939,13 @@ fn addSafetyCheckSentinelMismatch(
2799727939
27998 defer fail_block.instructions.deinit(gpa);27940 defer fail_block.instructions.deinit(gpa);
2799927941
28000 // A different PanicCause tag must be used depending on what payload type it can be fit into.27942 if (!zcu.backendSupportsFeature(.panic_fn)) {
28001 // If it cannot fit into any, the "other" tag can be used, which does not try to carry the27943 _ = try fail_block.addNoOp(.trap);
28002 // sentinel value data.
28003
28004 if (sentinel_ty.isUnsignedInt(zcu) and sentinel_ty.intInfo(zcu).bits <= Type.usize.intInfo(zcu).bits) {
28005 const mm_ty = try getBuiltinInnerType(sema, &fail_block, src, "PanicCause", "SentinelMismatchUsize");
28006 comptime {
28007 const fields = @typeInfo(std.builtin.PanicCause.SentinelMismatchUsize).@"struct".fields;
28008 assert(std.mem.eql(u8, fields[0].name, "expected"));
28009 assert(std.mem.eql(u8, fields[1].name, "found"));
28010 assert(fields.len == 2);
28011 }
28012 const panic_cause_payload = try fail_block.addAggregateInit(mm_ty, &.{ expected_sentinel, actual_sentinel });
28013 try callPanic(sema, &fail_block, src, .sentinel_mismatch_usize, panic_cause_payload, .@"safety check");
28014 } else {27944 } else {
28015 try callPanic(sema, &fail_block, src, .sentinel_mismatch_other, .void_value, .@"safety check");27945 const panic_fn = try pt.getBuiltin(func_name);
27946 try sema.callBuiltin(&fail_block, src, panic_fn, .auto, args, .@"safety check");
28016 }27947 }
27948
28017 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);27949 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
28018}27950}
2801927951
...@@ -33512,7 +33444,8 @@ fn analyzeSlice(...@@ -33512,7 +33444,8 @@ fn analyzeSlice(
33512 // requirement: start <= end33444 // requirement: start <= end
33513 assert(!block.is_comptime);33445 assert(!block.is_comptime);
33514 try sema.requireRuntimeBlock(block, src, runtime_src.?);33446 try sema.requireRuntimeBlock(block, src, runtime_src.?);
33515 try sema.addSafetyCheckStartGreaterThanEnd(block, src, start, end);33447 const ok = try block.addBinOp(.cmp_lte, start, end);
33448 try sema.addSafetyCheckCall(block, src, ok, "panicStartGreaterThanEnd", &.{ start, end });
33516 }33449 }
33517 const new_len = if (by_length)33450 const new_len = if (by_length)
33518 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)33451 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)