PageRenderTime 39ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/rlib/rgc.py

http://github.com/pypy/pypy
Python | 477 lines | 444 code | 13 blank | 20 comment | 2 complexity | cd300a3d09126c297914aeb5ed77c910 MD5 | raw file
  1. import gc
  2. import types
  3. from pypy.rlib import jit
  4. from pypy.rlib.objectmodel import we_are_translated, enforceargs, specialize
  5. from pypy.rlib.nonconst import NonConstant
  6. from pypy.rpython.extregistry import ExtRegistryEntry
  7. from pypy.rpython.lltypesystem import lltype, llmemory
  8. # ____________________________________________________________
  9. # General GC features
  10. collect = gc.collect
  11. def set_max_heap_size(nbytes):
  12. """Limit the heap size to n bytes.
  13. So far only implemented by the Boehm GC and the semispace/generation GCs.
  14. """
  15. pass
  16. # ____________________________________________________________
  17. # Annotation and specialization
  18. # Support for collection.
  19. class CollectEntry(ExtRegistryEntry):
  20. _about_ = gc.collect
  21. def compute_result_annotation(self, s_gen=None):
  22. from pypy.annotation import model as annmodel
  23. return annmodel.s_None
  24. def specialize_call(self, hop):
  25. hop.exception_cannot_occur()
  26. args_v = []
  27. if len(hop.args_s) == 1:
  28. args_v = hop.inputargs(lltype.Signed)
  29. return hop.genop('gc__collect', args_v, resulttype=hop.r_result)
  30. class SetMaxHeapSizeEntry(ExtRegistryEntry):
  31. _about_ = set_max_heap_size
  32. def compute_result_annotation(self, s_nbytes):
  33. from pypy.annotation import model as annmodel
  34. return annmodel.s_None
  35. def specialize_call(self, hop):
  36. [v_nbytes] = hop.inputargs(lltype.Signed)
  37. hop.exception_cannot_occur()
  38. return hop.genop('gc_set_max_heap_size', [v_nbytes],
  39. resulttype=lltype.Void)
  40. def can_move(p):
  41. """Check if the GC object 'p' is at an address that can move.
  42. Must not be called with None. With non-moving GCs, it is always False.
  43. With some moving GCs like the SemiSpace GC, it is always True.
  44. With other moving GCs like the MiniMark GC, it can be True for some
  45. time, then False for the same object, when we are sure that it won't
  46. move any more.
  47. """
  48. return True
  49. class CanMoveEntry(ExtRegistryEntry):
  50. _about_ = can_move
  51. def compute_result_annotation(self, s_p):
  52. from pypy.annotation import model as annmodel
  53. return annmodel.SomeBool()
  54. def specialize_call(self, hop):
  55. hop.exception_cannot_occur()
  56. return hop.genop('gc_can_move', hop.args_v, resulttype=hop.r_result)
  57. def _make_sure_does_not_move(p):
  58. """'p' is a non-null GC object. This (tries to) make sure that the
  59. object does not move any more, by forcing collections if needed.
  60. Warning: should ideally only be used with the minimark GC, and only
  61. on objects that are already a bit old, so have a chance to be
  62. already non-movable."""
  63. if not we_are_translated():
  64. return
  65. i = 0
  66. while can_move(p):
  67. if i > 6:
  68. raise NotImplementedError("can't make object non-movable!")
  69. collect(i)
  70. i += 1
  71. def _heap_stats():
  72. raise NotImplementedError # can't be run directly
  73. class DumpHeapEntry(ExtRegistryEntry):
  74. _about_ = _heap_stats
  75. def compute_result_annotation(self):
  76. from pypy.annotation import model as annmodel
  77. from pypy.rpython.memory.gc.base import ARRAY_TYPEID_MAP
  78. return annmodel.SomePtr(lltype.Ptr(ARRAY_TYPEID_MAP))
  79. def specialize_call(self, hop):
  80. from pypy.rpython.memory.gc.base import ARRAY_TYPEID_MAP
  81. hop.exception_is_here()
  82. return hop.genop('gc_heap_stats', [], resulttype=hop.r_result)
  83. def malloc_nonmovable(TP, n=None, zero=False):
  84. """ Allocate a non-moving buffer or return nullptr.
  85. When running directly, will pretend that gc is always
  86. moving (might be configurable in a future)
  87. """
  88. return lltype.nullptr(TP)
  89. class MallocNonMovingEntry(ExtRegistryEntry):
  90. _about_ = malloc_nonmovable
  91. def compute_result_annotation(self, s_TP, s_n=None, s_zero=None):
  92. # basically return the same as malloc
  93. from pypy.annotation.builtin import malloc
  94. return malloc(s_TP, s_n, s_zero=s_zero)
  95. def specialize_call(self, hop, i_zero=None):
  96. # XXX assume flavor and zero to be None by now
  97. assert hop.args_s[0].is_constant()
  98. vlist = [hop.inputarg(lltype.Void, arg=0)]
  99. opname = 'malloc_nonmovable'
  100. flags = {'flavor': 'gc'}
  101. if i_zero is not None:
  102. flags['zero'] = hop.args_s[i_zero].const
  103. nb_args = hop.nb_args - 1
  104. else:
  105. nb_args = hop.nb_args
  106. vlist.append(hop.inputconst(lltype.Void, flags))
  107. if nb_args == 2:
  108. vlist.append(hop.inputarg(lltype.Signed, arg=1))
  109. opname += '_varsize'
  110. hop.exception_cannot_occur()
  111. return hop.genop(opname, vlist, resulttype = hop.r_result.lowleveltype)
  112. @jit.oopspec('list.ll_arraycopy(source, dest, source_start, dest_start, length)')
  113. @specialize.ll()
  114. @enforceargs(None, None, int, int, int)
  115. def ll_arraycopy(source, dest, source_start, dest_start, length):
  116. from pypy.rpython.lltypesystem.lloperation import llop
  117. from pypy.rlib.objectmodel import keepalive_until_here
  118. # XXX: Hack to ensure that we get a proper effectinfo.write_descrs_arrays
  119. if NonConstant(False):
  120. dest[dest_start] = source[source_start]
  121. # supports non-overlapping copies only
  122. if not we_are_translated():
  123. if source == dest:
  124. assert (source_start + length <= dest_start or
  125. dest_start + length <= source_start)
  126. TP = lltype.typeOf(source).TO
  127. assert TP == lltype.typeOf(dest).TO
  128. if isinstance(TP.OF, lltype.Ptr) and TP.OF.TO._gckind == 'gc':
  129. # perform a write barrier that copies necessary flags from
  130. # source to dest
  131. if not llop.gc_writebarrier_before_copy(lltype.Bool, source, dest,
  132. source_start, dest_start,
  133. length):
  134. # if the write barrier is not supported, copy by hand
  135. i = 0
  136. while i < length:
  137. dest[i + dest_start] = source[i + source_start]
  138. i += 1
  139. return
  140. source_addr = llmemory.cast_ptr_to_adr(source)
  141. dest_addr = llmemory.cast_ptr_to_adr(dest)
  142. cp_source_addr = (source_addr + llmemory.itemoffsetof(TP, 0) +
  143. llmemory.sizeof(TP.OF) * source_start)
  144. cp_dest_addr = (dest_addr + llmemory.itemoffsetof(TP, 0) +
  145. llmemory.sizeof(TP.OF) * dest_start)
  146. llmemory.raw_memcopy(cp_source_addr, cp_dest_addr,
  147. llmemory.sizeof(TP.OF) * length)
  148. keepalive_until_here(source)
  149. keepalive_until_here(dest)
  150. def ll_shrink_array(p, smallerlength):
  151. from pypy.rpython.lltypesystem.lloperation import llop
  152. from pypy.rlib.objectmodel import keepalive_until_here
  153. if llop.shrink_array(lltype.Bool, p, smallerlength):
  154. return p # done by the GC
  155. # XXX we assume for now that the type of p is GcStruct containing a
  156. # variable array, with no further pointers anywhere, and exactly one
  157. # field in the fixed part -- like STR and UNICODE.
  158. TP = lltype.typeOf(p).TO
  159. newp = lltype.malloc(TP, smallerlength)
  160. assert len(TP._names) == 2
  161. field = getattr(p, TP._names[0])
  162. setattr(newp, TP._names[0], field)
  163. ARRAY = getattr(TP, TP._arrayfld)
  164. offset = (llmemory.offsetof(TP, TP._arrayfld) +
  165. llmemory.itemoffsetof(ARRAY, 0))
  166. source_addr = llmemory.cast_ptr_to_adr(p) + offset
  167. dest_addr = llmemory.cast_ptr_to_adr(newp) + offset
  168. llmemory.raw_memcopy(source_addr, dest_addr,
  169. llmemory.sizeof(ARRAY.OF) * smallerlength)
  170. keepalive_until_here(p)
  171. keepalive_until_here(newp)
  172. return newp
  173. ll_shrink_array._annspecialcase_ = 'specialize:ll'
  174. ll_shrink_array._jit_look_inside_ = False
  175. def no_collect(func):
  176. func._dont_inline_ = True
  177. func._gc_no_collect_ = True
  178. return func
  179. def must_be_light_finalizer(func):
  180. func._must_be_light_finalizer_ = True
  181. return func
  182. # ____________________________________________________________
  183. def get_rpy_roots():
  184. "NOT_RPYTHON"
  185. # Return the 'roots' from the GC.
  186. # This stub is not usable on top of CPython.
  187. # The gc typically returns a list that ends with a few NULL_GCREFs.
  188. raise NotImplementedError
  189. def get_rpy_referents(gcref):
  190. "NOT_RPYTHON"
  191. x = gcref._x
  192. if isinstance(x, list):
  193. d = x
  194. elif isinstance(x, dict):
  195. d = x.keys() + x.values()
  196. else:
  197. d = []
  198. if hasattr(x, '__dict__'):
  199. d = x.__dict__.values()
  200. if hasattr(type(x), '__slots__'):
  201. for slot in type(x).__slots__:
  202. try:
  203. d.append(getattr(x, slot))
  204. except AttributeError:
  205. pass
  206. # discard objects that are too random or that are _freeze_=True
  207. return [_GcRef(x) for x in d if _keep_object(x)]
  208. def _keep_object(x):
  209. if isinstance(x, type) or type(x) is types.ClassType:
  210. return False # don't keep any type
  211. if isinstance(x, (list, dict, str)):
  212. return True # keep lists and dicts and strings
  213. try:
  214. return not x._freeze_() # don't keep any frozen object
  215. except AttributeError:
  216. return type(x).__module__ != '__builtin__' # keep non-builtins
  217. except Exception:
  218. return False # don't keep objects whose _freeze_() method explodes
  219. def add_memory_pressure(estimate):
  220. """Add memory pressure for OpaquePtrs."""
  221. pass
  222. class AddMemoryPressureEntry(ExtRegistryEntry):
  223. _about_ = add_memory_pressure
  224. def compute_result_annotation(self, s_nbytes):
  225. from pypy.annotation import model as annmodel
  226. return annmodel.s_None
  227. def specialize_call(self, hop):
  228. [v_size] = hop.inputargs(lltype.Signed)
  229. hop.exception_cannot_occur()
  230. return hop.genop('gc_add_memory_pressure', [v_size],
  231. resulttype=lltype.Void)
  232. def get_rpy_memory_usage(gcref):
  233. "NOT_RPYTHON"
  234. # approximate implementation using CPython's type info
  235. Class = type(gcref._x)
  236. size = Class.__basicsize__
  237. if Class.__itemsize__ > 0:
  238. size += Class.__itemsize__ * len(gcref._x)
  239. return size
  240. def get_rpy_type_index(gcref):
  241. "NOT_RPYTHON"
  242. from pypy.rlib.rarithmetic import intmask
  243. Class = gcref._x.__class__
  244. return intmask(id(Class))
  245. def cast_gcref_to_int(gcref):
  246. if we_are_translated():
  247. return lltype.cast_ptr_to_int(gcref)
  248. else:
  249. return id(gcref._x)
  250. def dump_rpy_heap(fd):
  251. "NOT_RPYTHON"
  252. raise NotImplementedError
  253. def get_typeids_z():
  254. "NOT_RPYTHON"
  255. raise NotImplementedError
  256. ARRAY_OF_CHAR = lltype.Array(lltype.Char)
  257. NULL_GCREF = lltype.nullptr(llmemory.GCREF.TO)
  258. class _GcRef(object):
  259. # implementation-specific: there should not be any after translation
  260. __slots__ = ['_x']
  261. def __init__(self, x):
  262. self._x = x
  263. def __hash__(self):
  264. return object.__hash__(self._x)
  265. def __eq__(self, other):
  266. if isinstance(other, lltype._ptr):
  267. assert other == NULL_GCREF, (
  268. "comparing a _GcRef with a non-NULL lltype ptr")
  269. return False
  270. assert isinstance(other, _GcRef)
  271. return self._x is other._x
  272. def __ne__(self, other):
  273. return not self.__eq__(other)
  274. def __repr__(self):
  275. return "_GcRef(%r)" % (self._x, )
  276. def _freeze_(self):
  277. raise Exception("instances of rlib.rgc._GcRef cannot be translated")
  278. def cast_instance_to_gcref(x):
  279. # Before translation, casts an RPython instance into a _GcRef.
  280. # After translation, it is a variant of cast_object_to_ptr(GCREF).
  281. if we_are_translated():
  282. from pypy.rpython import annlowlevel
  283. x = annlowlevel.cast_instance_to_base_ptr(x)
  284. return lltype.cast_opaque_ptr(llmemory.GCREF, x)
  285. else:
  286. return _GcRef(x)
  287. cast_instance_to_gcref._annspecialcase_ = 'specialize:argtype(0)'
  288. def try_cast_gcref_to_instance(Class, gcref):
  289. # Before translation, unwraps the RPython instance contained in a _GcRef.
  290. # After translation, it is a type-check performed by the GC.
  291. if we_are_translated():
  292. from pypy.rpython.annlowlevel import base_ptr_lltype
  293. from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
  294. from pypy.rpython.lltypesystem import rclass
  295. if _is_rpy_instance(gcref):
  296. objptr = lltype.cast_opaque_ptr(base_ptr_lltype(), gcref)
  297. if objptr.typeptr: # may be NULL, e.g. in rdict's dummykeyobj
  298. clsptr = _get_llcls_from_cls(Class)
  299. if rclass.ll_isinstance(objptr, clsptr):
  300. return cast_base_ptr_to_instance(Class, objptr)
  301. return None
  302. else:
  303. if isinstance(gcref._x, Class):
  304. return gcref._x
  305. return None
  306. try_cast_gcref_to_instance._annspecialcase_ = 'specialize:arg(0)'
  307. # ------------------- implementation -------------------
  308. _cache_s_list_of_gcrefs = None
  309. def s_list_of_gcrefs():
  310. global _cache_s_list_of_gcrefs
  311. if _cache_s_list_of_gcrefs is None:
  312. from pypy.annotation import model as annmodel
  313. from pypy.annotation.listdef import ListDef
  314. s_gcref = annmodel.SomePtr(llmemory.GCREF)
  315. _cache_s_list_of_gcrefs = annmodel.SomeList(
  316. ListDef(None, s_gcref, mutated=True, resized=False))
  317. return _cache_s_list_of_gcrefs
  318. class Entry(ExtRegistryEntry):
  319. _about_ = get_rpy_roots
  320. def compute_result_annotation(self):
  321. return s_list_of_gcrefs()
  322. def specialize_call(self, hop):
  323. hop.exception_cannot_occur()
  324. return hop.genop('gc_get_rpy_roots', [], resulttype = hop.r_result)
  325. class Entry(ExtRegistryEntry):
  326. _about_ = get_rpy_referents
  327. def compute_result_annotation(self, s_gcref):
  328. from pypy.annotation import model as annmodel
  329. assert annmodel.SomePtr(llmemory.GCREF).contains(s_gcref)
  330. return s_list_of_gcrefs()
  331. def specialize_call(self, hop):
  332. vlist = hop.inputargs(hop.args_r[0])
  333. hop.exception_cannot_occur()
  334. return hop.genop('gc_get_rpy_referents', vlist,
  335. resulttype = hop.r_result)
  336. class Entry(ExtRegistryEntry):
  337. _about_ = get_rpy_memory_usage
  338. def compute_result_annotation(self, s_gcref):
  339. from pypy.annotation import model as annmodel
  340. return annmodel.SomeInteger()
  341. def specialize_call(self, hop):
  342. vlist = hop.inputargs(hop.args_r[0])
  343. hop.exception_cannot_occur()
  344. return hop.genop('gc_get_rpy_memory_usage', vlist,
  345. resulttype = hop.r_result)
  346. class Entry(ExtRegistryEntry):
  347. _about_ = get_rpy_type_index
  348. def compute_result_annotation(self, s_gcref):
  349. from pypy.annotation import model as annmodel
  350. return annmodel.SomeInteger()
  351. def specialize_call(self, hop):
  352. vlist = hop.inputargs(hop.args_r[0])
  353. hop.exception_cannot_occur()
  354. return hop.genop('gc_get_rpy_type_index', vlist,
  355. resulttype = hop.r_result)
  356. def _is_rpy_instance(gcref):
  357. "NOT_RPYTHON"
  358. raise NotImplementedError
  359. def _get_llcls_from_cls(Class):
  360. "NOT_RPYTHON"
  361. raise NotImplementedError
  362. class Entry(ExtRegistryEntry):
  363. _about_ = _is_rpy_instance
  364. def compute_result_annotation(self, s_gcref):
  365. from pypy.annotation import model as annmodel
  366. return annmodel.SomeBool()
  367. def specialize_call(self, hop):
  368. vlist = hop.inputargs(hop.args_r[0])
  369. hop.exception_cannot_occur()
  370. return hop.genop('gc_is_rpy_instance', vlist,
  371. resulttype = hop.r_result)
  372. class Entry(ExtRegistryEntry):
  373. _about_ = _get_llcls_from_cls
  374. def compute_result_annotation(self, s_Class):
  375. from pypy.annotation import model as annmodel
  376. from pypy.rpython.lltypesystem import rclass
  377. assert s_Class.is_constant()
  378. return annmodel.SomePtr(rclass.CLASSTYPE)
  379. def specialize_call(self, hop):
  380. from pypy.rpython.rclass import getclassrepr
  381. from pypy.objspace.flow.model import Constant
  382. from pypy.rpython.lltypesystem import rclass
  383. Class = hop.args_s[0].const
  384. classdef = hop.rtyper.annotator.bookkeeper.getuniqueclassdef(Class)
  385. classrepr = getclassrepr(hop.rtyper, classdef)
  386. vtable = classrepr.getvtable()
  387. assert lltype.typeOf(vtable) == rclass.CLASSTYPE
  388. hop.exception_cannot_occur()
  389. return Constant(vtable, concretetype=rclass.CLASSTYPE)
  390. class Entry(ExtRegistryEntry):
  391. _about_ = dump_rpy_heap
  392. def compute_result_annotation(self, s_fd):
  393. from pypy.annotation.model import s_Bool
  394. return s_Bool
  395. def specialize_call(self, hop):
  396. vlist = hop.inputargs(lltype.Signed)
  397. hop.exception_is_here()
  398. return hop.genop('gc_dump_rpy_heap', vlist, resulttype = hop.r_result)
  399. class Entry(ExtRegistryEntry):
  400. _about_ = get_typeids_z
  401. def compute_result_annotation(self):
  402. from pypy.annotation.model import SomePtr
  403. return SomePtr(lltype.Ptr(ARRAY_OF_CHAR))
  404. def specialize_call(self, hop):
  405. hop.exception_is_here()
  406. return hop.genop('gc_typeids_z', [], resulttype = hop.r_result)