PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/rpython/memory/gc/generation.py

https://bitbucket.org/pypy/pypy/
Python | 638 lines | 437 code | 39 blank | 162 comment | 36 complexity | d6107b1891414d667f2db829dc7d8074 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import sys
  2. from pypy.rpython.memory.gc.semispace import SemiSpaceGC
  3. from pypy.rpython.memory.gc.semispace import GCFLAG_EXTERNAL, GCFLAG_FORWARDED
  4. from pypy.rpython.memory.gc.semispace import GC_HASH_TAKEN_ADDR
  5. from pypy.rpython.memory.gc import env
  6. from pypy.rpython.lltypesystem.llmemory import NULL, raw_malloc_usage
  7. from pypy.rpython.lltypesystem import lltype, llmemory, llarena
  8. from pypy.rlib.objectmodel import free_non_gc_object
  9. from pypy.rlib.debug import ll_assert
  10. from pypy.rlib.debug import debug_print, debug_start, debug_stop
  11. from pypy.rlib.rarithmetic import intmask, LONG_BIT
  12. from pypy.rpython.lltypesystem.lloperation import llop
  13. WORD = LONG_BIT // 8
  14. # The following flag is never set on young objects, i.e. the ones living
  15. # in the nursery. It is initially set on all prebuilt and old objects,
  16. # and gets cleared by the write_barrier() when we write in them a
  17. # pointer to a young object.
  18. GCFLAG_NO_YOUNG_PTRS = SemiSpaceGC.first_unused_gcflag << 0
  19. # The following flag is set on some last-generation objects (== prebuilt
  20. # objects for GenerationGC, but see also HybridGC). The flag is set
  21. # unless the object is already listed in 'last_generation_root_objects'.
  22. # When a pointer is written inside an object with GCFLAG_NO_HEAP_PTRS
  23. # set, the write_barrier clears the flag and adds the object to
  24. # 'last_generation_root_objects'.
  25. GCFLAG_NO_HEAP_PTRS = SemiSpaceGC.first_unused_gcflag << 1
  26. class GenerationGC(SemiSpaceGC):
  27. """A basic generational GC: it's a SemiSpaceGC with an additional
  28. nursery for young objects. A write barrier is used to ensure that
  29. old objects that contain pointers to young objects are recorded in
  30. a list.
  31. """
  32. inline_simple_malloc = True
  33. inline_simple_malloc_varsize = True
  34. needs_write_barrier = True
  35. prebuilt_gc_objects_are_static_roots = False
  36. first_unused_gcflag = SemiSpaceGC.first_unused_gcflag << 2
  37. # the following values override the default arguments of __init__ when
  38. # translating to a real backend.
  39. TRANSLATION_PARAMS = {'space_size': 8*1024*1024, # XXX adjust
  40. 'nursery_size': 896*1024,
  41. 'min_nursery_size': 48*1024,
  42. 'auto_nursery_size': True}
  43. nursery_hash_base = -1
  44. def __init__(self, config,
  45. nursery_size=32*WORD,
  46. min_nursery_size=32*WORD,
  47. auto_nursery_size=False,
  48. space_size=1024*WORD,
  49. max_space_size=sys.maxint//2+1,
  50. **kwds):
  51. SemiSpaceGC.__init__(self, config,
  52. space_size = space_size,
  53. max_space_size = max_space_size,
  54. **kwds)
  55. assert min_nursery_size <= nursery_size <= space_size // 2
  56. self.initial_nursery_size = nursery_size
  57. self.auto_nursery_size = auto_nursery_size
  58. self.min_nursery_size = min_nursery_size
  59. # define nursery fields
  60. self.reset_nursery()
  61. self._setup_wb()
  62. # compute the constant lower bounds for the attributes
  63. # largest_young_fixedsize and largest_young_var_basesize.
  64. # It is expected that most (or all) objects have a fixedsize
  65. # that is much lower anyway.
  66. sz = self.get_young_fixedsize(self.min_nursery_size)
  67. self.lb_young_fixedsize = sz
  68. sz = self.get_young_var_basesize(self.min_nursery_size)
  69. self.lb_young_var_basesize = sz
  70. def setup(self):
  71. self.old_objects_pointing_to_young = self.AddressStack()
  72. # ^^^ a list of addresses inside the old objects space; it
  73. # may contain static prebuilt objects as well. More precisely,
  74. # it lists exactly the old and static objects whose
  75. # GCFLAG_NO_YOUNG_PTRS bit is not set.
  76. self.young_objects_with_weakrefs = self.AddressStack()
  77. self.last_generation_root_objects = self.AddressStack()
  78. self.young_objects_with_id = self.AddressDict()
  79. SemiSpaceGC.setup(self)
  80. self.set_nursery_size(self.initial_nursery_size)
  81. # the GC is fully setup now. The rest can make use of it.
  82. if self.auto_nursery_size:
  83. newsize = nursery_size_from_env()
  84. if newsize <= 0:
  85. newsize = env.estimate_best_nursery_size()
  86. if newsize > 0:
  87. self.set_nursery_size(newsize)
  88. self.reset_nursery()
  89. def _teardown(self):
  90. self.collect() # should restore last gen objects flags
  91. SemiSpaceGC._teardown(self)
  92. def reset_nursery(self):
  93. self.nursery = NULL
  94. self.nursery_top = NULL
  95. self.nursery_free = NULL
  96. def set_nursery_size(self, newsize):
  97. debug_start("gc-set-nursery-size")
  98. if newsize < self.min_nursery_size:
  99. newsize = self.min_nursery_size
  100. if newsize > self.space_size // 2:
  101. newsize = self.space_size // 2
  102. # Compute the new bounds for how large young objects can be
  103. # (larger objects are allocated directly old). XXX adjust
  104. self.nursery_size = newsize
  105. self.largest_young_fixedsize = self.get_young_fixedsize(newsize)
  106. self.largest_young_var_basesize = self.get_young_var_basesize(newsize)
  107. scale = 0
  108. while (self.min_nursery_size << (scale+1)) <= newsize:
  109. scale += 1
  110. self.nursery_scale = scale
  111. debug_print("nursery_size =", newsize)
  112. debug_print("largest_young_fixedsize =",
  113. self.largest_young_fixedsize)
  114. debug_print("largest_young_var_basesize =",
  115. self.largest_young_var_basesize)
  116. debug_print("nursery_scale =", scale)
  117. # we get the following invariant:
  118. assert self.nursery_size >= (self.min_nursery_size << scale)
  119. # Force a full collect to remove the current nursery whose size
  120. # no longer matches the bounds that we just computed. This must
  121. # be done after changing the bounds, because it might re-create
  122. # a new nursery (e.g. if it invokes finalizers).
  123. self.semispace_collect()
  124. debug_stop("gc-set-nursery-size")
  125. @staticmethod
  126. def get_young_fixedsize(nursery_size):
  127. return nursery_size // 2 - 1
  128. @staticmethod
  129. def get_young_var_basesize(nursery_size):
  130. return nursery_size // 4 - 1
  131. @classmethod
  132. def JIT_max_size_of_young_obj(cls):
  133. min_nurs_size = cls.TRANSLATION_PARAMS['min_nursery_size']
  134. return cls.get_young_fixedsize(min_nurs_size)
  135. def is_in_nursery(self, addr):
  136. ll_assert(llmemory.cast_adr_to_int(addr) & 1 == 0,
  137. "odd-valued (i.e. tagged) pointer unexpected here")
  138. return self.nursery <= addr < self.nursery_top
  139. def appears_to_be_in_nursery(self, addr):
  140. # same as is_in_nursery(), but may return True accidentally if
  141. # 'addr' is a tagged pointer with just the wrong value.
  142. if not self.translated_to_c:
  143. if not self.is_valid_gc_object(addr):
  144. return False
  145. return self.nursery <= addr < self.nursery_top
  146. def malloc_fixedsize_clear(self, typeid, size,
  147. has_finalizer=False,
  148. is_finalizer_light=False,
  149. contains_weakptr=False):
  150. if (has_finalizer or
  151. (raw_malloc_usage(size) > self.lb_young_fixedsize and
  152. raw_malloc_usage(size) > self.largest_young_fixedsize)):
  153. # ^^^ we do two size comparisons; the first one appears redundant,
  154. # but it can be constant-folded if 'size' is a constant; then
  155. # it almost always folds down to False, which kills the
  156. # second comparison as well.
  157. ll_assert(not contains_weakptr, "wrong case for mallocing weakref")
  158. # "non-simple" case or object too big: don't use the nursery
  159. return SemiSpaceGC.malloc_fixedsize_clear(self, typeid, size,
  160. has_finalizer,
  161. is_finalizer_light,
  162. contains_weakptr)
  163. size_gc_header = self.gcheaderbuilder.size_gc_header
  164. totalsize = size_gc_header + size
  165. result = self.nursery_free
  166. if raw_malloc_usage(totalsize) > self.nursery_top - result:
  167. result = self.collect_nursery()
  168. llarena.arena_reserve(result, totalsize)
  169. # GCFLAG_NO_YOUNG_PTRS is never set on young objs
  170. self.init_gc_object(result, typeid, flags=0)
  171. self.nursery_free = result + totalsize
  172. if contains_weakptr:
  173. self.young_objects_with_weakrefs.append(result + size_gc_header)
  174. return llmemory.cast_adr_to_ptr(result+size_gc_header, llmemory.GCREF)
  175. def malloc_varsize_clear(self, typeid, length, size, itemsize,
  176. offset_to_length):
  177. # Only use the nursery if there are not too many items.
  178. if not raw_malloc_usage(itemsize):
  179. too_many_items = False
  180. else:
  181. # The following line is usually constant-folded because both
  182. # min_nursery_size and itemsize are constants (the latter
  183. # due to inlining).
  184. maxlength_for_minimal_nursery = (self.min_nursery_size // 4 //
  185. raw_malloc_usage(itemsize))
  186. # The actual maximum length for our nursery depends on how
  187. # many times our nursery is bigger than the minimal size.
  188. # The computation is done in this roundabout way so that
  189. # only the only remaining computation is the following
  190. # shift.
  191. maxlength = maxlength_for_minimal_nursery << self.nursery_scale
  192. too_many_items = length > maxlength
  193. if (too_many_items or
  194. (raw_malloc_usage(size) > self.lb_young_var_basesize and
  195. raw_malloc_usage(size) > self.largest_young_var_basesize)):
  196. # ^^^ we do two size comparisons; the first one appears redundant,
  197. # but it can be constant-folded if 'size' is a constant; then
  198. # it almost always folds down to False, which kills the
  199. # second comparison as well.
  200. return SemiSpaceGC.malloc_varsize_clear(self, typeid, length, size,
  201. itemsize, offset_to_length)
  202. # with the above checks we know now that totalsize cannot be more
  203. # than about half of the nursery size; in particular, the + and *
  204. # cannot overflow
  205. size_gc_header = self.gcheaderbuilder.size_gc_header
  206. totalsize = size_gc_header + size + itemsize * length
  207. result = self.nursery_free
  208. if raw_malloc_usage(totalsize) > self.nursery_top - result:
  209. result = self.collect_nursery()
  210. llarena.arena_reserve(result, totalsize)
  211. # GCFLAG_NO_YOUNG_PTRS is never set on young objs
  212. self.init_gc_object(result, typeid, flags=0)
  213. (result + size_gc_header + offset_to_length).signed[0] = length
  214. self.nursery_free = result + llarena.round_up_for_allocation(totalsize)
  215. return llmemory.cast_adr_to_ptr(result+size_gc_header, llmemory.GCREF)
  216. # override the init_gc_object methods to change the default value of 'flags',
  217. # used by objects that are directly created outside the nursery by the SemiSpaceGC.
  218. # These objects must have the GCFLAG_NO_YOUNG_PTRS flag set immediately.
  219. def init_gc_object(self, addr, typeid, flags=GCFLAG_NO_YOUNG_PTRS):
  220. SemiSpaceGC.init_gc_object(self, addr, typeid, flags)
  221. def init_gc_object_immortal(self, addr, typeid,
  222. flags=GCFLAG_NO_YOUNG_PTRS|GCFLAG_NO_HEAP_PTRS):
  223. SemiSpaceGC.init_gc_object_immortal(self, addr, typeid, flags)
  224. # flags exposed for the HybridGC subclass
  225. GCFLAGS_FOR_NEW_YOUNG_OBJECTS = 0 # NO_YOUNG_PTRS never set on young objs
  226. GCFLAGS_FOR_NEW_EXTERNAL_OBJECTS = (GCFLAG_EXTERNAL | GCFLAG_FORWARDED |
  227. GCFLAG_NO_YOUNG_PTRS |
  228. GC_HASH_TAKEN_ADDR)
  229. # ____________________________________________________________
  230. # Support code for full collections
  231. def collect(self, gen=1):
  232. if gen == 0:
  233. self.collect_nursery()
  234. else:
  235. SemiSpaceGC.collect(self)
  236. def semispace_collect(self, size_changing=False):
  237. self.reset_young_gcflags() # we are doing a full collection anyway
  238. self.weakrefs_grow_older()
  239. self.ids_grow_older()
  240. self.reset_nursery()
  241. SemiSpaceGC.semispace_collect(self, size_changing)
  242. def make_a_copy(self, obj, objsize):
  243. tid = self.header(obj).tid
  244. # During a full collect, all copied objects might implicitly come
  245. # from the nursery. In case they do, we must add this flag:
  246. tid |= GCFLAG_NO_YOUNG_PTRS
  247. return self._make_a_copy_with_tid(obj, objsize, tid)
  248. # history: this was missing and caused an object to become old but without the
  249. # flag set. Such an object is bogus in the sense that the write_barrier doesn't
  250. # work on it. So it can eventually contain a ptr to a young object but we didn't
  251. # know about it. That ptr was not updated in the next minor collect... boom at
  252. # the next usage.
  253. def reset_young_gcflags(self):
  254. # This empties self.old_objects_pointing_to_young, and puts the
  255. # GCFLAG_NO_YOUNG_PTRS back on all these objects. We could put
  256. # the flag back more lazily but we expect this list to be short
  257. # anyway, and it's much saner to stick to the invariant:
  258. # non-young objects all have GCFLAG_NO_YOUNG_PTRS set unless
  259. # they are listed in old_objects_pointing_to_young.
  260. oldlist = self.old_objects_pointing_to_young
  261. while oldlist.non_empty():
  262. obj = oldlist.pop()
  263. hdr = self.header(obj)
  264. hdr.tid |= GCFLAG_NO_YOUNG_PTRS
  265. def weakrefs_grow_older(self):
  266. while self.young_objects_with_weakrefs.non_empty():
  267. obj = self.young_objects_with_weakrefs.pop()
  268. self.objects_with_weakrefs.append(obj)
  269. def collect_roots(self):
  270. """GenerationGC: collects all roots.
  271. HybridGC: collects all roots, excluding the generation 3 ones.
  272. """
  273. # Warning! References from static (and possibly gen3) objects
  274. # are found by collect_last_generation_roots(), which must be
  275. # called *first*! If it is called after walk_roots(), then the
  276. # HybridGC explodes if one of the _collect_root causes an object
  277. # to be added to self.last_generation_root_objects. Indeed, in
  278. # this case, the newly added object is traced twice: once by
  279. # collect_last_generation_roots() and once because it was added
  280. # in self.rawmalloced_objects_to_trace.
  281. self.collect_last_generation_roots()
  282. self.root_walker.walk_roots(
  283. SemiSpaceGC._collect_root, # stack roots
  284. SemiSpaceGC._collect_root, # static in prebuilt non-gc structures
  285. None) # we don't need the static in prebuilt gc objects
  286. def collect_last_generation_roots(self):
  287. stack = self.last_generation_root_objects
  288. self.last_generation_root_objects = self.AddressStack()
  289. while stack.non_empty():
  290. obj = stack.pop()
  291. self.header(obj).tid |= GCFLAG_NO_HEAP_PTRS
  292. # ^^^ the flag we just added will be removed immediately if
  293. # the object still contains pointers to younger objects
  294. self.trace(obj, self._trace_external_obj, obj)
  295. stack.delete()
  296. def _trace_external_obj(self, pointer, obj):
  297. addr = pointer.address[0]
  298. newaddr = self.copy(addr)
  299. pointer.address[0] = newaddr
  300. self.write_into_last_generation_obj(obj, newaddr)
  301. # ____________________________________________________________
  302. # Implementation of nursery-only collections
  303. def collect_nursery(self):
  304. if self.nursery_size > self.top_of_space - self.free:
  305. # the semispace is running out, do a full collect
  306. self.obtain_free_space(self.nursery_size)
  307. ll_assert(self.nursery_size <= self.top_of_space - self.free,
  308. "obtain_free_space failed to do its job")
  309. if self.nursery:
  310. debug_start("gc-minor")
  311. debug_print("--- minor collect ---")
  312. debug_print("nursery:", self.nursery, "to", self.nursery_top)
  313. # a nursery-only collection
  314. scan = beginning = self.free
  315. self.collect_oldrefs_to_nursery()
  316. self.collect_roots_in_nursery()
  317. scan = self.scan_objects_just_copied_out_of_nursery(scan)
  318. # at this point, all static and old objects have got their
  319. # GCFLAG_NO_YOUNG_PTRS set again by trace_and_drag_out_of_nursery
  320. if self.young_objects_with_weakrefs.non_empty():
  321. self.invalidate_young_weakrefs()
  322. if self.young_objects_with_id.length() > 0:
  323. self.update_young_objects_with_id()
  324. # mark the nursery as free and fill it with zeroes again
  325. llarena.arena_reset(self.nursery, self.nursery_size, 2)
  326. debug_print("survived (fraction of the size):",
  327. float(scan - beginning) / self.nursery_size)
  328. debug_stop("gc-minor")
  329. #self.debug_check_consistency() # -- quite expensive
  330. else:
  331. # no nursery - this occurs after a full collect, triggered either
  332. # just above or by some previous non-nursery-based allocation.
  333. # Grab a piece of the current space for the nursery.
  334. self.nursery = self.free
  335. self.nursery_top = self.nursery + self.nursery_size
  336. self.free = self.nursery_top
  337. self.nursery_free = self.nursery
  338. # at this point we know that the nursery is empty
  339. self.change_nursery_hash_base()
  340. return self.nursery_free
  341. def change_nursery_hash_base(self):
  342. # The following should be enough to ensure that young objects
  343. # tend to always get a different hash. It also makes sure that
  344. # nursery_hash_base is not a multiple of 4, to avoid collisions
  345. # with the hash of non-young objects.
  346. hash_base = self.nursery_hash_base
  347. hash_base += self.nursery_size - 1
  348. if (hash_base & 3) == 0:
  349. hash_base -= 1
  350. self.nursery_hash_base = intmask(hash_base)
  351. # NB. we can use self.copy() to move objects out of the nursery,
  352. # but only if the object was really in the nursery.
  353. def collect_oldrefs_to_nursery(self):
  354. # Follow the old_objects_pointing_to_young list and move the
  355. # young objects they point to out of the nursery.
  356. count = 0
  357. oldlist = self.old_objects_pointing_to_young
  358. while oldlist.non_empty():
  359. count += 1
  360. obj = oldlist.pop()
  361. hdr = self.header(obj)
  362. hdr.tid |= GCFLAG_NO_YOUNG_PTRS
  363. self.trace_and_drag_out_of_nursery(obj)
  364. debug_print("collect_oldrefs_to_nursery", count)
  365. def collect_roots_in_nursery(self):
  366. # we don't need to trace prebuilt GcStructs during a minor collect:
  367. # if a prebuilt GcStruct contains a pointer to a young object,
  368. # then the write_barrier must have ensured that the prebuilt
  369. # GcStruct is in the list self.old_objects_pointing_to_young.
  370. self.root_walker.walk_roots(
  371. GenerationGC._collect_root_in_nursery, # stack roots
  372. GenerationGC._collect_root_in_nursery, # static in prebuilt non-gc
  373. None) # static in prebuilt gc
  374. def _collect_root_in_nursery(self, root):
  375. obj = root.address[0]
  376. if self.is_in_nursery(obj):
  377. root.address[0] = self.copy(obj)
  378. def scan_objects_just_copied_out_of_nursery(self, scan):
  379. while scan < self.free:
  380. curr = scan + self.size_gc_header()
  381. self.trace_and_drag_out_of_nursery(curr)
  382. scan += self.size_gc_header() + self.get_size_incl_hash(curr)
  383. return scan
  384. def trace_and_drag_out_of_nursery(self, obj):
  385. """obj must not be in the nursery. This copies all the
  386. young objects it references out of the nursery.
  387. """
  388. self.trace(obj, self._trace_drag_out, None)
  389. def _trace_drag_out(self, pointer, ignored):
  390. if self.is_in_nursery(pointer.address[0]):
  391. pointer.address[0] = self.copy(pointer.address[0])
  392. # The code relies on the fact that no weakref can be an old object
  393. # weakly pointing to a young object. Indeed, weakrefs are immutable
  394. # so they cannot point to an object that was created after it.
  395. def invalidate_young_weakrefs(self):
  396. # walk over the list of objects that contain weakrefs and are in the
  397. # nursery. if the object it references survives then update the
  398. # weakref; otherwise invalidate the weakref
  399. while self.young_objects_with_weakrefs.non_empty():
  400. obj = self.young_objects_with_weakrefs.pop()
  401. if not self.surviving(obj):
  402. continue # weakref itself dies
  403. obj = self.get_forwarding_address(obj)
  404. offset = self.weakpointer_offset(self.get_type_id(obj))
  405. pointing_to = (obj + offset).address[0]
  406. if self.is_in_nursery(pointing_to):
  407. if self.surviving(pointing_to):
  408. (obj + offset).address[0] = self.get_forwarding_address(
  409. pointing_to)
  410. else:
  411. (obj + offset).address[0] = NULL
  412. continue # no need to remember this weakref any longer
  413. self.objects_with_weakrefs.append(obj)
  414. # for the JIT: a minimal description of the write_barrier() method
  415. # (the JIT assumes it is of the shape
  416. # "if addr_struct.int0 & JIT_WB_IF_FLAG: remember_young_pointer()")
  417. JIT_WB_IF_FLAG = GCFLAG_NO_YOUNG_PTRS
  418. def write_barrier(self, newvalue, addr_struct):
  419. if self.header(addr_struct).tid & GCFLAG_NO_YOUNG_PTRS:
  420. self.remember_young_pointer(addr_struct, newvalue)
  421. def _setup_wb(self):
  422. DEBUG = self.DEBUG
  423. # The purpose of attaching remember_young_pointer to the instance
  424. # instead of keeping it as a regular method is to help the JIT call it.
  425. # Additionally, it makes the code in write_barrier() marginally smaller
  426. # (which is important because it is inlined *everywhere*).
  427. # For x86, there is also an extra requirement: when the JIT calls
  428. # remember_young_pointer(), it assumes that it will not touch the SSE
  429. # registers, so it does not save and restore them (that's a *hack*!).
  430. def remember_young_pointer(addr_struct, addr):
  431. #llop.debug_print(lltype.Void, "\tremember_young_pointer",
  432. # addr_struct, "<-", addr)
  433. if DEBUG:
  434. ll_assert(not self.is_in_nursery(addr_struct),
  435. "nursery object with GCFLAG_NO_YOUNG_PTRS")
  436. #
  437. # What is important in this function is that it *must*
  438. # clear the flag GCFLAG_NO_YOUNG_PTRS from 'addr_struct'
  439. # if 'addr' is in the nursery. It is ok if, accidentally,
  440. # it also clears the flag in some more rare cases, like
  441. # 'addr' being a tagged pointer whose value happens to be
  442. # a large integer that fools is_in_nursery().
  443. if self.appears_to_be_in_nursery(addr):
  444. self.old_objects_pointing_to_young.append(addr_struct)
  445. self.header(addr_struct).tid &= ~GCFLAG_NO_YOUNG_PTRS
  446. self.write_into_last_generation_obj(addr_struct, addr)
  447. remember_young_pointer._dont_inline_ = True
  448. self.remember_young_pointer = remember_young_pointer
  449. def write_into_last_generation_obj(self, addr_struct, addr):
  450. objhdr = self.header(addr_struct)
  451. if objhdr.tid & GCFLAG_NO_HEAP_PTRS:
  452. if (self.is_valid_gc_object(addr) and
  453. not self.is_last_generation(addr)):
  454. objhdr.tid &= ~GCFLAG_NO_HEAP_PTRS
  455. self.last_generation_root_objects.append(addr_struct)
  456. write_into_last_generation_obj._always_inline_ = True
  457. def assume_young_pointers(self, addr_struct):
  458. objhdr = self.header(addr_struct)
  459. if objhdr.tid & GCFLAG_NO_YOUNG_PTRS:
  460. self.old_objects_pointing_to_young.append(addr_struct)
  461. objhdr.tid &= ~GCFLAG_NO_YOUNG_PTRS
  462. if objhdr.tid & GCFLAG_NO_HEAP_PTRS:
  463. objhdr.tid &= ~GCFLAG_NO_HEAP_PTRS
  464. self.last_generation_root_objects.append(addr_struct)
  465. def writebarrier_before_copy(self, source_addr, dest_addr,
  466. source_start, dest_start, length):
  467. """ This has the same effect as calling writebarrier over
  468. each element in dest copied from source, except it might reset
  469. one of the following flags a bit too eagerly, which means we'll have
  470. a bit more objects to track, but being on the safe side.
  471. """
  472. source_hdr = self.header(source_addr)
  473. dest_hdr = self.header(dest_addr)
  474. if dest_hdr.tid & GCFLAG_NO_YOUNG_PTRS == 0:
  475. return True
  476. # ^^^ a fast path of write-barrier
  477. if source_hdr.tid & GCFLAG_NO_YOUNG_PTRS == 0:
  478. # there might be an object in source that is in nursery
  479. self.old_objects_pointing_to_young.append(dest_addr)
  480. dest_hdr.tid &= ~GCFLAG_NO_YOUNG_PTRS
  481. if dest_hdr.tid & GCFLAG_NO_HEAP_PTRS:
  482. if source_hdr.tid & GCFLAG_NO_HEAP_PTRS == 0:
  483. # ^^^ equivalend of addr from source not being in last
  484. # gen
  485. dest_hdr.tid &= ~GCFLAG_NO_HEAP_PTRS
  486. self.last_generation_root_objects.append(dest_addr)
  487. return True
  488. def is_last_generation(self, obj):
  489. # overridden by HybridGC
  490. return (self.header(obj).tid & GCFLAG_EXTERNAL) != 0
  491. def _compute_id(self, obj):
  492. if self.is_in_nursery(obj):
  493. result = self.young_objects_with_id.get(obj)
  494. if not result:
  495. result = self._next_id()
  496. self.young_objects_with_id.setitem(obj, result)
  497. return result
  498. else:
  499. return SemiSpaceGC._compute_id(self, obj)
  500. def update_young_objects_with_id(self):
  501. self.young_objects_with_id.foreach(self._update_object_id,
  502. self.objects_with_id)
  503. self.young_objects_with_id.clear()
  504. # NB. the clear() also makes the dictionary shrink back to its
  505. # minimal size, which is actually a good idea: a large, mostly-empty
  506. # table is bad for the next call to 'foreach'.
  507. def ids_grow_older(self):
  508. self.young_objects_with_id.foreach(self._id_grow_older, None)
  509. self.young_objects_with_id.clear()
  510. def _id_grow_older(self, obj, id, ignored):
  511. self.objects_with_id.setitem(obj, id)
  512. def _compute_current_nursery_hash(self, obj):
  513. return intmask(llmemory.cast_adr_to_int(obj) + self.nursery_hash_base)
  514. def enumerate_all_roots(self, callback, arg):
  515. self.last_generation_root_objects.foreach(callback, arg)
  516. SemiSpaceGC.enumerate_all_roots(self, callback, arg)
  517. enumerate_all_roots._annspecialcase_ = 'specialize:arg(1)'
  518. def debug_check_object(self, obj):
  519. """Check the invariants about 'obj' that should be true
  520. between collections."""
  521. SemiSpaceGC.debug_check_object(self, obj)
  522. tid = self.header(obj).tid
  523. if tid & GCFLAG_NO_YOUNG_PTRS:
  524. ll_assert(not self.is_in_nursery(obj),
  525. "nursery object with GCFLAG_NO_YOUNG_PTRS")
  526. self.trace(obj, self._debug_no_nursery_pointer, None)
  527. elif not self.is_in_nursery(obj):
  528. ll_assert(self._d_oopty.contains(obj),
  529. "missing from old_objects_pointing_to_young")
  530. if tid & GCFLAG_NO_HEAP_PTRS:
  531. ll_assert(self.is_last_generation(obj),
  532. "GCFLAG_NO_HEAP_PTRS on non-3rd-generation object")
  533. self.trace(obj, self._debug_no_gen1or2_pointer, None)
  534. elif self.is_last_generation(obj):
  535. ll_assert(self._d_lgro.contains(obj),
  536. "missing from last_generation_root_objects")
  537. def _debug_no_nursery_pointer(self, root, ignored):
  538. ll_assert(not self.is_in_nursery(root.address[0]),
  539. "GCFLAG_NO_YOUNG_PTRS but found a young pointer")
  540. def _debug_no_gen1or2_pointer(self, root, ignored):
  541. target = root.address[0]
  542. ll_assert(not target or self.is_last_generation(target),
  543. "GCFLAG_NO_HEAP_PTRS but found a pointer to gen1or2")
  544. def debug_check_consistency(self):
  545. if self.DEBUG:
  546. self._d_oopty = self.old_objects_pointing_to_young.stack2dict()
  547. self._d_lgro = self.last_generation_root_objects.stack2dict()
  548. SemiSpaceGC.debug_check_consistency(self)
  549. self._d_oopty.delete()
  550. self._d_lgro.delete()
  551. self.old_objects_pointing_to_young.foreach(
  552. self._debug_check_flag_1, None)
  553. self.last_generation_root_objects.foreach(
  554. self._debug_check_flag_2, None)
  555. def _debug_check_flag_1(self, obj, ignored):
  556. ll_assert(not (self.header(obj).tid & GCFLAG_NO_YOUNG_PTRS),
  557. "unexpected GCFLAG_NO_YOUNG_PTRS")
  558. def _debug_check_flag_2(self, obj, ignored):
  559. ll_assert(not (self.header(obj).tid & GCFLAG_NO_HEAP_PTRS),
  560. "unexpected GCFLAG_NO_HEAP_PTRS")
  561. def debug_check_can_copy(self, obj):
  562. if self.is_in_nursery(obj):
  563. pass # it's ok to copy an object out of the nursery
  564. else:
  565. SemiSpaceGC.debug_check_can_copy(self, obj)
  566. # ____________________________________________________________
  567. def nursery_size_from_env():
  568. return env.read_from_env('PYPY_GENERATIONGC_NURSERY')