[pypy-svn] r38437 - in pypy/dist/pypy: rpython rpython/lltypesystem rpython/memory/gctransform translator/backendopt translator/c translator/stackless

arigo at codespeak.net arigo at codespeak.net
Sun Feb 11 02:00:57 CET 2007


Author: arigo
Date: Sun Feb 11 02:00:53 2007
New Revision: 38437

Modified:
   pypy/dist/pypy/rpython/llinterp.py
   pypy/dist/pypy/rpython/lltypesystem/lloperation.py
   pypy/dist/pypy/rpython/memory/gctransform/framework.py
   pypy/dist/pypy/translator/backendopt/graphanalyze.py
   pypy/dist/pypy/translator/c/funcgen.py
   pypy/dist/pypy/translator/stackless/code.py
   pypy/dist/pypy/translator/stackless/frame.py
   pypy/dist/pypy/translator/stackless/transform.py
Log:
(pedronis, arigo)

Remove the unsafe_call operation because its C equivalent is really unsafe:
passing less arguments than expected in a C call is not really valid
and crashes on some platforms with some optimization levels.
Whacked instead at the stackless transform to avoid the operation.

The principle of conservation of mess means that we had to introduce
another similar operation, though, 'adr_call'.  Don't use it.



Modified: pypy/dist/pypy/rpython/llinterp.py
==============================================================================
--- pypy/dist/pypy/rpython/llinterp.py	(original)
+++ pypy/dist/pypy/rpython/llinterp.py	Sun Feb 11 02:00:53 2007
@@ -576,20 +576,20 @@
             log.warn("op_indirect_call with graphs=None:", f)
         return self.op_direct_call(f, *args)
 
-    def op_unsafe_call(self, TGT, f):
+    def op_adr_call(self, TGT, f, *inargs):
         checkadr(f)
         obj = self.llinterpreter.typer.type_system.deref(f.ref())
         assert hasattr(obj, 'graph') # don't want to think about that
         graph = obj.graph
         args = []
-        for arg in obj.graph.startblock.inputargs:
-            args.append(arg.concretetype._defl())
+        for inarg, arg in zip(inargs, obj.graph.startblock.inputargs):
+            args.append(lltype._cast_whatever(arg.concretetype, inarg))
         frame = self.__class__(graph, args, self.llinterpreter, self)
         result = frame.eval()
         from pypy.translator.stackless.frame import storage_type
         assert storage_type(lltype.typeOf(result)) == TGT
         return lltype._cast_whatever(TGT, result)
-    op_unsafe_call.need_result_type = True
+    op_adr_call.need_result_type = True
 
     def op_malloc(self, obj):
         if self.llinterpreter.gc is not None:

Modified: pypy/dist/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/lloperation.py	Sun Feb 11 02:00:53 2007
@@ -135,8 +135,6 @@
 
     'direct_call':          LLOp(canraise=(Exception,)),
     'indirect_call':        LLOp(canraise=(Exception,)),
-    #'safe_call':            LLOp(),
-    'unsafe_call':          LLOp(canraise=(Exception,)),
 
     # __________ numeric operations __________
 
@@ -347,6 +345,7 @@
     'adr_ne':               LLOp(canfold=True),
     'adr_gt':               LLOp(canfold=True),
     'adr_ge':               LLOp(canfold=True),
+    'adr_call':             LLOp(canraise=(Exception,)),
     'cast_ptr_to_adr':      LLOp(canfold=True),
     'cast_adr_to_ptr':      LLOp(canfold=True),
     'cast_ptr_to_weakadr':  LLOp(canfold=True),

Modified: pypy/dist/pypy/rpython/memory/gctransform/framework.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform/framework.py	(original)
+++ pypy/dist/pypy/rpython/memory/gctransform/framework.py	Sun Feb 11 02:00:53 2007
@@ -443,7 +443,6 @@
             self.default(hop)
 
     gct_indirect_call = gct_direct_call
-    gct_unsafe_call   = gct_direct_call
 
     def gct_malloc(self, hop):
         op = hop.spaceop

Modified: pypy/dist/pypy/translator/backendopt/graphanalyze.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/graphanalyze.py	(original)
+++ pypy/dist/pypy/translator/backendopt/graphanalyze.py	Sun Feb 11 02:00:53 2007
@@ -38,8 +38,6 @@
             if op.args[-1].value is None:
                 return True
             return self.analyze_indirect_call(op.args[-1].value, seen)
-        elif op.opname == "unsafe_call":
-            return True
         if self.operation_is_true(op):
             return True
 

Modified: pypy/dist/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/dist/pypy/translator/c/funcgen.py	(original)
+++ pypy/dist/pypy/translator/c/funcgen.py	Sun Feb 11 02:00:53 2007
@@ -10,7 +10,7 @@
 from pypy.rpython.lltypesystem.lltype import UnsignedLongLong, Char, UniChar
 from pypy.rpython.lltypesystem.lltype import pyobjectptr, ContainerType
 from pypy.rpython.lltypesystem.lltype import Struct, Array, FixedSizeArray
-from pypy.rpython.lltypesystem.lltype import ForwardReference
+from pypy.rpython.lltypesystem.lltype import ForwardReference, FuncType
 from pypy.rpython.lltypesystem.llmemory import Address, WeakGcAddress
 from pypy.translator.backendopt.ssa import SSI_to_SSA
 
@@ -377,10 +377,10 @@
         r = self.expr(op.result)
         return 'OP_CALL_ARGS((%s), %s);' % (', '.join(args), r)
 
-    def OP_DIRECT_CALL(self, op):
+    def generic_call(self, FUNC, fnexpr, args_v, v_result):
         args = []
-        fn = op.args[0]
-        for v, ARGTYPE in zip(op.args[1:], fn.concretetype.TO.ARGS):
+        assert len(args_v) == len(FUNC.TO.ARGS)
+        for v, ARGTYPE in zip(args_v, FUNC.TO.ARGS):
             if ARGTYPE is Void:
                 continue    # skip 'void' argument
             args.append(self.expr(v))
@@ -388,25 +388,32 @@
             if isinstance(ARGTYPE, ContainerType):
                 args[-1] = '*%s' % (args[-1],)
 
-        line = '%s(%s);' % (self.expr(fn), ', '.join(args))
-        if self.lltypemap(op.result) is not Void:
+        line = '%s(%s);' % (fnexpr, ', '.join(args))
+        if self.lltypemap(v_result) is not Void:
             # skip assignment of 'void' return value
-            r = self.expr(op.result)
+            r = self.expr(v_result)
             line = '%s = %s' % (r, line)
         return line
 
-    # the following works since the extra arguments that indirect_call has
-    # is removed by zip()
-    OP_INDIRECT_CALL = OP_DIRECT_CALL
-
-    def OP_UNSAFE_CALL(self, op):
-        line = '((%s (*)())(%s))();' % (cdecl(self.lltypename(op.result), ''),
-                                        self.expr(op.args[0]))
-        if self.lltypemap(op.result) is not Void:
-            r = self.expr(op.result)
-            line = '%s = %s' % (r, line)
-        return line
-            
+    def OP_DIRECT_CALL(self, op):
+        fn = op.args[0]
+        return self.generic_call(fn.concretetype, self.expr(fn),
+                                 op.args[1:], op.result)
+
+    def OP_INDIRECT_CALL(self, op):
+        fn = op.args[0]
+        return self.generic_call(fn.concretetype, self.expr(fn),
+                                 op.args[1:-1], op.result)
+
+    def OP_ADR_CALL(self, op):
+        ARGTYPES = [v.concretetype for v in op.args[1:]]
+        RESTYPE = op.result.concretetype
+        FUNC = Ptr(FuncType(ARGTYPES, RESTYPE))
+        typename = self.db.gettype(FUNC)
+        fnaddr = op.args[0]
+        fnexpr = '((%s)%s)' % (cdecl(typename, ''), self.expr(fnaddr))
+        return self.generic_call(FUNC, fnexpr, op.args[1:], op.result)
+
     # low-level operations
     def generic_get(self, op, sourceexpr):
         T = self.lltypemap(op.result)

