From 64275702cba323744b41b302e802a5073fbba757 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 20 Mar 2009 13:29:35 +0100 Subject: [PATCH] major cleanup, move argument default value caching to code generation phase --- Cython/Compiler/Code.py | 33 +++++++++++ Cython/Compiler/ModuleNode.py | 42 +++++++------- Cython/Compiler/Nodes.py | 103 +++++++++------------------------- Cython/Compiler/Symtab.py | 31 +--------- 4 files changed, 81 insertions(+), 128 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 121a87b7..7e5350d4 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -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) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index fcafd59e..4d39490d 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -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('}') diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 9bdbd614..dbbdef6c 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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) diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 8ce80ff1..a7663a29 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -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)) -- 2.26.2