[pypy-svn] r51239 - in pypy/branch/asmgcroot/pypy: rpython/memory/gctransform translator/c/gcc translator/c/gcc/test
arigo at codespeak.net
arigo at codespeak.net
Mon Feb 4 10:51:34 CET 2008
Author: arigo
Date: Mon Feb 4 10:51:34 2008
New Revision: 51239
Added:
pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track6.s
Modified:
pypy/branch/asmgcroot/pypy/rpython/memory/gctransform/asmgcroot.py
pypy/branch/asmgcroot/pypy/translator/c/gcc/test/test_trackgcroot.py
pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track0.s
pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track1.s
pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track2.s
pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track3.s
pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track4.s
pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track5.s
pypy/branch/asmgcroot/pypy/translator/c/gcc/trackgcroot.py
Log:
Refactor of the way the "location" integers are encoded and
decoded, to allow both %esp-based and %ebp-based stack frame
positions to be used. See the new track6.s test for why we
need it. Simplifies some stuff too.
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 Mon Feb 4 10:51:34 2008
@@ -104,31 +104,24 @@
#
# A "safe point" is the return address of a call.
# 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
+ # that represent "locations". A "location" can be
+ # either in the stack or in a register. See
+ # getlocation() for the decoding of this integer.
+ # The locations stored in a "shape" are as follows:
+ #
+ # * The "location" of the return address. This is just
+ # after the end of the frame of 'callee'; it is the
+ # first word of the frame of 'caller' (see picture
# below).
#
- # * Four integers that specify where the function saves
+ # * Four "locations" that specify where the function saves
# each of the four callee-saved registers (%ebx, %esi,
- # %edi, %ebp). This is a "location", see below.
+ # %edi, %ebp).
#
# * 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.)
+ # GC pointer is stored. This is a "location" too.
#
# XXX the details are completely specific to X86!!!
# a picture of the stack may help:
@@ -159,19 +152,9 @@
llop.debug_fatalerror(lltype.Void, "cannot find gc roots!")
return False
#
- # found! Now we can go to the caller_frame.
+ # found! Enumerate the GC roots in the caller frame
#
shape = item.address[1]
- framesize = shape.signed[0]
- if framesize == 0:
- # frame size not known, use %ebp to find the frame address
- caller_ebp = callee.regs_stored_at[INDEX_OF_EBP].address[0]
- caller.frame_address = caller_ebp + 4
- else:
- caller.frame_address = callee.frame_address + framesize
- #
- # enumerate the GC roots in the caller frame
- #
collect_stack_root = self.gcdata._gc_collect_stack_root
gc = self.gc
LIVELOCS = 1 + CALLEE_SAVED_REGS + 1 # index of the first gc root loc
@@ -179,37 +162,53 @@
while livecount > 0:
livecount -= 1
location = shape.signed[LIVELOCS + livecount]
- addr = self.getlocation(callee, caller, location)
+ addr = self.getlocation(callee, location)
if addr.address[0] != llmemory.NULL:
collect_stack_root(gc, addr)
#
# track where the caller_frame saved the registers from its own
# caller
#
- if shape.signed[1] == -1: # a marker that means "I'm the frame
- return False # of the entry point, stop walking"
+ location = shape.signed[0]
+ caller.frame_address = self.getlocation(callee, location)
+ if not caller.frame_address: # marker that means "I'm the frame
+ return False # of the entry point, stop walking"
reg = 0
while reg < CALLEE_SAVED_REGS:
location = shape.signed[1+reg]
- addr = self.getlocation(callee, caller, location)
+ addr = self.getlocation(callee, location)
caller.regs_stored_at[reg] = addr
reg += 1
return True
- def getlocation(self, callee, caller, location):
+ def getlocation(self, callee, location):
"""Get the location in the 'caller' frame of a variable, based
- on the integer 'location' that describes it. The 'callee' is
- only used if the location is in a register that was saved in the
- callee.
+ on the integer 'location' that describes it. All locations are
+ computed based on information saved by the 'callee'.
"""
- if location & 1: # register
- reg = location >> 1
+ kind = location & LOC_MASK
+ if kind == LOC_REG: # register
+ reg = location >> 2
ll_assert(0 <= reg < CALLEE_SAVED_REGS, "bad register location")
return callee.regs_stored_at[reg]
+ elif kind == LOC_ESP_BASED: # in the caller stack frame at N(%esp)
+ offset = location & ~ LOC_MASK
+ ll_assert(offset >= 0, "bad %esp-based location")
+ esp_in_caller = callee.frame_address + 4
+ return esp_in_caller + offset
+ elif kind == LOC_EBP_BASED: # in the caller stack frame at N(%ebp)
+ offset = location & ~ LOC_MASK
+ ebp_in_caller = callee.regs_stored_at[INDEX_OF_EBP].address[0]
+ return ebp_in_caller + offset
else:
- # in the stack frame
- return caller.frame_address + location
+ return llmemory.NULL
+
+LOC_NOWHERE = 0
+LOC_REG = 1
+LOC_EBP_BASED = 2
+LOC_ESP_BASED = 3
+LOC_MASK = 0x03
# ____________________________________________________________
Modified: pypy/branch/asmgcroot/pypy/translator/c/gcc/test/test_trackgcroot.py
==============================================================================
--- pypy/branch/asmgcroot/pypy/translator/c/gcc/test/test_trackgcroot.py (original)
+++ pypy/branch/asmgcroot/pypy/translator/c/gcc/test/test_trackgcroot.py Mon Feb 4 10:51:34 2008
@@ -1,5 +1,9 @@
import py
import sys, re
+from pypy.translator.c.gcc.trackgcroot import format_location
+from pypy.translator.c.gcc.trackgcroot import format_callshape
+from pypy.translator.c.gcc.trackgcroot import LOC_NOWHERE, LOC_REG
+from pypy.translator.c.gcc.trackgcroot import LOC_EBP_BASED, LOC_ESP_BASED
from pypy.translator.c.gcc.trackgcroot import GcRootTracker
from pypy.translator.c.gcc.trackgcroot import FunctionGcRootTracker
from StringIO import StringIO
@@ -7,6 +11,31 @@
this_dir = py.path.local(__file__).dirpath()
+def test_format_location():
+ assert format_location(LOC_NOWHERE) == '?'
+ assert format_location(LOC_REG | (0<<2)) == '%ebx'
+ assert format_location(LOC_REG | (1<<2)) == '%esi'
+ assert format_location(LOC_REG | (2<<2)) == '%edi'
+ assert format_location(LOC_REG | (3<<2)) == '%ebp'
+ assert format_location(LOC_EBP_BASED + 0) == '(%ebp)'
+ assert format_location(LOC_EBP_BASED + 4) == '4(%ebp)'
+ assert format_location(LOC_EBP_BASED - 4) == '-4(%ebp)'
+ assert format_location(LOC_ESP_BASED + 0) == '(%esp)'
+ assert format_location(LOC_ESP_BASED + 4) == '4(%esp)'
+ assert format_location(LOC_ESP_BASED - 4) == '-4(%esp)'
+
+def test_format_callshape():
+ expected = ('{4(%ebp) ' # position of the return address
+ '| 8(%ebp), 12(%ebp), 16(%ebp), 20(%ebp) ' # 4 saved regs
+ '| 24(%ebp), 28(%ebp)}') # GC roots
+ assert format_callshape((LOC_EBP_BASED+4,
+ LOC_EBP_BASED+8,
+ LOC_EBP_BASED+12,
+ LOC_EBP_BASED+16,
+ LOC_EBP_BASED+20,
+ LOC_EBP_BASED+24,
+ LOC_EBP_BASED+28)) == expected
+
def test_find_functions():
source = """\
\t.p2align 4,,15
@@ -46,7 +75,7 @@
yield check_computegcmaptable, path
r_globallabel = re.compile(r"([\w]+)[:]")
-r_expected = re.compile(r"\s*;;\s*expected\s*([(][-\d\s,+]+[)])")
+r_expected = re.compile(r"\s*;;\s*expected\s+([{].+[}])")
def check_computegcmaptable(path):
print
@@ -54,26 +83,24 @@
lines = path.readlines()
expectedlines = lines[:]
tracker = FunctionGcRootTracker(lines)
- tracker.is_main = tracker.funcname == 'main'
table = tracker.computegcmaptable(verbose=sys.maxint)
tabledict = {}
seen = {}
for entry in table:
- print entry
+ print '%s: %s' % (entry[0], format_callshape(entry[1]))
tabledict[entry[0]] = entry[1]
# find the ";; expected" lines
prevline = ""
for i, line in enumerate(lines):
match = r_expected.match(line)
if match:
- expected = eval(match.group(1))
- assert isinstance(expected, tuple)
+ expected = match.group(1)
prevmatch = r_globallabel.match(prevline)
assert prevmatch, "the computed table is not complete"
label = prevmatch.group(1)
assert label in tabledict
got = tabledict[label]
- assert got == expected
+ assert format_callshape(got) == expected
seen[label] = True
expectedlines.insert(i-2, '\t.globl\t%s\n' % (label,))
expectedlines.insert(i-1, '%s:\n' % (label,))
Modified: pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track0.s
==============================================================================
--- pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track0.s (original)
+++ pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track0.s Mon Feb 4 10:51:34 2008
@@ -14,7 +14,7 @@
movl %ebp, %ebx
movl $pypy_g_array_16, (%esp)
call open
- ;; expected (32, -16, -12, -8, -4)
+ ;; expected {28(%esp) | 12(%esp), 16(%esp), 20(%esp), 24(%esp) | }
cmpl $-1, %eax
movl %eax, %edi
movl 32(%esp), %esi
@@ -26,7 +26,7 @@
movl %esi, 4(%esp)
movl %edi, (%esp)
call read
- ;; expected (32, -16, -12, -8, -4)
+ ;; expected {28(%esp) | 12(%esp), 16(%esp), 20(%esp), 24(%esp) | }
testl %eax, %eax
jle .L280
.L285:
@@ -46,13 +46,13 @@
movl %eax, 8(%esp)
movl %edi, (%esp)
call read
- ;; expected (32, -16, -12, -8, -4)
+ ;; expected {28(%esp) | 12(%esp), 16(%esp), 20(%esp), 24(%esp) | }
testl %eax, %eax
jg .L288
.L280:
movl %edi, (%esp)
call close
- ;; expected (32, -16, -12, -8, -4)
+ ;; expected {28(%esp) | 12(%esp), 16(%esp), 20(%esp), 24(%esp) | }
movl %ebx, %eax
.L286:
.L274:
@@ -65,7 +65,7 @@
movl %ecx, 4(%esp)
movl %esi, (%esp)
call memset
- ;; expected (32, -16, -12, -8, -4)
+ ;; expected {28(%esp) | 12(%esp), 16(%esp), 20(%esp), 24(%esp) | }
.L270:
addl $12, %esp
popl %ebx
Modified: pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track1.s
==============================================================================
--- pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track1.s (original)
+++ pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track1.s Mon Feb 4 10:51:34 2008
@@ -14,7 +14,7 @@
movl $0, 4(%esp)
movl $pypy_g_array_16, (%esp)
call open
- ;; expected (80, 1, 3, 5, -4)
+ ;; expected {4(%ebp) | %ebx, %esi, %edi, (%ebp) | }
movl %eax, -12(%ebp)
.loc 2 1522 0
cmpl $-1, -12(%ebp)
@@ -55,7 +55,7 @@
movl -8(%ebp), %eax
movl %eax, (%esp)
call memset
- ;; expected (80, 1, 3, 5, -4)
+ ;; expected {4(%ebp) | %ebx, %esi, %edi, (%ebp) | }
.loc 2 1545 0
jmp .L542
.L545:
@@ -73,7 +73,7 @@
movl -12(%ebp), %eax
movl %eax, (%esp)
call close
- ;; expected (80, 1, 3, 5, -4)
+ ;; expected {4(%ebp) | %ebx, %esi, %edi, (%ebp) | }
movl %eax, -36(%ebp)
.loc 2 1556 0
movl -16(%ebp), %eax
@@ -111,7 +111,7 @@
movl -12(%ebp), %eax
movl %eax, (%esp)
call read
- ;; expected (80, 1, 3, 5, -4)
+ ;; expected {4(%ebp) | %ebx, %esi, %edi, (%ebp) | }
movl %eax, -48(%ebp)
.loc 2 1575 0
movl -48(%ebp), %eax
Modified: pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track2.s
==============================================================================
--- pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track2.s (original)
+++ pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track2.s Mon Feb 4 10:51:34 2008
@@ -54,7 +54,7 @@
movl %edx, 4(%esp)
movl %esi, (%esp)
call pypy_g_populate
- ;; expected (32, -8, -4, 5, 7, 1)
+ ;; expected {28(%esp) | 20(%esp), 24(%esp), %edi, %ebp | %ebx}
movl %ebx, %eax
#APP
/* GCROOT %eax */
@@ -73,7 +73,7 @@
.L1351:
.L1352:
call LL_stack_too_big
- ;; expected (32, -8, -4, 5, 7, 1)
+ ;; expected {28(%esp) | 20(%esp), 24(%esp), %edi, %ebp | %ebx}
testl %eax, %eax
jne .L1418
.L1361:
@@ -100,7 +100,7 @@
movl $24, %edx
movl %edx, 4(%esp)
call pypy_g_SemiSpaceGC_try_obtain_free_space
- ;; expected (32, -8, -4, 5, 7, 1)
+ ;; expected {28(%esp) | 20(%esp), 24(%esp), %edi, %ebp | %ebx}
movl pypy_g_ExcData, %edx
xorl %ecx, %ecx
testl %edx, %edx
@@ -121,7 +121,7 @@
movl $24, %ecx
movl %ecx, 4(%esp)
call pypy_g_SemiSpaceGC_try_obtain_free_space
- ;; expected (32, -8, -4, 5, 7, 1)
+ ;; expected {28(%esp) | 20(%esp), 24(%esp), %edi, %ebp | %ebx}
movl pypy_g_ExcData, %edx
xorl %ecx, %ecx
testl %edx, %edx
Modified: pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track3.s
==============================================================================
--- pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track3.s (original)
+++ pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track3.s Mon Feb 4 10:51:34 2008
@@ -56,7 +56,7 @@
movl %edx, 4(%esp)
movl %esi, (%esp)
call pypy_g_populate
- ;; expected (32, -12, -8, 5, -4, 1)
+ ;; expected {4(%ebp) | -8(%ebp), -4(%ebp), %edi, (%ebp) | %ebx}
movl %ebx, %eax
#APP
/* GCROOT %eax */
@@ -75,7 +75,7 @@
.L1350:
.L1351:
call LL_stack_too_big
- ;; expected (32, -12, -8, 5, -4, 1)
+ ;; expected {4(%ebp) | -8(%ebp), -4(%ebp), %edi, (%ebp) | %ebx}
testl %eax, %eax
jne .L1417
.L1360:
@@ -102,7 +102,7 @@
movl $24, %edx
movl %edx, 4(%esp)
call pypy_g_SemiSpaceGC_try_obtain_free_space
- ;; expected (32, -12, -8, 5, -4, 1)
+ ;; expected {4(%ebp) | -8(%ebp), -4(%ebp), %edi, (%ebp) | %ebx}
movl pypy_g_ExcData, %edx
xorl %ecx, %ecx
testl %edx, %edx
@@ -123,7 +123,7 @@
movl $24, %ecx
movl %ecx, 4(%esp)
call pypy_g_SemiSpaceGC_try_obtain_free_space
- ;; expected (32, -12, -8, 5, -4, 1)
+ ;; expected {4(%ebp) | -8(%ebp), -4(%ebp), %edi, (%ebp) | %ebx}
movl pypy_g_ExcData, %edx
xorl %ecx, %ecx
testl %edx, %edx
Modified: pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track4.s
==============================================================================
--- pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track4.s (original)
+++ pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track4.s Mon Feb 4 10:51:34 2008
@@ -7,11 +7,11 @@
movl %esp, %ebp
pushl %edi
subl $8, %esp
- andl $-15, %esp
+ andl $-16, %esp
movl %ebx, -8(%ebp)
movl 8(%ebp), %edi
call foobar
- ;; expected (20, -12, 3, -8, -4, 5)
+ ;; expected {4(%ebp) | -8(%ebp), %esi, -4(%ebp), (%ebp) | %edi}
.L1:
cmpl $0, %eax
je .L3
@@ -26,7 +26,7 @@
movl %esi, %ebx
movl $nonsense, %esi
call foobar
- ;; expected (36, -12, 1, -8, -4, -28, -16)
+ ;; expected {4(%ebp) | -8(%ebp), %ebx, -4(%ebp), (%ebp) | -12(%ebp), 4(%esp)}
addl %edi, %eax
movl 4(%esp), %eax
movl %ebx, %esi
@@ -39,7 +39,7 @@
;; end of inlined function
.L3:
call foobar
- ;; expected (20, -12, 3, -8, -4, 5)
+ ;; expected {4(%ebp) | -8(%ebp), %esi, -4(%ebp), (%ebp) | %edi}
#APP
/* GCROOT %edi */
#NO_APP
Modified: pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track5.s
==============================================================================
--- pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track5.s (original)
+++ pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track5.s Mon Feb 4 10:51:34 2008
@@ -15,11 +15,11 @@
movl %esi, 8(%esp)
movl %esi, (%esp)
call pypy_g_trace___trace_copy
- ;; expected (32, -8, -4, 5, 7)
+ ;; expected {28(%esp) | 20(%esp), 24(%esp), %edi, %ebp | }
movl %ebx, 4(%esp)
movl %esi, (%esp)
call pypy_g_SemiSpaceGC_get_size
- ;; expected (32, -8, -4, 5, 7)
+ ;; expected {28(%esp) | 20(%esp), 24(%esp), %edi, %ebp | }
addl %eax, %ebx
.L1216:
cmpl 12(%esi), %ebx
@@ -36,15 +36,15 @@
movl %esi, 8(%esp)
movl %esi, (%esp)
call pypy_g_trace___trace_copy
- ;; expected (32, -8, -4, 5, 7)
+ ;; expected {28(%esp) | 20(%esp), 24(%esp), %edi, %ebp | }
movl %ebx, 4(%esp)
movl %esi, (%esp)
call pypy_g_SemiSpaceGC_get_size
- ;; expected (32, -8, -4, 5, 7)
+ ;; expected {28(%esp) | 20(%esp), 24(%esp), %edi, %ebp | }
addl %eax, %ebx
.L1227:
call RPyAbort
- ;; expected (32, -8, -4, 5, 7)
+ ;; expected {28(%esp) | 20(%esp), 24(%esp), %edi, %ebp | }
cmpl 12(%esi), %ebx
jb .L1229
addl $20, %esp
Added: pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track6.s
==============================================================================
--- (empty file)
+++ pypy/branch/asmgcroot/pypy/translator/c/gcc/test/track6.s Mon Feb 4 10:51:34 2008
@@ -0,0 +1,26 @@
+ .type main, @function
+main:
+ ;; a minimal example showing what kind of code gcc
+ ;; can produce for main(): some local variable accesses
+ ;; are relative to %ebp, while others are relative to
+ ;; %esp, and the difference %ebp-%esp is not constant
+ ;; because of the 'andl' to align the stack
+ pushl %ebp
+ movl %esp, %ebp
+ subl $8, %esp
+ andl $-16, %esp
+ movl $globalptr1, -4(%ebp)
+ movl $globalptr2, (%esp)
+ pushl $0
+ call foobar
+ ;; expected {4(%ebp) | %ebx, %esi, %edi, (%ebp) | -4(%ebp), 4(%esp)}
+ popl %eax
+#APP
+ /* GCROOT -4(%ebp) */
+ /* GCROOT (%esp) */
+#NO_APP
+ movl %ebp, %esp
+ popl %ebp
+ ret
+
+ .size main, .-main
Modified: pypy/branch/asmgcroot/pypy/translator/c/gcc/trackgcroot.py
==============================================================================
--- pypy/branch/asmgcroot/pypy/translator/c/gcc/trackgcroot.py (original)
+++ pypy/branch/asmgcroot/pypy/translator/c/gcc/trackgcroot.py Mon Feb 4 10:51:34 2008
@@ -30,6 +30,7 @@
self.gcmaptable = []
self.verbose = verbose
self.seen_main = False
+ self.files_seen = 0
def dump(self, output):
assert self.seen_main
@@ -58,6 +59,11 @@
ret
.size pypy_asm_stackwalk_init, .-pypy_asm_stackwalk_init
"""
+ # XXX the two gcmap tables are a bit largish. They could easily
+ # be compressed by a factor of 4 or more. I suspect they also
+ # produce large linker tables which could be seriously reduced
+ # as well. A key observation is that in practice most functions
+ # seem to use exactly the same call shape for each call they contain.
print >> output, '\t.data'
print >> output, '\t.align\t4'
print >> output, '\t.globl\t__gcmapstart'
@@ -113,48 +119,37 @@
if in_function:
lines = self.process_function(lines, entrypoint, filename)
newfile.writelines(lines)
+ self.files_seen += 1
def process_function(self, lines, entrypoint, filename):
- tracker = FunctionGcRootTracker(lines)
- tracker.is_main = tracker.funcname == entrypoint
+ tracker = FunctionGcRootTracker(lines, filetag = self.files_seen)
if self.verbose:
print >> sys.stderr, '[trackgcroot:%s] %s' % (filename,
tracker.funcname)
table = tracker.computegcmaptable(self.verbose)
if self.verbose > 1:
for label, state in table:
- print >> sys.stderr, label, '\t', state
- if tracker.is_main:
- fp = tracker.uses_frame_pointer
- table = self.fixup_entrypoint_table(table, fp)
+ print >> sys.stderr, label, '\t', format_callshape(state)
+ if tracker.funcname == entrypoint:
+ table = self.fixup_entrypoint_table(table)
self.gcmaptable.extend(table)
return tracker.lines
- def fixup_entrypoint_table(self, table, uses_frame_pointer):
+ def fixup_entrypoint_table(self, table):
self.seen_main = True
- # as an end marker, set the CALLEE_SAVE_REGISTERS locations to -1.
- # this info is not useful because we don't go to main()'s caller.
+ # as an end marker, set all five initial entries of the callshapes
+ # to LOC_NOWHERE. This info is not useful anyway because we don't
+ # go to main()'s caller.
newtable = []
- MARKERS = (-1, -1, -1, -1)
+ MARKERS = (LOC_NOWHERE,) * 5
for label, shape in table:
- newtable.append((label, shape[:1] + MARKERS + shape[5:]))
- table = newtable
-
- if uses_frame_pointer:
- # the main() function may contain strange code that aligns the
- # stack pointer to a multiple of 16, which messes up our framesize
- # computation. So just for this function, we use a frame size
- # of 0 to ask asmgcroot to read %ebp to find the frame pointer.
- newtable = []
- for label, shape in table:
- newtable.append((label, (0,) + shape[1:]))
- table = newtable
- return table
+ newtable.append((label, MARKERS + shape[5:]))
+ return newtable
class FunctionGcRootTracker(object):
- def __init__(self, lines):
+ def __init__(self, lines, filetag=0):
match = r_functionstart.match(lines[0])
self.funcname = match.group(1)
match = r_functionend.match(lines[-1])
@@ -163,7 +158,7 @@
self.lines = lines
self.uses_frame_pointer = False
self.r_localvar = r_localvarnofp
- self.is_main = False
+ self.filetag = filetag
def computegcmaptable(self, verbose=0):
self.findlabels()
@@ -182,26 +177,33 @@
return self.gettable()
def gettable(self):
- """Returns a list [(label_after_call, shape_tuple)]
- where shape_tuple = (framesize, where_is_ebx_saved, ...
- ..., where_is_ebp_saved, gcroot0, gcroot1...)
+ """Returns a list [(label_after_call, callshape_tuple)]
+ See format_callshape() for more details about callshape_tuple.
"""
table = []
for insn in self.list_call_insns():
if not hasattr(insn, 'framesize'):
continue # calls that never end up reaching a RET
- shape = [insn.framesize + 4] # accounts for the return address
+ if self.uses_frame_pointer:
+ retaddr = frameloc(LOC_EBP_BASED, 4)
+ else:
+ retaddr = frameloc(LOC_ESP_BASED, insn.framesize)
+ shape = [retaddr]
# the first gcroots are always the ones corresponding to
# the callee-saved registers
for reg in CALLEE_SAVE_REGISTERS:
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
+ gcroots = []
+ for localvar, tag in insn.gcroots.items():
+ if isinstance(localvar, LocalVar):
+ loc = localvar.getlocation(insn.framesize,
+ self.uses_frame_pointer)
+ else:
+ assert localvar in REG2LOC
+ loc = REG2LOC[localvar]
+ assert isinstance(loc, int)
if tag is None:
- shape.append(loc)
+ gcroots.append(loc)
else:
regindex = CALLEE_SAVE_REGISTERS.index(tag)
shape[1 + regindex] = loc
@@ -209,6 +211,8 @@
reg = CALLEE_SAVE_REGISTERS[shape.index(None) - 1]
raise AssertionError("cannot track where register %s is saved"
% (reg,))
+ gcroots.sort()
+ shape.extend(gcroots)
table.append((insn.global_label, tuple(shape)))
return table
@@ -281,6 +285,11 @@
return [insn for insn in self.insns if isinstance(insn, InsnCall)]
def findframesize(self):
+ # the 'framesize' attached to an instruction is the number of bytes
+ # in the frame at this point. This doesn't count the return address
+ # which is the word immediately following the frame in memory.
+ # The 'framesize' is set to an odd value if it is only an estimate
+ # (see visit_andl()).
def walker(insn, size_delta):
check = deltas.setdefault(insn, size_delta)
@@ -318,17 +327,21 @@
localvar = getattr(insn, name)
match = r_localvar_esp.match(localvar)
if match:
+ if localvar == '0(%esp)': # for pushl and popl, by
+ hint = None # default ebp addressing is
+ else: # a bit nicer
+ hint = 'esp'
ofs_from_esp = int(match.group(1) or '0')
localvar = ofs_from_esp - insn.framesize
assert localvar != 0 # that's the return address
- setattr(insn, name, localvar)
+ setattr(insn, name, LocalVar(localvar, hint=hint))
elif self.uses_frame_pointer:
match = r_localvar_ebp.match(localvar)
if match:
ofs_from_ebp = int(match.group(1) or '0')
localvar = ofs_from_ebp - 4
assert localvar != 0 # that's the return address
- setattr(insn, name, localvar)
+ setattr(insn, name, LocalVar(localvar, hint='ebp'))
def trackgcroots(self):
@@ -346,7 +359,7 @@
def dump(self):
for insn in self.insns:
size = getattr(insn, 'framesize', '?')
- print '%4s %s' % (size, insn)
+ print >> sys.stderr, '%4s %s' % (size, insn)
def walk_instructions_backwards(self, walker, initial_insn, initial_state):
pending = []
@@ -388,7 +401,7 @@
if label is None:
k = call.lineno
while 1:
- label = '__gcmap_IN_%s_%d' % (self.funcname, k)
+ label = '__gcmap_IN%d_%s_%d' % (self.filetag, self.funcname, k)
if label not in self.labels:
break
k += 1
@@ -433,7 +446,9 @@
target = match.group(2)
if target == '%esp':
count = match.group(1)
- assert count.startswith('$')
+ if not count.startswith('$'):
+ # strange instruction - I've seen 'subl %eax, %esp'
+ return InsnCannotFollowEsp()
return InsnStackAdjust(sign * int(count[1:]))
elif self.r_localvar.match(target):
return InsnSetLocal(target)
@@ -471,15 +486,10 @@
target = match.group(2)
if target == '%esp':
# only for andl $-16, %esp used to align the stack in main().
- # If gcc compiled main() with a frame pointer, then it should use
- # %ebp-relative addressing and not %esp-relative addressing
- # and asmgcroot will read %ebp to find the frame. If main()
- # is compiled without a frame pointer, the total frame size that
- # we compute ends up being bogus but that's ok because gcc has
- # to use %esp-relative addressing only and we don't need to walk
- # to caller frames because it's main().
- assert self.is_main
- return []
+ # The exact amount of adjutment is not known yet, so we use
+ # an odd-valued estimate to make sure the real value is not used
+ # elsewhere by the FunctionGcRootTracker.
+ return InsnCannotFollowEsp()
else:
return self.binary_insn(line)
@@ -493,8 +503,10 @@
if not match:
framesize = None # strange instruction
else:
+ if not self.uses_frame_pointer:
+ raise UnrecognizedOperation('epilogue without prologue')
ofs_from_ebp = int(match.group(1) or '0')
- assert ofs_from_ebp < 0
+ assert ofs_from_ebp <= 0
framesize = 4 - ofs_from_ebp
return InsnEpilogue(framesize)
else:
@@ -524,10 +536,10 @@
def visit_pushl(self, line):
match = r_unaryinsn.match(line)
source = match.group(1)
- return [InsnStackAdjust(-4)] + self.insns_for_copy(source, '(%esp)')
+ return [InsnStackAdjust(-4)] + self.insns_for_copy(source, '0(%esp)')
def _visit_pop(self, target):
- return self.insns_for_copy('(%esp)', target) + [InsnStackAdjust(+4)]
+ return self.insns_for_copy('0(%esp)', target) + [InsnStackAdjust(+4)]
def visit_popl(self, line):
match = r_unaryinsn.match(line)
@@ -629,6 +641,39 @@
pass
somenewvalue = SomeNewValue()
+class LocalVar(object):
+ # A local variable location at position 'ofs_from_frame_end',
+ # which is counted from the end of the stack frame (so it is always
+ # negative, unless it refers to arguments of the current function).
+ def __init__(self, ofs_from_frame_end, hint=None):
+ self.ofs_from_frame_end = ofs_from_frame_end
+ self.hint = hint
+
+ def __repr__(self):
+ return '<%+d;%s>' % (self.ofs_from_frame_end, self.hint or 'e*p')
+
+ def __hash__(self):
+ return hash(self.ofs_from_frame_end)
+
+ def __cmp__(self, other):
+ if isinstance(other, LocalVar):
+ return cmp(self.ofs_from_frame_end, other.ofs_from_frame_end)
+ else:
+ return 1
+
+ def getlocation(self, framesize, uses_frame_pointer):
+ if (self.hint == 'esp' or not uses_frame_pointer
+ or self.ofs_from_frame_end % 2 != 0):
+ # try to use esp-relative addressing
+ ofs_from_esp = framesize + self.ofs_from_frame_end
+ if ofs_from_esp % 2 == 0:
+ return frameloc(LOC_ESP_BASED, ofs_from_esp)
+ # we can get an odd value if the framesize is marked as bogus
+ # by visit_andl()
+ assert uses_frame_pointer
+ ofs_from_ebp = self.ofs_from_frame_end + 4
+ return frameloc(LOC_EBP_BASED, ofs_from_ebp)
+
class Insn(object):
_args_ = []
@@ -659,7 +704,8 @@
self.arguments[reg] = somenewvalue
def source_of(self, localvar, tag):
if localvar not in self.arguments:
- assert isinstance(localvar, int) and localvar > 0, (
+ assert (isinstance(localvar, LocalVar) and
+ localvar.ofs_from_frame_end > 0), (
"must come from an argument to the function, got %r" %
(localvar,))
self.arguments[localvar] = somenewvalue
@@ -692,6 +738,10 @@
assert delta % 4 == 0
self.delta = delta
+class InsnCannotFollowEsp(InsnStackAdjust):
+ def __init__(self):
+ self.delta = 7 # use an odd value as marker
+
class InsnStop(Insn):
pass
@@ -704,30 +754,34 @@
_args_ = ['lineno', 'gcroots']
def __init__(self, lineno):
# 'gcroots' is a dict built by side-effect during the call to
- # FunctionGcRootTracker.trackgcroots(). Its meaning is as follows:
- # the keys are the location that contain gc roots (either register
- # names like '%esi', or negative integer offsets relative to the end
- # of the function frame). The value corresponding to a key is the
- # "tag", which is None for a normal gc root, or else the name of a
- # callee-saved register. In the latter case it means that this is
- # only a gc root if the corresponding register in the caller was
- # really containing a gc pointer. A typical example:
+ # FunctionGcRootTracker.trackgcroots(). Its meaning is as
+ # follows: the keys are the locations that contain gc roots
+ # (register names or LocalVar instances). The value
+ # corresponding to a key is the "tag", which is None for a
+ # normal gc root, or else the name of a callee-saved register.
+ # In the latter case it means that this is only a gc root if the
+ # corresponding register in the caller was really containing a
+ # gc pointer. A typical example:
#
- # InsnCall({'%ebp': '%ebp', -8: '%ebx', '%esi': None})
- #
- # means that %esi is a gc root across this call; that %ebp is a
- # gc root if it was in the caller (typically because %ebp is not
- # modified at all in the current function); and that the word at 8
- # bytes before the end of the current stack frame is a gc root if
- # %ebx was a gc root in the caller (typically because the current
- # function saves and restores %ebx from there in the prologue and
- # epilogue).
+ # InsnCall({LocalVar(-8)': None,
+ # '%esi': '%esi',
+ # LocalVar(-12)': '%ebx'})
#
+ # means that the value at -8 from the frame end is a gc root
+ # across this call; that %esi is a gc root if it was in the
+ # caller (typically because %esi is not modified at all in the
+ # current function); and that the value at -12 from the frame
+ # end is a gc root if %ebx was a gc root in the caller
+ # (typically because the current function saves and restores
+ # %ebx from there in the prologue and epilogue).
self.gcroots = {}
self.lineno = lineno
def source_of(self, localvar, tag):
- self.gcroots[localvar] = tag
+ tag1 = self.gcroots.setdefault(localvar, tag)
+ assert tag1 == tag, (
+ "conflicting entries for InsnCall.gcroots[%s]:\n%r and %r" % (
+ localvar, tag1, tag))
return localvar
class InsnGCROOT(Insn):
@@ -760,6 +814,66 @@
CALLEE_SAVE_REGISTERS_NOEBP = ['%ebx', '%esi', '%edi']
CALLEE_SAVE_REGISTERS = CALLEE_SAVE_REGISTERS_NOEBP + ['%ebp']
+LOC_NOWHERE = 0
+LOC_REG = 1
+LOC_EBP_BASED = 2
+LOC_ESP_BASED = 3
+LOC_MASK = 0x03
+
+REG2LOC = {}
+for _i, _reg in enumerate(CALLEE_SAVE_REGISTERS):
+ REG2LOC[_reg] = LOC_REG | (_i<<2)
+
+def frameloc(base, offset):
+ assert base in (LOC_EBP_BASED, LOC_ESP_BASED)
+ assert offset % 4 == 0
+ return base | offset
+
+# __________ debugging output __________
+
+def format_location(loc):
+ # A 'location' is a single number describing where a value is stored
+ # across a call. It can be in one of the CALLEE_SAVE_REGISTERS, or
+ # in the stack frame at an address relative to either %esp or %ebp.
+ # The last two bits of the location number are used to tell the cases
+ # apart; see format_location().
+ kind = loc & LOC_MASK
+ if kind == LOC_NOWHERE:
+ return '?'
+ elif kind == LOC_REG:
+ reg = loc >> 2
+ assert 0 <= reg <= 3
+ return CALLEE_SAVE_REGISTERS[reg]
+ else:
+ if kind == LOC_EBP_BASED:
+ result = '(%ebp)'
+ else:
+ result = '(%esp)'
+ offset = loc & ~ LOC_MASK
+ if offset != 0:
+ result = str(offset) + result
+ return result
+
+def format_callshape(shape):
+ # A 'call shape' is a tuple of locations in the sense of format_location().
+ # They describe where in a function frame interesting values are stored,
+ # when this function executes a 'call' instruction.
+ #
+ # shape[0] is the location that stores the fn's own return address
+ # (not the return address for the currently executing 'call')
+ # shape[1] is where the fn saved its own caller's %ebx value
+ # shape[2] is where the fn saved its own caller's %esi value
+ # shape[3] is where the fn saved its own caller's %edi value
+ # shape[4] is where the fn saved its own caller's %ebp value
+ # shape[>=5] are GC roots: where the fn has put its local GCPTR vars
+ #
+ assert isinstance(shape, tuple)
+ assert len(shape) >= 5
+ result = [format_location(loc) for loc in shape]
+ return '{%s | %s | %s}' % (result[0],
+ ', '.join(result[1:5]),
+ ', '.join(result[5:]))
+
if __name__ == '__main__':
if sys.argv and sys.argv[1] == '-v':
More information about the Pypy-commit
mailing list