PageRenderTime 110ms CodeModel.GetById 17ms RepoModel.GetById 2ms app.codeStats 0ms

/pypy/rpython/memory/gctransform/asmgcroot.py

https://bitbucket.org/pypy/pypy/
Python | 665 lines | 553 code | 39 blank | 73 comment | 36 complexity | b616ec1d788feab3d3302744ad0f3748 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from pypy.rpython.memory.gctransform.framework import FrameworkGCTransformer
  2. from pypy.rpython.memory.gctransform.framework import BaseRootWalker
  3. from pypy.rpython.lltypesystem import lltype, llmemory, rffi
  4. from pypy.rpython.lltypesystem.lloperation import llop
  5. from pypy.rpython.rbuiltin import gen_cast
  6. from pypy.rpython.annlowlevel import llhelper
  7. from pypy.objspace.flow.model import Constant, Variable, Block, Link, copygraph
  8. from pypy.objspace.flow.model import SpaceOperation
  9. from pypy.translator.unsimplify import copyvar
  10. from pypy.rlib.debug import ll_assert
  11. import sys
  12. #
  13. # This transformer avoids the use of a shadow stack in a completely
  14. # platform-specific way, by directing genc to insert asm() special
  15. # instructions in the C source, which are recognized by GCC.
  16. # The .s file produced by GCC is then parsed by trackgcroot.py.
  17. #
  18. IS_64_BITS = sys.maxint > 2147483647
  19. class AsmGcRootFrameworkGCTransformer(FrameworkGCTransformer):
  20. _asmgcc_save_restore_arguments = None
  21. def push_roots(self, hop, keep_current_args=False):
  22. livevars = self.get_livevars_for_roots(hop, keep_current_args)
  23. self.num_pushs += len(livevars)
  24. return livevars
  25. def pop_roots(self, hop, livevars):
  26. if not livevars:
  27. return
  28. # mark the values as gc roots
  29. for var in livevars:
  30. v_adr = gen_cast(hop.llops, llmemory.Address, var)
  31. v_newaddr = hop.genop("direct_call", [c_asm_gcroot, v_adr],
  32. resulttype=llmemory.Address)
  33. hop.genop("gc_reload_possibly_moved", [v_newaddr, var])
  34. def build_root_walker(self):
  35. return AsmStackRootWalker(self)
  36. def mark_call_cannotcollect(self, hop, name):
  37. hop.genop("direct_call", [c_asm_nocollect, name])
  38. def gct_direct_call(self, hop):
  39. fnptr = hop.spaceop.args[0].value
  40. try:
  41. close_stack = fnptr._obj._callable._gctransformer_hint_close_stack_
  42. except AttributeError:
  43. close_stack = False
  44. if close_stack:
  45. self.handle_call_with_close_stack(hop)
  46. else:
  47. FrameworkGCTransformer.gct_direct_call(self, hop)
  48. def handle_call_with_close_stack(self, hop):
  49. fnptr = hop.spaceop.args[0].value
  50. # We cannot easily pass variable amount of arguments of the call
  51. # across the call to the pypy_asm_stackwalk helper. So we store
  52. # them away and restore them. We need to make a new graph
  53. # that starts with restoring the arguments.
  54. if self._asmgcc_save_restore_arguments is None:
  55. self._asmgcc_save_restore_arguments = {}
  56. sradict = self._asmgcc_save_restore_arguments
  57. sra = [] # list of pointers to raw-malloced containers for args
  58. seen = {}
  59. FUNC1 = lltype.typeOf(fnptr).TO
  60. for TYPE in FUNC1.ARGS:
  61. if isinstance(TYPE, lltype.Ptr):
  62. TYPE = llmemory.Address
  63. num = seen.get(TYPE, 0)
  64. seen[TYPE] = num + 1
  65. key = (TYPE, num)
  66. if key not in sradict:
  67. CONTAINER = lltype.FixedSizeArray(TYPE, 1)
  68. p = lltype.malloc(CONTAINER, flavor='raw', zero=True,
  69. immortal=True)
  70. sradict[key] = Constant(p, lltype.Ptr(CONTAINER))
  71. sra.append(sradict[key])
  72. #
  73. # store the value of the arguments
  74. livevars = self.push_roots(hop)
  75. c_item0 = Constant('item0', lltype.Void)
  76. for v_arg, c_p in zip(hop.spaceop.args[1:], sra):
  77. if isinstance(v_arg.concretetype, lltype.Ptr):
  78. v_arg = hop.genop("cast_ptr_to_adr", [v_arg],
  79. resulttype=llmemory.Address)
  80. hop.genop("bare_setfield", [c_p, c_item0, v_arg])
  81. #
  82. # make a copy of the graph that will reload the values
  83. graph2 = copygraph(fnptr._obj.graph)
  84. block2 = graph2.startblock
  85. block1 = Block([])
  86. reloadedvars = []
  87. for v, c_p in zip(block2.inputargs, sra):
  88. v = copyvar(None, v)
  89. if isinstance(v.concretetype, lltype.Ptr):
  90. w = Variable('tmp')
  91. w.concretetype = llmemory.Address
  92. else:
  93. w = v
  94. block1.operations.append(SpaceOperation('getfield',
  95. [c_p, c_item0], w))
  96. if w is not v:
  97. block1.operations.append(SpaceOperation('cast_adr_to_ptr',
  98. [w], v))
  99. reloadedvars.append(v)
  100. block1.closeblock(Link(reloadedvars, block2))
  101. graph2.startblock = block1
  102. FUNC2 = lltype.FuncType([], FUNC1.RESULT)
  103. fnptr2 = lltype.functionptr(FUNC2,
  104. fnptr._obj._name + '_reload',
  105. graph=graph2)
  106. c_fnptr2 = Constant(fnptr2, lltype.Ptr(FUNC2))
  107. HELPERFUNC = lltype.FuncType([lltype.Ptr(FUNC2),
  108. ASM_FRAMEDATA_HEAD_PTR], FUNC1.RESULT)
  109. #
  110. v_asm_stackwalk = hop.genop("cast_pointer", [c_asm_stackwalk],
  111. resulttype=lltype.Ptr(HELPERFUNC))
  112. hop.genop("indirect_call",
  113. [v_asm_stackwalk, c_fnptr2, c_gcrootanchor,
  114. Constant(None, lltype.Void)],
  115. resultvar=hop.spaceop.result)
  116. self.pop_roots(hop, livevars)
  117. class AsmStackRootWalker(BaseRootWalker):
  118. def __init__(self, gctransformer):
  119. BaseRootWalker.__init__(self, gctransformer)
  120. def _asm_callback():
  121. self.walk_stack_from()
  122. self._asm_callback = _asm_callback
  123. self._shape_decompressor = ShapeDecompressor()
  124. if hasattr(gctransformer.translator, '_jit2gc'):
  125. jit2gc = gctransformer.translator._jit2gc
  126. self._extra_gcmapstart = jit2gc['gcmapstart']
  127. self._extra_gcmapend = jit2gc['gcmapend']
  128. self._extra_mark_sorted = jit2gc['gcmarksorted']
  129. else:
  130. self._extra_gcmapstart = lambda: llmemory.NULL
  131. self._extra_gcmapend = lambda: llmemory.NULL
  132. self._extra_mark_sorted = lambda: True
  133. def need_stacklet_support(self, gctransformer, getfn):
  134. # stacklet support: BIG HACK for rlib.rstacklet
  135. from pypy.rlib import _stacklet_asmgcc
  136. _stacklet_asmgcc._asmstackrootwalker = self # as a global! argh
  137. def need_thread_support(self, gctransformer, getfn):
  138. # Threads supported "out of the box" by the rest of the code.
  139. # The whole code in this function is only there to support
  140. # fork()ing in a multithreaded process :-(
  141. # For this, we need to handle gc_thread_start and gc_thread_die
  142. # to record the mapping {thread_id: stack_start}, and
  143. # gc_thread_before_fork and gc_thread_after_fork to get rid of
  144. # all ASM_FRAMEDATA structures that do no belong to the current
  145. # thread after a fork().
  146. from pypy.module.thread import ll_thread
  147. from pypy.rpython.memory.support import AddressDict
  148. from pypy.rpython.memory.support import copy_without_null_values
  149. from pypy.annotation import model as annmodel
  150. gcdata = self.gcdata
  151. def get_aid():
  152. """Return the thread identifier, cast to an (opaque) address."""
  153. return llmemory.cast_int_to_adr(ll_thread.get_ident())
  154. def thread_start():
  155. value = llop.stack_current(llmemory.Address)
  156. gcdata.aid2stack.setitem(get_aid(), value)
  157. thread_start._always_inline_ = True
  158. def thread_setup():
  159. gcdata.aid2stack = AddressDict()
  160. gcdata.dead_threads_count = 0
  161. # to also register the main thread's stack
  162. thread_start()
  163. thread_setup._always_inline_ = True
  164. def thread_die():
  165. gcdata.aid2stack.setitem(get_aid(), llmemory.NULL)
  166. # from time to time, rehash the dictionary to remove
  167. # old NULL entries
  168. gcdata.dead_threads_count += 1
  169. if (gcdata.dead_threads_count & 511) == 0:
  170. copy = copy_without_null_values(gcdata.aid2stack)
  171. gcdata.aid2stack.delete()
  172. gcdata.aid2stack = copy
  173. def belongs_to_current_thread(framedata):
  174. # xxx obscure: the answer is Yes if, as a pointer, framedata
  175. # lies between the start of the current stack and the top of it.
  176. stack_start = gcdata.aid2stack.get(get_aid(), llmemory.NULL)
  177. ll_assert(stack_start != llmemory.NULL,
  178. "current thread not found in gcdata.aid2stack!")
  179. stack_stop = llop.stack_current(llmemory.Address)
  180. return (stack_start <= framedata <= stack_stop or
  181. stack_start >= framedata >= stack_stop)
  182. def thread_before_fork():
  183. # before fork(): collect all ASM_FRAMEDATA structures that do
  184. # not belong to the current thread, and move them out of the
  185. # way, i.e. out of the main circular doubly linked list.
  186. detached_pieces = llmemory.NULL
  187. anchor = llmemory.cast_ptr_to_adr(gcrootanchor)
  188. initialframedata = anchor.address[1]
  189. while initialframedata != anchor: # while we have not looped back
  190. if not belongs_to_current_thread(initialframedata):
  191. # Unlink it
  192. prev = initialframedata.address[0]
  193. next = initialframedata.address[1]
  194. prev.address[1] = next
  195. next.address[0] = prev
  196. # Link it to the singly linked list 'detached_pieces'
  197. initialframedata.address[0] = detached_pieces
  198. detached_pieces = initialframedata
  199. rffi.stackcounter.stacks_counter -= 1
  200. # Then proceed to the next piece of stack
  201. initialframedata = initialframedata.address[1]
  202. return detached_pieces
  203. def thread_after_fork(result_of_fork, detached_pieces):
  204. if result_of_fork == 0:
  205. # We are in the child process. Assumes that only the
  206. # current thread survived. All the detached_pieces
  207. # are pointers in other stacks, so have likely been
  208. # freed already by the multithreaded library.
  209. # Nothing more for us to do.
  210. pass
  211. else:
  212. # We are still in the parent process. The fork() may
  213. # have succeeded or not, but that's irrelevant here.
  214. # We need to reattach the detached_pieces now, to the
  215. # circular doubly linked list at 'gcrootanchor'. The
  216. # order is not important.
  217. anchor = llmemory.cast_ptr_to_adr(gcrootanchor)
  218. while detached_pieces != llmemory.NULL:
  219. reattach = detached_pieces
  220. detached_pieces = detached_pieces.address[0]
  221. a_next = anchor.address[1]
  222. reattach.address[0] = anchor
  223. reattach.address[1] = a_next
  224. anchor.address[1] = reattach
  225. a_next.address[0] = reattach
  226. rffi.stackcounter.stacks_counter += 1
  227. self.thread_setup = thread_setup
  228. self.thread_start_ptr = getfn(thread_start, [], annmodel.s_None,
  229. inline=True)
  230. self.thread_die_ptr = getfn(thread_die, [], annmodel.s_None)
  231. self.thread_before_fork_ptr = getfn(thread_before_fork, [],
  232. annmodel.SomeAddress())
  233. self.thread_after_fork_ptr = getfn(thread_after_fork,
  234. [annmodel.SomeInteger(),
  235. annmodel.SomeAddress()],
  236. annmodel.s_None)
  237. def walk_stack_roots(self, collect_stack_root):
  238. gcdata = self.gcdata
  239. gcdata._gc_collect_stack_root = collect_stack_root
  240. pypy_asm_stackwalk(llhelper(ASM_CALLBACK_PTR, self._asm_callback),
  241. gcrootanchor)
  242. def walk_stack_from(self):
  243. curframe = lltype.malloc(WALKFRAME, flavor='raw')
  244. otherframe = lltype.malloc(WALKFRAME, flavor='raw')
  245. # Walk over all the pieces of stack. They are in a circular linked
  246. # list of structures of 7 words, the 2 first words being prev/next.
  247. # The anchor of this linked list is:
  248. anchor = llmemory.cast_ptr_to_adr(gcrootanchor)
  249. initialframedata = anchor.address[1]
  250. stackscount = 0
  251. while initialframedata != anchor: # while we have not looped back
  252. self.fill_initial_frame(curframe, initialframedata)
  253. # Loop over all the frames in the stack
  254. while self.walk_to_parent_frame(curframe, otherframe):
  255. swap = curframe
  256. curframe = otherframe # caller becomes callee
  257. otherframe = swap
  258. # Then proceed to the next piece of stack
  259. initialframedata = initialframedata.address[1]
  260. stackscount += 1
  261. #
  262. expected = rffi.stackcounter.stacks_counter
  263. ll_assert(not (stackscount < expected), "non-closed stacks around")
  264. ll_assert(not (stackscount > expected), "stacks counter corruption?")
  265. lltype.free(otherframe, flavor='raw')
  266. lltype.free(curframe, flavor='raw')
  267. def fill_initial_frame(self, curframe, initialframedata):
  268. # Read the information provided by initialframedata
  269. initialframedata += 2*sizeofaddr #skip the prev/next words at the start
  270. reg = 0
  271. while reg < CALLEE_SAVED_REGS:
  272. # NB. 'initialframedata' stores the actual values of the
  273. # registers %ebx etc., and if these values are modified
  274. # they are reloaded by pypy_asm_stackwalk(). By contrast,
  275. # 'regs_stored_at' merely points to the actual values
  276. # from the 'initialframedata'.
  277. curframe.regs_stored_at[reg] = initialframedata + reg*sizeofaddr
  278. reg += 1
  279. curframe.frame_address = initialframedata.address[CALLEE_SAVED_REGS]
  280. def walk_to_parent_frame(self, callee, caller):
  281. """Starting from 'callee', walk the next older frame on the stack
  282. and fill 'caller' accordingly. Also invokes the collect_stack_root()
  283. callback from the GC code for each GC root found in 'caller'.
  284. """
  285. #
  286. # The gcmap table is a list of entries, two machine words each:
  287. # void *SafePointAddress;
  288. # int Shape;
  289. #
  290. # A "safe point" is the return address of a call.
  291. # The "shape" of a safe point is a list of integers
  292. # that represent "locations". A "location" can be
  293. # either in the stack or in a register. See
  294. # getlocation() for the decoding of this integer.
  295. # The locations stored in a "shape" are as follows:
  296. #
  297. # * The "location" of the return address. This is just
  298. # after the end of the frame of 'callee'; it is the
  299. # first word of the frame of 'caller' (see picture
  300. # below).
  301. #
  302. # * Four "locations" that specify where the function saves
  303. # each of the four callee-saved registers (%ebx, %esi,
  304. # %edi, %ebp).
  305. #
  306. # * The number of live GC roots around the call.
  307. #
  308. # * For each GC root, an integer that specify where the
  309. # GC pointer is stored. This is a "location" too.
  310. #
  311. # XXX the details are completely specific to X86!!!
  312. # a picture of the stack may help:
  313. # ^ ^ ^
  314. # | ... | to older frames
  315. # +--------------+
  316. # | ret addr | <------ caller_frame (addr of retaddr)
  317. # | ... |
  318. # | caller frame |
  319. # | ... |
  320. # +--------------+
  321. # | ret addr | <------ callee_frame (addr of retaddr)
  322. # | ... |
  323. # | callee frame |
  324. # | ... | lower addresses
  325. # +--------------+ v v v
  326. #
  327. retaddr = callee.frame_address.address[0]
  328. #
  329. # try to locate the caller function based on retaddr.
  330. # set up self._shape_decompressor.
  331. #
  332. self.locate_caller_based_on_retaddr(retaddr)
  333. #
  334. # found! Enumerate the GC roots in the caller frame
  335. #
  336. collect_stack_root = self.gcdata._gc_collect_stack_root
  337. ebp_in_caller = callee.regs_stored_at[INDEX_OF_EBP].address[0]
  338. gc = self.gc
  339. while True:
  340. location = self._shape_decompressor.next()
  341. if location == 0:
  342. break
  343. addr = self.getlocation(callee, ebp_in_caller, location)
  344. if gc.points_to_valid_gc_object(addr):
  345. collect_stack_root(gc, addr)
  346. #
  347. # track where the caller_frame saved the registers from its own
  348. # caller
  349. #
  350. reg = CALLEE_SAVED_REGS - 1
  351. while reg >= 0:
  352. location = self._shape_decompressor.next()
  353. addr = self.getlocation(callee, ebp_in_caller, location)
  354. caller.regs_stored_at[reg] = addr
  355. reg -= 1
  356. location = self._shape_decompressor.next()
  357. caller.frame_address = self.getlocation(callee, ebp_in_caller,
  358. location)
  359. # we get a NULL marker to mean "I'm the frame
  360. # of the entry point, stop walking"
  361. return caller.frame_address != llmemory.NULL
  362. def locate_caller_based_on_retaddr(self, retaddr):
  363. gcmapstart = llop.gc_asmgcroot_static(llmemory.Address, 0)
  364. gcmapend = llop.gc_asmgcroot_static(llmemory.Address, 1)
  365. item = search_in_gcmap(gcmapstart, gcmapend, retaddr)
  366. if item:
  367. self._shape_decompressor.setpos(item.signed[1])
  368. return
  369. gcmapstart2 = self._extra_gcmapstart()
  370. gcmapend2 = self._extra_gcmapend()
  371. if gcmapstart2 != gcmapend2:
  372. # we have a non-empty JIT-produced table to look in
  373. item = search_in_gcmap2(gcmapstart2, gcmapend2, retaddr)
  374. if item:
  375. self._shape_decompressor.setaddr(item)
  376. return
  377. # maybe the JIT-produced table is not sorted?
  378. was_already_sorted = self._extra_mark_sorted()
  379. if not was_already_sorted:
  380. sort_gcmap(gcmapstart2, gcmapend2)
  381. item = search_in_gcmap2(gcmapstart2, gcmapend2, retaddr)
  382. if item:
  383. self._shape_decompressor.setaddr(item)
  384. return
  385. # there is a rare risk that the array contains *two* entries
  386. # with the same key, one of which is dead (null value), and we
  387. # found the dead one above. Solve this case by replacing all
  388. # dead keys with nulls, sorting again, and then trying again.
  389. replace_dead_entries_with_nulls(gcmapstart2, gcmapend2)
  390. sort_gcmap(gcmapstart2, gcmapend2)
  391. item = search_in_gcmap2(gcmapstart2, gcmapend2, retaddr)
  392. if item:
  393. self._shape_decompressor.setaddr(item)
  394. return
  395. # the item may have been not found because the main array was
  396. # not sorted. Sort it and try again.
  397. win32_follow_gcmap_jmp(gcmapstart, gcmapend)
  398. sort_gcmap(gcmapstart, gcmapend)
  399. item = search_in_gcmap(gcmapstart, gcmapend, retaddr)
  400. if item:
  401. self._shape_decompressor.setpos(item.signed[1])
  402. return
  403. llop.debug_fatalerror(lltype.Void, "cannot find gc roots!")
  404. def getlocation(self, callee, ebp_in_caller, location):
  405. """Get the location in the 'caller' frame of a variable, based
  406. on the integer 'location' that describes it. All locations are
  407. computed based on information saved by the 'callee'.
  408. """
  409. ll_assert(location >= 0, "negative location")
  410. kind = location & LOC_MASK
  411. offset = location & ~ LOC_MASK
  412. if kind == LOC_REG: # register
  413. if location == LOC_NOWHERE:
  414. return llmemory.NULL
  415. reg = (location >> 2) - 1
  416. ll_assert(reg < CALLEE_SAVED_REGS, "bad register location")
  417. return callee.regs_stored_at[reg]
  418. elif kind == LOC_ESP_PLUS: # in the caller stack frame at N(%esp)
  419. esp_in_caller = callee.frame_address + sizeofaddr
  420. return esp_in_caller + offset
  421. elif kind == LOC_EBP_PLUS: # in the caller stack frame at N(%ebp)
  422. return ebp_in_caller + offset
  423. else: # kind == LOC_EBP_MINUS: at -N(%ebp)
  424. return ebp_in_caller - offset
  425. LOC_REG = 0
  426. LOC_ESP_PLUS = 1
  427. LOC_EBP_PLUS = 2
  428. LOC_EBP_MINUS = 3
  429. LOC_MASK = 0x03
  430. LOC_NOWHERE = LOC_REG | 0
  431. # ____________________________________________________________
  432. sizeofaddr = llmemory.sizeof(llmemory.Address)
  433. arrayitemsize = 2 * sizeofaddr
  434. def binary_search(start, end, addr1):
  435. """Search for an element in a sorted array.
  436. The interval from the start address (included) to the end address
  437. (excluded) is assumed to be a sorted arrays of pairs (addr1, addr2).
  438. This searches for the item with a given addr1 and returns its
  439. address. If not found exactly, it tries to return the address
  440. of the item left of addr1 (i.e. such that result.address[0] < addr1).
  441. """
  442. count = (end - start) // arrayitemsize
  443. while count > 1:
  444. middleindex = count // 2
  445. middle = start + middleindex * arrayitemsize
  446. if addr1 < middle.address[0]:
  447. count = middleindex
  448. else:
  449. start = middle
  450. count -= middleindex
  451. return start
  452. def search_in_gcmap(gcmapstart, gcmapend, retaddr):
  453. item = binary_search(gcmapstart, gcmapend, retaddr)
  454. if item.address[0] == retaddr:
  455. return item # found
  456. # 'retaddr' not exactly found. Check that 'item' is the start of a
  457. # compressed range that includes 'retaddr'.
  458. if retaddr > item.address[0] and item.signed[1] < 0:
  459. return item # ok
  460. else:
  461. return llmemory.NULL # failed
  462. def search_in_gcmap2(gcmapstart, gcmapend, retaddr):
  463. # same as 'search_in_gcmap', but without range checking support
  464. # (item.signed[1] is an address in this case, not a signed at all!)
  465. item = binary_search(gcmapstart, gcmapend, retaddr)
  466. if item.address[0] == retaddr:
  467. return item.address[1] # found
  468. else:
  469. return llmemory.NULL # failed
  470. def sort_gcmap(gcmapstart, gcmapend):
  471. count = (gcmapend - gcmapstart) // arrayitemsize
  472. qsort(gcmapstart,
  473. rffi.cast(rffi.SIZE_T, count),
  474. rffi.cast(rffi.SIZE_T, arrayitemsize),
  475. llhelper(QSORT_CALLBACK_PTR, _compare_gcmap_entries))
  476. def replace_dead_entries_with_nulls(start, end):
  477. # replace the dead entries (null value) with a null key.
  478. count = (end - start) // arrayitemsize - 1
  479. while count >= 0:
  480. item = start + count * arrayitemsize
  481. if item.address[1] == llmemory.NULL:
  482. item.address[0] = llmemory.NULL
  483. count -= 1
  484. if sys.platform == 'win32':
  485. def win32_follow_gcmap_jmp(start, end):
  486. # The initial gcmap table contains addresses to a JMP
  487. # instruction that jumps indirectly to the real code.
  488. # Replace them with the target addresses.
  489. while start < end:
  490. code = rffi.cast(rffi.CCHARP, start.address[0])[0]
  491. if code == '\xe9': # jmp
  492. rel32 = rffi.cast(rffi.LONGP, start.address[0]+1)[0]
  493. target = start.address[0] + (rel32 + 5)
  494. start.address[0] = target
  495. start += arrayitemsize
  496. else:
  497. def win32_follow_gcmap_jmp(start, end):
  498. pass
  499. def _compare_gcmap_entries(addr1, addr2):
  500. key1 = addr1.address[0]
  501. key2 = addr2.address[0]
  502. if key1 < key2:
  503. result = -1
  504. elif key1 == key2:
  505. result = 0
  506. else:
  507. result = 1
  508. return rffi.cast(rffi.INT, result)
  509. # ____________________________________________________________
  510. class ShapeDecompressor:
  511. _alloc_flavor_ = "raw"
  512. def setpos(self, pos):
  513. if pos < 0:
  514. pos = ~ pos # can ignore this "range" marker here
  515. gccallshapes = llop.gc_asmgcroot_static(llmemory.Address, 2)
  516. self.addr = gccallshapes + pos
  517. def setaddr(self, addr):
  518. self.addr = addr
  519. def next(self):
  520. value = 0
  521. addr = self.addr
  522. while True:
  523. b = ord(addr.char[0])
  524. addr += 1
  525. value += b
  526. if b < 0x80:
  527. break
  528. value = (value - 0x80) << 7
  529. self.addr = addr
  530. return value
  531. # ____________________________________________________________
  532. #
  533. # The special pypy_asm_stackwalk(), implemented directly in
  534. # assembler, fills information about the current stack top in an
  535. # ASM_FRAMEDATA array and invokes an RPython callback with it.
  536. # An ASM_FRAMEDATA is an array of 5 values that describe everything
  537. # we need to know about a stack frame:
  538. #
  539. # - the value that %ebx had when the current function started
  540. # - the value that %esi had when the current function started
  541. # - the value that %edi had when the current function started
  542. # - the value that %ebp had when the current function started
  543. # - frame address (actually the addr of the retaddr of the current function;
  544. # that's the last word of the frame in memory)
  545. #
  546. if IS_64_BITS:
  547. CALLEE_SAVED_REGS = 6
  548. INDEX_OF_EBP = 5
  549. FRAME_PTR = CALLEE_SAVED_REGS
  550. else:
  551. CALLEE_SAVED_REGS = 4 # there are 4 callee-saved registers
  552. INDEX_OF_EBP = 3
  553. FRAME_PTR = CALLEE_SAVED_REGS # the frame is at index 4 in the array
  554. ASM_CALLBACK_PTR = lltype.Ptr(lltype.FuncType([], lltype.Void))
  555. # used internally by walk_stack_from()
  556. WALKFRAME = lltype.Struct('WALKFRAME',
  557. ('regs_stored_at', # address of where the registers have been saved
  558. lltype.FixedSizeArray(llmemory.Address, CALLEE_SAVED_REGS)),
  559. ('frame_address',
  560. llmemory.Address),
  561. )
  562. # We have a circular doubly-linked list of all the ASM_FRAMEDATAs currently
  563. # alive. The list's starting point is given by 'gcrootanchor', which is not
  564. # a full ASM_FRAMEDATA but only contains the prev/next pointers:
  565. ASM_FRAMEDATA_HEAD_PTR = lltype.Ptr(lltype.ForwardReference())
  566. ASM_FRAMEDATA_HEAD_PTR.TO.become(lltype.Struct('ASM_FRAMEDATA_HEAD',
  567. ('prev', ASM_FRAMEDATA_HEAD_PTR),
  568. ('next', ASM_FRAMEDATA_HEAD_PTR)
  569. ))
  570. gcrootanchor = lltype.malloc(ASM_FRAMEDATA_HEAD_PTR.TO, immortal=True)
  571. gcrootanchor.prev = gcrootanchor
  572. gcrootanchor.next = gcrootanchor
  573. c_gcrootanchor = Constant(gcrootanchor, ASM_FRAMEDATA_HEAD_PTR)
  574. pypy_asm_stackwalk = rffi.llexternal('pypy_asm_stackwalk',
  575. [ASM_CALLBACK_PTR,
  576. ASM_FRAMEDATA_HEAD_PTR],
  577. lltype.Signed,
  578. sandboxsafe=True,
  579. _nowrapper=True,
  580. random_effects_on_gcobjs=True)
  581. c_asm_stackwalk = Constant(pypy_asm_stackwalk,
  582. lltype.typeOf(pypy_asm_stackwalk))
  583. pypy_asm_gcroot = rffi.llexternal('pypy_asm_gcroot',
  584. [llmemory.Address],
  585. llmemory.Address,
  586. sandboxsafe=True,
  587. _nowrapper=True)
  588. c_asm_gcroot = Constant(pypy_asm_gcroot, lltype.typeOf(pypy_asm_gcroot))
  589. pypy_asm_nocollect = rffi.llexternal('pypy_asm_gc_nocollect',
  590. [rffi.CCHARP], lltype.Void,
  591. sandboxsafe=True,
  592. _nowrapper=True)
  593. c_asm_nocollect = Constant(pypy_asm_nocollect, lltype.typeOf(pypy_asm_nocollect))
  594. QSORT_CALLBACK_PTR = lltype.Ptr(lltype.FuncType([llmemory.Address,
  595. llmemory.Address], rffi.INT))
  596. qsort = rffi.llexternal('qsort',
  597. [llmemory.Address,
  598. rffi.SIZE_T,
  599. rffi.SIZE_T,
  600. QSORT_CALLBACK_PTR],
  601. lltype.Void,
  602. sandboxsafe=True,
  603. random_effects_on_gcobjs=False, # but has a callback
  604. _nowrapper=True)