Modified: pypy/dist/pypy/translator/stackless/code.py
==============================================================================
--- pypy/dist/pypy/translator/stackless/code.py	(original)
+++ pypy/dist/pypy/translator/stackless/code.py	Sun Feb 11 02:00:53 2007
@@ -1,4 +1,5 @@
 from pypy.rpython.lltypesystem import lltype, llmemory, lloperation
+from pypy.tool.sourcetools import func_with_new_name
 from pypy.rlib import rarithmetic
 from pypy.rpython import extfunctable
 from pypy.translator.stackless import frame
@@ -282,7 +283,6 @@
 
 for _lltype, typename in STORAGE_TYPES_AND_FIELDS:
     if typename == 'void': continue
-    if typename == 'weak': continue
     exec template%dict(typename=typename, TYPENAME=typename.upper())
 
 # ____________________________________________________________
@@ -296,30 +296,54 @@
         self.retval_float = 0.0
         self.retval_addr = llmemory.NULL
         self.retval_ref = frame.null_saved_ref
+        self.retval_weak = llmemory.WEAKNULL
         self.exception = None
         self.masterarray = lltype.malloc(frame.FRAME_INFO_ARRAY, 0,
                                          immortal=True)
 
 global_state = StacklessData()
 
-def call_function(fn, retval_code):
+# the following functions are patched by transform.py in finish()
+# so that they don't really do what they appear to - we discovered
+# that it was not safe at all to produce this kind of C code
+def define_call_function_retval(TYPE, typename):
+    FUNCTYPE = lltype.Ptr(lltype.FuncType([], TYPE))
+    def call_function_retval_xyz(fnaddr, signature_index):
+        fn = llmemory.cast_adr_to_ptr(fnaddr, FUNCTYPE)
+        return fn()
+    call_function_retval_xyz.stackless_explicit = True
+    call_function_retval_xyz.dont_inline = True
+    fnname = 'call_function_retval_' + typename
+    fn = func_with_new_name(call_function_retval_xyz, fnname)
+    globals()[fnname] = fn
+for _lltype, _typename in STORAGE_TYPES_AND_FIELDS:
+    define_call_function_retval(_lltype, _typename)
+
+
+def call_function(fn, signature_index):
+    retval_code = signature_index & frame.storage_type_bitmask
     if retval_code == frame.RETVAL_VOID:
-        lloperation.llop.unsafe_call(lltype.Void, fn)
+        call_function_retval_void(fn, signature_index)
     elif retval_code == frame.RETVAL_REF:
-        global_state.retval_ref = lloperation.llop.unsafe_call(
-            SAVED_REFERENCE, fn)
+        global_state.retval_ref = (
+            call_function_retval_ref(fn, signature_index))
     elif retval_code == frame.RETVAL_ADDR:
-        global_state.retval_addr = lloperation.llop.unsafe_call(
-            llmemory.Address, fn)
+        global_state.retval_addr = (
+            call_function_retval_addr(fn, signature_index))
     elif retval_code == frame.RETVAL_LONG:
-        global_state.retval_long = lloperation.llop.unsafe_call(
-            lltype.Signed, fn)
+        global_state.retval_long = (
+            call_function_retval_long(fn, signature_index))
     elif retval_code == frame.RETVAL_FLOAT:
-        global_state.retval_float = lloperation.llop.unsafe_call(
-            lltype.Float, fn)
+        global_state.retval_float = (
+            call_function_retval_float(fn, signature_index))
     elif retval_code == frame.RETVAL_LONGLONG:
-        global_state.retval_longlong = lloperation.llop.unsafe_call(
-            lltype.SignedLongLong, fn)
+        global_state.retval_longlong = (
+            call_function_retval_longlong(fn, signature_index))
+    elif retval_code == frame.RETVAL_WEAK:
+        global_state.retval_weak = (
+            call_function_retval_weak(fn, signature_index))
+    else:
+        assert False
 call_function.stackless_explicit = True
 
 class UnwindException(lloperation.StackException):
