1# This script generates the opcode.h header file.
2
3import sys
4import tokenize
5
6SCRIPT_NAME = "Tools/scripts/generate_opcode_h.py"
7PYTHON_OPCODE = "Lib/opcode.py"
8
9header = f"""
10// Auto-generated by {SCRIPT_NAME} from {PYTHON_OPCODE}
11
12#ifndef Py_OPCODE_H
13#define Py_OPCODE_H
14#ifdef __cplusplus
15extern "C" {{
16#endif
17
18
19/* Instruction opcodes for compiled code */
20""".lstrip()
21
22footer = """
23#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT)
24
25/* Reserve some bytecodes for internal use in the compiler.
26 * The value of 240 is arbitrary. */
27#define IS_ARTIFICIAL(op) ((op) > 240)
28
29#ifdef __cplusplus
30}
31#endif
32#endif /* !Py_OPCODE_H */
33"""
34
35internal_header = f"""
36// Auto-generated by {SCRIPT_NAME} from {PYTHON_OPCODE}
37
38#ifndef Py_INTERNAL_OPCODE_H
39#define Py_INTERNAL_OPCODE_H
40#ifdef __cplusplus
41extern "C" {{
42#endif
43
44#ifndef Py_BUILD_CORE
45#  error "this header requires Py_BUILD_CORE define"
46#endif
47
48#include "opcode.h"
49""".lstrip()
50
51internal_footer = """
52#ifdef __cplusplus
53}
54#endif
55#endif  // !Py_INTERNAL_OPCODE_H
56"""
57
58DEFINE = "#define {:<38} {:>3}\n"
59
60UINT32_MASK = (1<<32)-1
61
62def write_int_array_from_ops(name, ops, out):
63    bits = 0
64    for op in ops:
65        bits |= 1<<op
66    out.write(f"static const uint32_t {name}[8] = {{\n")
67    for i in range(8):
68        out.write(f"    {bits & UINT32_MASK}U,\n")
69        bits >>= 32
70    assert bits == 0
71    out.write(f"}};\n")
72
73def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/internal/pycore_opcode.h'):
74    opcode = {}
75    if hasattr(tokenize, 'open'):
76        fp = tokenize.open(opcode_py)   # Python 3.2+
77    else:
78        fp = open(opcode_py)            # Python 2.7
79    with fp:
80        code = fp.read()
81    exec(code, opcode)
82    opmap = opcode['opmap']
83    opname = opcode['opname']
84    hasconst = opcode['hasconst']
85    hasjrel = opcode['hasjrel']
86    hasjabs = opcode['hasjabs']
87    used = [ False ] * 256
88    next_op = 1
89
90    for name, op in opmap.items():
91        used[op] = True
92
93    specialized_opmap = {}
94    opname_including_specialized = opname.copy()
95    for name in opcode['_specialized_instructions']:
96        while used[next_op]:
97            next_op += 1
98        specialized_opmap[name] = next_op
99        opname_including_specialized[next_op] = name
100        used[next_op] = True
101    specialized_opmap['DO_TRACING'] = 255
102    opname_including_specialized[255] = 'DO_TRACING'
103    used[255] = True
104
105    with open(outfile, 'w') as fobj, open(internaloutfile, 'w') as iobj:
106        fobj.write(header)
107        iobj.write(internal_header)
108
109        for name in opname:
110            if name in opmap:
111                fobj.write(DEFINE.format(name, opmap[name]))
112            if name == 'POP_EXCEPT': # Special entry for HAVE_ARGUMENT
113                fobj.write(DEFINE.format("HAVE_ARGUMENT", opcode["HAVE_ARGUMENT"]))
114
115        for name, op in specialized_opmap.items():
116            fobj.write(DEFINE.format(name, op))
117
118        iobj.write("\nextern const uint8_t _PyOpcode_Caches[256];\n")
119        iobj.write("\nextern const uint8_t _PyOpcode_Deopt[256];\n")
120        iobj.write("\n#ifdef NEED_OPCODE_TABLES\n")
121        write_int_array_from_ops("_PyOpcode_RelativeJump", opcode['hasjrel'], iobj)
122        write_int_array_from_ops("_PyOpcode_Jump", opcode['hasjrel'] + opcode['hasjabs'], iobj)
123
124        iobj.write("\nconst uint8_t _PyOpcode_Caches[256] = {\n")
125        for i, entries in enumerate(opcode["_inline_cache_entries"]):
126            if entries:
127                iobj.write(f"    [{opname[i]}] = {entries},\n")
128        iobj.write("};\n")
129
130        deoptcodes = {}
131        for basic in opmap:
132            deoptcodes[basic] = basic
133        for basic, family in opcode["_specializations"].items():
134            for specialized in family:
135                deoptcodes[specialized] = basic
136        iobj.write("\nconst uint8_t _PyOpcode_Deopt[256] = {\n")
137        for opt, deopt in sorted(deoptcodes.items()):
138            iobj.write(f"    [{opt}] = {deopt},\n")
139        iobj.write("};\n")
140        iobj.write("#endif   // NEED_OPCODE_TABLES\n")
141
142        fobj.write("\n")
143        fobj.write("#define HAS_CONST(op) (false\\")
144        for op in hasconst:
145            fobj.write(f"\n    || ((op) == {op}) \\")
146        fobj.write("\n    )\n")
147
148        fobj.write("\n")
149        for i, (op, _) in enumerate(opcode["_nb_ops"]):
150            fobj.write(DEFINE.format(op, i))
151
152        iobj.write("\n")
153        iobj.write("#ifdef Py_DEBUG\n")
154        iobj.write("static const char *const _PyOpcode_OpName[256] = {\n")
155        for op, name in enumerate(opname_including_specialized):
156            if name[0] != "<":
157                op = name
158            iobj.write(f'''    [{op}] = "{name}",\n''')
159        iobj.write("};\n")
160        iobj.write("#endif\n")
161
162        iobj.write("\n")
163        iobj.write("#define EXTRA_CASES \\\n")
164        for i, flag in enumerate(used):
165            if not flag:
166                iobj.write(f"    case {i}: \\\n")
167        iobj.write("        ;\n")
168
169        fobj.write(footer)
170        iobj.write(internal_footer)
171
172
173    print(f"{outfile} regenerated from {opcode_py}")
174
175
176if __name__ == '__main__':
177    main(sys.argv[1], sys.argv[2], sys.argv[3])
178