[pypy-svn] r50287 - in pypy/branch/asmgcroot/pypy: rpython/memory/gctransform translator/c

arigo at codespeak.net arigo at codespeak.net
Thu Jan 3 14:13:33 CET 2008


Author: arigo
Date: Thu Jan  3 14:13:31 2008
New Revision: 50287

Modified:
   pypy/branch/asmgcroot/pypy/rpython/memory/gctransform/asmgcroot.py
   pypy/branch/asmgcroot/pypy/translator/c/trackgcroot.py
Log:
Small progresses.  I'm a bit blocked though.


Modified: pypy/branch/asmgcroot/pypy/rpython/memory/gctransform/asmgcroot.py
==============================================================================
--- pypy/branch/asmgcroot/pypy/rpython/memory/gctransform/asmgcroot.py	(original)
+++ pypy/branch/asmgcroot/pypy/rpython/memory/gctransform/asmgcroot.py	Thu Jan  3 14:13:31 2008
@@ -1,5 +1,5 @@
 from pypy.rpython.memory.gctransform.framework import FrameworkGCTransformer
-from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.rpython import rmodel
 from pypy.rpython.rbuiltin import gen_cast
@@ -54,11 +54,13 @@
             append_static_root = staticmethod(append_static_root)
             
             def __init__(self, with_static=True):
-                self.stack_current = llop.llvm_frameaddress(llmemory.Address)
+                self.callee_data = lltype.malloc(ASM_STACKWALK, flavor="raw")
+                self.caller_data = lltype.malloc(ASM_STACKWALK, flavor="raw")
+                pypy_asm_stackwalk_init(self.caller_data)
                 self.remaining_roots_in_current_frame = 0
                 # We must walk at least a couple of frames up the stack
                 # *now*, i.e. before we leave __init__, otherwise
-                # self.stack_current ends up pointing to a dead frame.
+                # the caller_data ends up pointing to a dead frame.
                 # We can walk until we find a real GC root; then we're
                 # definitely out of the GC code itself.
                 while self.remaining_roots_in_current_frame == 0:
@@ -86,77 +88,105 @@
                         return result
 
             def walk_to_parent_frame(self):
+                if not self.caller_data:
+                    return False
                 #
                 # The gcmap table is a list of pairs of pointers:
                 #     void *SafePointAddress;
                 #     void *Shape;
                 #
                 # A "safe point" is the return address of a call.
-                # The "shape" of a safe point records the size of the
-                # frame of the function containing it, as well as a
-                # list of the variables that contain gc roots at that
-                # time.  Each variable is described by its offset in
-                # the frame.
-                #
-                callee_frame = self.stack_current
-                if llmemory.cast_adr_to_int(callee_frame) & 1:
-                    return False   # odd bit set here when the callee_frame
-                                   # is the frame of main(), i.e. when there
-                                   # is nothing more for us in the stack.
+                # The "shape" of a safe point is a list of integers
+                # as follows:
+                #
+                #   * The size of the frame of the function containing the
+                #     call (in theory it can be different from call to call).
+                #     This size includes the return address (see picture
+                #     below).
+                #
+                #   * Four integers that specify where the function saves
+                #     each of the four callee-saved registers (%ebx, %esi,
+                #     %edi, %ebp).  This is a "location", see below.
+                #
+                #   * The number of live GC roots around the call.
+                #
+                #   * For each GC root, an integer that specify where the
+                #     GC pointer is stored.  This is a "location", see below.
+                #
+                # A "location" can be either in the stack or in a register.
+                # If it is in the stack, it is specified as an integer offset
+                # from the current frame (so it is typically < 0, as seen
+                # in the picture below, though it can also be > 0 if the
+                # location is an input argument to the current function and
+                # thus lives at the bottom of the caller's frame).
+                # A "location" can also be in a register; in our context it
+                # can only be a callee-saved register.  This is specified
+                # as a small odd-valued integer (1=%ebx, 3=%esi, etc.)
+
+                # In the code below, we walk the next older frame on the stack.
+                # The caller becomes the callee; we swap the two buffers and
+                # fill in the new caller's data.
+                callee = self.caller_data
+                caller = self.callee_data
+                self.caller_data = caller
+                self.callee_data = callee
                 #
                 # XXX the details are completely specific to X86!!!
                 # a picture of the stack may help:
                 #                                           ^ ^ ^
                 #     |     ...      |                 to older frames
                 #     +--------------+
-                #     |  first word  |  <------ caller_frame (addr of 1st word)
-                #     +              +
+                #     |   ret addr   |  <------ caller_frame (addr of retaddr)
+                #     |     ...      |
                 #     | caller frame |
                 #     |     ...      |
-                #     |  frame data  |  <------ frame_data_base
-                #     +--------------+
-                #     |   ret addr   |
                 #     +--------------+
