Commit 9c1bf4c5 authored by Robert Bradshaw's avatar Robert Bradshaw

Interned integer constants, created at module load time.

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.
parent cbcbdc0c
......@@ -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)))
......
......@@ -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(
......
......@@ -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_"
......
......@@ -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)
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment