PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/memory/gcwrapper.py

https://bitbucket.org/pypy/pypy/
Python | 354 lines | 292 code | 52 blank | 10 comment | 64 complexity | 84eb9c427d5ac3d1e6c3fc7a42f96747 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from rpython.translator.backendopt.finalizer import FinalizerAnalyzer
  2. from rpython.rtyper.lltypesystem import lltype, llmemory, llheap
  3. from rpython.rtyper import llinterp, rclass
  4. from rpython.rtyper.annlowlevel import llhelper, cast_nongc_instance_to_adr
  5. from rpython.memory import gctypelayout
  6. from rpython.flowspace.model import Constant
  7. class GCManagedHeap(object):
  8. def __init__(self, llinterp, flowgraphs, gc_class, GC_PARAMS={}):
  9. translator = llinterp.typer.annotator.translator
  10. config = translator.config.translation
  11. self.gc = gc_class(config,
  12. chunk_size = 10,
  13. translated_to_c = False,
  14. **GC_PARAMS)
  15. self.translator = translator
  16. self.gc.set_root_walker(LLInterpRootWalker(self))
  17. self.gc.DEBUG = True
  18. self.llinterp = llinterp
  19. self.prepare_graphs(flowgraphs)
  20. self.gc.setup()
  21. self.has_write_barrier_from_array = hasattr(self.gc,
  22. 'write_barrier_from_array')
  23. def prepare_graphs(self, flowgraphs):
  24. lltype2vtable = self.llinterp.typer.lltype2vtable
  25. layoutbuilder = DirectRunLayoutBuilder(self.gc.__class__,
  26. lltype2vtable,
  27. self.llinterp)
  28. self.get_type_id = layoutbuilder.get_type_id
  29. gcdata = layoutbuilder.initialize_gc_query_function(self.gc)
  30. self.gcdata = gcdata
  31. self.finalizer_queue_indexes = {}
  32. self.finalizer_handlers = []
  33. self.update_finalizer_handlers()
  34. constants = collect_constants(flowgraphs)
  35. for obj in constants:
  36. TYPE = lltype.typeOf(obj)
  37. layoutbuilder.consider_constant(TYPE, obj, self.gc)
  38. self.constantroots = layoutbuilder.addresses_of_static_ptrs
  39. self.constantrootsnongc = layoutbuilder.addresses_of_static_ptrs_in_nongc
  40. self.prepare_custom_trace_funcs(gcdata)
  41. self._all_prebuilt_gc = layoutbuilder.all_prebuilt_gc
  42. def prepare_custom_trace_funcs(self, gcdata):
  43. custom_trace_funcs = self.llinterp.typer.custom_trace_funcs
  44. def custom_trace(obj, typeid, callback, arg):
  45. for TP, func in custom_trace_funcs:
  46. if typeid == self.get_type_id(TP):
  47. func(self.gc, obj, callback, arg)
  48. return
  49. else:
  50. assert False
  51. for TP, func in custom_trace_funcs:
  52. gcdata._has_got_custom_trace(self.get_type_id(TP))
  53. self.gc.custom_trace_dispatcher = custom_trace
  54. # ____________________________________________________________
  55. #
  56. # Interface for the llinterp
  57. #
  58. def malloc(self, TYPE, n=None, flavor='gc', zero=False,
  59. track_allocation=True):
  60. if flavor == 'gc':
  61. typeid = self.get_type_id(TYPE)
  62. addr = self.gc.malloc(typeid, n, zero=zero)
  63. result = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(TYPE))
  64. if not self.gc.malloc_zero_filled:
  65. gctypelayout.zero_gc_pointers(result)
  66. return result
  67. else:
  68. return lltype.malloc(TYPE, n, flavor=flavor, zero=zero,
  69. track_allocation=track_allocation)
  70. def gettypeid(self, obj):
  71. return self.get_type_id(lltype.typeOf(obj).TO)
  72. def add_memory_pressure(self, size):
  73. if hasattr(self.gc, 'raw_malloc_memory_pressure'):
  74. self.gc.raw_malloc_memory_pressure(size)
  75. def shrink_array(self, p, smallersize):
  76. if hasattr(self.gc, 'shrink_array'):
  77. addr = llmemory.cast_ptr_to_adr(p)
  78. return self.gc.shrink_array(addr, smallersize)
  79. return False
  80. def free(self, TYPE, flavor='gc', track_allocation=True):
  81. assert flavor != 'gc'
  82. return lltype.free(TYPE, flavor=flavor,
  83. track_allocation=track_allocation)
  84. def setfield(self, obj, fieldname, fieldvalue):
  85. STRUCT = lltype.typeOf(obj).TO
  86. addr = llmemory.cast_ptr_to_adr(obj)
  87. addr += llmemory.offsetof(STRUCT, fieldname)
  88. self.setinterior(obj, addr, getattr(STRUCT, fieldname), fieldvalue)
  89. def setarrayitem(self, array, index, newitem):
  90. ARRAY = lltype.typeOf(array).TO
  91. addr = llmemory.cast_ptr_to_adr(array)
  92. addr += llmemory.itemoffsetof(ARRAY, index)
  93. self.setinterior(array, addr, ARRAY.OF, newitem, (index,))
  94. def setinterior(self, toplevelcontainer, inneraddr, INNERTYPE, newvalue,
  95. offsets=()):
  96. if (lltype.typeOf(toplevelcontainer).TO._gckind == 'gc' and
  97. isinstance(INNERTYPE, lltype.Ptr) and INNERTYPE.TO._gckind == 'gc'):
  98. #
  99. wb = True
  100. if self.has_write_barrier_from_array:
  101. for index in offsets:
  102. if type(index) is not str:
  103. assert (type(index) is int # <- fast path
  104. or lltype.typeOf(index) == lltype.Signed)
  105. self.gc.write_barrier_from_array(
  106. llmemory.cast_ptr_to_adr(toplevelcontainer),
  107. index)
  108. wb = False
  109. break
  110. #
  111. if wb:
  112. self.gc.write_barrier(
  113. llmemory.cast_ptr_to_adr(toplevelcontainer))
  114. llheap.setinterior(toplevelcontainer, inneraddr, INNERTYPE, newvalue)
  115. def collect(self, *gen):
  116. self.gc.collect(*gen)
  117. def can_move(self, addr):
  118. return self.gc.can_move(addr)
  119. def pin(self, addr):
  120. return self.gc.pin(addr)
  121. def unpin(self, addr):
  122. self.gc.unpin(addr)
  123. def _is_pinned(self, addr):
  124. return self.gc._is_pinned(addr)
  125. def weakref_create_getlazy(self, objgetter):
  126. # we have to be lazy in reading the llinterp variable containing
  127. # the 'obj' pointer, because the gc.malloc() call below could
  128. # move it around
  129. type_id = self.get_type_id(gctypelayout.WEAKREF)
  130. addr = self.gc.malloc(type_id, None, zero=False)
  131. result = llmemory.cast_adr_to_ptr(addr, gctypelayout.WEAKREFPTR)
  132. result.weakptr = llmemory.cast_ptr_to_adr(objgetter())
  133. return llmemory.cast_ptr_to_weakrefptr(result)
  134. def weakref_deref(self, PTRTYPE, obj):
  135. addr = gctypelayout.ll_weakref_deref(obj)
  136. return llmemory.cast_adr_to_ptr(addr, PTRTYPE)
  137. def gc_id(self, ptr):
  138. ptr = lltype.cast_opaque_ptr(llmemory.GCREF, ptr)
  139. return self.gc.id(ptr)
  140. def writebarrier_before_copy(self, source, dest,
  141. source_start, dest_start, length):
  142. if self.gc.needs_write_barrier:
  143. source_addr = llmemory.cast_ptr_to_adr(source)
  144. dest_addr = llmemory.cast_ptr_to_adr(dest)
  145. return self.gc.writebarrier_before_copy(source_addr, dest_addr,
  146. source_start, dest_start,
  147. length)
  148. else:
  149. return True
  150. def gcflag_extra(self, subopnum, *args):
  151. if subopnum == 1: # has_gcflag_extra
  152. assert len(args) == 0
  153. return self.gc.gcflag_extra != 0
  154. assert len(args) == 1
  155. addr = llmemory.cast_ptr_to_adr(args[0])
  156. hdr = self.gc.header(addr)
  157. if subopnum == 3: # toggle_gcflag_extra
  158. if hdr.tid & self.gc.gcflag_extra:
  159. hdr.tid &= ~self.gc.gcflag_extra
  160. else:
  161. hdr.tid |= self.gc.gcflag_extra
  162. return (hdr.tid & self.gc.gcflag_extra) != 0
  163. def thread_run(self):
  164. pass
  165. def _get_finalizer_trigger(self, fq):
  166. graph = self.translator._graphof(fq.finalizer_trigger.im_func)
  167. def ll_trigger():
  168. try:
  169. self.llinterp.eval_graph(graph, [None], recursive=True)
  170. except llinterp.LLException:
  171. raise RuntimeError(
  172. "finalizer_trigger() raised an exception, shouldn't happen")
  173. return ll_trigger
  174. def update_finalizer_handlers(self):
  175. handlers = self.finalizer_handlers
  176. ll_handlers = lltype.malloc(gctypelayout.FIN_HANDLER_ARRAY,
  177. len(handlers), immortal=True)
  178. for i in range(len(handlers)):
  179. fq, deque = handlers[i]
  180. ll_handlers[i].deque = cast_nongc_instance_to_adr(deque)
  181. ll_handlers[i].trigger = llhelper(
  182. lltype.Ptr(gctypelayout.FIN_TRIGGER_FUNC),
  183. self._get_finalizer_trigger(fq))
  184. self.gcdata.finalizer_handlers = llmemory.cast_ptr_to_adr(ll_handlers)
  185. def get_finalizer_queue_index(self, fq_tag):
  186. assert 'FinalizerQueue TAG' in fq_tag.expr
  187. fq = fq_tag.default
  188. try:
  189. index = self.finalizer_queue_indexes[fq]
  190. except KeyError:
  191. index = len(self.finalizer_handlers)
  192. self.finalizer_queue_indexes[fq] = index
  193. deque = self.gc.AddressDeque()
  194. self.finalizer_handlers.append((fq, deque))
  195. self.update_finalizer_handlers()
  196. return index
  197. def gc_fq_next_dead(self, fq_tag):
  198. index = self.get_finalizer_queue_index(fq_tag)
  199. deque = self.finalizer_handlers[index][1]
  200. if deque.non_empty():
  201. obj = deque.popleft()
  202. else:
  203. obj = llmemory.NULL
  204. return llmemory.cast_adr_to_ptr(obj, rclass.OBJECTPTR)
  205. def gc_fq_register(self, fq_tag, ptr):
  206. index = self.get_finalizer_queue_index(fq_tag)
  207. ptr = lltype.cast_opaque_ptr(llmemory.GCREF, ptr)
  208. self.gc.register_finalizer(index, ptr)
  209. # ____________________________________________________________
  210. class LLInterpRootWalker:
  211. _alloc_flavor_ = 'raw'
  212. def __init__(self, gcheap):
  213. self.gcheap = gcheap
  214. def walk_roots(self, collect_stack_root,
  215. collect_static_in_prebuilt_nongc,
  216. collect_static_in_prebuilt_gc,
  217. is_minor=False):
  218. gcheap = self.gcheap
  219. gc = gcheap.gc
  220. if collect_static_in_prebuilt_gc:
  221. for addrofaddr in gcheap.constantroots:
  222. if self.gcheap.gc.points_to_valid_gc_object(addrofaddr):
  223. collect_static_in_prebuilt_gc(gc, addrofaddr)
  224. if collect_static_in_prebuilt_nongc:
  225. for addrofaddr in gcheap.constantrootsnongc:
  226. if self.gcheap.gc.points_to_valid_gc_object(addrofaddr):
  227. collect_static_in_prebuilt_nongc(gc, addrofaddr)
  228. if collect_stack_root:
  229. for addrofaddr in gcheap.llinterp.find_roots(is_minor):
  230. if self.gcheap.gc.points_to_valid_gc_object(addrofaddr):
  231. collect_stack_root(gc, addrofaddr)
  232. def _walk_prebuilt_gc(self, collect): # debugging only! not RPython
  233. for obj in self.gcheap._all_prebuilt_gc:
  234. collect(llmemory.cast_ptr_to_adr(obj._as_ptr()))
  235. def finished_minor_collection(self):
  236. pass
  237. class DirectRunLayoutBuilder(gctypelayout.TypeLayoutBuilder):
  238. def __init__(self, GCClass, lltype2vtable, llinterp):
  239. self.llinterp = llinterp
  240. super(DirectRunLayoutBuilder, self).__init__(GCClass, lltype2vtable)
  241. def make_destructor_funcptr_for_type(self, TYPE):
  242. from rpython.memory.gctransform.support import get_rtti
  243. rtti = get_rtti(TYPE)
  244. if rtti is not None and hasattr(rtti._obj, 'destructor_funcptr'):
  245. destrptr = rtti._obj.destructor_funcptr
  246. DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0]
  247. destrgraph = destrptr._obj.graph
  248. else:
  249. return None, False
  250. t = self.llinterp.typer.annotator.translator
  251. is_light = not FinalizerAnalyzer(t).analyze_light_finalizer(destrgraph)
  252. def ll_destructor(addr):
  253. try:
  254. v = llmemory.cast_adr_to_ptr(addr, DESTR_ARG)
  255. self.llinterp.eval_graph(destrgraph, [v], recursive=True)
  256. except llinterp.LLException:
  257. raise RuntimeError(
  258. "a destructor raised an exception, shouldn't happen")
  259. return (llhelper(gctypelayout.GCData.CUSTOM_FUNC_PTR, ll_destructor),
  260. is_light)
  261. def make_custom_trace_funcptr_for_type(self, TYPE):
  262. from rpython.memory.gctransform.support import get_rtti
  263. rtti = get_rtti(TYPE)
  264. if rtti is not None and hasattr(rtti._obj, 'custom_trace_funcptr'):
  265. return rtti._obj.custom_trace_funcptr
  266. else:
  267. return None
  268. def collect_constants(graphs):
  269. constants = {}
  270. def collect_args(args):
  271. for arg in args:
  272. if (isinstance(arg, Constant) and
  273. arg.concretetype is not lltype.Void):
  274. reccollect(constants, arg.value)
  275. for graph in graphs:
  276. for block in graph.iterblocks():
  277. collect_args(block.inputargs)
  278. for op in block.operations:
  279. collect_args(op.args)
  280. for link in graph.iterlinks():
  281. collect_args(link.args)
  282. if hasattr(link, "llexitcase"):
  283. reccollect(constants, link.llexitcase)
  284. return constants
  285. def reccollect(constants, llvalue):
  286. if (isinstance(llvalue, lltype._abstract_ptr)
  287. and llvalue._obj is not None and llvalue._obj not in constants
  288. and not isinstance(llvalue._obj, int)):
  289. TYPE = llvalue._T
  290. constants[llvalue._obj] = True
  291. if isinstance(TYPE, lltype.Struct):
  292. for name in TYPE._names:
  293. reccollect(constants, getattr(llvalue, name))
  294. elif isinstance(TYPE, lltype.Array):
  295. for llitem in llvalue:
  296. reccollect(constants, llitem)
  297. parent, parentindex = lltype.parentlink(llvalue._obj)
  298. if parent is not None:
  299. reccollect(constants, parent._as_ptr())
  300. def prepare_graphs_and_create_gc(llinterp, GCClass, GC_PARAMS={}):
  301. flowgraphs = llinterp.typer.annotator.translator.graphs[:]
  302. llinterp.heap = GCManagedHeap(llinterp, flowgraphs, GCClass, GC_PARAMS)