-                #     |  first word  |  <------ callee_frame (addr of 1st word)
-                #     +              +
-                #     | callee frame |
+                #     |   ret addr   |  <------ callee_frame (addr of retaddr)
                 #     |     ...      |
-                #     |  frame data  |                 lower addresses
+                #     | callee frame |
+                #     |     ...      |                 lower addresses
                 #     +--------------+                      v v v
                 #
-                retaddr = callee_frame.address[1]
+                callee_frame = callee[FRAME_PTR]
+                retaddr = callee[RET_ADDR]
                 #
                 # try to locate the caller function based on retaddr.
                 #
                 gcmapstart = llop.llvm_gcmapstart(llmemory.Address)
                 gcmapend   = llop.llvm_gcmapend(llmemory.Address)
                 item = binary_search(gcmapstart, gcmapend, retaddr)
-                if item.address[0] == retaddr:
-                    #
-                    # found!  Setup pointers allowing us to
-                    # parse the caller's frame structure...
-                    #
-                    shape = item.address[1]
-                    # XXX assumes that .signed is 32-bit
-                    framesize = shape.signed[0]   # odd if it's main()
-                    livecount = shape.signed[1]
-                    caller_frame = callee_frame + 4 + framesize
-                    self.stack_current = caller_frame
-                    self.frame_data_base = callee_frame + 8
-                    self.remaining_roots_in_current_frame = livecount
-                    self.liveoffsets = shape + 8
-                    return True
-
-                # retaddr not found!
-                llop.debug_fatalerror(lltype.Void, "cannot find gc roots!")
-                return False
+                if item.address[0] != retaddr:
+                    # retaddr not found!
+                    llop.debug_fatalerror(lltype.Void, "cannot find gc roots!")
+                    return False
+
+                # found!  Now we can fill in 'caller'.
+                shape = item.address[1]
+                framesize = shape.signed[0]
+                caller[FRAME_PTR] = callee_frame + framesize
+                caller[RET_ADDR] = caller[FRAME_PTR].address[0]
+                reg = 0
+                while reg < CALLEE_SAVED_REGS:
+                    caller[reg] = self.read_from_location(shape.signed[1+reg])
+                    reg += 1
+                livecount = shape.signed[1+CALLEE_SAVED_REGS]
+                self.remaining_roots_in_current_frame = livecount
+                self.liveoffsets = shape + 4 * (1+CALLEE_SAVED_REGS+1)
+                return True
+
+            def finished(self):
+                lltype.free(self.stackwalkcur, flavor='raw')
+                self.stackwalkcur = lltype.nullptr(ASM_STACKWALK)
+                lltype.free(self.stackwalknext, flavor='raw')
+                self.stackwalknext = lltype.nullptr(ASM_STACKWALK)
 
             def next_gcroot_from_current_frame(self):
                 i = self.remaining_roots_in_current_frame - 1
                 self.remaining_roots_in_current_frame = i
                 ll_assert(i >= 0, "bad call to next_gcroot_from_current_frame")
                 liveoffset = self.liveoffsets.signed[i]
-                return self.frame_data_base + liveoffset
+                return self.callee_data[FRAME_PTR] + liveoffset
+            ...
 
 
         return StackRootIterator
@@ -206,3 +236,29 @@
         scan.address[0] = addr1
         scan.address[1] = addr2
         next += arrayitemsize
+
+#
+# The special pypy_asm_stackwalk_init(), implemented directly in
+# assembler, initializes an ASM_STACKWALK array in order to bootstrap
+# the stack walking code.  An ASM_STACKWALK is an array of 6 values
+# that describe everything we need to know about a stack frame:
+#
+#   - the value that %ebx had when the current function started
+#   - the value that %esi had when the current function started
+#   - the value that %edi had when the current function started
+#   - the value that %ebp had when the current function started
+#   - frame address (actually the addr of the retaddr of the current function;
+#                    that's the last word of the frame in memory)
+#   - the return address for when the current function finishes
+#                   (which is usually just the word at "frame address")
+#
+CALLEE_SAVED_REGS = 4       # there are 4 callee-saved registers
+FRAME_PTR      = CALLEE_SAVED_REGS
+RET_ADDR       = CALLEE_SAVED_REGS + 1
+ASM_STACKWALK  = lltype.FixedSizeArray(llmemory.Address, CALLEE_SAVED_REGS + 2)
+
+pypy_asm_stackwalk_init = rffi.llexternal('pypy_asm_stackwalk_init',
+                                          [lltype.Ptr(ASM_STACKWALK)],
+                                          lltype.Void,
+                                          sandboxsafe=True,
+                                          _nowrapper=True)

Modified: pypy/branch/asmgcroot/pypy/translator/c/trackgcroot.py
==============================================================================
--- pypy/branch/asmgcroot/pypy/translator/c/trackgcroot.py	(original)
+++ pypy/branch/asmgcroot/pypy/translator/c/trackgcroot.py	Thu Jan  3 14:13:31 2008
@@ -34,6 +34,22 @@
     def dump(self, output):
         assert self.seen_main
         shapes = {}
