Armin Rigo avatar Armin Rigo committed 31b709e

In-progress.

Comments (0)

Files changed (7)

pypy/config/translationoption.py

                default=IS_64_BITS, cmdline="--gcremovetypeptr"),
     ChoiceOption("gcrootfinder",
                  "Strategy for finding GC Roots (framework GCs only)",
-                 ["n/a", "shadowstack", "asmgcc"],
+                 ["n/a", "shadowstack", "asmgcc", "scan"],
                  "shadowstack",
                  cmdline="--gcrootfinder",
                  requires={
                      "shadowstack": [("translation.gctransformer", "framework")],
                      "asmgcc": [("translation.gctransformer", "framework"),
                                 ("translation.backend", "c")],
+                     "scan": [("translation.gctransformer", "framework")],
                     }),
 
     # other noticeable options

pypy/rpython/lltypesystem/lltype.py

 class GcStruct(RttiStruct):
     _gckind = 'gc'
 
+    def __init__(self, *args, **kwds):
+        RttiStruct.__init__(self, *args, **kwds)
+        # for --gcrootfinder=scan: we need a way to ensure that most gcrefs
+        # in a program cannot point to varsized stuff.  The easiest is to
+        # make sure that GcStructs with no _arrayfld cannot be extended into
+        # GcStructs with an _arrayfld.  If this property is needed later,
+        # think harder about pypy.rpython.memory.gctransform.scan.
+        if self._arrayfld is not None and self._first_struct() != (None, None):
+            raise TypeError("GcStruct has both an inlined first struct and "
+                            "an Array at the end")
+
 STRUCT_BY_FLAVOR = {'raw': Struct,
                     'gc':  GcStruct}
 

pypy/rpython/memory/gctransform/scan.py

+from pypy.rpython.memory.gctransform.framework import FrameworkGCTransformer
+from pypy.rpython.memory.gctransform.framework import BaseRootWalker
+from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rpython.annlowlevel import llhelper
+
+
+class ScanFrameworkGCTransformer(FrameworkGCTransformer):
+
+    def push_roots(self, hop, keep_current_args=False):
+        livevars = self.get_livevars_for_roots(hop, keep_current_args)
+        self.num_pushs += len(livevars)
+        return livevars
+
+    def pop_roots(self, hop, livevars):
+        if not livevars:
+            return
+        # mark the values as keepalives if they can point to var-sized objs
+        for var in livevars:
+            if self.can_point_to_varsized(var.concretetype):
+                hop.genop("keepalive", [var])
+
+    def can_point_to_varsized(self, TYPE):
+        if not isinstance(TYPE, lltype.Ptr) or TYPE.TO._gckind != "gc":
+            return False      # not a ptr-to-gc type at all
+        # this is where we use the fact that a GcStruct cannot inherit another
+        # GcStruct *and* add an _arrayfld:
+        if isinstance(TYPE.TO, lltype.GcStruct) and TYPE.TO._arrayfld is None:
+            return False      # can point only to a GcStruct with no _arrayfld
+        else:
+            return True       # other, including GCREF
+
+    def build_root_walker(self):
+        return ScanStackRootWalker(self)
+
+
+class ScanStackRootWalker(BaseRootWalker):
+
+    def __init__(self, gctransformer):
+        BaseRootWalker.__init__(self, gctransformer)
+
+        def _asm_callback():
+            self.walk_stack_from()
+        self._asm_callback = _asm_callback
+
+    #def need_stacklet_support(self, gctransformer, getfn):
+    #   anything needed?
+
+    #def need_thread_support(self, gctransformer, getfn):
+    #   xxx
+
+    def walk_stack_roots(self, collect_stack_root):
+        gcdata = self.gcdata
+        gcdata._gc_collect_stack_root = collect_stack_root
+        pypy_asm_close_for_scanning(
+            llhelper(ASM_CALLBACK_PTR, self._asm_callback))
+
+    def walk_stack_from(self):
+        raise NotImplementedError
+
+
+eci = ExternalCompilationInfo(
+    post_include_bits = ["extern void pypy_asm_close_for_scanning(void*);\n"],
+    separate_module_sources = ['''
+
+void pypy_asm_close_for_scanning(void *fn)
+{
+    /* We have to do the call by clobbering all registers.  This is
+       needed to ensure that all GC pointers are forced on the stack. */
+#if defined(__amd64__)
+    asm volatile("call *%%rsi" : : "rsi"(fn) :
+                 "memory", "rax", "rbx", "rcx", "rdx", "rbp", "rdi",
+                 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15");
+#else
+    asm volatile("call *%%eax" : : "eax"(fn) :
+                 "memory", "ebx", "ecx", "edx", "ebp", "esi", "edi");
+#endif
+}
+'''],
+    )
+
+ASM_CALLBACK_PTR = lltype.Ptr(lltype.FuncType([], lltype.Void))
+
+pypy_asm_close_for_scanning = rffi.llexternal('pypy_asm_close_for_scanning',
+                                              [ASM_CALLBACK_PTR], lltype.Void,
+                                              sandboxsafe=True,
+                                              _nowrapper=True,
+                                              random_effects_on_gcobjs=True,
+                                              compilation_info=eci)

