Interned integer constants, created at module load time.
authorRobert Bradshaw <robertwb@math.washington.edu>
Thu, 11 Oct 2007 08:33:28 +0000 (01:33 -0700)
committerRobert Bradshaw <robertwb@math.washington.edu>
Thu, 11 Oct 2007 08:33:28 +0000 (01:33 -0700)
For example, in the SAGE source we have

1158 PyInt_FromLong(0)
 776 PyInt_FromLong(1)
 258 PyInt_FromLong(2)
  33 PyInt_FromLong(3)
  21 PyInt_FromLong(10)

and a thousand or so others... Who knows how many of these are in loops too.

Cython/Compiler/ExprNodes.py
Cython/Compiler/ModuleNode.py
Cython/Compiler/Naming.py
Cython/Compiler/Nodes.py
Cython/Compiler/Symtab.py

index b0526ef40b568c85ec87ff04e54c71a11aa0549e..4a234c8cacd5489507b39ed0498bebaeff88b996 100644 (file)
@@ -596,6 +596,24 @@ class CharNode(ConstNode):
 class IntNode(ConstNode):
     type = PyrexTypes.c_long_type
 
+    def analyse_types(self, env):
+        self.entry = env.get_py_num(self.value)
+    
+    def coerce_to(self, dst_type, env):
+        # Arrange for a Python version of the string to be pre-allocated
+        # when coercing to a Python type.
+        if dst_type.is_pyobject:
+            self.type = PyrexTypes.py_object_type
+        # We still need to perform normal coerce_to processing on the
+        # result, because we might be coercing to an extension type,
+        # in which case a type test node will be needed.
+        return ConstNode.coerce_to(self, dst_type, env)
+
+    def calculate_result_code(self):
+        if self.type.is_pyobject:
+            return self.entry.cname
+        else:
+            return str(self.value)
 
 class FloatNode(ConstNode):
     type = PyrexTypes.c_double_type
@@ -2323,6 +2341,8 @@ unop_node_classes = {
 def unop_node(pos, operator, operand):
     # Construct unnop node of appropriate class for 
     # given operator.
+    if isinstance(operand, IntNode) and operator == '-':
+        return IntNode(pos = operand.pos, value = -int(operand.value))
     return unop_node_classes[operator](pos, 
         operator = operator, 
         operand = operand)
@@ -2744,7 +2764,7 @@ class BoolBinopNode(ExprNode):
         if self.type.is_pyobject:
             test_result = self.temp_bool.result_code
             code.putln(
-                "%s = PyObject_IsTrue(%s); %s" % (
+                "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
                     test_result,
                     self.operand1.py_result(),
                     code.error_goto_if_neg(test_result, self.pos)))
index 47acf235aba473da0df627f1606c63d6dba9bc00..4aeff24f6e448f94c98b56d8ecd4acd3f5129118 100644 (file)
@@ -125,6 +125,7 @@ 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_name_decls(env, code)
         self.generate_py_string_decls(env, code)
         self.generate_cached_builtins_decls(env, code)
@@ -1238,6 +1239,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
                     code.error_goto(self.pos)));
     
     def generate_intern_code(self, env, code):
+        for entry in env.pynum_entries:
+            code.putln("%s = PyInt_FromLong(%s); %s;" % (
+                entry.cname,
+                entry.init,
+                code.error_goto_if_null(entry.cname, self.pos)))
         if env.intern_map:
             env.use_utility_code(Nodes.init_intern_tab_utility_code);
             code.putln(
index 6ea000f237ca503bed2ed3ae54a319865efe339e..152edb07c4c4fb85905cf7984c5cb197db4d0ef7 100644 (file)
@@ -21,6 +21,7 @@ pymethdef_prefix  = pyrex_prefix + "mdef_"
 methtab_prefix    = pyrex_prefix + "methods_"
 memtab_prefix     = pyrex_prefix + "members_"
 interned_prefix   = pyrex_prefix + "n_"
+interned_num_prefix = pyrex_prefix + "num_"
 objstruct_prefix  = pyrex_prefix + "obj_"
 typeptr_prefix    = pyrex_prefix + "ptype_"
 prop_set_prefix   = pyrex_prefix + "setprop_"
index 6ba8f5437c9574203c68595cd8f6afca4a9b981f..8c20c1c26e3998aa21c7b6ff8213b81eacaa583b 100644 (file)
@@ -115,6 +115,18 @@ class BlockNode:
             for entry in entries:
                 code.putln(
                     "static PyObject *%s;" % entry.pystring_cname)
+    
+    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:
+            code.putln("")
+            for entry in entries:
+                code.putln(
+                    "static PyObject *%s;" % entry.cname)
+            del entries[:]
 
     def generate_cached_builtins_decls(self, env, code):
         entries = env.builtin_scope().undeclared_cached_entries
@@ -556,6 +568,7 @@ class FuncDefNode(StatNode, BlockNode):
         # Code for nested function definitions would go here
         # if we supported them, which we probably won't.
         # ----- Top-level constants used by this function
+        self.generate_interned_num_decls(lenv, code)
         self.generate_interned_name_decls(lenv, code)
         self.generate_py_string_decls(lenv, code)
         self.generate_cached_builtins_decls(lenv, code)
index b9a97cf317cc963f32066de144884982baeadc08..f18ce2b5d766887c62df84efbb42462bbc7901e0 100644 (file)
@@ -123,6 +123,7 @@ class Scope:
     # free_temp_entries [Entry]            Temp variables currently unused
     # temp_counter      integer            Counter for naming temp vars
     # cname_to_entry    {string : Entry}   Temp cname to entry mapping
+    # int_to_entry      {int : Entry}      Temp cname to entry mapping
     # pow_function_used boolean            The C pow() function is used
     # return_type       PyrexType or None  Return type of function owning scope
     # is_py_class_scope boolean            Is a Python class scope
@@ -169,6 +170,7 @@ class Scope:
         self.cname_to_entry = {}
         self.pow_function_used = 0
         self.string_to_entry = {}
+        self.num_to_entry = {}
         self.pystring_entries = []
     
     def __str__(self):
@@ -394,6 +396,28 @@ class Scope:
                 entry.pystring_cname = entry.cname + "p"
                 self.pystring_entries.append(entry)
                 self.global_scope().all_pystring_entries.append(entry)
+                
+    def add_py_num(self, value):
+        # Add an entry for an int constant.
+        cname = "%s%s" % (Naming.interned_num_prefix, value)
+        cname = cname.replace('-', 'neg_').replace('.','_')
+        entry = Entry("", cname, c_long_type, init = value)
+        entry.used = 1
+        entry.is_interned = 1
+        self.const_entries.append(entry)
+        self.interned_nums.append(entry)
+        return entry
+        
+    def get_py_num(self, value):
+        # Get entry for int constant. Returns an existing
+        # one if possible, otherwise creates a new one.
+        genv = self.global_scope()
+        entry = genv.num_to_entry.get(value)
+        if not entry:
+            entry = genv.add_py_num(value)
+            genv.num_to_entry[value] = entry
+            genv.pynum_entries.append(entry)
+        return entry
     
     def new_const_cname(self):
         # Create a new globally-unique name for a constant.
@@ -608,8 +632,10 @@ class ModuleScope(Scope):
         self.cimported_modules = []
         self.intern_map = {}
         self.interned_names = []
+        self.interned_nums = []
         self.all_pystring_entries = []
         self.types_imported = {}
+        self.pynum_entries = []
     
     def qualifying_scope(self):
         return self.parent_module