summaryrefslogtreecommitdiff
path: root/build-aux/measurestack/app_output.py
blob: 5cf7d17b7debd592f0d602513abfc6072cecaa66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# build-aux/measurestack/app_output.py - Generate `stack.c` files
#
# Copyright (C) 2024-2025  Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later

import typing

from . import analyze
from .analyze import QName

# pylint: disable=unused-variable
__all__ = [
    "print_c",
]


def print_group(
    result: analyze.AnalyzeResult,
    location_xform: typing.Callable[[QName], str],
    grp_name: str,
) -> None:
    grp = result.groups[grp_name]
    if not grp.rows:
        print(f"= {grp_name} (empty) =")
        return

    nsum = sum(v.nstatic * v.cnt for v in grp.rows.values())
    nmax = max(v.nstatic for v in grp.rows.values())

    # Figure sizes.
    namelen = max(
        [len(location_xform(k)) for k in grp.rows.keys()] + [len(grp_name) + 4]
    )
    numlen = len(str(nsum))
    sep1 = ("=" * namelen) + " " + "=" * numlen
    sep2 = ("-" * namelen) + " " + "-" * numlen

    # Print.
    print("= " + grp_name + " " + sep1[len(grp_name) + 3 :])
    for qname, val in sorted(grp.rows.items()):
        name = location_xform(qname)
        if val.nstatic == 0:
            continue
        print(
            f"{name:<{namelen}} {val.nstatic:>{numlen}}"
            + (f" * {val.cnt}" if val.cnt != 1 else "")
        )
    print(sep2)
    print(f"{'Total':<{namelen}} {nsum:>{numlen}}")
    print(f"{'Maximum':<{namelen}} {nmax:>{numlen}}")
    print(sep1)


def lm_round_up(n: int, d: int) -> int:
    return ((n + d - 1) // d) * d


def print_c(
    result: analyze.AnalyzeResult, location_xform: typing.Callable[[QName], str]
) -> None:
    print('#include "config.h" /* for COROUTINE_STACK_* extern declarations */')
    print()
    print("/*")
    print_group(result, location_xform, "Threads")
    print_group(result, location_xform, "Interrupt handlers")
    print("*/")
    intrstack = max(
        v.nstatic for v in result.groups["Interrupt handlers"].rows.values()
    )
    stack_guard_size = 16 * 2

    class CrRow(typing.NamedTuple):
        name: str
        cnt: int
        base: int
        size: int

    print("[[gnu::aligned]] void _bogus_aligned_fn(void) {};")
    print("#define STACK_ALIGNED [[gnu::aligned(__alignof__(_bogus_aligned_fn))]]")

    rows: list[CrRow] = []
    mainrow: CrRow | None = None
    for funcname, val in result.groups["Threads"].rows.items():
        name = str(funcname.base())
        base = val.nstatic
        size = base + intrstack
        if name in ["main", "_entry_point"]:
            mainrow = CrRow(name=name, cnt=1, base=base, size=size)
        else:
            size = lm_round_up(size + stack_guard_size, 512)
            rows.append(CrRow(name=name, cnt=val.cnt, base=base, size=size))
    namelen = max(len(f"{r.name}{r.cnt}" if r.cnt > 1 else r.name) for r in rows)
    baselen = max(len(str(r.base)) for r in rows)
    sizesum = sum(r.cnt * (r.size + stack_guard_size) for r in rows)
    sizelen = len(str(max(sizesum, mainrow.size if mainrow else 0)))

    def print_row(comment: bool, name: str, size: int, eqn: str | None = None) -> None:
        prefix = "STACK_ALIGNED char COROUTINE_STACK_"
        if comment:
            print(f"/* {name}".ljust(len(prefix) + namelen), end="")
        else:
            print(f"{prefix}{name:<{namelen}}", end="")
        print(f"[{size:>{sizelen}}];", end="")
        if comment:
            print(" */", end="")
        elif eqn:
            print("   ", end="")
        if eqn:
            print(f" /* {eqn} */", end="")
        print()

    for row in sorted(rows):
        comment = (
            f"LM_ROUND_UP({row.base:>{baselen}}+{intrstack}+{stack_guard_size}, 512)"
        )
        if row.cnt > 1:
            for i in range(row.cnt):
                print_row(False, f"{row.name}{i}", row.size, comment)
        else:
            print_row(False, row.name, row.size, comment)
    print_row(True, "TOTAL", sizesum)
    if mainrow:
        print_row(
            True,
            "MAIN/KERNEL",
            mainrow.size,
            f"                   {mainrow.base:>{baselen}}+{intrstack}",
        )
    print()
    for row in sorted(rows):
        name = row.name
        if row.cnt > 1:
            name += "0"
            print(f"char *const COROUTINE_STACK_{row.name}[{row.cnt}] = {{")
            for i in range(row.cnt):
                print(f"\tCOROUTINE_STACK_{row.name}{i},")
            print("};")
        print(
            f"const size_t COROUTINE_STACK_{row.name}_len = sizeof(COROUTINE_STACK_{name});"
        )

    print()
    print("/*")
    print_group(result, location_xform, "Misc")

    for funcname in sorted(result.missing):
        print(f"warning: missing: {location_xform(funcname)}")
    for funcname in sorted(result.dynamic):
        print(f"warning: dynamic-stack-usage: {location_xform(funcname)}")

    print("*/")
    print("")
    print("/*")
    print_group(result, location_xform, "Extra")
    for funcname in sorted(result.included_funcs):
        print(f"included: {location_xform(funcname)}")
    print("*/")