@@ -343,9 +367,9 @@
     while True:
         back = pending.f_back
         decoded = frame.decodestate(pending.f_restart)
-        (fn, global_state.restart_substate, retval_type) = decoded
+        (fn, global_state.restart_substate, signature_index) = decoded
         try:
-            call_function(fn, retval_type)
+            call_function(fn, signature_index)
         except UnwindException, u:   #XXX annotation support needed
             if u.frame_bottom:
                 u.frame_bottom.f_back = back
@@ -425,3 +449,12 @@
         global_state.retval_ref = frame.null_saved_ref
         return res
 fetch_retval_ref.stackless_explicit = True
+
+def fetch_retval_weak():
+    e = global_state.exception
+    if e:
+        global_state.exception = None
+        raise e
+    else:
+        return global_state.retval_weak
+fetch_retval_weak.stackless_explicit = True

Modified: pypy/dist/pypy/translator/stackless/frame.py
==============================================================================
--- pypy/dist/pypy/translator/stackless/frame.py	(original)
+++ pypy/dist/pypy/translator/stackless/frame.py	Sun Feb 11 02:00:53 2007
@@ -30,6 +30,9 @@
     if _TYPE not in STORAGE_TYPES:
         STORAGE_TYPES.append(_TYPE)
 
+storage_type_bitmask = 0x07     # a power of two - 1
+assert storage_type_bitmask >= len(STORAGE_TYPES)
+
 STORAGE_FIELDS = dict(STORAGE_TYPES_AND_FIELDS)
 del STORAGE_FIELDS[lltype.Void]
 
@@ -98,7 +101,7 @@
         finfo = masterarray[index - restartstate]
     return (finfo.fnaddr,  # function ptr
             restartstate,  # restart state within function
-            finfo.info)    # retval_type
+            finfo.info)    # signature_index
 decodestate.stackless_explicit = True
 
 
@@ -116,7 +119,7 @@
         self.resume_point_count = resume_point_count
         self.frame_types = ()
 
-    def compress(self, rtyper):
+    def compress(self, signaturecodes, rtyper):
         """This returns sufficient information to be able to build the
         entries that will go in the global array of restart
         information."""
@@ -126,10 +129,18 @@
             if not isinstance(graph, FunctionGraph):
                 graph = bk.getdesc(graph).getuniquegraph()
             funcptr = getfunctionptr(graph)
-            rettype = lltype.typeOf(funcptr).TO.RESULT
-            retval_type = STORAGE_TYPES.index(storage_type(rettype))
-
-            result = [(llmemory.cast_ptr_to_adr(funcptr), retval_type)]
+            FUNC = lltype.typeOf(funcptr).TO
+            rettype_index = STORAGE_TYPES.index(storage_type(FUNC.RESULT))
+            cache = signaturecodes[rettype_index]
+            key = tuple([storage_type(ARG) for ARG in FUNC.ARGS])
+            try:
+                signature_index = cache[key]
+            except KeyError:
+                signature_index = len(cache) * (storage_type_bitmask+1)
+                signature_index |= rettype_index
+                cache[key] = signature_index
+            assert (signature_index & storage_type_bitmask) == rettype_index
+            result = [(llmemory.cast_ptr_to_adr(funcptr), signature_index)]
             for i in range(1, self.resume_point_count):
                 result.append((llmemory.NULL, i))
         else:

Modified: pypy/dist/pypy/translator/stackless/transform.py
==============================================================================
--- pypy/dist/pypy/translator/stackless/transform.py	(original)
+++ pypy/dist/pypy/translator/stackless/transform.py	Sun Feb 11 02:00:53 2007
@@ -293,7 +293,14 @@
         self.frametyper = FrameTyper(stackless_gc, self)
         self.masterarray1 = []
         self.curr_graph = None
