opcode_36pypy.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. # (C) Copyright 2019-2021, 2023-2024 by Rocky Bernstein
  2. """
  3. PYPY 3.6 opcodes
  4. This is a like PyPy 3.6's opcode.py with some classification
  5. of stack usage and information for formatting instructions.
  6. """
  7. import sys
  8. from typing import List, Optional, Tuple
  9. import xdis.opcodes.opcode_36 as opcode_36
  10. from xdis.opcodes.base import (
  11. call_op,
  12. def_op,
  13. finalize_opcodes,
  14. init_opdata,
  15. jrel_op,
  16. name_op,
  17. rm_op,
  18. update_pj3,
  19. varargs_op,
  20. )
  21. from xdis.opcodes.opcode_36 import opcode_arg_fmt36, opcode_extended_fmt36
  22. version_tuple = (3, 6)
  23. python_implementation = "PyPy"
  24. # oppush[op] => number of stack entries pushed
  25. oppush: List[int] = [0] * 256
  26. # oppop[op] => number of stack entries popped
  27. oppop: List[int] = [0] * 256
  28. loc = locals()
  29. init_opdata(loc, opcode_36, version_tuple, is_pypy=True)
  30. ## FIXME: DRY common PYPY opcode additions
  31. # Opcodes removed from 3.6.
  32. # fmt: off
  33. rm_op(loc, "CALL_FUNCTION_EX", 142)
  34. rm_op(loc, "BUILD_TUPLE_UNPACK_WITH_CALL", 158)
  35. # The following were removed from 3.6 but still in Pypy 3.6
  36. def_op(loc, "MAKE_CLOSURE", 134, 9, 1) # TOS is number of items to pop
  37. call_op(loc, "CALL_FUNCTION_VAR", 140, 9, 1) # #args + (#kwargs << 8)
  38. call_op(loc, "CALL_FUNCTION_KW", 141, 9, 1) # #args + (#kwargs << 8)
  39. call_op(loc, "CALL_FUNCTION_VAR_KW", 142, 9, 1) # #args + (#kwargs << 8)
  40. # PyPy only
  41. # ----------
  42. # See:
  43. # https://doc.pypy.org/en/latest/interpreter-optimizations.html#lookup-method-call-method
  44. name_op(loc, "LOOKUP_METHOD", 201, 1, 2)
  45. call_op(loc, "CALL_METHOD", 202, -1, 1)
  46. loc["hasvargs"].append(202)
  47. # Used only in single-mode compilation list-comprehension generators
  48. varargs_op(loc, "BUILD_LIST_FROM_ARG", 203)
  49. # Used only in assert statements
  50. jrel_op(loc, "JUMP_IF_NOT_DEBUG", 204, conditional=True)
  51. # fmt: on
  52. # PyPy 3.6.1 (and 2.7.13) start to introduce LOAD_REVDB_VAR
  53. if sys.version_info[:3] >= (3, 6, 1):
  54. def_op(loc, "LOAD_REVDB_VAR", 205)
  55. def extended_format_CALL_METHOD(opc, instructions) -> str:
  56. """argc has the number of positional arguments.
  57. TOS starts the positional arguments
  58. values for each keyword argument.
  59. After that is a slot to cache a method function
  60. Below that is the method attribute to that is looked up to find
  61. the method name."""
  62. call_method_inst = instructions[0]
  63. assert call_method_inst.opname == "CALL_METHOD"
  64. method_pos = call_method_inst.arg + 1
  65. assert len(instructions) >= method_pos + 1
  66. s = ""
  67. i = -1
  68. for i, inst in enumerate(instructions[1:]):
  69. if i == method_pos:
  70. break
  71. if inst.is_jump_target:
  72. i += 1
  73. break
  74. # Make sure we are in the same basic block
  75. # and ... ?
  76. opcode = inst.opcode
  77. if inst.optype in ("nargs", "vargs"):
  78. break
  79. if inst.optype != "name":
  80. method_pos += (oppop[opcode] - oppush[opcode]) + 1
  81. if inst.opname in ("LOOKUP_METHOD"):
  82. method_pos = i
  83. break
  84. pass
  85. if i == method_pos and len(instructions) > method_pos + 2:
  86. if instructions[method_pos + 2].opname in ("LOAD_NAME", "LOAD_FAST"):
  87. s += "%s.%s(), " % (
  88. instructions[method_pos + 2].argrepr,
  89. instructions[method_pos + 1].argrepr,
  90. )
  91. pass
  92. pass
  93. s += format_CALL_METHOD(call_method_inst.arg)
  94. return s
  95. def extended_format_CALL_METHOD_KW(opc, instructions) -> str:
  96. """argc has the number of positional plus keyword arguments.
  97. TOS is a tuple of keyword argument names and below that are
  98. values for each keyword argument.
  99. Below that are positional arguments.
  100. After that is a slot to cache a method function
  101. Below that is the method attribute to that is looked up to find
  102. the method name."""
  103. call_method_inst = instructions[0]
  104. assert call_method_inst.opname == "CALL_METHOD_KW"
  105. method_pos = call_method_inst.arg + 2
  106. assert len(instructions) >= method_pos + 1
  107. kw_names = instructions[1]
  108. assert isinstance(kw_names, tuple)
  109. kw_name_str = "=..., ".join(kw_names.argval) + "=..."
  110. s = ""
  111. i = -1
  112. for i, inst in enumerate(instructions[2:]):
  113. if i == method_pos:
  114. break
  115. if inst.is_jump_target:
  116. i += 1
  117. break
  118. # Make sure we are in the same basic block
  119. # and ... ?
  120. opcode = inst.opcode
  121. if inst.optype in ("nargs", "vargs"):
  122. break
  123. if inst.optype != "name":
  124. method_pos += (oppop[opcode] - oppush[opcode]) + 1
  125. if inst.opname in ("LOOKUP_METHOD"):
  126. method_pos = i
  127. break
  128. pass
  129. if i == method_pos:
  130. if instructions[method_pos + 3].opname in ("LOAD_NAME", "LOAD_FAST"):
  131. s += "%s.%s" % (
  132. instructions[method_pos + 3].argrepr,
  133. instructions[method_pos + 2].argrepr,
  134. )
  135. pass
  136. if call_method_inst.arg > len(kw_names.argval):
  137. s += "(..., %s), " % kw_name_str
  138. else:
  139. s += "(%s), " % kw_name_str
  140. pass
  141. s += format_CALL_METHOD_KW(call_method_inst.arg, len(kw_names.argval))
  142. return s
  143. def extended_format_LOOKUP_METHOD(opc, instructions: list) -> Tuple[str, Optional[int]]:
  144. if instructions[1].opcode in opc.NAME_OPS | opc.CONST_OPS | opc.LOCAL_OPS:
  145. return (
  146. f"{instructions[1].argrepr}.{instructions[0].argrepr}",
  147. instructions[1].offset,
  148. )
  149. return "", None
  150. def format_CALL_METHOD(argc):
  151. """argc has the number of positional arguments.
  152. TOS starts the positional arguments
  153. After that is a slot to cache a method function
  154. Below that is the method attribute to that is looked up to find
  155. the method name."""
  156. return "%d positional" % (argc)
  157. def format_CALL_METHOD_KW(argc, kwarg_count: int | None=None):
  158. """argc has the number of positional plus keyword arguments.
  159. TOS is a tuple of keyword argument names and below that are
  160. values for each keyword argument.
  161. Below that are positional arguments.
  162. After that is a slot to cache a method function
  163. Below that is the method attribute to that is looked up to find
  164. the method name."""
  165. if isinstance(kwarg_count, int):
  166. positional_argc = argc - kwarg_count
  167. return "%d positional, %d keyword" % (positional_argc, kwarg_count)
  168. else:
  169. return "%d positional + keyword" % (argc)
  170. opcode_arg_fmt = opcode_arg_fmt36pypy = {
  171. **opcode_arg_fmt36,
  172. }
  173. opcode_extended_fmt = opcode_extended_fmt36pypy = {
  174. **opcode_extended_fmt36,
  175. }
  176. # FIXME remove (fix uncompyle6)
  177. update_pj3(globals(), loc)
  178. finalize_opcodes(loc)