+        print >> output, """\t.text
+        .globl pypy_asm_stackwalk_init
+            .type pypy_asm_stackwalk_init, @function
+        pypy_asm_stackwalk_init:
+            /* See description in asmgcroot.py */
+            movl   4(%esp), %edx     /* argument */
+            movl   %ebx, (%edx)
+            movl   %esi, 4(%edx)
+            movl   %edi, 8(%edx)
+            movl   %ebp, 12(%edx)
+            movl   %esp, 16(%edx)
+            movl   (%esp), %eax
+            movl   %eax, 20(%edx)
+            ret
+        .size pypy_asm_stackwalk_init, .-pypy_asm_stackwalk_init
+        """
         print >> output, '\t.data'
         print >> output, '\t.align\t4'
         print >> output, '\t.globl\t__gcmapstart'
@@ -54,11 +70,13 @@
         print >> output, '\t.align\t4'
         keys = shapes.keys()
         keys.sort()
+        FIXED = 1 + len(CALLEE_SAVE_REGISTERS)
         for state in keys:
             print >> output, '%s:' % (shapes[state],)
-            print >> output, '\t.long\t%d' % (state[0],)      # frame size
-            print >> output, '\t.long\t%d' % (len(state)-1,)  # gcroot count
-            for p in state[1:]:
+            for i in range(FIXED):
+                print >> output, '\t.long\t%d' % (state[i],)
+            print >> output, '\t.long\t%d' % (len(state)-FIXED,)
+            for p in state[FIXED:]:
                 print >> output, '\t.long\t%d' % (p,)         # gcroots
 
     def process(self, iterlines, newfile, entrypoint='main', filename='?'):
@@ -89,14 +107,25 @@
             for label, state in table:
                 print >> sys.stderr, label, '\t', state
         if tracker.funcname == entrypoint:
-            self.seen_main = True
-            table = [(label, (-1,)+info[1:]) for label, info in table]
-            # ^^^ we set the framesize of the entry point to -1 as a marker
-            # (the code in asmgcroot.py actually takes any odd-valued number
-            # as marker.)
+            table = self.fixup_entrypoint_table(table)
         self.gcmaptable.extend(table)
         newfile.writelines(tracker.lines)
 
+    def fixup_entrypoint_table(self, table):
+        self.seen_main = True
+        # we don't need to track where the main() function saved its
+        # own caller's registers.  So as a marker, we set the
+        # corresponding entries in the shape to -1.  The code in
+        # asmgcroot recognizes these markers as meaning "stop walking
+        # the stack".
+        newtable = []
+        for label, shape in table:
+            shape = list(shape)
+            for i in range(len(CALLEE_SAVE_REGISTERS)):
+                shape[1+i] = -1
+            newtable.append((label, tuple(shape)))
+        return newtable
+
 
 class FunctionGcRootTracker(object):
 
@@ -127,31 +156,34 @@
         return self.gettable()
 
     def gettable(self):
-        "Returns a list [(label_after_call, (framesize, gcroot0, gcroot1,..))]"
+        """Returns a list [(label_after_call, shape_tuple)]
+        where shape_tuple = (framesize, where_is_ebx_saved, ...
+                            ..., where_is_ebp_saved, gcroot0, gcroot1...)
+        """
         table = []
         for insn in self.list_call_insns():
             if not hasattr(insn, 'framesize'):
                 continue     # calls that never end up reaching a RET
-            info = [insn.framesize]
+            shape = [insn.framesize + 4]     # accounts for the return address
             # the first gcroots are always the ones corresponding to
             # the callee-saved registers
             for reg in CALLEE_SAVE_REGISTERS:
-                info.append(None)
+                shape.append(None)
             for loc, tag in insn.gcroots.items():
                 if not isinstance(loc, int):
                     # a special representation for a register location,
                     # as an odd-valued number
                     loc = CALLEE_SAVE_REGISTERS.index(loc) * 2 + 1
                 if tag is None:
-                    info.append(loc)
+                    shape.append(loc)
                 else:
                     regindex = CALLEE_SAVE_REGISTERS.index(tag)
-                    info[1 + regindex] = loc
-            if None in info:
-                reg = CALLEE_SAVE_REGISTERS[info.index(None) - 1]
+                    shape[1 + regindex] = loc
+            if None in shape:
+                reg = CALLEE_SAVE_REGISTERS[shape.index(None) - 1]
                 raise AssertionError("cannot track where register %s is saved"
                                      % (reg,))
-            table.append((insn.global_label, tuple(info)))
+            table.append((insn.global_label, tuple(shape)))
         return table
 
     def findlabels(self):


More information about the pypy-svn mailing list