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) {
803803 memcpy_alias,
804804 noreturn_returned,
805805 explicit_call: []const u8,
806 sentinel_mismatch_isize: SentinelMismatchIsize,
806807
807808 pub const IndexOutOfBounds = struct {
808809 index: usize,
......@@ -819,22 +820,82 @@ pub const PanicCause = union(enum) {
819820 found: usize,
820821 };
821822
823 pub const SentinelMismatchIsize = struct {
824 expected: isize,
825 found: isize,
826 };
827
822828 pub const InactiveUnionField = struct {
823829 active: []const u8,
824830 accessed: []const u8,
825831 };
826832};
827833
828pub noinline fn returnError(st: *StackTrace) void {
834pub fn panicSentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn {
829835 @branchHint(.cold);
830 @setRuntimeSafety(false);
831 addErrRetTraceAddr(st, @returnAddress());
836 switch (@typeInfo(@TypeOf(expected))) {
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());
832863}
833864
834pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
835 if (st.index < st.instruction_addresses.len)
836 st.instruction_addresses[st.index] = addr;
865pub fn panicUnwrapError(ert: ?*StackTrace, err: anyerror) noreturn {
866 @branchHint(.cold);
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();
838899 st.index += 1;
839900}
840901
lib/std/debug.zig+17-2
......@@ -453,7 +453,7 @@ threadlocal var panic_stage: usize = 0;
453453// This function avoids a dependency on formatted printing.
454454pub fn defaultPanic(
455455 cause: std.builtin.PanicCause,
456 trace: ?*const std.builtin.StackTrace,
456 error_return_trace: ?*const std.builtin.StackTrace,
457457 first_trace_addr: ?usize,
458458) noreturn {
459459 @branchHint(.cold);
......@@ -568,7 +568,7 @@ pub fn defaultPanic(
568568 defer unlockStdErr();
569569
570570 io.getStdErr().writeAll(msg) catch posix.abort();
571 if (trace) |t| dumpStackTrace(t.*);
571 if (error_return_trace) |t| dumpStackTrace(t.*);
572572 dumpCurrentStackTrace(first_trace_addr orelse @returnAddress());
573573 }
574574
......@@ -621,6 +621,12 @@ pub fn fmtPanicCause(buffer: []u8, cause: std.builtin.PanicCause) usize {
621621 i += fmtBuf(buffer[i..], ", found ");
622622 i += fmtInt10(buffer[i..], mm.found);
623623 },
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 },
624630 .sentinel_mismatch_other => i += fmtBuf(buffer[i..], "sentinel mismatch"),
625631 .unwrap_error => |err| {
626632 i += fmtBuf(buffer[i..], "attempt to unwrap error: ");
......@@ -653,6 +659,15 @@ fn fmtBuf(out_buf: []u8, s: []const u8) usize {
653659 return s.len;
654660}
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
656671fn fmtInt10(out_buf: []u8, integer_value: usize) usize {
657672 var tmp_buf: [50]u8 = undefined;
658673 var i: usize = tmp_buf.len;
src/Sema.zig+42-109
......@@ -14249,7 +14249,7 @@ fn maybeErrorUnwrap(
1424914249 .as_node => try sema.zirAsNode(block, inst),
1425014250 .field_val => try sema.zirFieldVal(block, inst),
1425114251 .@"unreachable" => {
14252 try callPanic(sema, block, operand_src, .unwrap_error, operand, .@"safety check");
14252 try safetyPanicUnwrapError(sema, block, operand_src, operand);
1425314253 return true;
1425414254 },
1425514255 .panic => {
......@@ -27827,11 +27827,24 @@ fn addSafetyCheckUnwrapError(
2782727827 defer fail_block.instructions.deinit(gpa);
2782827828
2782927829 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
2783227832 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
2783327833}
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
2783527848fn addSafetyCheckIndexOob(
2783627849 sema: *Sema,
2783727850 parent_block: *Block,
......@@ -27841,68 +27854,8 @@ fn addSafetyCheckIndexOob(
2784127854 cmp_op: Air.Inst.Tag,
2784227855) !void {
2784327856 assert(!parent_block.is_comptime);
27844 const gpa = sema.gpa;
2784527857 const ok = try parent_block.addBinOp(cmp_op, index, len);
27846
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);
27858 return addSafetyCheckCall(sema, parent_block, src, ok, "panicOutOfBounds", &.{ index, len });
2790627859}
2790727860
2790827861fn addSafetyCheckInactiveUnionField(
......@@ -27913,36 +27866,8 @@ fn addSafetyCheckInactiveUnionField(
2791327866 wanted_tag: Air.Inst.Ref,
2791427867) !void {
2791527868 assert(!parent_block.is_comptime);
27916 const gpa = sema.gpa;
2791727869 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
27918
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);
27870 return addSafetyCheckCall(sema, parent_block, src, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
2794627871}
2794727872
2794827873fn addSafetyCheckSentinelMismatch(
......@@ -27955,7 +27880,6 @@ fn addSafetyCheckSentinelMismatch(
2795527880 sentinel_index: Air.Inst.Ref,
2795627881) !void {
2795727882 assert(!parent_block.is_comptime);
27958 const gpa = sema.gpa;
2795927883 const pt = sema.pt;
2796027884 const zcu = pt.zcu;
2796127885 const expected_sentinel_val = maybe_sentinel orelse return;
......@@ -27984,6 +27908,24 @@ fn addSafetyCheckSentinelMismatch(
2798427908 break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
2798527909 };
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
2798727929 var fail_block: Block = .{
2798827930 .parent = parent_block,
2798927931 .sema = sema,
......@@ -27997,23 +27939,13 @@ fn addSafetyCheckSentinelMismatch(
2799727939
2799827940 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.
28001 // If it cannot fit into any, the "other" tag can be used, which does not try to carry the
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");
27942 if (!zcu.backendSupportsFeature(.panic_fn)) {
27943 _ = try fail_block.addNoOp(.trap);
2801427944 } 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");
2801627947 }
27948
2801727949 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
2801827950}
2801927951
......@@ -33512,7 +33444,8 @@ fn analyzeSlice(
3351233444 // requirement: start <= end
3351333445 assert(!block.is_comptime);
3351433446 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 });
3351633449 }
3351733450 const new_len = if (by_length)
3351833451 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)