pypy/translator/c/gc.py

      typeOf, Ptr, ContainerType, RttiStruct, \
      RuntimeTypeInfo, getRuntimeTypeInfo, top_container
 from pypy.rpython.memory.gctransform import \
-     refcounting, boehm, framework, asmgcroot
+     refcounting, boehm, framework, asmgcroot, scan
 from pypy.rpython.lltypesystem import lltype, llmemory
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
 
     def OP_GC_STACK_BOTTOM(self, funcgen, op):
         return 'pypy_asm_stack_bottom();'
 
+class ScanFrameworkGcPolicy(FrameworkGcPolicy):
+    transformerclass = scan.ScanFrameworkGCTransformer
+
+    def GC_KEEPALIVE(self, funcgen, v):
+        return 'pypy_asm_keepalive(%s);' % funcgen.expr(v)
+
+    def OP_GC_RELOAD_POSSIBLY_MOVED(self, funcgen, op):
+        raise Exception("should not be produced with --gcrootfinder=scan")
+
 
 name_to_gcpolicy = {
     'boehm': BoehmGcPolicy,
     'none': NoneGcPolicy,
     'framework': FrameworkGcPolicy,
     'framework+asmgcroot': AsmGcRootFrameworkGcPolicy,
+    'framework+scan': ScanFrameworkGcPolicy,
 }
-
-

pypy/translator/c/genc.py

             name = self.config.translation.gctransformer
             if self.config.translation.gcrootfinder == "asmgcc":
                 name = "%s+asmgcroot" % (name,)
+            if self.config.translation.gcrootfinder == "scan":
+                name = "%s+scan" % (name,)
             return gc.name_to_gcpolicy[name]
         return self.gcpolicy
 

pypy/translator/c/test/test_newgc.py

 
 class TestUsingFramework(object):
     gcpolicy = "marksweep"
+    gcrootfinder = "shadowstack"
     should_be_moving = False
     removetypeptr = False
     taggedpointers = False
         t = Translation(main, standalone=True, gc=cls.gcpolicy,
                         policy=annpolicy.StrictAnnotatorPolicy(),
                         taggedpointers=cls.taggedpointers,
-                        gcremovetypeptr=cls.removetypeptr)
+                        gcremovetypeptr=cls.removetypeptr,
+                        gcrootfinder=cls.gcrootfinder)
         t.disable(['backendopt'])
         t.set_backend_extra_options(c_debug_defines=True)
         t.rtype()

pypy/translator/c/test/test_scan.py

+from pypy.translator.c.test import test_newgc
+
+
+class TestMiniMarkGC(test_newgc.TestMiniMarkGC):
+    gcrootfinder = "scan"
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.