[pypy-svn] r50141 - in pypy/branch/llvmgcroot/pypy: rpython/memory/gctransform translator/c translator/c/src

arigo at codespeak.net arigo at codespeak.net
Thu Dec 27 18:12:07 CET 2007


Author: arigo
Date: Thu Dec 27 18:12:05 2007
New Revision: 50141

Modified:
   pypy/branch/llvmgcroot/pypy/rpython/memory/gctransform/llvmgcroot.py
   pypy/branch/llvmgcroot/pypy/translator/c/genc.py
   pypy/branch/llvmgcroot/pypy/translator/c/src/mem.h
   pypy/branch/llvmgcroot/pypy/translator/c/trackgcroot.py
Log:
targetgcbench works now via genc using the --llvmgcroot logic.
Only for 386-compatible Linux boxes.

To try it out, use

    translate.py --llvmgcroot --backend=c --gc=xxx --text targetxxx

and use the generated Makefile to process the result.


Modified: pypy/branch/llvmgcroot/pypy/rpython/memory/gctransform/llvmgcroot.py
==============================================================================
--- pypy/branch/llvmgcroot/pypy/rpython/memory/gctransform/llvmgcroot.py	(original)
+++ pypy/branch/llvmgcroot/pypy/rpython/memory/gctransform/llvmgcroot.py	Thu Dec 27 18:12:05 2007
@@ -69,6 +69,15 @@
             def __init__(self, with_static=True):
                 self.stack_current = llop.llvm_frameaddress(llmemory.Address)
                 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.
+                # 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:
+                    if not self.walk_to_parent_frame():
+                        break     # not a single GC root? unlikely but not
+                                  # impossible I guess
                 if with_static:
                     self.static_current = gcdata.static_root_end
                 else:

Modified: pypy/branch/llvmgcroot/pypy/translator/c/genc.py
==============================================================================
--- pypy/branch/llvmgcroot/pypy/translator/c/genc.py	(original)
+++ pypy/branch/llvmgcroot/pypy/translator/c/genc.py	Thu Dec 27 18:12:05 2007
@@ -1,6 +1,6 @@
 import autopath
 import py
-import sys
+import sys, os
 from pypy.translator.c.node import PyObjectNode, FuncNode
 from pypy.translator.c.database import LowLevelDatabase
 from pypy.translator.c.extfunc import pre_include_code_lines
@@ -275,6 +275,10 @@
             self.eci, compiler_exe = cc, profbased = profbased)
 
     def compile(self):
