cranelift_codegen/isa/
unwind.rs

1//! Represents information relating to function unwinding.
2
3use crate::machinst::RealReg;
4
5#[cfg(feature = "enable-serde")]
6use serde::{Deserialize, Serialize};
7
8#[cfg(feature = "unwind")]
9pub mod systemv;
10
11#[cfg(feature = "unwind")]
12pub mod winx64;
13
14/// Represents unwind information for a single function.
15#[derive(Clone, Debug, PartialEq, Eq)]
16#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
17#[non_exhaustive]
18pub enum UnwindInfo {
19    /// Windows x64 ABI unwind information.
20    #[cfg(feature = "unwind")]
21    WindowsX64(winx64::UnwindInfo),
22    /// System V ABI unwind information.
23    #[cfg(feature = "unwind")]
24    SystemV(systemv::UnwindInfo),
25}
26
27/// Unwind pseudoinstruction used in VCode backends: represents that
28/// at the present location, an action has just been taken.
29///
30/// VCode backends always emit unwind info that is relative to a frame
31/// pointer, because we are planning to allow for dynamic frame allocation,
32/// and because it makes the design quite a lot simpler in general: we don't
33/// have to be precise about SP adjustments throughout the body of the function.
34///
35/// We include only unwind info for prologues at this time. Note that unwind
36/// info for epilogues is only necessary if one expects to unwind while within
37/// the last few instructions of the function (after FP has been restored) or
38/// if one wishes to instruction-step through the epilogue and see a backtrace
39/// at every point. This is not necessary for correct operation otherwise and so
40/// we simplify the world a bit by omitting epilogue information. (Note that
41/// some platforms also don't require or have a way to describe unwind
42/// information for epilogues at all: for example, on Windows, the `UNWIND_INFO`
43/// format only stores information for the function prologue.)
44///
45/// Because we are defining an abstraction over multiple unwind formats (at
46/// least Windows/fastcall and System V) and multiple architectures (at least
47/// x86-64 and aarch64), we have to be a little bit flexible in how we describe
48/// the frame. However, it turns out that a least-common-denominator prologue
49/// works for all of the cases we have to worry about today!
50///
51/// We assume the stack looks something like this:
52///
53///
54/// ```plain
55///                  +----------------------------------------------+
56///                  | stack arg area, etc (according to ABI)       |
57///                  | ...                                          |
58///   SP at call --> +----------------------------------------------+
59///                  | return address (pushed by HW or SW)          |
60///                  +----------------------------------------------+
61///                  | old frame pointer (FP)                       |
62///   FP in this --> +----------------------------------------------+
63///   function       | clobbered callee-save registers              |
64///                  | ...                                          |
65///   start of   --> +----------------------------------------------+
66///   clobbers       | (rest of function's frame, irrelevant here)  |
67///                  | ...                                          |
68///   SP in this --> +----------------------------------------------+
69///   function
70/// ```
71///
72/// We assume that the prologue consists of:
73///
74/// * `PushFrameRegs`: A push operation that adds the old FP to the stack (and
75///    maybe the link register, on architectures that do not push return addresses
76///    in hardware)
77/// * `DefineFrame`: An update that sets FP to SP to establish a new frame
78/// * `SaveReg`: A number of stores or pushes to the stack to save clobbered registers
79///
80/// Each of these steps has a corresponding pseudo-instruction. At each step,
81/// we need some information to determine where the current stack frame is
82/// relative to SP or FP. When the `PushFrameRegs` occurs, we need to know how
83/// much SP was decremented by, so we can allow the unwinder to continue to find
84/// the caller's frame. When we define the new frame, we need to know where FP
85/// is in relation to "SP at call" and also "start of clobbers", because
86/// different unwind formats define one or the other of those as the anchor by
87/// which we define the frame. Finally, when registers are saved, we need to
88/// know which ones, and where.
89///
90/// Different unwind formats work differently; here is a whirlwind tour of how
91/// they define frames to help understanding:
92///
93/// - Windows unwind information defines a frame that must start below the
94///   clobber area, because all clobber-save offsets are non-negative. We set it
95///   at the "start of clobbers" in the figure above. The `UNWIND_INFO` contains
96///   a "frame pointer offset" field; when we define the new frame, the frame is
97///   understood to be the value of FP (`RBP`) *minus* this offset. In other
98///   words, the FP is *at the frame pointer offset* relative to the
99///   start-of-clobber-frame. We use the "FP offset down to clobber area" offset
100///   to generate this info.
101///
102/// - System V unwind information defines a frame in terms of the CFA
103///   (call-frame address), which is equal to the "SP at call" above. SysV
104///   allows negative offsets, so there is no issue defining clobber-save
105///   locations in terms of CFA. The format allows us to define CFA flexibly in
106///   terms of any register plus an offset; we define it in terms of FP plus
107///   the clobber-to-caller-SP offset once FP is established.
108///
109/// Note that certain architectures impose limits on offsets: for example, on
110/// Windows, the base of the clobber area must not be more than 240 bytes below
111/// FP.
112///
113/// Unwind pseudoinstructions are emitted inline by ABI code as it generates
114/// a prologue. Thus, for the usual case, a prologue might look like (using x64
115/// as an example):
116///
117/// ```plain
118/// push rbp
119/// unwind UnwindInst::PushFrameRegs { offset_upward_to_caller_sp: 16 }
120/// mov rbp, rsp
121/// unwind UnwindInst::DefineNewFrame { offset_upward_to_caller_sp: 16,
122///                                     offset_downward_to_clobbers: 16 }
123/// sub rsp, 32
124/// mov [rsp+16], r12
125/// unwind UnwindInst::SaveReg { reg: R12, clobber_offset: 0 }
126/// mov [rsp+24], r13
127/// unwind UnwindInst::SaveReg { reg: R13, clobber_offset: 8 }
128/// ...
129/// ```
130#[derive(Clone, Debug, PartialEq, Eq)]
131#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
132pub enum UnwindInst {
133    /// The frame-pointer register for this architecture has just been pushed to
134    /// the stack (and on architectures where return-addresses are not pushed by
135    /// hardware, the link register as well). The FP has not been set to this
136    /// frame yet. The current location of SP is such that
137    /// `offset_upward_to_caller_sp` is the distance to SP-at-callsite (our
138    /// caller's frame).
139    PushFrameRegs {
140        /// The offset from the current SP (after push) to the SP at
141        /// caller's callsite.
142        offset_upward_to_caller_sp: u32,
143    },
144    /// The frame-pointer register for this architecture has just been
145    /// set to the current stack location. We wish to define a new
146    /// frame that is anchored on this new FP value. Offsets are provided
147    /// upward to the caller's stack frame and downward toward the clobber
148    /// area. We expect this pseudo-op to come after `PushFrameRegs`.
149    DefineNewFrame {
150        /// The offset from the current SP and FP value upward to the value of
151        /// SP at the callsite that invoked us.
152        offset_upward_to_caller_sp: u32,
153        /// The offset from the current SP and FP value downward to the start of
154        /// the clobber area.
155        offset_downward_to_clobbers: u32,
156    },
157    /// The stack pointer was adjusted to allocate the stack.
158    StackAlloc {
159        /// Size to allocate.
160        size: u32,
161    },
162    /// The stack slot at the given offset from the clobber-area base has been
163    /// used to save the given register.
164    ///
165    /// Given that `CreateFrame` has occurred first with some
166    /// `offset_downward_to_clobbers`, `SaveReg` with `clobber_offset` indicates
167    /// that the value of `reg` is saved on the stack at address `FP -
168    /// offset_downward_to_clobbers + clobber_offset`.
169    SaveReg {
170        /// The offset from the start of the clobber area to this register's
171        /// stack location.
172        clobber_offset: u32,
173        /// The saved register.
174        reg: RealReg,
175    },
176    /// Defines if the aarch64-specific pointer authentication available for ARM v8.3+ devices
177    /// is enabled for certain pointers or not.
178    Aarch64SetPointerAuth {
179        /// Whether return addresses (hold in LR) contain a pointer-authentication code.
180        return_addresses: bool,
181    },
182}