-        
+
+        self.signaturecodes = [{} for RETTYPE in frame.STORAGE_TYPES]
+        # self.signaturecodes[n] is a dict {ARGTYPES: signature_index}
+        # where n is the return type as an index in STORAGE_TYPES.
+        # the signature_index is an arbitrary number but it encodes
+        # the type of the result, i.e.
+        #     n == (signature_index & storage_type_bitmask)
+
         bk = translator.annotator.bookkeeper
 
         self.unwind_exception_type = getinstancerepr(
@@ -363,6 +370,8 @@
                 code.fetch_retval_addr, [], annmodel.SomeAddress()),
             SAVED_REFERENCE: mixlevelannotator.constfunc(
                 code.fetch_retval_ref, [], annmodel.SomePtr(SAVED_REFERENCE)),
+            llmemory.WeakGcAddress: mixlevelannotator.constfunc(
+                code.fetch_retval_weak, [], annmodel.SomeWeakGcAddress()),
             }
 
         s_StatePtr = annmodel.SomePtr(frame.OPAQUE_STATE_HEADER_PTR)
@@ -413,6 +422,10 @@
                 code.resume_after_ref,
                 [s_StatePtr, annmodel.SomePtr(SAVED_REFERENCE)],
                 annmodel.s_None),
+            llmemory.WeakGcAddress: mixlevelannotator.constfunc(
+                code.resume_after_weak,
+                [s_StatePtr, annmodel.SomeWeakGcAddress()],
+                annmodel.s_None),
             }
         exception_def = bk.getuniqueclassdef(Exception)
         self.resume_after_raising_ptr = mixlevelannotator.constfunc(
@@ -1098,7 +1111,7 @@
     def register_restart_info(self, restartinfo):
         assert not self.is_finished
         rtyper = self.translator.rtyper
-        for frame_info in restartinfo.compress(rtyper):
+        for frame_info in restartinfo.compress(self.signaturecodes, rtyper):
             self.masterarray1.append(frame_info)
 
     def finish(self):
@@ -1108,6 +1121,44 @@
             import cPickle
             cPickle.dump(self.stats, open('stackless-stats.pickle', 'wb'))
 
+        # fun fun fun patching the call_function_retval_xyz() functions!
+        for RESTYPE, typename in frame.STORAGE_TYPES_AND_FIELDS:
+            rettype_index = STORAGE_TYPES.index(RESTYPE)
+            cache = self.signaturecodes[rettype_index]
+            if not cache:
+                continue # not used anyway, don't produce a broken empty switch
+            func = getattr(code, 'call_function_retval_' + typename)
+            desc = self.translator.annotator.bookkeeper.getdesc(func)
+            graph = desc.getuniquegraph()
+
+            [v_fnaddr, v_signature_index] = graph.getargs()
+            block = model.Block([v_fnaddr, v_signature_index])
+            block.exitswitch = v_signature_index
+            block.isstartblock = True
+            graph.startblock = block
+            switchlinks = []
+
+            for ARGTYPES, signature_index in cache.items():
+                # XXX because of type erasure, the following cast is
+                # kind of invalid, but we hope that nobody will notice
+                FUNCTYPE = lltype.Ptr(lltype.FuncType(ARGTYPES, RESTYPE))
+                v_fnaddr1 = varoftype(v_fnaddr.concretetype)
+                callblock = model.Block([v_fnaddr1])
+                llops = LowLevelOpList()
+                args_v = [model.Constant(TYPE._defl(), concretetype=TYPE)
+                          for TYPE in ARGTYPES]
+                v_res = llops.genop('adr_call', [v_fnaddr1] + args_v,
+                                    resulttype = RESTYPE)
+                callblock.operations[:] = llops
+                callblock.closeblock(model.Link([v_res], graph.returnblock))
+                link = model.Link([v_fnaddr], callblock)
+                link.exitcase = signature_index
+                link.llexitcase = signature_index
+                switchlinks.append(link)
+
+            block.closeblock(*switchlinks)
+            model.checkgraph(graph)
+
         self.is_finished = True
         masterarray = lltype.malloc(frame.FRAME_INFO_ARRAY,
                                     len(self.masterarray1),



More information about the Pypy-commit mailing list