xref: /llvm-project/lld/test/MachO/tools/generate-cfi-funcs.py (revision f98ee40f4b5d7474fc67e82824bf6abbaedb7b1c)
1#!/usr/bin/env python
2
3"""Generate skeletal functions with a variety .cfi_ directives.
4The purpose is to produce object-file test inputs to lld with a
5variety of compact unwind encodings.
6"""
7from __future__ import print_function
8import random
9import argparse
10import string
11from math import factorial
12from itertools import permutations
13
14lsda_n = 0
15lsda_odds = 0.0
16func_size_low = 0x10
17func_size_high = 0x100
18saved_regs = ["%r15", "%r14", "%r13", "%r12", "%rbx"]
19saved_regs_combined = list(list(permutations(saved_regs, i)) for i in range(0, 6))
20
21
22def print_function(name):
23    global lsda_odds
24    have_lsda = random.random() < lsda_odds
25    frame_size = random.randint(4, 64) * 16
26    frame_offset = -random.randint(0, int(frame_size / 16 - 4)) * 16
27    global func_size_low, func_size_high
28    func_size = random.randint(func_size_low, func_size_high) * 0x10
29    func_size_high += 1
30    if func_size_high % 0x10 == 0:
31        func_size_low += 1
32
33    print(
34        """\
35### %s frame=%d lsda=%s size=%d
36    .section __TEXT,__text,regular,pure_instructions
37    .p2align 4, 0x90
38    .globl %s
39%s:
40    .cfi_startproc"""
41        % (name, frame_size, have_lsda, func_size, name, name)
42    )
43    if have_lsda:
44        global lsda_n
45        lsda_n += 1
46        print(
47            """\
48    .cfi_personality 155, ___gxx_personality_v0
49    .cfi_lsda 16, Lexception%d"""
50            % lsda_n
51        )
52    print(
53        """\
54    pushq %%rbp
55    .cfi_def_cfa_offset %d
56    .cfi_offset %%rbp, %d
57    movq %%rsp, %%rbp
58    .cfi_def_cfa_register %%rbp"""
59        % (frame_size, frame_offset + 6 * 8)
60    )
61    print(
62        """\
63    .fill %d
64    popq %%rbp
65    retq
66    .cfi_endproc
67"""
68        % (func_size - 6)
69    )
70
71    if have_lsda:
72        print(
73            """\
74    .section __TEXT,__gcc_except_tab
75    .p2align 2
76Lexception%d:
77    .space 0x10
78"""
79            % lsda_n
80        )
81    return func_size
82
83
84def random_seed():
85    """Generate a seed that can easily be passed back in via --seed=STRING"""
86    return "".join(random.choice(string.ascii_lowercase) for i in range(10))
87
88
89def main():
90    parser = argparse.ArgumentParser(
91        description=__doc__,
92        epilog="""\
93Function sizes begin small then monotonically increase.  The goal is
94to produce early pages that are full and later pages that are less
95than full, in order to test handling for both cases.  Full pages
96contain the maximum of 1021 compact unwind entries for a total page
97size = 4 KiB.
98
99Use --pages=N or --functions=N to control the size of the output.
100Default is --pages=2, meaning produce at least two full pages of
101compact unwind entries, plus some more. The calculation is sloppy.
102""",
103    )
104    parser.add_argument(
105        "--seed",
106        type=str,
107        default=random_seed(),
108        help="Seed the random number generator",
109    )
110    parser.add_argument(
111        "--pages", type=int, default=2, help="Number of compact-unwind pages"
112    )
113    parser.add_argument(
114        "--functions", type=int, default=None, help="Number of functions to generate"
115    )
116    parser.add_argument(
117        "--encodings",
118        type=int,
119        default=127,
120        help="Maximum number of unique unwind encodings (default = 127)",
121    )
122    parser.add_argument(
123        "--lsda",
124        type=int,
125        default=0,
126        help="Percentage of functions with personality & LSDA (default = 10",
127    )
128    args = parser.parse_args()
129    random.seed(args.seed)
130    p2align = 14
131    global lsda_odds
132    lsda_odds = args.lsda / 100.0
133
134    print(
135        """\
136### seed=%s lsda=%f p2align=%d
137    .section __TEXT,__text,regular,pure_instructions
138    .p2align %d, 0x90
139"""
140        % (args.seed, lsda_odds, p2align, p2align)
141    )
142
143    size = 0
144    base = 1 << p2align
145    if args.functions:
146        for n in range(args.functions):
147            size += print_function("x%08x" % (size + base))
148    else:
149        while size < (args.pages << 24):
150            size += print_function("x%08x" % (size + base))
151
152    print(
153        """\
154    .section __TEXT,__text,regular,pure_instructions
155    .globl _main
156    .p2align 4, 0x90
157_main:
158    retq
159
160    .p2align 4, 0x90
161___gxx_personality_v0:
162    retq
163"""
164    )
165
166
167if __name__ == "__main__":
168    main()
169