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

/rpython/memory/gc/generation.py

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