PageRenderTime 55ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/pypy/rpython/memory/test/test_transformed_gc.py

https://bitbucket.org/dac_io/pypy
Python | 1419 lines | 1380 code | 34 blank | 5 comment | 37 complexity | 01b5742536db7593a4bf52a672382284 MD5 | raw file
  1. import py
  2. import sys
  3. import inspect
  4. from pypy.translator.c import gc
  5. from pypy.annotation import model as annmodel
  6. from pypy.annotation import policy as annpolicy
  7. from pypy.rpython.lltypesystem import lltype, llmemory, llarena, rffi, llgroup
  8. from pypy.rpython.memory.gctransform import framework
  9. from pypy.rpython.lltypesystem.lloperation import llop, void
  10. from pypy.rlib.objectmodel import compute_unique_id, we_are_translated
  11. from pypy.rlib.debug import ll_assert
  12. from pypy.rlib import rgc
  13. from pypy import conftest
  14. from pypy.rlib.rstring import StringBuilder
  15. from pypy.rlib.rarithmetic import LONG_BIT
  16. WORD = LONG_BIT // 8
  17. def rtype(func, inputtypes, specialize=True, gcname='ref',
  18. backendopt=False, **extraconfigopts):
  19. from pypy.translator.translator import TranslationContext
  20. t = TranslationContext()
  21. # XXX XXX XXX mess
  22. t.config.translation.gc = gcname
  23. t.config.translation.gcremovetypeptr = True
  24. t.config.set(**extraconfigopts)
  25. ann = t.buildannotator(policy=annpolicy.StrictAnnotatorPolicy())
  26. ann.build_types(func, inputtypes)
  27. if specialize:
  28. t.buildrtyper().specialize()
  29. if backendopt:
  30. from pypy.translator.backendopt.all import backend_optimizations
  31. backend_optimizations(t)
  32. if conftest.option.view:
  33. t.viewcg()
  34. return t
  35. ARGS = lltype.FixedSizeArray(lltype.Signed, 3)
  36. class GCTest(object):
  37. gcpolicy = None
  38. GC_CAN_MOVE = False
  39. GC_CAN_MALLOC_NONMOVABLE = True
  40. taggedpointers = False
  41. def setup_class(cls):
  42. funcs0 = []
  43. funcs2 = []
  44. cleanups = []
  45. name_to_func = {}
  46. mixlevelstuff = []
  47. for fullname in dir(cls):
  48. if not fullname.startswith('define'):
  49. continue
  50. definefunc = getattr(cls, fullname)
  51. _, name = fullname.split('_', 1)
  52. func_fixup = definefunc.im_func(cls)
  53. cleanup = None
  54. if isinstance(func_fixup, tuple):
  55. func, cleanup, fixup = func_fixup
  56. mixlevelstuff.append(fixup)
  57. else:
  58. func = func_fixup
  59. func.func_name = "f_%s" % name
  60. if cleanup:
  61. cleanup.func_name = "clean_%s" % name
  62. nargs = len(inspect.getargspec(func)[0])
  63. name_to_func[name] = len(funcs0)
  64. if nargs == 2:
  65. funcs2.append(func)
  66. funcs0.append(None)
  67. elif nargs == 0:
  68. funcs0.append(func)
  69. funcs2.append(None)
  70. else:
  71. raise NotImplementedError(
  72. "defined test functions should have 0/2 arguments")
  73. # used to let test cleanup static root pointing to runtime
  74. # allocated stuff
  75. cleanups.append(cleanup)
  76. def entrypoint(args):
  77. num = args[0]
  78. func = funcs0[num]
  79. if func:
  80. res = func()
  81. else:
  82. func = funcs2[num]
  83. res = func(args[1], args[2])
  84. cleanup = cleanups[num]
  85. if cleanup:
  86. cleanup()
  87. return res
  88. from pypy.translator.c.genc import CStandaloneBuilder
  89. s_args = annmodel.SomePtr(lltype.Ptr(ARGS))
  90. t = rtype(entrypoint, [s_args], gcname=cls.gcname,
  91. taggedpointers=cls.taggedpointers)
  92. for fixup in mixlevelstuff:
  93. if fixup:
  94. fixup(t)
  95. cbuild = CStandaloneBuilder(t, entrypoint, config=t.config,
  96. gcpolicy=cls.gcpolicy)
  97. db = cbuild.generate_graphs_for_llinterp()
  98. entrypointptr = cbuild.getentrypointptr()
  99. entrygraph = entrypointptr._obj.graph
  100. if conftest.option.view:
  101. t.viewcg()
  102. cls.name_to_func = name_to_func
  103. cls.entrygraph = entrygraph
  104. cls.rtyper = t.rtyper
  105. cls.db = db
  106. def runner(self, name, statistics=False, transformer=False):
  107. db = self.db
  108. name_to_func = self.name_to_func
  109. entrygraph = self.entrygraph
  110. from pypy.rpython.llinterp import LLInterpreter
  111. llinterp = LLInterpreter(self.rtyper)
  112. gct = db.gctransformer
  113. if self.__class__.__dict__.get('_used', False):
  114. teardowngraph = gct.frameworkgc__teardown_ptr.value._obj.graph
  115. llinterp.eval_graph(teardowngraph, [])
  116. self.__class__._used = True
  117. # FIIIIISH
  118. setupgraph = gct.frameworkgc_setup_ptr.value._obj.graph
  119. # setup => resets the gc
  120. llinterp.eval_graph(setupgraph, [])
  121. def run(args):
  122. ll_args = lltype.malloc(ARGS, immortal=True)
  123. ll_args[0] = name_to_func[name]
  124. for i in range(len(args)):
  125. ll_args[1+i] = args[i]
  126. res = llinterp.eval_graph(entrygraph, [ll_args])
  127. return res
  128. if statistics:
  129. statisticsgraph = gct.statistics_ptr.value._obj.graph
  130. ll_gc = gct.c_const_gc.value
  131. def statistics(index):
  132. return llinterp.eval_graph(statisticsgraph, [ll_gc, index])
  133. return run, statistics
  134. elif transformer:
  135. return run, gct
  136. else:
  137. return run
  138. class GenericGCTests(GCTest):
  139. GC_CAN_SHRINK_ARRAY = False
  140. def heap_usage(self, statistics):
  141. try:
  142. GCClass = self.gcpolicy.transformerclass.GCClass
  143. except AttributeError:
  144. from pypy.rpython.memory.gc.marksweep import MarkSweepGC as GCClass
  145. if hasattr(GCClass, 'STAT_HEAP_USAGE'):
  146. return statistics(GCClass.STAT_HEAP_USAGE)
  147. else:
  148. return -1 # xxx
  149. def define_instances(cls):
  150. class A(object):
  151. pass
  152. class B(A):
  153. def __init__(self, something):
  154. self.something = something
  155. def malloc_a_lot():
  156. i = 0
  157. first = None
  158. while i < 10:
  159. i += 1
  160. a = somea = A()
  161. a.last = first
  162. first = a
  163. j = 0
  164. while j < 30:
  165. b = B(somea)
  166. b.last = first
  167. j += 1
  168. return 0
  169. return malloc_a_lot
  170. def test_instances(self):
  171. run, statistics = self.runner("instances", statistics=True)
  172. run([])
  173. heap_size = self.heap_usage(statistics)
  174. def define_llinterp_lists(cls):
  175. def malloc_a_lot():
  176. i = 0
  177. while i < 10:
  178. i += 1
  179. a = [1] * 10
  180. j = 0
  181. while j < 30:
  182. j += 1
  183. a.append(j)
  184. return 0
  185. return malloc_a_lot
  186. def test_llinterp_lists(self):
  187. run, statistics = self.runner("llinterp_lists", statistics=True)
  188. run([])
  189. heap_size = self.heap_usage(statistics)
  190. assert heap_size < 16000 * WORD / 4 # xxx
  191. def define_llinterp_tuples(cls):
  192. def malloc_a_lot():
  193. i = 0
  194. while i < 10:
  195. i += 1
  196. a = (1, 2, i)
  197. b = [a] * 10
  198. j = 0
  199. while j < 20:
  200. j += 1
  201. b.append((1, j, i))
  202. return 0
  203. return malloc_a_lot
  204. def test_llinterp_tuples(self):
  205. run, statistics = self.runner("llinterp_tuples", statistics=True)
  206. run([])
  207. heap_size = self.heap_usage(statistics)
  208. assert heap_size < 16000 * WORD / 4 # xxx
  209. def define_llinterp_dict(self):
  210. class A(object):
  211. pass
  212. def malloc_a_lot():
  213. i = 0
  214. while i < 10:
  215. i += 1
  216. a = (1, 2, i)
  217. b = {a: A()}
  218. j = 0
  219. while j < 20:
  220. j += 1
  221. b[1, j, i] = A()
  222. return 0
  223. return malloc_a_lot
  224. def test_llinterp_dict(self):
  225. run = self.runner("llinterp_dict")
  226. run([])
  227. def skipdefine_global_list(cls):
  228. gl = []
  229. class Box:
  230. def __init__(self):
  231. self.lst = gl
  232. box = Box()
  233. def append_to_list(i, j):
  234. box.lst.append([i] * 50)
  235. llop.gc__collect(lltype.Void)
  236. return box.lst[j][0]
  237. return append_to_list, None, None
  238. def test_global_list(self):
  239. py.test.skip("doesn't fit in the model, tested elsewhere too")
  240. run = self.runner("global_list")
  241. res = run([0, 0])
  242. assert res == 0
  243. for i in range(1, 5):
  244. res = run([i, i - 1])
  245. assert res == i - 1 # crashes if constants are not considered roots
  246. def define_string_concatenation(cls):
  247. def concat(j, dummy):
  248. lst = []
  249. for i in range(j):
  250. lst.append(str(i))
  251. return len("".join(lst))
  252. return concat
  253. def test_string_concatenation(self):
  254. run, statistics = self.runner("string_concatenation", statistics=True)
  255. res = run([100, 0])
  256. assert res == len(''.join([str(x) for x in range(100)]))
  257. heap_size = self.heap_usage(statistics)
  258. assert heap_size < 16000 * WORD / 4 # xxx
  259. def define_nongc_static_root(cls):
  260. T1 = lltype.GcStruct("C", ('x', lltype.Signed))
  261. T2 = lltype.Struct("C", ('p', lltype.Ptr(T1)))
  262. static = lltype.malloc(T2, immortal=True)
  263. def f():
  264. t1 = lltype.malloc(T1)
  265. t1.x = 42
  266. static.p = t1
  267. llop.gc__collect(lltype.Void)
  268. return static.p.x
  269. def cleanup():
  270. static.p = lltype.nullptr(T1)
  271. return f, cleanup, None
  272. def test_nongc_static_root(self):
  273. run = self.runner("nongc_static_root")
  274. res = run([])
  275. assert res == 42
  276. def define_finalizer(cls):
  277. class B(object):
  278. pass
  279. b = B()
  280. b.nextid = 0
  281. b.num_deleted = 0
  282. class A(object):
  283. def __init__(self):
  284. self.id = b.nextid
  285. b.nextid += 1
  286. def __del__(self):
  287. b.num_deleted += 1
  288. def f(x, y):
  289. a = A()
  290. i = 0
  291. while i < x:
  292. i += 1
  293. a = A()
  294. llop.gc__collect(lltype.Void)
  295. llop.gc__collect(lltype.Void)
  296. return b.num_deleted
  297. return f
  298. def test_finalizer(self):
  299. run = self.runner("finalizer")
  300. res = run([5, 42]) #XXX pure lazyness here too
  301. assert res == 6
  302. def define_finalizer_calls_malloc(cls):
  303. class B(object):
  304. pass
  305. b = B()
  306. b.nextid = 0
  307. b.num_deleted = 0
  308. class AAA(object):
  309. def __init__(self):
  310. self.id = b.nextid
  311. b.nextid += 1
  312. def __del__(self):
  313. b.num_deleted += 1
  314. C()
  315. class C(AAA):
  316. def __del__(self):
  317. b.num_deleted += 1
  318. def f(x, y):
  319. a = AAA()
  320. i = 0
  321. while i < x:
  322. i += 1
  323. a = AAA()
  324. llop.gc__collect(lltype.Void)
  325. llop.gc__collect(lltype.Void)
  326. return b.num_deleted
  327. return f
  328. def test_finalizer_calls_malloc(self):
  329. run = self.runner("finalizer_calls_malloc")
  330. res = run([5, 42]) #XXX pure lazyness here too
  331. assert res == 12
  332. def define_finalizer_resurrects(cls):
  333. class B(object):
  334. pass
  335. b = B()
  336. b.nextid = 0
  337. b.num_deleted = 0
  338. class A(object):
  339. def __init__(self):
  340. self.id = b.nextid
  341. b.nextid += 1
  342. def __del__(self):
  343. b.num_deleted += 1
  344. b.a = self
  345. def f(x, y):
  346. a = A()
  347. i = 0
  348. while i < x:
  349. i += 1
  350. a = A()
  351. llop.gc__collect(lltype.Void)
  352. llop.gc__collect(lltype.Void)
  353. aid = b.a.id
  354. b.a = None
  355. # check that __del__ is not called again
  356. llop.gc__collect(lltype.Void)
  357. llop.gc__collect(lltype.Void)
  358. return b.num_deleted * 10 + aid + 100 * (b.a is None)
  359. return f
  360. def test_finalizer_resurrects(self):
  361. run = self.runner("finalizer_resurrects")
  362. res = run([5, 42]) #XXX pure lazyness here too
  363. assert 160 <= res <= 165
  364. def define_custom_trace(cls):
  365. from pypy.rpython.annlowlevel import llhelper
  366. from pypy.rpython.lltypesystem import llmemory
  367. #
  368. S = lltype.GcStruct('S', ('x', llmemory.Address), rtti=True)
  369. T = lltype.GcStruct('T', ('z', lltype.Signed))
  370. offset_of_x = llmemory.offsetof(S, 'x')
  371. def customtrace(obj, prev):
  372. if not prev:
  373. return obj + offset_of_x
  374. else:
  375. return llmemory.NULL
  376. CUSTOMTRACEFUNC = lltype.FuncType([llmemory.Address, llmemory.Address],
  377. llmemory.Address)
  378. customtraceptr = llhelper(lltype.Ptr(CUSTOMTRACEFUNC), customtrace)
  379. lltype.attachRuntimeTypeInfo(S, customtraceptr=customtraceptr)
  380. #
  381. def setup():
  382. s1 = lltype.malloc(S)
  383. tx = lltype.malloc(T)
  384. tx.z = 4243
  385. s1.x = llmemory.cast_ptr_to_adr(tx)
  386. return s1
  387. def f():
  388. s1 = setup()
  389. llop.gc__collect(lltype.Void)
  390. return llmemory.cast_adr_to_ptr(s1.x, lltype.Ptr(T)).z
  391. return f
  392. def test_custom_trace(self):
  393. run = self.runner("custom_trace")
  394. res = run([])
  395. assert res == 4243
  396. def define_weakref(cls):
  397. import weakref, gc
  398. class A(object):
  399. pass
  400. def g():
  401. a = A()
  402. return weakref.ref(a)
  403. def f():
  404. a = A()
  405. ref = weakref.ref(a)
  406. result = ref() is a
  407. ref = g()
  408. llop.gc__collect(lltype.Void)
  409. result = result and (ref() is None)
  410. # check that a further collection is fine
  411. llop.gc__collect(lltype.Void)
  412. result = result and (ref() is None)
  413. return result
  414. return f
  415. def test_weakref(self):
  416. run = self.runner("weakref")
  417. res = run([])
  418. assert res
  419. def define_weakref_to_object_with_finalizer(cls):
  420. import weakref, gc
  421. class A(object):
  422. count = 0
  423. a = A()
  424. class B(object):
  425. def __del__(self):
  426. a.count += 1
  427. def g():
  428. b = B()
  429. return weakref.ref(b)
  430. def f():
  431. ref = g()
  432. llop.gc__collect(lltype.Void)
  433. llop.gc__collect(lltype.Void)
  434. result = a.count == 1 and (ref() is None)
  435. return result
  436. return f
  437. def test_weakref_to_object_with_finalizer(self):
  438. run = self.runner("weakref_to_object_with_finalizer")
  439. res = run([])
  440. assert res
  441. def define_collect_during_collect(cls):
  442. class B(object):
  443. pass
  444. b = B()
  445. b.nextid = 1
  446. b.num_deleted = 0
  447. b.num_deleted_c = 0
  448. class A(object):
  449. def __init__(self):
  450. self.id = b.nextid
  451. b.nextid += 1
  452. def __del__(self):
  453. llop.gc__collect(lltype.Void)
  454. b.num_deleted += 1
  455. C()
  456. C()
  457. class C(A):
  458. def __del__(self):
  459. b.num_deleted += 1
  460. b.num_deleted_c += 1
  461. def f(x, y):
  462. persistent_a1 = A()
  463. persistent_a2 = A()
  464. i = 0
  465. while i < x:
  466. i += 1
  467. a = A()
  468. persistent_a3 = A()
  469. persistent_a4 = A()
  470. llop.gc__collect(lltype.Void)
  471. llop.gc__collect(lltype.Void)
  472. b.bla = persistent_a1.id + persistent_a2.id + persistent_a3.id + persistent_a4.id
  473. # NB print would create a static root!
  474. llop.debug_print(lltype.Void, b.num_deleted_c)
  475. return b.num_deleted
  476. return f
  477. def test_collect_during_collect(self):
  478. run = self.runner("collect_during_collect")
  479. # runs collect recursively 4 times
  480. res = run([4, 42]) #XXX pure lazyness here too
  481. assert res == 12
  482. def define_collect_0(cls):
  483. def concat(j, dummy):
  484. lst = []
  485. for i in range(j):
  486. lst.append(str(i))
  487. result = len("".join(lst))
  488. if we_are_translated():
  489. llop.gc__collect(lltype.Void, 0)
  490. return result
  491. return concat
  492. def test_collect_0(self):
  493. run = self.runner("collect_0")
  494. res = run([100, 0])
  495. assert res == len(''.join([str(x) for x in range(100)]))
  496. def define_interior_ptrs(cls):
  497. from pypy.rpython.lltypesystem.lltype import Struct, GcStruct, GcArray
  498. from pypy.rpython.lltypesystem.lltype import Array, Signed, malloc
  499. S1 = Struct("S1", ('x', Signed))
  500. T1 = GcStruct("T1", ('s', S1))
  501. def f1():
  502. t = malloc(T1)
  503. t.s.x = 1
  504. return t.s.x
  505. S2 = Struct("S2", ('x', Signed))
  506. T2 = GcArray(S2)
  507. def f2():
  508. t = malloc(T2, 1)
  509. t[0].x = 1
  510. return t[0].x
  511. S3 = Struct("S3", ('x', Signed))
  512. T3 = GcStruct("T3", ('items', Array(S3)))
  513. def f3():
  514. t = malloc(T3, 1)
  515. t.items[0].x = 1
  516. return t.items[0].x
  517. S4 = Struct("S4", ('x', Signed))
  518. T4 = Struct("T4", ('s', S4))
  519. U4 = GcArray(T4)
  520. def f4():
  521. u = malloc(U4, 1)
  522. u[0].s.x = 1
  523. return u[0].s.x
  524. S5 = Struct("S5", ('x', Signed))
  525. T5 = GcStruct("T5", ('items', Array(S5)))
  526. def f5():
  527. t = malloc(T5, 1)
  528. return len(t.items)
  529. T6 = GcStruct("T6", ('s', Array(Signed)))
  530. def f6():
  531. t = malloc(T6, 1)
  532. t.s[0] = 1
  533. return t.s[0]
  534. def func():
  535. return (f1() * 100000 +
  536. f2() * 10000 +
  537. f3() * 1000 +
  538. f4() * 100 +
  539. f5() * 10 +
  540. f6())
  541. assert func() == 111111
  542. return func
  543. def test_interior_ptrs(self):
  544. run = self.runner("interior_ptrs")
  545. res = run([])
  546. assert res == 111111
  547. def define_id(cls):
  548. class A(object):
  549. pass
  550. a1 = A()
  551. def func():
  552. a2 = A()
  553. a3 = A()
  554. id1 = compute_unique_id(a1)
  555. id2 = compute_unique_id(a2)
  556. id3 = compute_unique_id(a3)
  557. llop.gc__collect(lltype.Void)
  558. error = 0
  559. if id1 != compute_unique_id(a1): error += 1
  560. if id2 != compute_unique_id(a2): error += 2
  561. if id3 != compute_unique_id(a3): error += 4
  562. return error
  563. return func
  564. def test_id(self):
  565. run = self.runner("id")
  566. res = run([])
  567. assert res == 0
  568. def define_can_move(cls):
  569. TP = lltype.GcArray(lltype.Float)
  570. def func():
  571. return rgc.can_move(lltype.malloc(TP, 1))
  572. return func
  573. def test_can_move(self):
  574. run = self.runner("can_move")
  575. res = run([])
  576. assert res == self.GC_CAN_MOVE
  577. def define_malloc_nonmovable(cls):
  578. TP = lltype.GcArray(lltype.Char)
  579. def func():
  580. #try:
  581. a = rgc.malloc_nonmovable(TP, 3, zero=True)
  582. rgc.collect()
  583. if a:
  584. assert not rgc.can_move(a)
  585. return 1
  586. return 0
  587. #except Exception, e:
  588. # return 2
  589. return func
  590. def test_malloc_nonmovable(self):
  591. run = self.runner("malloc_nonmovable")
  592. assert int(self.GC_CAN_MALLOC_NONMOVABLE) == run([])
  593. def define_malloc_nonmovable_fixsize(cls):
  594. S = lltype.GcStruct('S', ('x', lltype.Float))
  595. TP = lltype.GcStruct('T', ('s', lltype.Ptr(S)))
  596. def func():
  597. try:
  598. a = rgc.malloc_nonmovable(TP)
  599. rgc.collect()
  600. if a:
  601. assert not rgc.can_move(a)
  602. return 1
  603. return 0
  604. except Exception, e:
  605. return 2
  606. return func
  607. def test_malloc_nonmovable_fixsize(self):
  608. run = self.runner("malloc_nonmovable_fixsize")
  609. assert run([]) == int(self.GC_CAN_MALLOC_NONMOVABLE)
  610. def define_shrink_array(cls):
  611. from pypy.rpython.lltypesystem.rstr import STR
  612. def f():
  613. ptr = lltype.malloc(STR, 3)
  614. ptr.hash = 0x62
  615. ptr.chars[0] = '0'
  616. ptr.chars[1] = 'B'
  617. ptr.chars[2] = 'C'
  618. ptr2 = rgc.ll_shrink_array(ptr, 2)
  619. return ((ptr == ptr2) +
  620. ord(ptr2.chars[0]) +
  621. (ord(ptr2.chars[1]) << 8) +
  622. (len(ptr2.chars) << 16) +
  623. (ptr2.hash << 24))
  624. return f
  625. def test_shrink_array(self):
  626. run = self.runner("shrink_array")
  627. if self.GC_CAN_SHRINK_ARRAY:
  628. expected = 0x62024231
  629. else:
  630. expected = 0x62024230
  631. assert run([]) == expected
  632. def define_string_builder_over_allocation(cls):
  633. import gc
  634. def fn():
  635. s = StringBuilder(4)
  636. s.append("abcd")
  637. s.append("defg")
  638. s.append("rty")
  639. s.append_multiple_char('y', 1000)
  640. gc.collect()
  641. s.append_multiple_char('y', 1000)
  642. res = s.build()[1000]
  643. gc.collect()
  644. return ord(res)
  645. return fn
  646. def test_string_builder_over_allocation(self):
  647. fn = self.runner("string_builder_over_allocation")
  648. res = fn([])
  649. assert res == ord('y')
  650. class GenericMovingGCTests(GenericGCTests):
  651. GC_CAN_MOVE = True
  652. GC_CAN_MALLOC_NONMOVABLE = False
  653. GC_CAN_TEST_ID = False
  654. def define_many_ids(cls):
  655. class A(object):
  656. pass
  657. def f():
  658. from pypy.rpython.lltypesystem import rffi
  659. alist = [A() for i in range(50)]
  660. idarray = lltype.malloc(rffi.SIGNEDP.TO, len(alist), flavor='raw')
  661. # Compute the id of all the elements of the list. The goal is
  662. # to not allocate memory, so that if the GC needs memory to
  663. # remember the ids, it will trigger some collections itself
  664. i = 0
  665. while i < len(alist):
  666. idarray[i] = compute_unique_id(alist[i])
  667. i += 1
  668. j = 0
  669. while j < 2:
  670. if j == 1: # allocate some stuff between the two iterations
  671. [A() for i in range(20)]
  672. i = 0
  673. while i < len(alist):
  674. assert idarray[i] == compute_unique_id(alist[i])
  675. i += 1
  676. j += 1
  677. lltype.free(idarray, flavor='raw')
  678. return 0
  679. return f
  680. def test_many_ids(self):
  681. if not self.GC_CAN_TEST_ID:
  682. py.test.skip("fails for bad reasons in lltype.py :-(")
  683. run = self.runner("many_ids")
  684. run([])
  685. @classmethod
  686. def ensure_layoutbuilder(cls, translator):
  687. jit2gc = getattr(translator, '_jit2gc', None)
  688. if jit2gc:
  689. return jit2gc['layoutbuilder']
  690. GCClass = cls.gcpolicy.transformerclass.GCClass
  691. layoutbuilder = framework.TransformerLayoutBuilder(translator, GCClass)
  692. layoutbuilder.delay_encoding()
  693. translator._jit2gc = {
  694. 'layoutbuilder': layoutbuilder,
  695. }
  696. return layoutbuilder
  697. def define_do_malloc_operations(cls):
  698. P = lltype.GcStruct('P', ('x', lltype.Signed))
  699. def g():
  700. r = lltype.malloc(P)
  701. r.x = 1
  702. p = llop.do_malloc_fixedsize_clear(llmemory.GCREF) # placeholder
  703. p = lltype.cast_opaque_ptr(lltype.Ptr(P), p)
  704. p.x = r.x
  705. return p.x
  706. def f():
  707. i = 0
  708. while i < 40:
  709. g()
  710. i += 1
  711. return 0
  712. def fix_graph_of_g(translator):
  713. from pypy.translator.translator import graphof
  714. from pypy.objspace.flow.model import Constant
  715. from pypy.rpython.lltypesystem import rffi
  716. layoutbuilder = cls.ensure_layoutbuilder(translator)
  717. type_id = layoutbuilder.get_type_id(P)
  718. #
  719. # now fix the do_malloc_fixedsize_clear in the graph of g
  720. graph = graphof(translator, g)
  721. for op in graph.startblock.operations:
  722. if op.opname == 'do_malloc_fixedsize_clear':
  723. op.args = [Constant(type_id, llgroup.HALFWORD),
  724. Constant(llmemory.sizeof(P), lltype.Signed),
  725. Constant(False, lltype.Bool), # has_finalizer
  726. Constant(False, lltype.Bool), # is_finalizer_light
  727. Constant(False, lltype.Bool)] # contains_weakptr
  728. break
  729. else:
  730. assert 0, "oups, not found"
  731. return f, None, fix_graph_of_g
  732. def test_do_malloc_operations(self):
  733. run = self.runner("do_malloc_operations")
  734. run([])
  735. def define_do_malloc_operations_in_call(cls):
  736. P = lltype.GcStruct('P', ('x', lltype.Signed))
  737. def g():
  738. llop.do_malloc_fixedsize_clear(llmemory.GCREF) # placeholder
  739. def f():
  740. q = lltype.malloc(P)
  741. q.x = 1
  742. i = 0
  743. while i < 40:
  744. g()
  745. i += q.x
  746. return 0
  747. def fix_graph_of_g(translator):
  748. from pypy.translator.translator import graphof
  749. from pypy.objspace.flow.model import Constant
  750. from pypy.rpython.lltypesystem import rffi
  751. layoutbuilder = cls.ensure_layoutbuilder(translator)
  752. type_id = layoutbuilder.get_type_id(P)
  753. #
  754. # now fix the do_malloc_fixedsize_clear in the graph of g
  755. graph = graphof(translator, g)
  756. for op in graph.startblock.operations:
  757. if op.opname == 'do_malloc_fixedsize_clear':
  758. op.args = [Constant(type_id, llgroup.HALFWORD),
  759. Constant(llmemory.sizeof(P), lltype.Signed),
  760. Constant(False, lltype.Bool), # has_finalizer
  761. Constant(False, lltype.Bool), # is_finalizer_light
  762. Constant(False, lltype.Bool)] # contains_weakptr
  763. break
  764. else:
  765. assert 0, "oups, not found"
  766. return f, None, fix_graph_of_g
  767. def test_do_malloc_operations_in_call(self):
  768. run = self.runner("do_malloc_operations_in_call")
  769. run([])
  770. def define_gc_heap_stats(cls):
  771. S = lltype.GcStruct('S', ('x', lltype.Signed))
  772. l1 = []
  773. l2 = []
  774. l3 = []
  775. l4 = []
  776. def f():
  777. for i in range(10):
  778. s = lltype.malloc(S)
  779. l1.append(s)
  780. l2.append(s)
  781. if i < 3:
  782. l3.append(s)
  783. l4.append(s)
  784. # We cheat here and only read the table which we later on
  785. # process ourselves, otherwise this test takes ages
  786. llop.gc__collect(lltype.Void)
  787. tb = rgc._heap_stats()
  788. a = 0
  789. nr = 0
  790. b = 0
  791. c = 0
  792. d = 0
  793. e = 0
  794. for i in range(len(tb)):
  795. if tb[i].count == 10:
  796. a += 1
  797. nr = i
  798. if tb[i].count > 50:
  799. d += 1
  800. for i in range(len(tb)):
  801. if tb[i].count == 4:
  802. b += 1
  803. c += tb[i].links[nr]
  804. e += tb[i].size
  805. return d * 1000 + c * 100 + b * 10 + a
  806. return f
  807. def test_gc_heap_stats(self):
  808. py.test.skip("this test makes the following test crash. Investigate.")
  809. run = self.runner("gc_heap_stats")
  810. res = run([])
  811. assert res % 10000 == 2611
  812. totsize = (res / 10000)
  813. size_of_int = rffi.sizeof(lltype.Signed)
  814. assert (totsize - 26 * size_of_int) % 4 == 0
  815. # ^^^ a crude assumption that totsize - varsize would be dividable by 4
  816. # (and give fixedsize)
  817. def define_writebarrier_before_copy(cls):
  818. S = lltype.GcStruct('S', ('x', lltype.Char))
  819. TP = lltype.GcArray(lltype.Ptr(S))
  820. def fn():
  821. l = lltype.malloc(TP, 100)
  822. l2 = lltype.malloc(TP, 100)
  823. for i in range(100):
  824. l[i] = lltype.malloc(S)
  825. rgc.ll_arraycopy(l, l2, 50, 0, 50)
  826. # force nursery collect
  827. x = []
  828. for i in range(20):
  829. x.append((1, lltype.malloc(S)))
  830. for i in range(50):
  831. assert l2[i] == l[50 + i]
  832. return 0
  833. return fn
  834. def test_writebarrier_before_copy(self):
  835. run = self.runner("writebarrier_before_copy")
  836. run([])
  837. # ________________________________________________________________
  838. class TestMarkSweepGC(GenericGCTests):
  839. gcname = "marksweep"
  840. class gcpolicy(gc.FrameworkGcPolicy):
  841. class transformerclass(framework.FrameworkGCTransformer):
  842. GC_PARAMS = {'start_heap_size': 1024*WORD,
  843. 'translated_to_c': False}
  844. root_stack_depth = 200
  845. class TestPrintingGC(GenericGCTests):
  846. gcname = "statistics"
  847. class gcpolicy(gc.FrameworkGcPolicy):
  848. class transformerclass(framework.FrameworkGCTransformer):
  849. from pypy.rpython.memory.gc.marksweep import PrintingMarkSweepGC as GCClass
  850. GC_PARAMS = {'start_heap_size': 1024*WORD,
  851. 'translated_to_c': False}
  852. root_stack_depth = 200
  853. class TestSemiSpaceGC(GenericMovingGCTests):
  854. gcname = "semispace"
  855. GC_CAN_SHRINK_ARRAY = True
  856. class gcpolicy(gc.FrameworkGcPolicy):
  857. class transformerclass(framework.FrameworkGCTransformer):
  858. from pypy.rpython.memory.gc.semispace import SemiSpaceGC as GCClass
  859. GC_PARAMS = {'space_size': 512*WORD,
  860. 'translated_to_c': False}
  861. root_stack_depth = 200
  862. class TestMarkCompactGC(GenericMovingGCTests):
  863. gcname = 'markcompact'
  864. class gcpolicy(gc.FrameworkGcPolicy):
  865. class transformerclass(framework.FrameworkGCTransformer):
  866. from pypy.rpython.memory.gc.markcompact import MarkCompactGC as GCClass
  867. GC_PARAMS = {'space_size': 4096*WORD,
  868. 'translated_to_c': False}
  869. root_stack_depth = 200
  870. class TestGenerationGC(GenericMovingGCTests):
  871. gcname = "generation"
  872. GC_CAN_SHRINK_ARRAY = True
  873. class gcpolicy(gc.FrameworkGcPolicy):
  874. class transformerclass(framework.FrameworkGCTransformer):
  875. from pypy.rpython.memory.gc.generation import GenerationGC as \
  876. GCClass
  877. GC_PARAMS = {'space_size': 512*WORD,
  878. 'nursery_size': 32*WORD,
  879. 'translated_to_c': False}
  880. root_stack_depth = 200
  881. def define_weakref_across_minor_collection(cls):
  882. import weakref
  883. class A:
  884. pass
  885. def f():
  886. x = 20 # for GenerationGC, enough for a minor collection
  887. a = A()
  888. a.foo = x
  889. ref = weakref.ref(a)
  890. all = [None] * x
  891. i = 0
  892. while i < x:
  893. all[i] = [i] * i
  894. i += 1
  895. assert ref() is a
  896. llop.gc__collect(lltype.Void)
  897. assert ref() is a
  898. return a.foo + len(all)
  899. return f
  900. def test_weakref_across_minor_collection(self):
  901. run = self.runner("weakref_across_minor_collection")
  902. res = run([])
  903. assert res == 20 + 20
  904. def define_nongc_static_root_minor_collect(cls):
  905. T1 = lltype.GcStruct("C", ('x', lltype.Signed))
  906. T2 = lltype.Struct("C", ('p', lltype.Ptr(T1)))
  907. static = lltype.malloc(T2, immortal=True)
  908. def f():
  909. t1 = lltype.malloc(T1)
  910. t1.x = 42
  911. static.p = t1
  912. x = 20
  913. all = [None] * x
  914. i = 0
  915. while i < x: # enough to cause a minor collect
  916. all[i] = [i] * i
  917. i += 1
  918. i = static.p.x
  919. llop.gc__collect(lltype.Void)
  920. return static.p.x + i
  921. def cleanup():
  922. static.p = lltype.nullptr(T1)
  923. return f, cleanup, None
  924. def test_nongc_static_root_minor_collect(self):
  925. run = self.runner("nongc_static_root_minor_collect")
  926. res = run([])
  927. assert res == 84
  928. def define_static_root_minor_collect(cls):
  929. class A:
  930. pass
  931. class B:
  932. pass
  933. static = A()
  934. static.p = None
  935. def f():
  936. t1 = B()
  937. t1.x = 42
  938. static.p = t1
  939. x = 20
  940. all = [None] * x
  941. i = 0
  942. while i < x: # enough to cause a minor collect
  943. all[i] = [i] * i
  944. i += 1
  945. i = static.p.x
  946. llop.gc__collect(lltype.Void)
  947. return static.p.x + i
  948. def cleanup():
  949. static.p = None
  950. return f, cleanup, None
  951. def test_static_root_minor_collect(self):
  952. run = self.runner("static_root_minor_collect")
  953. res = run([])
  954. assert res == 84
  955. def define_many_weakrefs(cls):
  956. # test for the case where allocating the weakref itself triggers
  957. # a collection
  958. import weakref
  959. class A:
  960. pass
  961. def f():
  962. a = A()
  963. i = 0
  964. while i < 17:
  965. ref = weakref.ref(a)
  966. assert ref() is a
  967. i += 1
  968. return 0
  969. return f
  970. def test_many_weakrefs(self):
  971. run = self.runner("many_weakrefs")
  972. run([])
  973. def define_immutable_to_old_promotion(cls):
  974. T_CHILD = lltype.Ptr(lltype.GcStruct('Child', ('field', lltype.Signed)))
  975. T_PARENT = lltype.Ptr(lltype.GcStruct('Parent', ('sub', T_CHILD)))
  976. child = lltype.malloc(T_CHILD.TO)
  977. child2 = lltype.malloc(T_CHILD.TO)
  978. parent = lltype.malloc(T_PARENT.TO)
  979. parent2 = lltype.malloc(T_PARENT.TO)
  980. parent.sub = child
  981. child.field = 3
  982. parent2.sub = child2
  983. child2.field = 8
  984. T_ALL = lltype.Ptr(lltype.GcArray(T_PARENT))
  985. all = lltype.malloc(T_ALL.TO, 2)
  986. all[0] = parent
  987. all[1] = parent2
  988. def f(x, y):
  989. res = all[x]
  990. #all[x] = lltype.nullptr(T_PARENT.TO)
  991. return res.sub.field
  992. return f
  993. def test_immutable_to_old_promotion(self):
  994. run, transformer = self.runner("immutable_to_old_promotion", transformer=True)
  995. run([1, 4])
  996. if not transformer.GCClass.prebuilt_gc_objects_are_static_roots:
  997. assert len(transformer.layoutbuilder.addresses_of_static_ptrs) == 0
  998. else:
  999. assert len(transformer.layoutbuilder.addresses_of_static_ptrs) >= 4
  1000. # NB. Remember that the number above does not count
  1001. # the number of prebuilt GC objects, but the number of locations
  1002. # within prebuilt GC objects that are of type Ptr(Gc).
  1003. # At the moment we get additional_roots_sources == 6:
  1004. # * all[0]
  1005. # * all[1]
  1006. # * parent.sub
  1007. # * parent2.sub
  1008. # * the GcArray pointer from gc.wr_to_objects_with_id
  1009. # * the GcArray pointer from gc.object_id_dict.
  1010. def define_adr_of_nursery(cls):
  1011. class A(object):
  1012. pass
  1013. def f():
  1014. # we need at least 1 obj to allocate a nursery
  1015. a = A()
  1016. nf_a = llop.gc_adr_of_nursery_free(llmemory.Address)
  1017. nt_a = llop.gc_adr_of_nursery_top(llmemory.Address)
  1018. nf0 = nf_a.address[0]
  1019. nt0 = nt_a.address[0]
  1020. a0 = A()
  1021. a1 = A()
  1022. nf1 = nf_a.address[0]
  1023. nt1 = nt_a.address[0]
  1024. assert nf1 > nf0
  1025. assert nt1 > nf1
  1026. assert nt1 == nt0
  1027. return 0
  1028. return f
  1029. def test_adr_of_nursery(self):
  1030. run = self.runner("adr_of_nursery")
  1031. res = run([])
  1032. class TestGenerationalNoFullCollectGC(GCTest):
  1033. # test that nursery is doing its job and that no full collection
  1034. # is needed when most allocated objects die quickly
  1035. gcname = "generation"
  1036. class gcpolicy(gc.FrameworkGcPolicy):
  1037. class transformerclass(framework.FrameworkGCTransformer):
  1038. from pypy.rpython.memory.gc.generation import GenerationGC
  1039. class GCClass(GenerationGC):
  1040. __ready = False
  1041. def setup(self):
  1042. from pypy.rpython.memory.gc.generation import GenerationGC
  1043. GenerationGC.setup(self)
  1044. self.__ready = True
  1045. def semispace_collect(self, size_changing=False):
  1046. ll_assert(not self.__ready,
  1047. "no full collect should occur in this test")
  1048. def _teardown(self):
  1049. self.__ready = False # collecting here is expected
  1050. GenerationGC._teardown(self)
  1051. GC_PARAMS = {'space_size': 512*WORD,
  1052. 'nursery_size': 128*WORD,
  1053. 'translated_to_c': False}
  1054. root_stack_depth = 200
  1055. def define_working_nursery(cls):
  1056. def f():
  1057. total = 0
  1058. i = 0
  1059. while i < 40:
  1060. lst = []
  1061. j = 0
  1062. while j < 5:
  1063. lst.append(i*j)
  1064. j += 1
  1065. total += len(lst)
  1066. i += 1
  1067. return total
  1068. return f
  1069. def test_working_nursery(self):
  1070. run = self.runner("working_nursery")
  1071. res = run([])
  1072. assert res == 40 * 5
  1073. class TestHybridGC(TestGenerationGC):
  1074. gcname = "hybrid"
  1075. GC_CAN_MALLOC_NONMOVABLE = True
  1076. class gcpolicy(gc.FrameworkGcPolicy):
  1077. class transformerclass(framework.FrameworkGCTransformer):
  1078. from pypy.rpython.memory.gc.hybrid import HybridGC as GCClass
  1079. GC_PARAMS = {'space_size': 512*WORD,
  1080. 'nursery_size': 32*WORD,
  1081. 'large_object': 8*WORD,
  1082. 'translated_to_c': False}
  1083. root_stack_depth = 200
  1084. def define_ref_from_rawmalloced_to_regular(cls):
  1085. import gc
  1086. S = lltype.GcStruct('S', ('x', lltype.Signed))
  1087. A = lltype.GcStruct('A', ('p', lltype.Ptr(S)),
  1088. ('a', lltype.Array(lltype.Char)))
  1089. def setup(j):
  1090. p = lltype.malloc(S)
  1091. p.x = j*2
  1092. lst = lltype.malloc(A, j)
  1093. # the following line generates a write_barrier call at the moment,
  1094. # which is important because the 'lst' can be allocated directly
  1095. # in generation 2. This can only occur with varsized mallocs.
  1096. lst.p = p
  1097. return lst
  1098. def f(i, j):
  1099. lst = setup(j)
  1100. gc.collect()
  1101. return lst.p.x
  1102. return f
  1103. def test_ref_from_rawmalloced_to_regular(self):
  1104. run = self.runner("ref_from_rawmalloced_to_regular")
  1105. res = run([100, 100])
  1106. assert res == 200
  1107. def define_assume_young_pointers(cls):
  1108. from pypy.rlib import rgc
  1109. S = lltype.GcForwardReference()
  1110. S.become(lltype.GcStruct('S',
  1111. ('x', lltype.Signed),
  1112. ('prev', lltype.Ptr(S)),
  1113. ('next', lltype.Ptr(S))))
  1114. s0 = lltype.malloc(S, immortal=True)
  1115. def f():
  1116. s = lltype.malloc(S)
  1117. s.x = 42
  1118. llop.bare_setfield(lltype.Void, s0, void('next'), s)
  1119. llop.gc_assume_young_pointers(lltype.Void,
  1120. llmemory.cast_ptr_to_adr(s0))
  1121. rgc.collect(0)
  1122. return s0.next.x
  1123. def cleanup():
  1124. s0.next = lltype.nullptr(S)
  1125. return f, cleanup, None
  1126. def test_assume_young_pointers(self):
  1127. run = self.runner("assume_young_pointers")
  1128. res = run([])
  1129. assert res == 42
  1130. def test_malloc_nonmovable_fixsize(self):
  1131. py.test.skip("not supported")
  1132. class TestMiniMarkGC(TestHybridGC):
  1133. gcname = "minimark"
  1134. GC_CAN_TEST_ID = True
  1135. class gcpolicy(gc.FrameworkGcPolicy):
  1136. class transformerclass(framework.FrameworkGCTransformer):
  1137. from pypy.rpython.memory.gc.minimark import MiniMarkGC as GCClass
  1138. GC_PARAMS = {'nursery_size': 32*WORD,
  1139. 'page_size': 16*WORD,
  1140. 'arena_size': 64*WORD,
  1141. 'small_request_threshold': 5*WORD,
  1142. 'large_object': 8*WORD,
  1143. 'card_page_indices': 4,
  1144. 'translated_to_c': False,
  1145. }
  1146. root_stack_depth = 200
  1147. def define_no_clean_setarrayitems(cls):
  1148. # The optimization find_clean_setarrayitems() in
  1149. # gctransformer/framework.py does not work with card marking.
  1150. # Check that it is turned off.
  1151. S = lltype.GcStruct('S', ('x', lltype.Signed))
  1152. A = lltype.GcArray(lltype.Ptr(S))
  1153. def sub(lst):
  1154. lst[15] = lltype.malloc(S) # 'lst' is set the single mark "12-15"
  1155. lst[15].x = 123
  1156. lst[0] = lst[15] # that would be a "clean_setarrayitem"
  1157. def f():
  1158. lst = lltype.malloc(A, 16) # 16 > 10
  1159. rgc.collect()
  1160. sub(lst)
  1161. null = lltype.nullptr(S)
  1162. lst[15] = null # clear, so that A() is only visible via lst[0]
  1163. rgc.collect() # -> crash
  1164. return lst[0].x
  1165. return f
  1166. def test_no_clean_setarrayitems(self):
  1167. run = self.runner("no_clean_setarrayitems")
  1168. res = run([])
  1169. assert res == 123
  1170. # ________________________________________________________________
  1171. # tagged pointers
  1172. class TaggedPointerGCTests(GCTest):
  1173. taggedpointers = True
  1174. def define_tagged_simple(cls):
  1175. class Unrelated(object):
  1176. pass
  1177. u = Unrelated()
  1178. u.x = UnboxedObject(47)
  1179. def fn(n):
  1180. rgc.collect() # check that a prebuilt tagged pointer doesn't explode
  1181. if n > 0:
  1182. x = BoxedObject(n)
  1183. else:
  1184. x = UnboxedObject(n)
  1185. u.x = x # invoke write barrier
  1186. rgc.collect()
  1187. return x.meth(100)
  1188. def func():
  1189. return fn(1000) + fn(-1000)
  1190. assert func() == 205
  1191. return func
  1192. def test_tagged_simple(self):
  1193. func = self.runner("tagged_simple")
  1194. res = func([])
  1195. assert res == 205
  1196. def define_tagged_prebuilt(cls):
  1197. class F:
  1198. pass
  1199. f = F()
  1200. f.l = [UnboxedObject(10)]
  1201. def fn(n):
  1202. if n > 0:
  1203. x = BoxedObject(n)
  1204. else:
  1205. x = UnboxedObject(n)
  1206. f.l.append(x)
  1207. rgc.collect()
  1208. return f.l[-1].meth(100)
  1209. def func():
  1210. return fn(1000) ^ fn(-1000)
  1211. assert func() == -1999
  1212. return func
  1213. def test_tagged_prebuilt(self):
  1214. func = self.runner("tagged_prebuilt")
  1215. res = func([])
  1216. assert res == -1999
  1217. from pypy.rlib.objectmodel import UnboxedValue
  1218. class TaggedBase(object):
  1219. __slots__ = ()
  1220. def meth(self, x):
  1221. raise NotImplementedError
  1222. class BoxedObject(TaggedBase):
  1223. attrvalue = 66
  1224. def __init__(self, normalint):
  1225. self.normalint = normalint
  1226. def meth(self, x):
  1227. return self.normalint + x + 2
  1228. class UnboxedObject(TaggedBase, UnboxedValue):
  1229. __slots__ = 'smallint'
  1230. def meth(self, x):
  1231. return self.smallint + x + 3
  1232. class TestMarkSweepTaggedPointerGC(TaggedPointerGCTests):
  1233. gcname = "marksweep"
  1234. class gcpolicy(gc.FrameworkGcPolicy):
  1235. class transformerclass(framework.FrameworkGCTransformer):
  1236. GC_PARAMS = {'start_heap_size': 1024*WORD,
  1237. 'translated_to_c': False}
  1238. root_stack_depth = 200
  1239. class TestHybridTaggedPointerGC(TaggedPointerGCTests):
  1240. gcname = "hybrid"
  1241. class gcpolicy(gc.FrameworkGcPolicy):
  1242. class transformerclass(framework.FrameworkGCTransformer):
  1243. from pypy.rpython.memory.gc.generation import GenerationGC as \
  1244. GCClass
  1245. GC_PARAMS = {'space_size': 512*WORD,
  1246. 'nursery_size': 32*WORD,
  1247. 'translated_to_c': False}
  1248. root_stack_depth = 200
  1249. class TestMarkCompactTaggedpointerGC(TaggedPointerGCTests):
  1250. gcname = 'markcompact'
  1251. class gcpolicy(gc.FrameworkGcPolicy):
  1252. class transformerclass(framework.FrameworkGCTransformer):
  1253. from pypy.rpython.memory.gc.markcompact import MarkCompactGC as GCClass
  1254. GC_PARAMS = {'space_size': 4096*WORD,
  1255. 'translated_to_c': False}
  1256. root_stack_depth = 200