major cleanup, move argument default value caching to code generation phase
authorStefan Behnel <scoder@users.berlios.de>
Fri, 20 Mar 2009 12:29:35 +0000 (13:29 +0100)
committerStefan Behnel <scoder@users.berlios.de>
Fri, 20 Mar 2009 12:29:35 +0000 (13:29 +0100)
Cython/Compiler/Code.py
Cython/Compiler/ModuleNode.py
Cython/Compiler/Nodes.py
Cython/Compiler/Symtab.py

index 121a87b71db59f26263fe833bf05413af688620c..7e5350d4bf398348011c483a6866e25f97743191 100644 (file)
@@ -214,6 +214,16 @@ class IntConst(object):
         self.value = value
         self.is_long = is_long
 
+class PyObjectConst(object):
+    """Global info about a generic constant held by GlobalState.
+    """
+    # cname       string
+    # type        PyrexType
+
+    def __init__(self, cname, type):
+        self.cname = cname
+        self.type = type
+
 possible_identifier = re.compile(ur"(?![0-9])\w+$", re.U).match
 nice_identifier = re.compile('^[a-zA-Z0-0_]+$').match
 
@@ -321,6 +331,7 @@ class GlobalState(object):
         self.const_cname_counter = 1
         self.string_const_index = {}
         self.int_const_index = {}
+        self.py_constants = []
 
     def initwriters(self, rootwriter):
         self.utilprotowriter = rootwriter.new_writer()
@@ -394,6 +405,10 @@ class GlobalState(object):
             c = self.new_int_const(str_value, longness)
         return c
 
+    def get_py_const(self, type):
+        # create a new Python object constant
+        return self.new_py_const(type)
+
     def get_string_const(self, text):
         # return a C string constant, creating a new one if necessary
         if text.is_unicode:
@@ -424,6 +439,12 @@ class GlobalState(object):
         self.int_const_index[(value, longness)] = c
         return c
 
+    def new_py_const(self, type):
+        cname = self.new_const_cname()
+        c = PyObjectConst(cname, type)
+        self.py_constants.append(c)
+        return c
+
     def new_string_const_cname(self, value, intern=None):
         # Create a new globally-unique nice name for a C string constant.
         if len(value) < 20 and nice_identifier(value):
@@ -458,6 +479,15 @@ class GlobalState(object):
     def generate_const_declarations(self):
         self.generate_string_constants()
         self.generate_int_constants()
+        self.generate_object_constant_decls()
+
+    def generate_object_constant_decls(self):
+        consts = [ (len(c.cname), c.cname, c)
+                   for c in self.py_constants ]
+        consts.sort()
+        for _, cname, c in consts:
+            self.decls_writer.putln(
+                "static %s;" % c.type.declaration_code(cname))
 
     def generate_string_constants(self):
         c_consts = [ (len(c.cname), c.cname, c)
@@ -762,6 +792,9 @@ class CCodeWriter(object):
     def get_py_string_const(self, text, identifier=None):
         return self.globalstate.get_py_string_const(text, identifier).cname
 
+    def get_argument_default_const(self, type):
+        return self.globalstate.get_py_const(type).cname
+
     def intern(self, text):
         return self.get_py_string_const(text)
 
index fcafd59e5852becd28752db8978ec97a80c57a11..4d39490d04fc9a859df993617cc51c81e8e9804e 100644 (file)
@@ -255,10 +255,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
 
         code.putln("")
         code.putln("/* Implementation of %s */" % env.qualified_name)
-        self.generate_const_definitions(env, code)
-        self.generate_interned_num_decls(env, code)
-        self.generate_interned_string_decls(env, code)
-        self.generate_py_string_decls(env, code)
 
         code.globalstate.insert_global_var_declarations_into(code)
 
@@ -663,13 +659,18 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
                 " 'cdef extern from' block")
         else:
             last_entry = enum_values[-1]
+            # this does not really generate code, just builds the result value
             for value_entry in enum_values:
-                if value_entry.value == value_entry.name:
+                if value_entry.value_node is not None:
+                    value_entry.value_node.generate_evaluation_code(code)
+
+            for value_entry in enum_values:
+                if value_entry.value_node is None:
                     value_code = value_entry.cname
                 else:
                     value_code = ("%s = %s" % (
                         value_entry.cname,
-                        value_entry.value))
+                        value_entry.value_node.result()))
                 if value_entry is not last_entry:
                     value_code += ","
                 code.putln(value_code)
@@ -761,9 +762,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
                     entry.type.typeptr_cname)
         code.put_var_declarations(env.var_entries, static = 1, 
             dll_linkage = "DL_EXPORT", definition = definition)
-        if definition:
-            code.put_var_declarations(env.default_entries, static = 1,
-                                      definition = definition)
     
     def generate_cfunction_predeclarations(self, env, code, definition):
         for entry in env.cfunc_entries:
@@ -1702,19 +1700,19 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         code.put_decref_clear(Naming.empty_tuple,
                               PyrexTypes.py_object_type,
                               nanny=False)
-        for entry in env.pynum_entries:
-            code.put_decref_clear(entry.cname,
-                                  PyrexTypes.py_object_type,
-                                  nanny=False)
-        for entry in env.all_pystring_entries:
-            if entry.is_interned:
-                code.put_decref_clear(entry.pystring_cname,
-                                      PyrexTypes.py_object_type,
-                                      nanny=False)
-        for entry in env.default_entries:
-            if entry.type.is_pyobject and entry.used:
-                code.putln("Py_DECREF(%s); %s = 0;" % (
-                    code.entry_as_pyobject(entry), entry.cname))
+#        for entry in env.pynum_entries:
+#            code.put_decref_clear(entry.cname,
+#                                  PyrexTypes.py_object_type,
+#                                  nanny=False)
+#        for entry in env.all_pystring_entries:
+#            if entry.is_interned:
+#                code.put_decref_clear(entry.pystring_cname,
+#                                      PyrexTypes.py_object_type,
+#                                      nanny=False)
+#        for entry in env.default_entries:
+#            if entry.type.is_pyobject and entry.used:
+#                code.putln("Py_DECREF(%s); %s = 0;" % (
+#                    code.entry_as_pyobject(entry), entry.cname))
         code.putln("Py_INCREF(Py_None); return Py_None;")
         code.putln('}')
 
index 9bdbd614ece79ba52783425112218682759883e0..dbbdef6cd55f9c70feabc89d2197bf38f2366dbb 100644 (file)
@@ -302,38 +302,6 @@ class CompilerDirectivesNode(Node):
 class BlockNode(object):
     #  Mixin class for nodes representing a declaration block.
 
-    def generate_const_definitions(self, env, code):
-        if env.const_entries:
-            for entry in env.const_entries:
-                if not entry.is_interned:
-                    code.globalstate.add_const_definition(entry)
-
-    def generate_interned_string_decls(self, env, code):
-        entries = env.global_scope().new_interned_string_entries
-        if entries:
-            for entry in entries:
-                code.globalstate.add_interned_string_decl(entry)
-            del entries[:]
-
-    def generate_py_string_decls(self, env, code):
-        if env is None:
-            return # earlier error
-        entries = env.pystring_entries
-        if entries:
-            for entry in entries:
-                if not entry.is_interned:
-                    code.globalstate.add_py_string_decl(entry)
-
-    def generate_interned_num_decls(self, env, code):
-        #  Flush accumulated interned nums from the global scope
-        #  and generate declarations for them.
-        genv = env.global_scope()
-        entries = genv.interned_nums
-        if entries:
-            for entry in entries:
-                code.globalstate.add_interned_num_decl(entry)
-            del entries[:]
-
     def generate_cached_builtins_decls(self, env, code):
         entries = env.global_scope().undeclared_cached_builtins
         for entry in entries:
@@ -609,8 +577,7 @@ class CArgDeclNode(Node):
     # declarator     CDeclaratorNode
     # not_none       boolean            Tagged with 'not None'
     # default        ExprNode or None
-    # default_entry  Symtab.Entry       Entry for the variable holding the default value
-    # default_result_code string        cname or code fragment for default value
+    # default_value  PyObjectConst      constant for default value
     # is_self_arg    boolean            Is the "self" arg of an extension type method
     # is_kw_only     boolean            Is a keyword-only argument
 
@@ -620,6 +587,7 @@ class CArgDeclNode(Node):
     is_generic = 1
     type = None
     name_declarator = None
+    default_value = None
 
     def analyse(self, env, nonempty = 0):
         #print "CArgDeclNode.analyse: is_self_arg =", self.is_self_arg ###
@@ -641,16 +609,15 @@ class CArgDeclNode(Node):
         else:
             return self.name_declarator, self.type
 
-    def prepare_default_result_code(self, code):
-        if self.default:
-            if self.default.is_literal:
-                # FIXME: IS IT OK TO CALL THIS HERE???
-                self.default.generate_evaluation_code(code)
-                self.default_result_code = self.default.result()
-                if self.default.type != self.type and not self.type.is_int:
-                    self.default_result_code = self.type.cast_code(self.default_result_code)
-            else:
-                self.default_result_code = self.default_entry.cname
+    def calculate_default_value_code(self, code):
+        if self.default_value is None:
+            if self.default:
+                if self.default.is_literal:
+                    # will not output any code, just assign the result_code
+                    self.default.generate_evaluation_code(code)
+                    return self.type.cast_code(self.default.result())
+                self.default_value = code.get_argument_default_const(self.type)
+        return self.default_value
 
     def annotate(self, code):
         if self.default:
