[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