opcode_311.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. # (C) Copyright 2024-2025
  2. # by Rocky Bernstein
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; either version 2
  7. # of the License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. """
  18. CPython 3.11 bytecode opcodes
  19. This is like Python 3.11's opcode.py with some classification
  20. of stack usage and information for formatting instructions.
  21. """
  22. from typing import Dict, List, Optional, Tuple
  23. import xdis.opcodes.opcode_310 as opcode_310
  24. from xdis.instruction import Instruction
  25. from xdis.opcodes.base import (
  26. binary_op,
  27. def_op,
  28. finalize_opcodes,
  29. free_op,
  30. init_opdata,
  31. jrel_op,
  32. rm_op,
  33. store_op,
  34. update_pj3,
  35. )
  36. from xdis.opcodes.format.extended import (
  37. NULL_EXTENDED_OP,
  38. extended_format_binary_op,
  39. extended_format_unary_op,
  40. extended_function_signature,
  41. )
  42. from xdis.opcodes.opcode_310 import opcode_arg_fmt310, opcode_extended_fmt310
  43. version_tuple = (3, 11)
  44. python_implementation = "CPython"
  45. # oppush[op] => number of stack entries pushed
  46. oppush: List[int] = [0] * 256
  47. # oppop[op] => number of stack entries popped
  48. oppop: List[int] = [0] * 256
  49. # opmap[opcode_name] => opcode_number
  50. opmap: Dict[str, int] = {}
  51. _nb_ops = [
  52. ("NB_ADD", "+"),
  53. ("NB_AND", "&"),
  54. ("NB_FLOOR_DIVIDE", "//"),
  55. ("NB_LSHIFT", "<<"),
  56. ("NB_MATRIX_MULTIPLY", "@"),
  57. ("NB_MULTIPLY", "*"),
  58. ("NB_MODULO", "%"),
  59. ("NB_OR", "|"),
  60. ("NB_POWER", "**"),
  61. ("NB_RSHIFT", ">>"),
  62. ("NB_SUBTRACT", "-"),
  63. ("NB_TRUE_DIVIDE", "/"),
  64. ("NB_XOR", "^"),
  65. ("NB_INPLACE_ADD", "+="),
  66. ("NB_INPLACE_AND", "&="),
  67. ("NB_INPLACE_FLOOR_DIVIDE", "//="),
  68. ("NB_INPLACE_LSHIFT", "<<="),
  69. ("NB_INPLACE_MATRIX_MULTIPLY", "@="),
  70. ("NB_INPLACE_MULTIPLY", "*="),
  71. ("NB_INPLACE_MODULO", "%="),
  72. ("NB_INPLACE_OR", "|="),
  73. ("NB_INPLACE_POWER", "**="),
  74. ("NB_INPLACE_RSHIFT", ">>="),
  75. ("NB_INPLACE_SUBTRACT", "-="),
  76. ("NB_INPLACE_TRUE_DIVIDE", "/="),
  77. ("NB_INPLACE_XOR", "^="),
  78. ]
  79. loc = locals()
  80. init_opdata(loc, opcode_310, version_tuple)
  81. # fmt: off
  82. ## These are removed / replaced since 3.10...
  83. # OP NAME OPCODE
  84. #---------------------------------------
  85. # Binary ops
  86. rm_op(loc, "BINARY_POWER", 19)
  87. rm_op(loc, "BINARY_MULTIPLY", 20)
  88. rm_op(loc, "BINARY_MATRIX_MULTIPLY", 16)
  89. rm_op(loc, "BINARY_FLOOR_DIVIDE", 26)
  90. rm_op(loc, "BINARY_TRUE_DIVIDE", 27)
  91. rm_op(loc, "BINARY_MODULO", 22)
  92. rm_op(loc, "BINARY_ADD", 23)
  93. rm_op(loc, "BINARY_SUBTRACT", 24)
  94. rm_op(loc, "BINARY_LSHIFT", 62)
  95. rm_op(loc, "BINARY_RSHIFT", 63)
  96. rm_op(loc, "BINARY_AND", 64)
  97. rm_op(loc, "BINARY_XOR", 65)
  98. rm_op(loc, "BINARY_OR", 66)
  99. # inplace ops
  100. rm_op(loc, "INPLACE_POWER", 67)
  101. rm_op(loc, "INPLACE_MULTIPLY", 57)
  102. rm_op(loc, "INPLACE_MATRIX_MULTIPLY", 17)
  103. rm_op(loc, "INPLACE_FLOOR_DIVIDE", 28)
  104. rm_op(loc, "INPLACE_TRUE_DIVIDE", 29)
  105. rm_op(loc, "INPLACE_MODULO", 59)
  106. rm_op(loc, "INPLACE_ADD", 55)
  107. rm_op(loc, "INPLACE_SUBTRACT", 56)
  108. rm_op(loc, "INPLACE_LSHIFT", 75)
  109. rm_op(loc, "INPLACE_RSHIFT", 76)
  110. rm_op(loc, "INPLACE_AND", 77)
  111. rm_op(loc, "INPLACE_XOR", 78)
  112. rm_op(loc, "INPLACE_OR", 79)
  113. # call ops
  114. rm_op(loc, "CALL_FUNCTION", 131)
  115. rm_op(loc, "CALL_FUNCTION_KW", 141)
  116. rm_op(loc, "CALL_METHOD", 161)
  117. # DUP and ROT ops
  118. rm_op(loc, "DUP_TOP", 4)
  119. rm_op(loc, "DUP_TOP_TWO", 5)
  120. rm_op(loc, "ROT_TWO", 2)
  121. rm_op(loc, "ROT_THREE", 3)
  122. rm_op(loc, "ROT_FOUR", 6)
  123. rm_op(loc, "ROT_N", 99)
  124. # exception check and jump
  125. rm_op(loc, "JUMP_IF_NOT_EXC_MATCH", 121)
  126. # jumps
  127. rm_op(loc, "JUMP_ABSOLUTE", 113)
  128. rm_op(loc, "POP_JUMP_IF_FALSE", 114)
  129. rm_op(loc, "POP_JUMP_IF_TRUE", 115)
  130. # setup with
  131. rm_op(loc, "SETUP_WITH", 143)
  132. rm_op(loc, "SETUP_ASYNC_WITH", 154)
  133. # fully removed ops
  134. rm_op(loc, "COPY_DICT_WITHOUT_KEYS", 34)
  135. rm_op(loc, "GEN_START", 129)
  136. rm_op(loc, "POP_BLOCK", 87)
  137. rm_op(loc, "SETUP_FINALLY", 122)
  138. rm_op(loc, "YIELD_FROM", 72)
  139. # match, these two ops had stack effects changed
  140. rm_op(loc, "MATCH_CLASS", 152)
  141. rm_op(loc, "MATCH_KEYS", 33)
  142. ## Redefined OPS
  143. # We to redefine 138 from DELETE_REF to STORE_DEREF.
  144. # So we have to rm DELETE_DEREF with opcode 138 *before* adding
  145. # STORE_DEREF with opcode 138.
  146. rm_op(loc, "GET_AWAITABLE", 73)
  147. rm_op(loc, "LOAD_CLOSURE", 135)
  148. rm_op(loc, "LOAD_DEREF", 136)
  149. rm_op(loc, "STORE_DEREF", 137)
  150. rm_op(loc, "DELETE_DEREF", 138)
  151. ## Redefined OPS
  152. def_op(loc, "GET_AWAITABLE", 131, 0, 0)
  153. free_op(loc, "LOAD_CLOSURE", 136, 0, 1)
  154. loc["nullaryloadop"].add(136)
  155. free_op(loc, "LOAD_DEREF", 137, 0, 1)
  156. loc["nullaryop"].add(137)
  157. loc["nullaryloadop"].add(137)
  158. store_op(loc, "STORE_DEREF", 138, 1, 0, is_type="free")
  159. free_op(loc, "DELETE_DEREF", 139, 0, 0)
  160. # These are added since 3.10...
  161. # OP NAME OPCODE POP PUSH
  162. #---------------------------------------------------------
  163. # replaced binary and inplace ops
  164. def_op(loc, "CACHE", 0, 0, 0)
  165. binary_op(loc, "BINARY_OP", 122)
  166. # call ops
  167. def_op(loc, "CALL", 171, 1, 0)
  168. def_op(loc, "KW_NAMES", 172, 0, 0)
  169. def_op(loc, "PRECALL", 166, 100, 0)
  170. def_op(loc, "PUSH_NULL", 2, 0, 1)
  171. # replaced DUP and ROT ops
  172. def_op(loc, "COPY", 120, 0, 1)
  173. def_op(loc, "SWAP", 99, 0, 0)
  174. # exception check
  175. def_op(loc, "CHECK_EXC_MATCH", 36, 0, 0)
  176. # jumps, all jumps are now relative jumps
  177. # TODO will likely have to redefine all abs jump ops as reljumps
  178. jrel_op(loc, "JUMP_BACKWARD", 140, 0, 0)
  179. jrel_op(loc, "POP_JUMP_BACKWARD_IF_FALSE", 175, 1, 0)
  180. jrel_op(loc, "POP_JUMP_BACKWARD_IF_TRUE", 176, 1, 0)
  181. jrel_op(loc, "POP_JUMP_BACKWARD_IF_NOT_NONE", 173, 1, 0)
  182. jrel_op(loc, "POP_JUMP_BACKWARD_IF_NONE", 174, 1, 0)
  183. jrel_op(loc, "POP_JUMP_FORWARD_IF_FALSE", 114, 1, 0)
  184. jrel_op(loc, "POP_JUMP_FORWARD_IF_TRUE", 115, 1, 0)
  185. jrel_op(loc, "POP_JUMP_FORWARD_IF_NOT_NONE", 128, 1, 0)
  186. jrel_op(loc, "POP_JUMP_FORWARD_IF_NONE", 129, 1, 0)
  187. # setup with
  188. def_op(loc, "BEFORE_WITH", 53, 0, 1)
  189. # match
  190. def_op(loc, "MATCH_CLASS", 152, 3, 1)
  191. def_op(loc, "MATCH_KEYS", 33, 0, 1)
  192. # generators and co-routines
  193. def_op(loc, "ASYNC_GEN_WRAP", 87, 0, 0)
  194. def_op(loc, "RETURN_GENERATOR", 75, 0, 0)
  195. def_op(loc, "SEND", 123, 0, 0)
  196. # copy free vars for closures
  197. def_op(loc, "COPY_FREE_VARS", 149, 0, 0)
  198. # new jump
  199. jrel_op(loc, "JUMP_BACKWARD_NO_INTERRUPT", 134, 0, 0)
  200. # new create cells op
  201. free_op(loc, "MAKE_CELL", 135, 0, 0)
  202. # new exception handling
  203. jrel_op(loc, "PUSH_EXC_INFO", 35, 0, 1)
  204. jrel_op(loc, "CHECK_EG_MATCH", 37, 0, 0)
  205. jrel_op(loc, "PREP_RERAISE_STAR", 88, 1, 0)
  206. # resume, acts like a nop
  207. def_op(loc, "RESUME", 151, 0, 0)
  208. ## Update tables
  209. # removed jrel ops 35, 37, 143, 88, 154
  210. loc["hasconst"].append(172) # KW_NAMES
  211. loc["hasfree"].extend((135, 136, 137, 138, 139))
  212. loc["hasjabs"] = []
  213. loc["hasjrel"] = [
  214. 93, 110, 111, 112, 114, 115, 123, 128, 129, 134, 140, 173, 174, 175,
  215. 176]
  216. # Changed stack effects
  217. oppop[opmap["POP_EXCEPT"]] = 1
  218. oppop[opmap["END_ASYNC_FOR"]] = 2
  219. oppop[opmap["RERAISE"]] = 1
  220. # fmt: on
  221. def extended_format_BINARY_OP(opc, instructions) -> Tuple[str, Optional[int]]:
  222. opname = _nb_ops[instructions[0].argval][1]
  223. if opname == "%":
  224. opname = "%%"
  225. elif opname == "%=":
  226. opname = "%%="
  227. return extended_format_binary_op(opc, instructions, f"%s {opname} %s")
  228. def extended_format_COPY_OP(
  229. opc, instructions: List[Instruction]
  230. ) -> Tuple[str, Optional[int]]:
  231. """Try to extract TOS value and show that surrounded in a "push() ".
  232. The trailing space at the used as a sentinal for `get_instruction_tos_str()`
  233. which tries to remove the push() part when the operand value string is needed.
  234. """
  235. # We add a space at the end as a sentinal to use in get_instruction_tos_str()
  236. if instructions[1].optype not in ["jrel", "jabs"]:
  237. return extended_format_unary_op(opc, instructions, "copy(%s) ")
  238. else:
  239. return NULL_EXTENDED_OP
  240. def extended_format_MAKE_FUNCTION_311(
  241. opc, instructions: List[Instruction]
  242. ) -> Tuple[str, int]:
  243. """
  244. Like MAKE_FUNCTION_36 but qualified name at TOS was removed.
  245. See: https://github.com/python/cpython/issues/93270
  246. """
  247. assert len(instructions) >= 2
  248. inst = instructions[0]
  249. assert inst.opname in ("MAKE_FUNCTION", "MAKE_CLOSURE")
  250. s = ""
  251. code_inst = instructions[1]
  252. start_offset = code_inst.offset
  253. if code_inst.opname == "LOAD_CONST" and hasattr(code_inst.argval, "co_name"):
  254. arg_flags = instructions[0].argval
  255. param_elision_str = extended_function_signature(code_inst.argval) if arg_flags != 0 else ""
  256. s += (
  257. f"def {code_inst.argval.co_name}({param_elision_str}): ..."
  258. )
  259. return s, start_offset
  260. return s, start_offset
  261. def extended_format_SWAP(
  262. opc, instructions: List[Instruction]
  263. ) -> Tuple[str, Optional[int]]:
  264. """call_function_inst should be a "SWAP" instruction. See if
  265. `we can find the two instructions to be swapped. If not we'll
  266. return None.
  267. """
  268. # From opcode description: argc indicates the total number of
  269. # positional and keyword arguments. Sometimes the function name
  270. # is in the stack arg positions back.
  271. # From opcode description: arg_count indicates the total number of
  272. # positional and keyword arguments.
  273. swap_instr = instructions[0]
  274. i = swap_instr.argval
  275. # s = ""
  276. if i is None or not (0 < i < len(instructions)):
  277. return "", None
  278. # To be continued
  279. return "", None
  280. def format_BINARY_OP(arg: int) -> str:
  281. return _nb_ops[arg][1]
  282. def format_SWAP_OP(arg: int) -> str:
  283. return f"TOS <-> TOS{arg-1}"
  284. opcode_arg_fmt311 = opcode_arg_fmt310.copy()
  285. del opcode_arg_fmt311["CALL_FUNCTION"]
  286. del opcode_arg_fmt311["CALL_FUNCTION_KW"]
  287. del opcode_arg_fmt311["CALL_METHOD"]
  288. opcode_arg_fmt = opcode_arg_fmt311 = {
  289. **opcode_arg_fmt310,
  290. **{
  291. "BINARY_OP": format_BINARY_OP,
  292. "SWAP": format_SWAP_OP,
  293. },
  294. }
  295. opcode_extended_fmt = opcode_extended_fmt311 = {
  296. **opcode_extended_fmt310,
  297. **{
  298. "BINARY_OP": extended_format_BINARY_OP,
  299. "COPY": extended_format_COPY_OP,
  300. "MAKE_FUNCTION": extended_format_MAKE_FUNCTION_311,
  301. },
  302. }
  303. del opcode_extended_fmt311["BINARY_ADD"]
  304. del opcode_extended_fmt311["BINARY_AND"]
  305. del opcode_extended_fmt311["BINARY_FLOOR_DIVIDE"]
  306. del opcode_extended_fmt311["BINARY_LSHIFT"]
  307. del opcode_extended_fmt311["BINARY_MATRIX_MULTIPLY"]
  308. del opcode_extended_fmt311["BINARY_MODULO"]
  309. del opcode_extended_fmt311["BINARY_MULTIPLY"]
  310. del opcode_extended_fmt311["BINARY_OR"]
  311. del opcode_extended_fmt311["BINARY_POWER"]
  312. del opcode_extended_fmt311["BINARY_RSHIFT"]
  313. del opcode_extended_fmt311["BINARY_SUBTRACT"]
  314. del opcode_extended_fmt311["BINARY_TRUE_DIVIDE"]
  315. del opcode_extended_fmt311["BINARY_XOR"]
  316. del opcode_extended_fmt311["CALL_FUNCTION"]
  317. del opcode_extended_fmt311["CALL_FUNCTION_KW"]
  318. # del opcode_extended_fmt311["CALL_METHOD"]
  319. del opcode_extended_fmt311["INPLACE_ADD"]
  320. del opcode_extended_fmt311["INPLACE_AND"]
  321. del opcode_extended_fmt311["INPLACE_FLOOR_DIVIDE"]
  322. del opcode_extended_fmt311["INPLACE_LSHIFT"]
  323. del opcode_extended_fmt311["INPLACE_MATRIX_MULTIPLY"]
  324. del opcode_extended_fmt311["INPLACE_MODULO"]
  325. del opcode_extended_fmt311["INPLACE_MULTIPLY"]
  326. del opcode_extended_fmt311["INPLACE_OR"]
  327. del opcode_extended_fmt311["INPLACE_POWER"]
  328. del opcode_extended_fmt311["INPLACE_RSHIFT"]
  329. del opcode_extended_fmt311["INPLACE_SUBTRACT"]
  330. del opcode_extended_fmt311["INPLACE_TRUE_DIVIDE"]
  331. del opcode_extended_fmt311["INPLACE_XOR"]
  332. from xdis.opcodes.opcode_310 import findlinestarts
  333. update_pj3(globals(), loc)
  334. finalize_opcodes(loc)