+        if self.config.translation.llvmgcroot:
+            raise Exception("Dunno how to compile with --llvmgcroot. "
+                            "Just go to the %s directory and type 'make'."
+                            % (self.targetdir,))
         assert self.c_source_filename
         assert not self._compiled
         eci = self.eci.merge(ExternalCompilationInfo(includes=
@@ -344,6 +348,9 @@
         print >> f
         write_list(sfiles, 'ASMFILES =')
         print >> f
+        print >> f, 'TRACKGCROOT = "%s"' % (os.path.join(autopath.this_dir,
+                                                         'trackgcroot.py'),)
+        print >> f
         args = ['-l'+libname for libname in self.eci.libraries]
         print >> f, 'LIBS =', ' '.join(args)
         args = ['-L'+path for path in self.eci.library_dirs]
@@ -773,14 +780,17 @@
 
 MAKEFILE = '''
 
-$(TARGET): $(ASMFILES)
-\t$(CC) $(LDFLAGS) $(TFLAGS) -o $@ $(ASMFILES) $(LIBDIRS) $(LIBS)
+$(TARGET): $(ASMFILES) gcmaptable.s
+\t$(CC) $(LDFLAGS) $(TFLAGS) -o $@ $(ASMFILES) gcmaptable.s $(LIBDIRS) $(LIBS)
 
 %.s: %.c
 \t$(CC) $(CFLAGS) -o $@ -S $< $(INCLUDEDIRS)
 
+gcmaptable.s: $(ASMFILES)
+\t$(TRACKGCROOT) $(ASMFILES) > $@ || (rm -f $@ && exit 1)
+
 clean:
-\trm -f $(ASMFILES) $(TARGET)
+\trm -f $(ASMFILES) gcmaptable.s $(TARGET)
 
 debug:
 \t$(MAKE) CFLAGS="-g -DRPY_ASSERT"
@@ -803,6 +813,6 @@
 profopt:
 \t$(MAKE) CFLAGS="-fprofile-generate $(CFLAGS)" LDFLAGS="-fprofile-generate $(LDFLAGS)"
 \t./$(TARGET) $(PROFOPT)
-\trm -f $(ASMFILES) $(TARGET)
+\trm -f $(ASMFILES) gcmaptable.s $(TARGET)
 \t$(MAKE) CFLAGS="-fprofile-use $(CFLAGS)" LDFLAGS="-fprofile-use $(LDFLAGS)"
 '''

Modified: pypy/branch/llvmgcroot/pypy/translator/c/src/mem.h
==============================================================================
--- pypy/branch/llvmgcroot/pypy/translator/c/src/mem.h	(original)
+++ pypy/branch/llvmgcroot/pypy/translator/c/src/mem.h	Thu Dec 27 18:12:05 2007
@@ -8,6 +8,17 @@
 #define ROUND_UP_FOR_ALLOCATION(x)	\
 		(((x) + (MEMORY_ALIGNMENT-1)) & ~(MEMORY_ALIGNMENT-1))
 
+extern char __gcmapstart;
+extern char __gcmapend;
+extern char* __gcmap_frame_address(void);
+
+#define OP_LLVM_GCMAPSTART(r)	r = &__gcmapstart
+#define OP_LLVM_GCMAPEND(r)	r = &__gcmapend
+#define OP_LLVM_FRAMEADDRESS(r)	asm ("pypygetframeaddress %0" : "=r" (r))
+/* NB. we cannot use __builtin_frame_address(0) - apparently, gcc thinks
+   it can return %ebp even if -fomit-frame-pointer is specified, which
+   doesn't work.  So we need a bit of help from trackgcroot.py... */
+
 
 #define RAW_MALLOC_ZERO_FILLED 0
 

Modified: pypy/branch/llvmgcroot/pypy/translator/c/trackgcroot.py
==============================================================================
--- pypy/branch/llvmgcroot/pypy/translator/c/trackgcroot.py	(original)
+++ pypy/branch/llvmgcroot/pypy/translator/c/trackgcroot.py	Thu Dec 27 18:12:05 2007
@@ -5,10 +5,12 @@
 r_functionstart = re.compile(r"\t.type\s+(\w+),\s*[@]function\s*$")
 r_functionend   = re.compile(r"\t.size\s+\w+,\s*[.]-\w+\s*$")
 r_label         = re.compile(r"([.]?\w+)[:]\s*$")
+r_globl         = re.compile(r"\t[.]globl\t(\w+)\s*$")
 r_insn          = re.compile(r"\t([a-z]\w*)\s")
 r_jump          = re.compile(r"\tj\w+\s+([.]?\w+)\s*$")
-OPERAND         =            r"[\w$%-+]+(?:[(][\w%,]+[)])?|[(][\w%,]+[)]"
+OPERAND         =            r"[-\w$%+.:@]+(?:[(][\w%,]+[)])?|[(][\w%,]+[)]"
 r_unaryinsn     = re.compile(r"\t[a-z]\w*\s+("+OPERAND+")\s*$")
+r_unaryinsn_star= re.compile(r"\t[a-z]\w*\s+([*]"+OPERAND+")\s*$")
 r_binaryinsn    = re.compile(r"\t[a-z]\w*\s+("+OPERAND+"),\s*("+OPERAND+")\s*$")
 r_gcroot_marker = re.compile(r"\t/[*](STORE|LOAD) GCROOT ")
 r_gcroot_op     = re.compile(r"\t/[*](STORE|LOAD) GCROOT (\d*)[(]%esp[)][*]/\s*$")
@@ -28,17 +30,22 @@
         shapes = {}
         print >> output, '\t.data'
         print >> output, '\t.align\t4'
+        print >> output, '\t.globl\t__gcmapstart'
         print >> output, '__gcmapstart:'
         for label, state in self.gcmaptable:
             if state not in shapes:
                 lst = ['__gcmap_shape']
-                lst.extend(map(str, state))
+                for n in state:
+                    if n < 0:
+                        n = 'minus%d' % (-n,)
+                    lst.append(str(n))
                 shapes[state] = '_'.join(lst)
             print >> output, '\t.long\t%s' % (label,)
             print >> output, '\t.long\t%s' % (shapes[state],)
+        print >> output, '\t.globl\t__gcmapend'
         print >> output, '__gcmapend:'
-        print >> output, '\t.section\trodata'
-        print >> output, '\t.align\t4'
+        #print >> output, '\t.section\trodata'
+        #print >> output, '\t.align\t4'
         keys = shapes.keys()
         keys.sort()
         for state in keys:
@@ -48,7 +55,7 @@
             for p in state[1:]:
                 print >> output, '\t.long\t%d' % (p,)         # gcroots
 
-    def process(self, iterlines, newfile):
+    def process(self, iterlines, newfile, entrypoint='main', filename='?'):
         functionlines = None
         for line in iterlines:
             if r_functionstart.match(line):
@@ -62,17 +69,29 @@
             if r_functionend.match(line):
                 assert functionlines is not None, (
                     "missed the start of the current function")
-                self.process_function(functionlines, newfile)
+                self.process_function(functionlines, newfile, entrypoint,
+                                      filename)
                 functionlines = None
 
-    def process_function(self, lines, newfile):
+    def process_function(self, lines, newfile, entrypoint, filename):
         tracker = FunctionGcRootTracker(lines)
         if self.verbose:
-            print >> sys.stderr, tracker.funcname
+            print >> sys.stderr, '[trackgcroot:%s] %s' % (filename,
+                                                          tracker.funcname)
         table = tracker.computegcmaptable()
-        if self.verbose:
+        #if self.verbose:
+        #    for label, state in table:
+        #        print >> sys.stderr, label, '\t', state
+        if tracker.funcname == entrypoint:
             for label, state in table:
-                print >> sys.stderr, label, '\t', state
+                assert len(state) == 1, (
+                    "XXX for now the entry point should not have any gc roots")
+            table = [(label, (-1,)) for label, _ in table]
+            # ^^^ we set the framesize of the entry point to -1 as a marker
+        else:
+            assert not tracker.uses_frame_pointer, (
+                "Function %r uses the frame pointer %ebp.  FIXME: implement it"
+                % (tracker.funcname,))
         self.gcmaptable.extend(table)
         newfile.writelines(tracker.lines)
 
@@ -87,6 +106,7 @@
 
     def computegcmaptable(self):
         self.findlabels()
+        self.uses_frame_pointer = False
         self.calls = {}         # {label_after_call: state}
         self.ignore_calls = {}
         self.missing_labels_after_call = []
@@ -116,6 +136,7 @@
         self.missing_labels_after_call.reverse()
         for linenum, label in self.missing_labels_after_call:
             self.lines.insert(linenum, '%s:\n' % (label,))
+            self.lines.insert(linenum, '\t.globl\t%s\n' % (label,))
 
     def follow_control_flow(self):
         # 'states' is a list [(framesize, gcroot0, gcroot1, gcroot2...)]
@@ -212,8 +233,9 @@
     def visit_(self, line):
         # fallback for all operations.  By default, ignore the operation,
         # unless it appears to do something with %esp
-        if r_esp_outside_paren.match(line):
-            raise UnrecognizedOperation(line)
+        if not self.uses_frame_pointer:
+            if r_esp_outside_paren.match(line):
+                raise UnrecognizedOperation(line)
 
     def visit_push(self, line):
         raise UnrecognizedOperation(line)
@@ -247,6 +269,17 @@
             self.framesize -= count
             assert self.framesize >= 0, "stack underflow"
 
+    def visit_movl(self, line):
+        match = r_binaryinsn.match(line)
+        if match.group(1) == '%esp':
+            # only for movl %esp, %ebp
+            if match.group(2) != '%ebp':
+                raise UnrecognizedOperation(line)
+            assert self.framesize == 4      # only %ebp should have been pushed
+            self.uses_frame_pointer = True
+        elif match.group(2) == '%esp':
+            raise UnrecognizedOperation(line)
+
     def visit_ret(self, line):
         raise LeaveBasicBlock
 
@@ -279,21 +312,36 @@
     visit_jae = conditional_jump
     visit_jb = conditional_jump
     visit_jbe = conditional_jump
+    visit_jp = conditional_jump
+    visit_jnp = conditional_jump
+    visit_js = conditional_jump
+    visit_jns = conditional_jump
+    visit_jo = conditional_jump
+    visit_jno = conditional_jump
 
     def visit_call(self, line):
         match = r_unaryinsn.match(line)
-        target = match.group(1)
-        if target == "abort":
-            self.ignore_calls[self.currentlinenum] = None
-            raise LeaveBasicBlock
-        # we need a label just after the call.  Reuse one if it is already
-        # there (e.g. from a previous run of this script); otherwise
-        # invent a name and schedule the line insertion.
-        nextline = self.lines[self.currentlinenum+1]
-        match = r_label.match(nextline)
-        if match and not match.group(1).startswith('.'):
-            label = match.group(1)
+        if match is None:
+            assert r_unaryinsn_star.match(line)   # indirect call
         else:
+            target = match.group(1)
+            if target in FUNCTIONS_NOT_RETURNING:
+                self.ignore_calls[self.currentlinenum] = None
+                raise LeaveBasicBlock
+        # we need a globally-declared label just after the call.
+        # Reuse one if it is already there (e.g. from a previous run of this
+        # script); otherwise invent a name and schedule the line insertion.
+        label = None
+        # this checks for a ".globl NAME" followed by "NAME:"
+        match = r_globl.match(self.lines[self.currentlinenum+1])
+        if match:
+            label1 = match.group(1)
+            match = r_label.match(self.lines[self.currentlinenum+2])
+            if match:
+                label2 = match.group(1)
+                if label1 == label2:
+                    label = label2
+        if label is None:
             k = 0
             while 1:
                 label = '__gcmap_IN_%s_%d' % (self.funcname, k)
@@ -305,6 +353,18 @@
                 (self.currentlinenum+1, label))
         self.calls[self.currentlinenum] = label, self.getstate()
 
+    def visit_pypygetframeaddress(self, line):
+        # this is a pseudo-instruction that is emitted to find the first
+        # frame address on the stack.  We cannot just use
+        # __builtin_frame_address(0) - apparently, gcc thinks it can
+        # return %ebp even if -fomit-frame-pointer is specified, which
+        # doesn't work.
+        match = r_unaryinsn.match(line)
+        reg = match.group(1)
+        newline = '\tleal\t%d(%%esp), %s\t/* pypygetframeaddress */\n' % (
+            self.framesize-4, reg)
+        self.lines[self.currentlinenum] = newline
+
 
 class LeaveBasicBlock(Exception):
     pass
@@ -312,6 +372,11 @@
 class UnrecognizedOperation(Exception):
     pass
 
+FUNCTIONS_NOT_RETURNING = {
+    'abort': None,
+    '__assert_fail': None,
+    }
+
 
 if __name__ == '__main__':
     tracker = GcRootTracker(verbose=True)
@@ -319,7 +384,7 @@
         tmpfn = fn + '.TMP'
         f = open(fn, 'r')
         g = open(tmpfn, 'w')
-        tracker.process(f, g)
+        tracker.process(f, g, filename=fn)
         f.close()
         g.close()
         os.unlink(fn)



More information about the Pypy-commit mailing list