PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/jit/metainterp/history.py

https://bitbucket.org/pypy/pypy/
Python | 1055 lines | 811 code | 183 blank | 61 comment | 142 complexity | edf4c1afd6f392137402d02e7a7f42a7 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import sys
  2. from rpython.rtyper.extregistry import ExtRegistryEntry
  3. from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
  4. from rpython.rlib.objectmodel import we_are_translated, Symbolic
  5. from rpython.rlib.objectmodel import compute_unique_id, specialize
  6. from rpython.rlib.rarithmetic import r_int64, is_valid_int
  7. from rpython.rlib.rarithmetic import LONG_BIT, intmask, r_uint
  8. from rpython.rlib.jit import Counters
  9. from rpython.conftest import option
  10. from rpython.jit.metainterp.resoperation import ResOperation, rop,\
  11. AbstractValue, oparity, AbstractResOp, IntOp, RefOp, FloatOp,\
  12. opclasses
  13. from rpython.jit.codewriter import heaptracker, longlong
  14. import weakref
  15. from rpython.jit.metainterp import jitexc
  16. # ____________________________________________________________
  17. INT = 'i'
  18. REF = 'r'
  19. FLOAT = 'f'
  20. STRUCT = 's'
  21. HOLE = '_'
  22. VOID = 'v'
  23. VECTOR = 'V'
  24. FAILARGS_LIMIT = 1000
  25. class SwitchToBlackhole(jitexc.JitException):
  26. def __init__(self, reason, raising_exception=False):
  27. self.reason = reason
  28. self.raising_exception = raising_exception
  29. # ^^^ must be set to True if the SwitchToBlackhole is raised at a
  30. # point where the exception on metainterp.last_exc_value
  31. # is supposed to be raised. The default False means that it
  32. # should just be copied into the blackhole interp, but not raised.
  33. def getkind(TYPE, supports_floats=True,
  34. supports_longlong=True,
  35. supports_singlefloats=True):
  36. if TYPE is lltype.Void:
  37. return "void"
  38. elif isinstance(TYPE, lltype.Primitive):
  39. if TYPE is lltype.Float and supports_floats:
  40. return 'float'
  41. if TYPE is lltype.SingleFloat and supports_singlefloats:
  42. return 'int' # singlefloats are stored in an int
  43. if TYPE in (lltype.Float, lltype.SingleFloat):
  44. raise NotImplementedError("type %s not supported" % TYPE)
  45. if (TYPE != llmemory.Address and
  46. rffi.sizeof(TYPE) > rffi.sizeof(lltype.Signed)):
  47. if supports_longlong and TYPE is not lltype.LongFloat:
  48. assert rffi.sizeof(TYPE) == 8
  49. return 'float'
  50. raise NotImplementedError("type %s is too large" % TYPE)
  51. return "int"
  52. elif isinstance(TYPE, lltype.Ptr):
  53. if TYPE.TO._gckind == 'raw':
  54. return "int"
  55. else:
  56. return "ref"
  57. else:
  58. raise NotImplementedError("type %s not supported" % TYPE)
  59. getkind._annspecialcase_ = 'specialize:memo'
  60. def repr_pointer(box):
  61. from rpython.rtyper.lltypesystem import rstr
  62. try:
  63. T = box.value._obj.container._normalizedcontainer(check=False)._TYPE
  64. if T is rstr.STR:
  65. return repr(box._get_str())
  66. return '*%s' % (T._name,)
  67. except AttributeError:
  68. return box.value
  69. def repr_object(box):
  70. try:
  71. TYPE = box.value.obj._TYPE
  72. return repr(TYPE)
  73. except AttributeError:
  74. return box.value
  75. def repr_rpython(box, typechars):
  76. return '%s/%s' % (box._get_hash_(), typechars,
  77. ) #compute_unique_id(box))
  78. class AbstractDescr(AbstractValue):
  79. __slots__ = ('descr_index', 'ei_index')
  80. llopaque = True
  81. descr_index = -1
  82. ei_index = sys.maxint
  83. def repr_of_descr(self):
  84. return '%r' % (self,)
  85. def hide(self, cpu):
  86. descr_ptr = cpu.ts.cast_instance_to_base_ref(self)
  87. return cpu.ts.cast_to_ref(descr_ptr)
  88. @staticmethod
  89. def show(cpu, descr_gcref):
  90. from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance
  91. descr_ptr = cpu.ts.cast_to_baseclass(descr_gcref)
  92. return cast_base_ptr_to_instance(AbstractDescr, descr_ptr)
  93. def get_vinfo(self):
  94. raise NotImplementedError
  95. DONT_CHANGE = AbstractDescr()
  96. class AbstractFailDescr(AbstractDescr):
  97. index = -1
  98. final_descr = False
  99. _attrs_ = ('adr_jump_offset', 'rd_locs', 'rd_loop_token', 'rd_vector_info')
  100. rd_vector_info = None
  101. def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
  102. raise NotImplementedError
  103. def compile_and_attach(self, metainterp, new_loop, orig_inputargs):
  104. raise NotImplementedError
  105. def exits_early(self):
  106. # is this guard either a guard_early_exit resop,
  107. # or it has been moved before an guard_early_exit
  108. return False
  109. def loop_version(self):
  110. # compile a loop version out of this guard?
  111. return False
  112. def attach_vector_info(self, info):
  113. from rpython.jit.metainterp.resume import VectorInfo
  114. assert isinstance(info, VectorInfo)
  115. info.prev = self.rd_vector_info
  116. self.rd_vector_info = info
  117. class BasicFinalDescr(AbstractFailDescr):
  118. final_descr = True
  119. def __init__(self, identifier=None):
  120. self.identifier = identifier # for testing
  121. class BasicFailDescr(AbstractFailDescr):
  122. def __init__(self, identifier=None):
  123. self.identifier = identifier # for testing
  124. def make_a_counter_per_value(self, op, index):
  125. pass # for testing
  126. @specialize.argtype(0)
  127. def newconst(value):
  128. if value is None:
  129. return ConstPtr(lltype.nullptr(llmemory.GCREF.TO))
  130. elif lltype.typeOf(value) == lltype.Signed:
  131. return ConstInt(value)
  132. elif isinstance(value, bool):
  133. return ConstInt(int(value))
  134. elif lltype.typeOf(value) == longlong.FLOATSTORAGE:
  135. return ConstFloat(value)
  136. else:
  137. assert lltype.typeOf(value) == llmemory.GCREF
  138. return ConstPtr(value)
  139. class MissingValue(object):
  140. "NOT_RPYTHON"
  141. class Const(AbstractValue):
  142. _attrs_ = ()
  143. @staticmethod
  144. def _new(x):
  145. "NOT_RPYTHON"
  146. T = lltype.typeOf(x)
  147. kind = getkind(T)
  148. if kind == "int":
  149. if isinstance(T, lltype.Ptr):
  150. intval = heaptracker.adr2int(llmemory.cast_ptr_to_adr(x))
  151. else:
  152. intval = lltype.cast_primitive(lltype.Signed, x)
  153. return ConstInt(intval)
  154. elif kind == "ref":
  155. return cpu.ts.new_ConstRef(x)
  156. elif kind == "float":
  157. return ConstFloat(longlong.getfloatstorage(x))
  158. else:
  159. raise NotImplementedError(kind)
  160. def constbox(self):
  161. return self
  162. def same_box(self, other):
  163. return self.same_constant(other)
  164. def same_constant(self, other):
  165. raise NotImplementedError
  166. def repr(self, memo):
  167. return self.repr_rpython()
  168. def is_constant(self):
  169. return True
  170. def __repr__(self):
  171. return 'Const(%s)' % self._getrepr_()
  172. class ConstInt(Const):
  173. type = INT
  174. value = 0
  175. _attrs_ = ('value',)
  176. def __init__(self, value):
  177. if not we_are_translated():
  178. if is_valid_int(value):
  179. value = int(value) # bool -> int
  180. else:
  181. assert isinstance(value, Symbolic)
  182. self.value = value
  183. def getint(self):
  184. return self.value
  185. getvalue = getint
  186. def getaddr(self):
  187. return heaptracker.int2adr(self.value)
  188. def _get_hash_(self):
  189. return make_hashable_int(self.value)
  190. def same_constant(self, other):
  191. if isinstance(other, ConstInt):
  192. return self.value == other.value
  193. return False
  194. def nonnull(self):
  195. return self.value != 0
  196. def _getrepr_(self):
  197. return self.value
  198. def repr_rpython(self):
  199. return repr_rpython(self, 'ci')
  200. CONST_FALSE = ConstInt(0)
  201. CONST_TRUE = ConstInt(1)
  202. class ConstFloat(Const):
  203. type = FLOAT
  204. value = longlong.ZEROF
  205. _attrs_ = ('value',)
  206. def __init__(self, valuestorage):
  207. assert lltype.typeOf(valuestorage) is longlong.FLOATSTORAGE
  208. self.value = valuestorage
  209. @staticmethod
  210. def fromfloat(x):
  211. return ConstFloat(longlong.getfloatstorage(x))
  212. def getfloatstorage(self):
  213. return self.value
  214. def getfloat(self):
  215. return longlong.getrealfloat(self.value)
  216. getvalue = getfloatstorage
  217. def _get_hash_(self):
  218. return longlong.gethash(self.value)
  219. def same_constant(self, other):
  220. if isinstance(other, ConstFloat):
  221. # careful in this comparison: if self.value and other.value
  222. # are both NaN, stored as regular floats (i.e. on 64-bit),
  223. # then just using "==" would say False: two NaNs are always
  224. # different from each other. Conversely, "0.0 == -0.0" but
  225. # they are not the same constant.
  226. return (longlong.extract_bits(self.value) ==
  227. longlong.extract_bits(other.value))
  228. return False
  229. def nonnull(self):
  230. return bool(longlong.extract_bits(self.value))
  231. def _getrepr_(self):
  232. return self.getfloat()
  233. def repr_rpython(self):
  234. return repr_rpython(self, 'cf')
  235. CONST_FZERO = ConstFloat(longlong.ZEROF)
  236. class ConstPtr(Const):
  237. type = REF
  238. value = lltype.nullptr(llmemory.GCREF.TO)
  239. _attrs_ = ('value',)
  240. def __init__(self, value):
  241. assert lltype.typeOf(value) == llmemory.GCREF
  242. self.value = value
  243. def getref_base(self):
  244. return self.value
  245. getvalue = getref_base
  246. def getref(self, PTR):
  247. return lltype.cast_opaque_ptr(PTR, self.getref_base())
  248. getref._annspecialcase_ = 'specialize:arg(1)'
  249. def _get_hash_(self):
  250. if self.value:
  251. return lltype.identityhash(self.value)
  252. else:
  253. return 0
  254. def same_constant(self, other):
  255. if isinstance(other, ConstPtr):
  256. return self.value == other.value
  257. return False
  258. def nonnull(self):
  259. return bool(self.value)
  260. _getrepr_ = repr_pointer
  261. def repr_rpython(self):
  262. return repr_rpython(self, 'cp')
  263. def _get_str(self): # for debugging only
  264. from rpython.rtyper.annlowlevel import hlstr
  265. from rpython.rtyper.lltypesystem import rstr
  266. try:
  267. return hlstr(lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR),
  268. self.value))
  269. except lltype.UninitializedMemoryAccess:
  270. return '<uninitialized string>'
  271. CONST_NULL = ConstPtr(ConstPtr.value)
  272. # ____________________________________________________________
  273. def make_hashable_int(i):
  274. from rpython.rtyper.lltypesystem.ll2ctypes import NotCtypesAllocatedStructure
  275. if not we_are_translated() and isinstance(i, llmemory.AddressAsInt):
  276. # Warning: such a hash changes at the time of translation
  277. adr = heaptracker.int2adr(i)
  278. try:
  279. return llmemory.cast_adr_to_int(adr, "emulated")
  280. except NotCtypesAllocatedStructure:
  281. return 12345 # use an arbitrary number for the hash
  282. return i
  283. def get_const_ptr_for_string(s):
  284. from rpython.rtyper.annlowlevel import llstr
  285. if not we_are_translated():
  286. try:
  287. return _const_ptr_for_string[s]
  288. except KeyError:
  289. pass
  290. result = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, llstr(s)))
  291. if not we_are_translated():
  292. _const_ptr_for_string[s] = result
  293. return result
  294. _const_ptr_for_string = {}
  295. def get_const_ptr_for_unicode(s):
  296. from rpython.rtyper.annlowlevel import llunicode
  297. if not we_are_translated():
  298. try:
  299. return _const_ptr_for_unicode[s]
  300. except KeyError:
  301. pass
  302. if isinstance(s, str):
  303. s = unicode(s)
  304. result = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, llunicode(s)))
  305. if not we_are_translated():
  306. _const_ptr_for_unicode[s] = result
  307. return result
  308. _const_ptr_for_unicode = {}
  309. # ____________________________________________________________
  310. # The JitCellToken class is the root of a tree of traces. Each branch ends
  311. # in a jump which goes to a LABEL operation; or it ends in a FINISH.
  312. class JitCellToken(AbstractDescr):
  313. """Used for rop.JUMP, giving the target of the jump.
  314. This is different from TreeLoop: the TreeLoop class contains the
  315. whole loop, including 'operations', and goes away after the loop
  316. was compiled; but the LoopDescr remains alive and points to the
  317. generated assembler.
  318. """
  319. target_tokens = None
  320. failed_states = None
  321. retraced_count = 0
  322. terminating = False # see TerminatingLoopToken in compile.py
  323. invalidated = False
  324. outermost_jitdriver_sd = None
  325. # and more data specified by the backend when the loop is compiled
  326. number = -1
  327. generation = r_int64(0)
  328. # one purpose of LoopToken is to keep alive the CompiledLoopToken
  329. # returned by the backend. When the LoopToken goes away, the
  330. # CompiledLoopToken has its __del__ called, which frees the assembler
  331. # memory and the ResumeGuards.
  332. compiled_loop_token = None
  333. def __init__(self):
  334. # For memory management of assembled loops
  335. self._keepalive_jitcell_tokens = {} # set of other JitCellToken
  336. def record_jump_to(self, jitcell_token):
  337. assert isinstance(jitcell_token, JitCellToken)
  338. self._keepalive_jitcell_tokens[jitcell_token] = None
  339. def __repr__(self):
  340. return '<Loop %d, gen=%d>' % (self.number, self.generation)
  341. def repr_of_descr(self):
  342. return '<Loop%d>' % self.number
  343. def dump(self):
  344. self.compiled_loop_token.cpu.dump_loop_token(self)
  345. class TargetToken(AbstractDescr):
  346. _ll_loop_code = 0 # for the backend. If 0, we know that it is
  347. # a LABEL that was not compiled yet.
  348. def __init__(self, targeting_jitcell_token=None,
  349. original_jitcell_token=None):
  350. # Warning, two different jitcell_tokens here!
  351. #
  352. # * 'targeting_jitcell_token' is only useful for the front-end,
  353. # and it means: consider the LABEL that uses this TargetToken.
  354. # At this position, the state is logically the one given
  355. # by targeting_jitcell_token. So e.g. if we want to enter the
  356. # JIT with some given green args, if the jitcell matches, then
  357. # we can jump to this LABEL.
  358. #
  359. # * 'original_jitcell_token' is information from the backend's
  360. # point of view: it means that this TargetToken is used in
  361. # a LABEL that belongs to either:
  362. # - a loop; then 'original_jitcell_token' is this loop
  363. # - or a bridge; then 'original_jitcell_token' is the loop
  364. # out of which we made this bridge
  365. #
  366. self.targeting_jitcell_token = targeting_jitcell_token
  367. self.original_jitcell_token = original_jitcell_token
  368. self.virtual_state = None
  369. self.short_preamble = None
  370. def repr_of_descr(self):
  371. return 'TargetToken(%d)' % compute_unique_id(self)
  372. class TreeLoop(object):
  373. inputargs = None
  374. operations = None
  375. call_pure_results = None
  376. logops = None
  377. quasi_immutable_deps = None
  378. def _token(*args):
  379. raise Exception("TreeLoop.token is killed")
  380. token = property(_token, _token)
  381. # This is the jitcell where the trace starts. Labels within the
  382. # trace might belong to some other jitcells, i.e. they might have
  383. # TargetTokens with a different value for 'targeting_jitcell_token'.
  384. # But these TargetTokens also have a 'original_jitcell_token' field,
  385. # which must be equal to this one.
  386. original_jitcell_token = None
  387. def __init__(self, name):
  388. self.name = name
  389. # self.operations = list of ResOperations
  390. # ops of the kind 'guard_xxx' contain a further list of operations,
  391. # which may itself contain 'guard_xxx' and so on, making a tree.
  392. def _all_operations(self, omit_finish=False):
  393. "NOT_RPYTHON"
  394. result = []
  395. _list_all_operations(result, self.operations, omit_finish)
  396. return result
  397. def summary(self, adding_insns={}, omit_finish=True): # for debugging
  398. "NOT_RPYTHON"
  399. insns = adding_insns.copy()
  400. for op in self._all_operations(omit_finish=omit_finish):
  401. opname = op.getopname()
  402. insns[opname] = insns.get(opname, 0) + 1
  403. return insns
  404. def get_operations(self):
  405. return self.operations
  406. def get_display_text(self, memo): # for graphpage.py
  407. return '%s\n[%s]' % (
  408. self.name,
  409. ', '.join([box.repr(memo) for box in self.inputargs]))
  410. def show(self, errmsg=None):
  411. "NOT_RPYTHON"
  412. from rpython.jit.metainterp.graphpage import display_procedures
  413. display_procedures([self], errmsg)
  414. def check_consistency(self, check_descr=True): # for testing
  415. "NOT_RPYTHON"
  416. self.check_consistency_of(self.inputargs, self.operations,
  417. check_descr=check_descr)
  418. for op in self.operations:
  419. descr = op.getdescr()
  420. if check_descr and op.getopnum() == rop.LABEL and isinstance(descr, TargetToken):
  421. assert descr.original_jitcell_token is self.original_jitcell_token
  422. @staticmethod
  423. def check_consistency_of(inputargs, operations, check_descr=True):
  424. "NOT_RPYTHON"
  425. for box in inputargs:
  426. assert not isinstance(box, Const), "Loop.inputargs contains %r" % (box,)
  427. seen = dict.fromkeys(inputargs)
  428. assert len(seen) == len(inputargs), (
  429. "duplicate Box in the Loop.inputargs")
  430. TreeLoop.check_consistency_of_branch(operations, seen,
  431. check_descr=check_descr)
  432. @staticmethod
  433. def check_consistency_of_branch(operations, seen, check_descr=True):
  434. "NOT_RPYTHON"
  435. for num, op in enumerate(operations):
  436. if op.is_ovf():
  437. assert operations[num + 1].getopnum() in (rop.GUARD_NO_OVERFLOW,
  438. rop.GUARD_OVERFLOW)
  439. for i in range(op.numargs()):
  440. box = op.getarg(i)
  441. if not isinstance(box, Const):
  442. assert box in seen
  443. if op.is_guard() and check_descr:
  444. assert op.getdescr() is not None
  445. if hasattr(op.getdescr(), '_debug_suboperations'):
  446. ops = op.getdescr()._debug_suboperations
  447. TreeLoop.check_consistency_of_branch(ops, seen.copy())
  448. for box in op.getfailargs() or []:
  449. if box is not None:
  450. assert not isinstance(box, Const)
  451. assert box in seen
  452. elif check_descr:
  453. assert op.getfailargs() is None
  454. if op.type != 'v':
  455. seen[op] = True
  456. if op.getopnum() == rop.LABEL:
  457. inputargs = op.getarglist()
  458. for box in inputargs:
  459. assert not isinstance(box, Const), "LABEL contains %r" % (box,)
  460. seen = dict.fromkeys(inputargs)
  461. assert len(seen) == len(inputargs), (
  462. "duplicate Box in the LABEL arguments")
  463. #assert operations[-1].is_final()
  464. if operations[-1].getopnum() == rop.JUMP:
  465. target = operations[-1].getdescr()
  466. if target is not None:
  467. assert isinstance(target, TargetToken)
  468. def dump(self):
  469. # RPython-friendly
  470. print '%r: inputargs =' % self, self._dump_args(self.inputargs)
  471. for op in self.operations:
  472. args = op.getarglist()
  473. print '\t', op.getopname(), self._dump_args(args), \
  474. self._dump_box(op.result)
  475. def _dump_args(self, boxes):
  476. return '[' + ', '.join([self._dump_box(box) for box in boxes]) + ']'
  477. def _dump_box(self, box):
  478. if box is None:
  479. return 'None'
  480. else:
  481. return box.repr_rpython()
  482. def __repr__(self):
  483. return '<%s>' % (self.name,)
  484. def _list_all_operations(result, operations, omit_finish=True):
  485. if omit_finish and operations[-1].getopnum() == rop.FINISH:
  486. # xxx obscure
  487. return
  488. result.extend(operations)
  489. for op in operations:
  490. if op.is_guard() and op.getdescr():
  491. if hasattr(op.getdescr(), '_debug_suboperations'):
  492. ops = op.getdescr()._debug_suboperations
  493. _list_all_operations(result, ops, omit_finish)
  494. # ____________________________________________________________
  495. FO_REPLACED_WITH_CONST = r_uint(1)
  496. FO_POSITION_SHIFT = 1
  497. FO_POSITION_MASK = r_uint(0xFFFFFFFE)
  498. class FrontendOp(AbstractResOp):
  499. type = 'v'
  500. _attrs_ = ('position_and_flags',)
  501. def __init__(self, pos):
  502. # p is the 32-bit position shifted left by one (might be negative,
  503. # but casted to the 32-bit UINT type)
  504. p = rffi.cast(rffi.UINT, pos << FO_POSITION_SHIFT)
  505. self.position_and_flags = r_uint(p) # zero-extended to a full word
  506. def get_position(self):
  507. # p is the signed 32-bit position, from self.position_and_flags
  508. p = rffi.cast(rffi.INT, self.position_and_flags)
  509. return intmask(p) >> FO_POSITION_SHIFT
  510. def set_position(self, new_pos):
  511. assert new_pos >= 0
  512. self.position_and_flags &= ~FO_POSITION_MASK
  513. self.position_and_flags |= r_uint(new_pos << FO_POSITION_SHIFT)
  514. def is_replaced_with_const(self):
  515. return bool(self.position_and_flags & FO_REPLACED_WITH_CONST)
  516. def set_replaced_with_const(self):
  517. self.position_and_flags |= FO_REPLACED_WITH_CONST
  518. def __repr__(self):
  519. return '%s(0x%x)' % (self.__class__.__name__, self.position_and_flags)
  520. class IntFrontendOp(IntOp, FrontendOp):
  521. _attrs_ = ('position_and_flags', '_resint')
  522. def copy_value_from(self, other):
  523. self._resint = other.getint()
  524. class FloatFrontendOp(FloatOp, FrontendOp):
  525. _attrs_ = ('position_and_flags', '_resfloat')
  526. def copy_value_from(self, other):
  527. self._resfloat = other.getfloatstorage()
  528. class RefFrontendOp(RefOp, FrontendOp):
  529. _attrs_ = ('position_and_flags', '_resref', '_heapc_deps')
  530. if LONG_BIT == 32:
  531. _attrs_ += ('_heapc_flags',) # on 64 bit, this gets stored into the
  532. _heapc_flags = r_uint(0) # high 32 bits of 'position_and_flags'
  533. _heapc_deps = None
  534. def copy_value_from(self, other):
  535. self._resref = other.getref_base()
  536. if LONG_BIT == 32:
  537. def _get_heapc_flags(self):
  538. return self._heapc_flags
  539. def _set_heapc_flags(self, value):
  540. self._heapc_flags = value
  541. else:
  542. def _get_heapc_flags(self):
  543. return self.position_and_flags >> 32
  544. def _set_heapc_flags(self, value):
  545. self.position_and_flags = (
  546. (self.position_and_flags & 0xFFFFFFFF) |
  547. (value << 32))
  548. class History(object):
  549. ends_with_jump = False
  550. trace = None
  551. def __init__(self):
  552. self.descr_cache = {}
  553. self.descrs = {}
  554. self.consts = []
  555. self._cache = []
  556. def set_inputargs(self, inpargs, metainterp_sd):
  557. from rpython.jit.metainterp.opencoder import Trace
  558. self.trace = Trace(inpargs, metainterp_sd)
  559. self.inputargs = inpargs
  560. if self._cache is not None:
  561. # hack to record the ops *after* we know our inputargs
  562. for (opnum, argboxes, op, descr) in self._cache:
  563. pos = self.trace.record_op(opnum, argboxes, descr)
  564. op.set_position(pos)
  565. self._cache = None
  566. def length(self):
  567. return self.trace._count - len(self.trace.inputargs)
  568. def get_trace_position(self):
  569. return self.trace.cut_point()
  570. def cut(self, cut_at):
  571. self.trace.cut_at(cut_at)
  572. def any_operation(self):
  573. return self.trace._count > self.trace._start
  574. @specialize.argtype(2)
  575. def set_op_value(self, op, value):
  576. if value is None:
  577. return
  578. elif isinstance(value, bool):
  579. op.setint(int(value))
  580. elif lltype.typeOf(value) == lltype.Signed:
  581. op.setint(value)
  582. elif lltype.typeOf(value) is longlong.FLOATSTORAGE:
  583. op.setfloatstorage(value)
  584. else:
  585. assert lltype.typeOf(value) == llmemory.GCREF
  586. op.setref_base(value)
  587. def _record_op(self, opnum, argboxes, descr=None):
  588. from rpython.jit.metainterp.opencoder import FrontendTagOverflow
  589. try:
  590. return self.trace.record_op(opnum, argboxes, descr)
  591. except FrontendTagOverflow:
  592. # note that with the default settings this one should not
  593. # happen - however if we hit that case, we don't get
  594. # anything disabled
  595. raise SwitchToBlackhole(Counters.ABORT_TOO_LONG)
  596. @specialize.argtype(3)
  597. def record(self, opnum, argboxes, value, descr=None):
  598. if self.trace is None:
  599. pos = 2**14 - 1
  600. else:
  601. pos = self._record_op(opnum, argboxes, descr)
  602. if value is None:
  603. op = FrontendOp(pos)
  604. elif isinstance(value, bool):
  605. op = IntFrontendOp(pos)
  606. elif lltype.typeOf(value) == lltype.Signed:
  607. op = IntFrontendOp(pos)
  608. elif lltype.typeOf(value) is longlong.FLOATSTORAGE:
  609. op = FloatFrontendOp(pos)
  610. else:
  611. op = RefFrontendOp(pos)
  612. if self.trace is None:
  613. self._cache.append((opnum, argboxes, op, descr))
  614. self.set_op_value(op, value)
  615. return op
  616. def record_nospec(self, opnum, argboxes, descr=None):
  617. tp = opclasses[opnum].type
  618. pos = self._record_op(opnum, argboxes, descr)
  619. if tp == 'v':
  620. return FrontendOp(pos)
  621. elif tp == 'i':
  622. return IntFrontendOp(pos)
  623. elif tp == 'f':
  624. return FloatFrontendOp(pos)
  625. assert tp == 'r'
  626. return RefFrontendOp(pos)
  627. def record_default_val(self, opnum, argboxes, descr=None):
  628. assert rop.is_same_as(opnum)
  629. op = self.record_nospec(opnum, argboxes, descr)
  630. op.copy_value_from(argboxes[0])
  631. return op
  632. # ____________________________________________________________
  633. class NoStats(object):
  634. def set_history(self, history):
  635. pass
  636. def aborted(self):
  637. pass
  638. def entered(self):
  639. pass
  640. def compiled(self):
  641. pass
  642. def add_merge_point_location(self, loc):
  643. pass
  644. def name_for_new_loop(self):
  645. return 'Loop'
  646. def add_new_loop(self, loop):
  647. pass
  648. def record_aborted(self, greenkey):
  649. pass
  650. def view(self, **kwds):
  651. pass
  652. def clear(self):
  653. pass
  654. def add_jitcell_token(self, token):
  655. pass
  656. class Stats(object):
  657. """For tests."""
  658. compiled_count = 0
  659. enter_count = 0
  660. aborted_count = 0
  661. def __init__(self, metainterp_sd):
  662. self.loops = []
  663. self.locations = []
  664. self.aborted_keys = []
  665. self.invalidated_token_numbers = set() # <- not RPython
  666. self.jitcell_token_wrefs = []
  667. self.jitcell_dicts = [] # <- not RPython
  668. self.metainterp_sd = metainterp_sd
  669. def clear(self):
  670. del self.loops[:]
  671. del self.locations[:]
  672. del self.aborted_keys[:]
  673. del self.jitcell_token_wrefs[:]
  674. self.invalidated_token_numbers.clear()
  675. self.compiled_count = 0
  676. self.enter_count = 0
  677. self.aborted_count = 0
  678. for dict in self.jitcell_dicts:
  679. dict.clear()
  680. def add_jitcell_token(self, token):
  681. assert isinstance(token, JitCellToken)
  682. self.jitcell_token_wrefs.append(weakref.ref(token))
  683. def set_history(self, history):
  684. self.history = history
  685. def aborted(self):
  686. self.aborted_count += 1
  687. def entered(self):
  688. self.enter_count += 1
  689. def compiled(self):
  690. self.compiled_count += 1
  691. def add_merge_point_location(self, loc):
  692. self.locations.append(loc)
  693. def name_for_new_loop(self):
  694. return 'Loop #%d' % len(self.loops)
  695. def add_new_loop(self, loop):
  696. self.loops.append(loop)
  697. def record_aborted(self, greenkey):
  698. self.aborted_keys.append(greenkey)
  699. # test read interface
  700. def get_all_loops(self):
  701. return self.loops
  702. def get_all_jitcell_tokens(self):
  703. tokens = [t() for t in self.jitcell_token_wrefs]
  704. if None in tokens:
  705. assert False, ("get_all_jitcell_tokens will not work as "
  706. "loops have been freed")
  707. return tokens
  708. def check_history(self, expected=None, **check):
  709. insns = {}
  710. t = self.history.trace.get_iter()
  711. while not t.done():
  712. op = t.next()
  713. opname = op.getopname()
  714. insns[opname] = insns.get(opname, 0) + 1
  715. if expected is not None:
  716. insns.pop('debug_merge_point', None)
  717. insns.pop('enter_portal_frame', None)
  718. insns.pop('leave_portal_frame', None)
  719. assert insns == expected
  720. for insn, expected_count in check.items():
  721. getattr(rop, insn.upper()) # fails if 'rop.INSN' does not exist
  722. found = insns.get(insn, 0)
  723. assert found == expected_count, (
  724. "found %d %r, expected %d" % (found, insn, expected_count))
  725. return insns
  726. def check_resops(self, expected=None, omit_finish=True, **check):
  727. insns = {}
  728. if 'call' in check:
  729. assert check.pop('call') == 0
  730. check['call_i'] = check['call_r'] = check['call_f'] = check['call_n'] = 0
  731. if 'call_pure' in check:
  732. assert check.pop('call_pure') == 0
  733. check['call_pure_i'] = check['call_pure_r'] = check['call_pure_f'] = 0
  734. if 'call_may_force' in check:
  735. assert check.pop('call_may_force') == 0
  736. check['call_may_force_i'] = check['call_may_force_r'] = check['call_may_force_f'] = check['call_may_force_n'] = 0
  737. if 'call_assembler' in check:
  738. assert check.pop('call_assembler') == 0
  739. check['call_assembler_i'] = check['call_assembler_r'] = check['call_assembler_f'] = check['call_assembler_n'] = 0
  740. if 'getfield_gc' in check:
  741. assert check.pop('getfield_gc') == 0
  742. check['getfield_gc_i'] = check['getfield_gc_r'] = check['getfield_gc_f'] = 0
  743. if 'getarrayitem_gc_pure' in check:
  744. assert check.pop('getarrayitem_gc_pure') == 0
  745. check['getarrayitem_gc_pure_i'] = check['getarrayitem_gc_pure_r'] = check['getarrayitem_gc_pure_f'] = 0
  746. if 'getarrayitem_gc' in check:
  747. assert check.pop('getarrayitem_gc') == 0
  748. check['getarrayitem_gc_i'] = check['getarrayitem_gc_r'] = check['getarrayitem_gc_f'] = 0
  749. for loop in self.get_all_loops():
  750. insns = loop.summary(adding_insns=insns, omit_finish=omit_finish)
  751. return self._check_insns(insns, expected, check)
  752. def _check_insns(self, insns, expected, check):
  753. if expected is not None:
  754. insns.pop('debug_merge_point', None)
  755. insns.pop('enter_portal_frame', None)
  756. insns.pop('leave_portal_frame', None)
  757. insns.pop('label', None)
  758. assert insns == expected
  759. for insn, expected_count in check.items():
  760. getattr(rop, insn.upper()) # fails if 'rop.INSN' does not exist
  761. found = insns.get(insn, 0)
  762. assert found == expected_count, (
  763. "found %d %r, expected %d" % (found, insn, expected_count))
  764. return insns
  765. def check_simple_loop(self, expected=None, **check):
  766. """ Usefull in the simplest case when we have only one trace ending with
  767. a jump back to itself and possibly a few bridges.
  768. Only the operations within the loop formed by that single jump will
  769. be counted.
  770. """
  771. loops = self.get_all_loops()
  772. assert len(loops) == 1
  773. loop = loops[0]
  774. jumpop = loop.operations[-1]
  775. assert jumpop.getopnum() == rop.JUMP
  776. labels = [op for op in loop.operations if op.getopnum() == rop.LABEL]
  777. targets = [op._descr_wref() for op in labels]
  778. assert None not in targets # TargetToken was freed, give up
  779. target = jumpop._descr_wref()
  780. assert target
  781. assert targets.count(target) == 1
  782. i = loop.operations.index(labels[targets.index(target)])
  783. insns = {}
  784. for op in loop.operations[i:]:
  785. opname = op.getopname()
  786. insns[opname] = insns.get(opname, 0) + 1
  787. return self._check_insns(insns, expected, check)
  788. def check_loops(self, expected=None, everywhere=False, **check):
  789. insns = {}
  790. for loop in self.get_all_loops():
  791. #if not everywhere:
  792. # if getattr(loop, '_ignore_during_counting', False):
  793. # continue
  794. insns = loop.summary(adding_insns=insns)
  795. if expected is not None:
  796. insns.pop('debug_merge_point', None)
  797. print
  798. print
  799. print " self.check_resops(%s)" % str(insns)
  800. print
  801. import pdb; pdb.set_trace()
  802. else:
  803. chk = ['%s=%d' % (i, insns.get(i, 0)) for i in check]
  804. print
  805. print
  806. print " self.check_resops(%s)" % ', '.join(chk)
  807. print
  808. import pdb; pdb.set_trace()
  809. return
  810. for insn, expected_count in check.items():
  811. getattr(rop, insn.upper()) # fails if 'rop.INSN' does not exist
  812. found = insns.get(insn, 0)
  813. assert found == expected_count, (
  814. "found %d %r, expected %d" % (found, insn, expected_count))
  815. return insns
  816. def check_consistency(self):
  817. "NOT_RPYTHON"
  818. for loop in self.get_all_loops():
  819. loop.check_consistency()
  820. def maybe_view(self):
  821. if option.view:
  822. self.view()
  823. def view(self, errmsg=None, extraprocedures=[], metainterp_sd=None):
  824. from rpython.jit.metainterp.graphpage import display_procedures
  825. procedures = self.get_all_loops()[:]
  826. for procedure in extraprocedures:
  827. if procedure in procedures:
  828. procedures.remove(procedure)
  829. procedures.append(procedure)
  830. highlight_procedures = dict.fromkeys(extraprocedures, 1)
  831. for procedure in procedures:
  832. if hasattr(procedure, '_looptoken_number') and (
  833. procedure._looptoken_number in self.invalidated_token_numbers):
  834. highlight_procedures.setdefault(procedure, 2)
  835. display_procedures(procedures, errmsg, highlight_procedures, metainterp_sd)
  836. # ----------------------------------------------------------------
  837. class Options:
  838. def __init__(self, listops=False, failargs_limit=FAILARGS_LIMIT):
  839. self.listops = listops
  840. self.failargs_limit = failargs_limit
  841. def _freeze_(self):
  842. return True
  843. # ----------------------------------------------------------------
  844. def check_descr(x):
  845. """Check that 'x' is None or an instance of AbstractDescr.
  846. Explodes if the annotator only thinks it is an instance of AbstractValue.
  847. """
  848. if x is not None:
  849. assert isinstance(x, AbstractDescr)
  850. class Entry(ExtRegistryEntry):
  851. _about_ = check_descr
  852. def compute_result_annotation(self, s_x):
  853. # Failures here mean that 'descr' is not correctly an AbstractDescr.
  854. # Please don't check in disabling of this test!
  855. from rpython.annotator import model as annmodel
  856. if not annmodel.s_None.contains(s_x):
  857. assert isinstance(s_x, annmodel.SomeInstance)
  858. # the following assert fails if we somehow did not manage
  859. # to ensure that the 'descr' field of ResOperation is really
  860. # an instance of AbstractDescr, a subclass of AbstractValue.
  861. assert issubclass(s_x.classdef.classdesc.pyobj, AbstractDescr)
  862. def specialize_call(self, hop):
  863. hop.exception_cannot_occur()