@@ -944,11 +911,9 @@ class CEnumDefItemNode(StatNode):
             if not self.value.type.is_int:
                 self.value = self.value.coerce_to(PyrexTypes.c_int_type, env)
                 self.value.analyse_const_expression(env)
-            value = self.value.get_constant_result_code()
-        else:
-            value = self.name
         entry = env.declare_const(self.name, enum_entry.type, 
-            value, self.pos, cname = self.cname, visibility = enum_entry.visibility)
+            self.value, self.pos, cname = self.cname,
+            visibility = enum_entry.visibility)
         enum_entry.enum_values.append(entry)
 
 
@@ -997,14 +962,7 @@ class FuncDefNode(StatNode, BlockNode):
                     if not hasattr(arg, 'default_entry'):
                         arg.default.analyse_types(env)
                         arg.default = arg.default.coerce_to(arg.type, genv)
-                        if arg.default.is_literal:
-                            arg.default_entry = arg.default
-                        else:
-                            arg.default.allocate_temps(genv)
-                            arg.default_entry = genv.add_default_value(arg.type)
-                            if arg.type.is_pyobject:
-                                arg.default_entry.init = 0
-                            arg.default_entry.used = 1
+                        arg.default.allocate_temps(genv)
                 else:
                     error(arg.pos,
                         "This argument cannot have a default value")
@@ -1042,13 +1000,7 @@ class FuncDefNode(StatNode, BlockNode):
             
         # ----- Top-level constants used by this function
         code.mark_pos(self.pos)
-        self.generate_interned_num_decls(lenv, code)
-        self.generate_interned_string_decls(lenv, code)
-        self.generate_py_string_decls(lenv, code)
         self.generate_cached_builtins_decls(lenv, code)
-        #code.putln("")
-        #code.put_var_declarations(lenv.const_entries, static = 1)
-        self.generate_const_definitions(lenv, code)
         # ----- Function header
         code.putln("")
         if self.py_func:
@@ -1244,14 +1196,15 @@ class FuncDefNode(StatNode, BlockNode):
                 if not default.is_literal:
                     default.generate_evaluation_code(code)
                     default.make_owned_reference(code)
+                    result = default.result_as(arg.type)
                     code.putln(
                         "%s = %s;" % (
-                            arg.default_entry.cname,
-                            default.result_as(arg.default_entry.type)))
-                    if default.is_temp and default.type.is_pyobject:
-                        code.putln("%s = 0;" % default.result())
+                            arg.calculate_default_value_code(code),
+                            result))
+                    if arg.type.is_pyobject:
+                        code.put_giveref(default.result())
+                    default.generate_post_assignment_code(code)
                     default.free_temps(code)
-                    code.put_var_giveref(arg.default_entry)
         # For Python class methods, create and store function object
         if self.assmt:
             self.assmt.generate_execution_code(code)
@@ -1438,9 +1391,9 @@ class CFuncDefNode(FuncDefNode):
     def generate_argument_declarations(self, env, code):
         for arg in self.args:
             if arg.default:
-                arg.prepare_default_result_code(code)
+                result = arg.calculate_default_value_code(code)
                 code.putln('%s = %s;' % (
-                    arg.type.declaration_code(arg.cname), arg.default_result_code))
+                    arg.type.declaration_code(arg.cname), result))
 
     def generate_keyword_list(self, code):
         pass
@@ -1881,9 +1834,7 @@ class DefNode(FuncDefNode):
                     code.putln("PyObject *%s = 0;" % arg.hdr_cname)
                 else:
                     code.put_var_declaration(arg.entry)
-            if arg.default:
-                arg.prepare_default_result_code(code)
-    
+
     def generate_keyword_list(self, code):
         if self.signature_has_generic_args() and \
                 self.signature_has_nongeneric_args():
@@ -2143,7 +2094,7 @@ class DefNode(FuncDefNode):
                 code.putln(
                     "%s = %s;" % (
                         arg.entry.cname,
-                        arg.default_result_code))
+                        arg.calculate_default_value_code(code)))
 
     def generate_stararg_init_code(self, max_positional_args, code):
         if self.starstar_arg:
@@ -2185,7 +2136,7 @@ class DefNode(FuncDefNode):
         default_args = []
         for i, arg in enumerate(all_args):
             if arg.default and arg.type.is_pyobject:
