authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-10-07 05:50:37+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-10-07 16:44:25+02:00
log9760068826e01e5540da9168d2f02e15957a99cc
tree9fc96a21c4fd0932ed735a0af2939d8be0f1d5b1
parentd28006153e3f221a3755d78c74f9c7716ad660b5

std.debug: prefer FP unwinding on targets where it is ideal

If the ABI requires a backchain pointer, FP unwinding is always possible, safe, and fast, so there's really no reason not to use it.

1 files changed, 37 insertions(+), 26 deletions(-)

lib/std/debug.zig+37-26
...@@ -697,7 +697,10 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_...@@ -697,7 +697,10 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_
697 var printed_any_frame = false;697 var printed_any_frame = false;
698 while (true) switch (it.next()) {698 while (true) switch (it.next()) {
699 .switch_to_fp => |unwind_error| {699 .switch_to_fp => |unwind_error| {
700 if (StackIterator.abi_requires_backchain or StackIterator.fp_unwind_is_safe) continue; // no need to even warn700 switch (StackIterator.fp_usability) {
701 .useless, .unsafe => {},
702 .safe, .ideal => continue, // no need to even warn
703 }
701 const module_name = di.getModuleName(di_gpa, unwind_error.address) catch "???";704 const module_name = di.getModuleName(di_gpa, unwind_error.address) catch "???";
702 const caption: []const u8 = switch (unwind_error.err) {705 const caption: []const u8 = switch (unwind_error.err) {
703 error.MissingDebugInfo => "unwind info unavailable",706 error.MissingDebugInfo => "unwind info unavailable",
...@@ -840,7 +843,8 @@ const StackIterator = union(enum) {...@@ -840,7 +843,8 @@ const StackIterator = union(enum) {
840 if (!(builtin.zig_backend == .stage2_c and builtin.target.abi == .msvc) and843 if (!(builtin.zig_backend == .stage2_c and builtin.target.abi == .msvc) and
841 SelfInfo != void and844 SelfInfo != void and
842 SelfInfo.can_unwind and845 SelfInfo.can_unwind and
843 cpu_context.Native != noreturn)846 cpu_context.Native != noreturn and
847 fp_usability != .ideal)
844 {848 {
845 // We don't need `di_first` here, because our PC is in `std.debug`; we're only interested849 // We don't need `di_first` here, because our PC is in `std.debug`; we're only interested
846 // in our caller's frame and above.850 // in our caller's frame and above.
...@@ -855,32 +859,41 @@ const StackIterator = union(enum) {...@@ -855,32 +859,41 @@ const StackIterator = union(enum) {
855 }859 }
856 }860 }
857861
858 /// Some architectures make FP unwinding too impractical. For example, due to its very silly ABI862 const FpUsability = enum {
859 /// design decisions, it's not possible to do generic FP unwinding on MIPS; we would need to do863 /// FP unwinding is impractical on this target. For example, due to its very silly ABI
860 /// a complicated code scanning algorithm instead. At that point, we may as well just use DWARF.864 /// design decisions, it's not possible to do generic FP unwinding on MIPS without a
861 const fp_unwind_is_impossible = switch (builtin.cpu.arch) {865 /// complicated code scanning algorithm.
866 useless,
867 /// FP unwinding is unsafe on this target; we may crash when doing so. We will only perform
868 /// FP unwinding in the case of crashes/panics, or if the user opts in.
869 unsafe,
870 /// FP unwinding is guaranteed to be safe on this target. We will do so if unwinding with
871 /// debug info does not work, and if this compilation has frame pointers enabled.
872 safe,
873 /// FP unwinding is the best option on this target. This is usually because the ABI requires
874 /// a backchain pointer, thus making it always available, safe, and fast.
875 ideal,
876 };
877
878 const fp_usability: FpUsability = switch (builtin.target.cpu.arch) {
862 .mips,879 .mips,
863 .mipsel,880 .mipsel,
864 .mips64,881 .mips64,
865 .mips64el,882 .mips64el,
866 => true,883 => .useless,
867 else => false,
868 };
869
870 /// <https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-purpose-of-specific-CPU-registers>
871 const fp_unwind_is_safe = builtin.cpu.arch == .aarch64 and builtin.os.tag.isDarwin();
872
873 /// On some architectures, we can do FP unwinding even with `-fomit-frame-pointer` due to ABI
874 /// requirements. Typically this is because the ABI requires a backchain regardless of whether
875 /// codegen actually uses a frame pointer for stack frame access.
876 const abi_requires_backchain = switch (builtin.cpu.arch) {
877 .hexagon,884 .hexagon,
885 // The PowerPC ABIs don't actually strictly require a backchain pointer; they allow omitting
886 // it when full unwind info is present. Despite this, both GCC and Clang always enforce the
887 // presence of the backchain pointer no matter what options they are given. This seems to be
888 // a case of "the spec is only a polite suggestion", except it works in our favor this time!
878 .powerpc,889 .powerpc,
879 .powerpcle,890 .powerpcle,
880 .powerpc64,891 .powerpc64,
881 .powerpc64le,892 .powerpc64le,
882 => true,893 => .ideal,
883 else => false,894 // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-purpose-of-specific-CPU-registers
895 .aarch64 => if (builtin.target.os.tag.isDarwin()) .safe else .unsafe,
896 else => .unsafe,
884 };897 };
885898
886 /// Whether the current unwind strategy is allowed given `allow_unsafe`.899 /// Whether the current unwind strategy is allowed given `allow_unsafe`.
...@@ -891,13 +904,11 @@ const StackIterator = union(enum) {...@@ -891,13 +904,11 @@ const StackIterator = union(enum) {
891 // immediately regardless of anything. But FPs could also be omitted from a different904 // immediately regardless of anything. But FPs could also be omitted from a different
892 // linked object, so it's not guaranteed to be safe, unless the target specifically905 // linked object, so it's not guaranteed to be safe, unless the target specifically
893 // requires it.906 // requires it.
894 .fp => s: {907 .fp => switch (fp_usability) {
895 if (fp_unwind_is_impossible) break :s false;908 .useless => false,
896 if (abi_requires_backchain) break :s true;909 .unsafe => allow_unsafe and !builtin.omit_frame_pointer,
897 if (builtin.omit_frame_pointer) break :s false;910 .safe => !builtin.omit_frame_pointer,
898 if (fp_unwind_is_safe) break :s true;911 .ideal => true,
899 if (allow_unsafe) break :s true;
900 break :s false;
901 },912 },
902 };913 };
903 }914 }