However, because of the number of modules and libraries it offers and the simplicity of its syntax, Python has grown to become one of the most important languages of today. Other than machine learning applications, Python can be used for game development, back-end development, data science, data visualization, and a lot more.
Prologs and epilogs tend to mirror each other. By taking advantage of this common trait, the size of the metadata needed to describe unwinding can be greatly reduced. Within the body of the function, it doesn't matter whether the prolog's operations are undone, or the epilog's operations are done in a forward manner. Both should produce identical results.
Dedicated frame pointer register: If the sp is saved in another register (x29) in the prolog, that register remains untouched throughout the function. It means the original sp may be recovered at any time.
For frame chained functions, the fp and lr pair can be saved at any position in the local variable area, depending on optimization considerations. The goal is to maximize the number of locals that can be reached by a single instruction based on the frame pointer (x29) or stack pointer (sp). However, for alloca functions, it must be chained, and x29 must point to the bottom of stack. To allow for better register-pair-addressing-mode coverage, nonvolatile register save areas are positioned at the top of the Local area stack. Here are examples that illustrate several of the most efficient prolog sequences. For the sake of clarity and better cache locality, the order of storing callee-saved registers in all canonical prologs is in "growing up" order. #framesz below represents the size of entire stack (excluding alloca area). #localsz and #outsz denote local area size (including the save area for the pair) and outgoing parameter size, respectively.
Compared to the first prolog example above, this example has an advantage: all register save instructions are ready to execute after only one stack allocation instruction. That means there's no anti-dependence on sp that prevents instruction level parallelism.
Although the prolog and each epilog has its own index into the unwind codes, the table is shared between them. It's entirely possible (and not altogether uncommon) that they can all share the same codes. (For an example, see Example 2 in the Examples section.) Compiler writers should optimize for this case in particular. It's because the largest index that can be specified is 255, which limits the total number of unwind codes for a particular function.
The array of unwind codes is a pool of sequences that describe exactly how to undo the effects of the prolog. They're stored in the same order the operations need to be undone. The unwind codes can be thought of as a small instruction set, encoded as a string of bytes. When execution is complete, the return address to the calling function is in the lr register. And, all non-volatile registers are restored to their values at the time the function was called.
If exceptions were guaranteed to only ever occur within a function body, and never within a prolog or any epilog, then only a single sequence would be necessary. However, the Windows unwinding model requires that code can unwind from within a partially executed prolog or epilog. To meet this requirement, the unwind codes have been carefully designed so they unambiguously map 1:1 to each relevant opcode in the prolog and epilog. This design has several implications:
By counting the number of instructions before the end of the prolog, it's possible to skip the equivalent number of unwind codes. We can execute the rest of the sequence to undo only those parts of the prolog that have completed execution.
The unwind codes are encoded according to the table below. All unwind codes are a single/double byte, except the one that allocates a huge stack (alloc_l). There are 22 unwind codes in total. Each unwind code maps exactly one instruction in the prolog/epilog, to allow for unwinding of partially executed prologs and epilogs.
In instructions with large values covering multiple bytes, the most significant bits are stored first. This design makes it possible to find the total size in bytes of the unwind code by looking up only the first byte of the code. Since each unwind code is exactly mapped to an instruction in a prolog or epilog, you can compute the size of the prolog or epilog. Walk from the sequence start to the end, and use a lookup table or similar device to determine the length of the corresponding opcode.
Post-indexed offset addressing isn't allowed in a prolog. All offset ranges (#Z) match the encoding of stp/str addressing except save_r19r20_x, in which 248 is sufficient for all save areas (10 Int registers + 8 FP registers + 8 input registers).
end_c is designed to handle noncontiguous function fragments for optimization purposes. An end_c that indicates the end of unwind codes in the current scope must be followed by another series of unwind codes ending with a real end. The unwind codes between end_c and end represent the prolog operations in the parent region (a "phantom" prolog). More details and examples are described in the section below.
For functions whose prologs and epilogs follow the canonical form described below, packed unwind data can be used. It eliminates the need for an .xdata record entirely, and significantly reduces the cost of providing unwind data. The canonical prologs and epilogs are designed to meet the common requirements of a simple function: One that doesn't require an exception handler, and which does its setup and teardown operations in a standard order.
Canonical prologs that fall into categories 1, 2 (without outgoing parameter area), 3 and 4 in section above can be represented by packed unwind format. The epilogs for canonical functions follow a similar form, except H has no effect, the set_fp instruction is omitted, and the order of steps and the instructions in each step are reversed in the epilog. The algorithm for packed .xdata follows these steps, detailed in the following table:
In the most common unwinding situations, the exception or call occurs in the body of the function, away from the prolog and all epilogs. In these situations, unwinding is straightforward: the unwinder simply executes the codes in the unwind array. It begins at index 0 and continues until an end opcode is detected.
It's more difficult to correctly unwind in the case where an exception or interrupt occurs while executing a prolog or epilog. In these situations, the stack frame is only partially constructed. The problem is to determine exactly what's been done, to correctly undo it.
Next to each opcode is the appropriate unwind code describing this operation. You can see how the series of unwind codes for the prolog is an exact mirror image of the unwind codes for the epilog (not counting the final instruction of the epilog). It's a common situation: It's why we always assume the unwind codes for the prolog are stored in reverse order from the prolog's execution order.
It turns out that a similar logic works for the prolog, except in reverse. If we start unwinding from offset 0 in the prolog, we want to execute nothing. If we unwind from offset 2, which is one instruction in, then we want to start executing the unwind sequence one unwind code from the end. (Remember, the codes are stored in reverse order.) And here too, we can generalize: if we start unwinding from instruction n in the prolog, we should start executing n unwind codes from the end of the list of codes.
If unwinding from within the prolog, use index 0 as your starting point. Compute the length of the prolog code from the sequence, and then compute how many bytes the PC in question is from the end of the prolog. Then advance forward through the unwind codes, skipping unwind codes until all of the not-yet-executed instructions are accounted for. Then execute starting at that point.
These rules mean the unwind codes for the prolog must always be the first in the array. And, they're also the codes used to unwind in the general case of unwinding from within the body. Any epilog-specific code sequences should follow immediately after.
For each separated secondary fragment that has its own prolog, it's expected that no stack adjustment is done in its prolog. All stack space required by a secondary region must be pre-allocated by its parent region (or called host region). This preallocation keeps stack pointer manipulation strictly in the function's original prolog.
Only the prolog must be described. This prolog can't be represented in the compact .pdata format. In the full .xdata case, it can be represented by setting Epilog Count = 0. See region 1 in the example above.
It's assumed that by the time control jumps into this region, all prolog codes have been executed. Partial unwind can happen in epilogs the same way as in a normal function. This type of region can't be represented by compact .pdata. In a full .xdata record, it can be encoded with a "phantom" prolog, bracketed by an end_c and end unwind code pair. The leading end_c indicates the size of prolog is zero. Epilog start index of the single epilog points to set_fp.
Only the first fragment of the function will contain a prolog; all other fragments are marked as having no prolog. Depending on the number of epilogs present, each fragment may contain zero or more epilogs. Keep in mind that each epilog scope in a fragment specifies its starting offset relative to the start of the fragment, not the start of the function.
Part 3 contains four studies that investigate more directly some functional aspects of the narrative and less the auditory or visual narration. These include when protagonists are introduced (the setup, Study 7), when characters converse (the complication and the development, Study 8), when action shots occur that might distinguish genres (mostly in the climax but with some surprises elsewhere, Study 9), and when and how scenes change (Study 10). 2b1af7f3a8