-                default_value = arg.default_result_code
+                default_value = arg.calculate_default_value_code(code)
                 if arg.type is not PyrexTypes.py_object_type:
                     default_value = "(PyObject*)"+default_value
                 default_args.append((i, default_value))
@@ -2331,7 +2282,7 @@ class DefNode(FuncDefNode):
                 code.putln(
                     "%s = %s;" % (
                         arg.entry.cname,
-                        arg.default_result_code))
+                        arg.calculate_default_value_code(code)))
                 code.putln('}')
 
     def generate_argument_conversion_code(self, code):
@@ -2582,7 +2533,6 @@ class PyClassDefNode(ClassDefNode):
         #self.target.release_target_temp(env)
     
     def generate_function_definitions(self, env, code):
-        self.generate_py_string_decls(self.scope, code)
         self.body.generate_function_definitions(self.scope, code)
     
     def generate_execution_code(self, code):
@@ -2716,7 +2666,6 @@ class CClassDefNode(ClassDefNode):
             self.body.analyse_expressions(scope)
     
     def generate_function_definitions(self, env, code):
-        self.generate_py_string_decls(self.entry.type.scope, code)
         if self.body:
             self.body.generate_function_definitions(
                 self.entry.type.scope, code)
index 8ce80ff1db4aac91f25a13fca402c11d29bd163f..a7663a29a5b1f8841f7df68ca9fb4e9cffe06916 100644 (file)
@@ -296,7 +296,7 @@ class Scope(object):
     
     def qualify_name(self, name):
         return "%s.%s" % (self.qualified_name, name)
-    
+
     def declare_const(self, name, type, value, pos, cname = None, visibility = 'private'):
         # Add an entry for a named constant.
         if not cname:
@@ -306,7 +306,7 @@ class Scope(object):
                 cname = self.mangle(Naming.enum_prefix, name)
         entry = self.declare(name, cname, type, pos, visibility)
         entry.is_const = 1
-        entry.value = value
+        entry.value_node = value
         return entry
     
     def declare_type(self, name, type, pos, 
@@ -683,9 +683,7 @@ class ModuleScope(Scope):
     # method_table_cname   string             C name of method table
     # doc                  string             Module doc string
     # doc_cname            string             C name of module doc string
-    # const_counter        integer            Counter for naming constants (PS: MOVED TO GLOBAL)
     # utility_code_list    [(UtilityCode, string)] Queuing utility codes for forwarding to Code.py
-    # default_entries      [Entry]            Function argument default entries
     # python_include_files [string]           Standard  Python headers to be included
     # include_files        [string]           Other C headers to be included
     # string_to_entry      {string : Entry}   Map string const to entry
@@ -697,9 +695,6 @@ class ModuleScope(Scope):
     # included_files       [string]           Cython sources included with 'include'
     # pxd_file_loaded      boolean            Corresponding .pxd file has been processed
     # cimported_modules    [ModuleScope]      Modules imported with cimport
-    # new_interned_string_entries [Entry]     New interned strings waiting to be declared
-    # interned_nums        [int/long]         Interned numeric constants
-    # all_pystring_entries [Entry]            Python string consts from all scopes
     # types_imported       {PyrexType : 1}    Set of types for which import code generated
     # has_import_star      boolean            Module contains import *
     
@@ -723,20 +718,14 @@ class ModuleScope(Scope):
         self.doc = ""
         self.doc_cname = Naming.moddoc_cname
         self.utility_code_list = []
-        self.default_entries = []
         self.module_entries = {}
         self.python_include_files = ["Python.h", "structmember.h"]
         self.include_files = []
         self.type_names = dict(outer_scope.type_names)
         self.pxd_file_loaded = 0
         self.cimported_modules = []
-        self.new_interned_string_entries = []
-        self.interned_nums = []
-        self.interned_objs = []
-        self.all_pystring_entries = []
         self.types_imported = {}
         self.included_files = []
-        self.pynum_entries = []
         self.has_extern_class = 0
         self.cached_builtins = []
         self.undeclared_cached_builtins = []
@@ -869,22 +858,6 @@ class ModuleScope(Scope):
         if not entry:
             self.declare_var(name, py_object_type, pos)
     
-    def add_default_value(self, type):
-        # Add an entry for holding a function argument
-        # default value.
-        cname = self.new_const_cname()
-        entry = Entry("", cname, type)
-        self.default_entries.append(entry)
-        return entry
-        
-    def new_const_cname(self):
-        global const_counter
-        # Create a new globally-unique name for a constant.
-        prefix=''
-        n = const_counter
-        const_counter = n + 1
-        return "%s%s%d" % (Naming.const_prefix, prefix, n)
-    
     def use_utility_code(self, new_code, name=None):
         if new_code is not None:
             self.utility_code_list.append((new_code, name))