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

/rpython/rlib/_stacklet_asmgcc.py

https://bitbucket.org/pypy/pypy/
Python | 325 lines | 213 code | 41 blank | 71 comment | 18 complexity | fbf40ad6745f7c7325faf49db940cf00 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from rpython.rlib.debug import ll_assert
  2. from rpython.rlib import rgc
  3. from rpython.rlib.objectmodel import specialize
  4. from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
  5. from rpython.rtyper.lltypesystem.lloperation import llop
  6. from rpython.rtyper.annlowlevel import llhelper, MixLevelHelperAnnotator
  7. from rpython.annotator import model as annmodel
  8. from rpython.rtyper.llannotation import lltype_to_annotation
  9. from rpython.rlib import _rffi_stacklet as _c
  10. _asmstackrootwalker = None # BIG HACK: monkey-patched by asmgcroot.py
  11. _stackletrootwalker = None
  12. def get_stackletrootwalker():
  13. # XXX this is too complicated now; we don't need a StackletRootWalker
  14. # instance to store global state. We could rewrite it all in one big
  15. # function. We don't care enough for now.
  16. # lazily called, to make the following imports lazy
  17. global _stackletrootwalker
  18. if _stackletrootwalker is not None:
  19. return _stackletrootwalker
  20. from rpython.memory.gctransform.asmgcroot import (
  21. WALKFRAME, CALLEE_SAVED_REGS, INDEX_OF_EBP, sizeofaddr)
  22. assert _asmstackrootwalker is not None, "should have been monkey-patched"
  23. basewalker = _asmstackrootwalker
  24. class StackletRootWalker(object):
  25. _alloc_flavor_ = "raw"
  26. def setup(self, obj):
  27. # initialization: read the SUSPSTACK object
  28. p = llmemory.cast_adr_to_ptr(obj, lltype.Ptr(SUSPSTACK))
  29. if not p.handle:
  30. return False
  31. self.context = llmemory.cast_ptr_to_adr(p.handle)
  32. self.next_callback_piece = p.callback_pieces
  33. anchor = p.anchor
  34. del p
  35. self.curframe = lltype.malloc(WALKFRAME, flavor='raw')
  36. self.otherframe = lltype.malloc(WALKFRAME, flavor='raw')
  37. self.fill_initial_frame(self.curframe, anchor)
  38. return True
  39. def fill_initial_frame(self, curframe, initialframedata):
  40. # Copy&paste :-(
  41. initialframedata += 2*sizeofaddr
  42. reg = 0
  43. while reg < CALLEE_SAVED_REGS:
  44. curframe.regs_stored_at[reg] = initialframedata+reg*sizeofaddr
  45. reg += 1
  46. retaddraddr = initialframedata + CALLEE_SAVED_REGS * sizeofaddr
  47. retaddraddr = self.translateptr(retaddraddr)
  48. curframe.frame_address = retaddraddr.address[0]
  49. def fetch_next_stack_piece(self):
  50. if self.next_callback_piece == llmemory.NULL:
  51. lltype.free(self.curframe, flavor='raw')
  52. lltype.free(self.otherframe, flavor='raw')
  53. self.context = llmemory.NULL
  54. return False
  55. else:
  56. anchor = self.next_callback_piece
  57. nextaddr = anchor + sizeofaddr
  58. nextaddr = self.translateptr(nextaddr)
  59. self.next_callback_piece = nextaddr.address[0]
  60. self.fill_initial_frame(self.curframe, anchor)
  61. return True
  62. @specialize.arg(3)
  63. def customtrace(self, gc, obj, callback, arg):
  64. #
  65. # Pointers to the stack can be "translated" or not:
  66. #
  67. # * Non-translated pointers point to where the data would be
  68. # if the stack was installed and running.
  69. #
  70. # * Translated pointers correspond to where the data
  71. # is now really in memory.
  72. #
  73. # Note that 'curframe' contains non-translated pointers, and
  74. # of course the stack itself is full of non-translated pointers.
  75. #
  76. if not self.setup(obj):
  77. return
  78. while True:
  79. callee = self.curframe
  80. retaddraddr = self.translateptr(callee.frame_address)
  81. retaddr = retaddraddr.address[0]
  82. ebp_in_caller = callee.regs_stored_at[INDEX_OF_EBP]
  83. ebp_in_caller = self.translateptr(ebp_in_caller)
  84. ebp_in_caller = ebp_in_caller.address[0]
  85. basewalker.locate_caller_based_on_retaddr(retaddr,
  86. ebp_in_caller)
  87. # see asmgcroot for similarity:
  88. while True:
  89. location = basewalker._shape_decompressor.next()
  90. if location == 0:
  91. break
  92. addr = basewalker.getlocation(callee, ebp_in_caller,
  93. location)
  94. # yield the translated addr of the next GCREF in the stack
  95. addr = self.translateptr(addr)
  96. gc._trace_callback(callback, arg, addr)
  97. caller = self.otherframe
  98. reg = CALLEE_SAVED_REGS - 1
  99. while reg >= 0:
  100. location = basewalker._shape_decompressor.next()
  101. addr = basewalker.getlocation(callee, ebp_in_caller,
  102. location)
  103. caller.regs_stored_at[reg] = addr # non-translated
  104. reg -= 1
  105. location = basewalker._shape_decompressor.next()
  106. caller.frame_address = basewalker.getlocation(callee,
  107. ebp_in_caller,
  108. location)
  109. # ^^^ non-translated
  110. if caller.frame_address == llmemory.NULL:
  111. # completely done with this piece of stack
  112. if not self.fetch_next_stack_piece():
  113. return
  114. continue
  115. #
  116. self.otherframe = callee
  117. self.curframe = caller
  118. # loop back
  119. def translateptr(self, addr):
  120. return _c._translate_pointer(self.context, addr)
  121. _stackletrootwalker = StackletRootWalker()
  122. return _stackletrootwalker
  123. get_stackletrootwalker._annspecialcase_ = 'specialize:memo'
  124. def complete_destrptr(gctransformer):
  125. translator = gctransformer.translator
  126. mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
  127. args_s = [lltype_to_annotation(lltype.Ptr(SUSPSTACK))]
  128. s_result = annmodel.s_None
  129. destrptr = mixlevelannotator.delayedfunction(suspstack_destructor,
  130. args_s, s_result)
  131. mixlevelannotator.finish()
  132. lltype.attachRuntimeTypeInfo(SUSPSTACK, destrptr=destrptr)
  133. def customtrace(gc, obj, callback, arg):
  134. stackletrootwalker = get_stackletrootwalker()
  135. stackletrootwalker.customtrace(gc, obj, callback, arg)
  136. lambda_customtrace = lambda: customtrace
  137. def suspstack_destructor(suspstack):
  138. h = suspstack.handle
  139. if h:
  140. _c.destroy(h)
  141. SUSPSTACK = lltype.GcStruct('SuspStack',
  142. ('handle', _c.handle),
  143. ('anchor', llmemory.Address),
  144. ('callback_pieces', llmemory.Address),
  145. rtti=True)
  146. NULL_SUSPSTACK = lltype.nullptr(SUSPSTACK)
  147. ASM_FRAMEDATA_HEAD_PTR = lltype.Ptr(lltype.ForwardReference())
  148. ASM_FRAMEDATA_HEAD_PTR.TO.become(lltype.Struct('ASM_FRAMEDATA_HEAD',
  149. ('prev', ASM_FRAMEDATA_HEAD_PTR),
  150. ('next', ASM_FRAMEDATA_HEAD_PTR)
  151. ))
  152. alternateanchor = lltype.malloc(ASM_FRAMEDATA_HEAD_PTR.TO,
  153. immortal=True)
  154. alternateanchor.prev = alternateanchor
  155. alternateanchor.next = alternateanchor
  156. FUNCNOARG_P = lltype.Ptr(lltype.FuncType([], _c.handle))
  157. pypy_asm_stackwalk2 = rffi.llexternal('pypy_asm_stackwalk',
  158. [FUNCNOARG_P,
  159. ASM_FRAMEDATA_HEAD_PTR],
  160. lltype.Signed, sandboxsafe=True,
  161. _nowrapper=True)
  162. def _new_callback():
  163. # Here, we just closed the stack. Get the stack anchor, store
  164. # it in the gcrootfinder.suspstack.anchor, and create a new
  165. # stacklet with stacklet_new(). If this call fails, then we
  166. # are just returning NULL.
  167. _stack_just_closed()
  168. #
  169. return _c.new(gcrootfinder.newthrd, llhelper(_c.run_fn, _new_runfn),
  170. llmemory.NULL)
  171. def _stack_just_closed():
  172. # Immediately unlink the new stackanchor from the doubly-linked
  173. # chained list. When returning from pypy_asm_stackwalk2, the
  174. # assembler code will try to unlink it again, which should be
  175. # a no-op given that the doubly-linked list is empty.
  176. stackanchor = llmemory.cast_ptr_to_adr(alternateanchor.next)
  177. gcrootfinder.suspstack.anchor = stackanchor
  178. alternateanchor.prev = alternateanchor
  179. alternateanchor.next = alternateanchor
  180. def _new_runfn(h, _):
  181. # Here, we are in a fresh new stacklet.
  182. llop.gc_stack_bottom(lltype.Void) # marker for trackgcroot.py
  183. #
  184. # There is a fresh suspstack object waiting on the gcrootfinder,
  185. # so populate it with data that represents the parent suspended
  186. # stacklet and detach the suspstack object from gcrootfinder.
  187. suspstack = gcrootfinder.attach_handle_on_suspstack(h)
  188. #
  189. # Call the main function provided by the (RPython) user.
  190. suspstack = gcrootfinder.runfn(suspstack, gcrootfinder.arg)
  191. #
  192. # Here, suspstack points to the target stacklet to which we want
  193. # to jump to next. Read the 'handle' and forget about the
  194. # suspstack object.
  195. return _consume_suspstack(suspstack)
  196. def _consume_suspstack(suspstack):
  197. h = suspstack.handle
  198. ll_assert(bool(h), "_consume_suspstack: null handle")
  199. suspstack.handle = _c.null_handle
  200. return h
  201. def _switch_callback():
  202. # Here, we just closed the stack. Get the stack anchor, store
  203. # it in the gcrootfinder.suspstack.anchor, and switch to this
  204. # suspstack with stacklet_switch(). If this call fails, then we
  205. # are just returning NULL.
  206. oldanchor = gcrootfinder.suspstack.anchor
  207. _stack_just_closed()
  208. h = _consume_suspstack(gcrootfinder.suspstack)
  209. #
  210. # gcrootfinder.suspstack.anchor is left with the anchor of the
  211. # previous place (i.e. before the call to switch()).
  212. h2 = _c.switch(h)
  213. #
  214. if not h2: # MemoryError: restore
  215. gcrootfinder.suspstack.anchor = oldanchor
  216. gcrootfinder.suspstack.handle = h
  217. return h2
  218. class StackletGcRootFinder(object):
  219. suspstack = NULL_SUSPSTACK
  220. def new(self, thrd, callback, arg):
  221. self.newthrd = thrd._thrd
  222. self.runfn = callback
  223. self.arg = arg
  224. # make a fresh new clean SUSPSTACK
  225. rgc.register_custom_trace_hook(SUSPSTACK, lambda_customtrace)
  226. newsuspstack = lltype.malloc(SUSPSTACK)
  227. newsuspstack.handle = _c.null_handle
  228. self.suspstack = newsuspstack
  229. # Invoke '_new_callback' by closing the stack
  230. #
  231. callback_pieces = llop.gc_detach_callback_pieces(llmemory.Address)
  232. newsuspstack.callback_pieces = callback_pieces
  233. #
  234. h = pypy_asm_stackwalk2(llhelper(FUNCNOARG_P, _new_callback),
  235. alternateanchor)
  236. h = rffi.cast(_c.handle, h)
  237. #
  238. llop.gc_reattach_callback_pieces(lltype.Void, callback_pieces)
  239. return self.get_result_suspstack(h)
  240. def switch(self, suspstack):
  241. # Immediately before the switch, 'suspstack' describes the suspended
  242. # state of the *target* of the switch. Then it is theoretically
  243. # freed. In fact what occurs is that we reuse the same 'suspstack'
  244. # object in the target, just after the switch, to store the
  245. # description of where we came from. Then that "other" 'suspstack'
  246. # object is returned.
  247. self.suspstack = suspstack
  248. #
  249. callback_pieces = llop.gc_detach_callback_pieces(llmemory.Address)
  250. old_callback_pieces = suspstack.callback_pieces
  251. suspstack.callback_pieces = callback_pieces
  252. #
  253. h = pypy_asm_stackwalk2(llhelper(FUNCNOARG_P, _switch_callback),
  254. alternateanchor)
  255. h = rffi.cast(_c.handle, h)
  256. #
  257. llop.gc_reattach_callback_pieces(lltype.Void, callback_pieces)
  258. if not h:
  259. self.suspstack.callback_pieces = old_callback_pieces
  260. #
  261. return self.get_result_suspstack(h)
  262. def attach_handle_on_suspstack(self, handle):
  263. s = self.suspstack
  264. self.suspstack = NULL_SUSPSTACK
  265. ll_assert(bool(s.anchor), "s.anchor should not be null")
  266. s.handle = handle
  267. llop.gc_writebarrier(lltype.Void, llmemory.cast_ptr_to_adr(s))
  268. return s
  269. def get_result_suspstack(self, h):
  270. #
  271. # Return from a new() or a switch(): 'h' is a handle, possibly
  272. # an empty one, that says from where we switched to.
  273. if not h:
  274. raise MemoryError
  275. elif _c.is_empty_handle(h):
  276. return NULL_SUSPSTACK
  277. else:
  278. # This is a return that gave us a real handle. Store it.
  279. return self.attach_handle_on_suspstack(h)
  280. def is_empty_handle(self, suspstack):
  281. return not suspstack
  282. def get_null_handle(self):
  283. return NULL_SUSPSTACK
  284. gcrootfinder = StackletGcRootFinder()