compiler/inline: cleanup reference manager, recognize Fraction

This commit is contained in:
Sebastien Bourdeauducq 2014-08-13 17:56:55 +08:00
parent fe5b3cc67e
commit 5481baac1d
1 changed files with 37 additions and 52 deletions

View File

@ -1,17 +1,11 @@
from collections import namedtuple, defaultdict from collections import namedtuple, defaultdict
from fractions import Fraction
import inspect, textwrap, ast import inspect, textwrap, ast
from artiq.compiler.tools import eval_ast, value_to_ast from artiq.compiler.tools import eval_ast, value_to_ast
from artiq.language import core as core_language from artiq.language import core as core_language
from artiq.language import units from artiq.language import units
def _replace_global(obj, ref):
try:
value = eval_ast(ref, inspect.getmodule(obj).__dict__)
except:
return None
return value_to_ast(value)
_UserVariable = namedtuple("_UserVariable", "name") _UserVariable = namedtuple("_UserVariable", "name")
def _is_in_attr_list(obj, attr, al): def _is_in_attr_list(obj, attr, al):
@ -30,12 +24,13 @@ class _ReferenceManager:
self.kernel_attr_init = [] self.kernel_attr_init = []
# reserved names # reserved names
self.use_count["Quantity"] = 1
self.use_count["base_s_unit"] = 1
self.use_count["base_Hz_unit"] = 1
for kg in core_language.kernel_globals: for kg in core_language.kernel_globals:
self.use_count[kg] = 1 self.use_count[kg] = 1
self.use_count["range"] = 1 self.use_count["range"] = 1
self.use_count["Fraction"] = 1
self.use_count["Quantity"] = 1
self.use_count["s_unit"] = 1
self.use_count["Hz_unit"] = 1
def new_name(self, base_name): def new_name(self, base_name):
if base_name[-1].isdigit(): if base_name[-1].isdigit():
@ -54,26 +49,12 @@ class _ReferenceManager:
if isinstance(ref, ast.Name): if isinstance(ref, ast.Name):
key = (id(obj), funcname, ref.id) key = (id(obj), funcname, ref.id)
try: try:
ival = self.to_inlined[key] return self.to_inlined[key]
except KeyError: except KeyError:
if store: if store:
iname = self.new_name(ref.id) ival = _UserVariable(self.new_name(ref.id))
self.to_inlined[key] = _UserVariable(iname) self.to_inlined[key] = ival
return ast.Name(iname, ast.Store())
else:
if isinstance(ival, _UserVariable):
return ast.Name(ival.name, ref.ctx)
elif isinstance(ival, ast.AST):
assert(not store)
return ival return ival
else:
if store:
raise NotImplementedError("Cannot turn object into user variable")
else:
a = value_to_ast(ival)
if a is None:
raise NotImplementedError("Cannot represent inlined value")
return a
if isinstance(ref, ast.Attribute) and isinstance(ref.value, ast.Name): if isinstance(ref, ast.Attribute) and isinstance(ref.value, ast.Name):
try: try:
@ -82,35 +63,31 @@ class _ReferenceManager:
pass pass
else: else:
if _is_in_attr_list(value, ref.attr, "kernel_attr_ro"): if _is_in_attr_list(value, ref.attr, "kernel_attr_ro"):
if isinstance(ref.ctx, ast.Store): if store:
raise TypeError("Attempted to assign to read-only kernel attribute") raise TypeError("Attempted to assign to read-only kernel attribute")
a = value_to_ast(getattr(value, ref.attr)) return getattr(value, ref.attr)
if a is None:
raise NotImplementedError("Cannot represent read-only kernel attribute")
return a
if _is_in_attr_list(value, ref.attr, "kernel_attr"): if _is_in_attr_list(value, ref.attr, "kernel_attr"):
key = (id(value), ref.attr, None) key = (id(value), ref.attr, None)
try: try:
ival = self.to_inlined[key] ival = self.to_inlined[key]
assert(isinstance(ival, _UserVariable)) assert(isinstance(ival, _UserVariable))
iname = ival.name
except KeyError: except KeyError:
iname = self.new_name(ref.attr) iname = self.new_name(ref.attr)
ival = _UserVariable(iname) ival = _UserVariable(iname)
self.to_inlined[key] = _UserVariable(iname) self.to_inlined[key] = ival
a = value_to_ast(getattr(value, ref.attr)) a = value_to_ast(getattr(value, ref.attr))
if a is None: if a is None:
raise NotImplementedError("Cannot represent initial value of kernel attribute") raise NotImplementedError("Cannot represent initial value of kernel attribute")
self.kernel_attr_init.append(ast.Assign( self.kernel_attr_init.append(ast.Assign(
[ast.Name(iname, ast.Store())], a)) [ast.Name(iname, ast.Store())], a))
return ast.Name(iname, ref.ctx) return ival
if not store: if not store:
repl = _replace_global(obj, ref) evd = self.get_constants(obj, funcname)
if repl is not None: evd.update(inspect.getmodule(obj).__dict__)
return repl return eval_ast(ref, evd)
else:
raise KeyError raise KeyError
def set(self, obj, funcname, name, value): def set(self, obj, funcname, name, value):
self.to_inlined[(id(obj), funcname, name)] = value self.to_inlined[(id(obj), funcname, name)] = value
@ -119,14 +96,14 @@ class _ReferenceManager:
return {local: v for (objid, funcname, local), v return {local: v for (objid, funcname, local), v
in self.to_inlined.items() in self.to_inlined.items()
if objid == id(r_obj) if objid == id(r_obj)
and funcname == r_funcname and funcname == r_funcname
and not isinstance(v, (_UserVariable, ast.AST))} and not isinstance(v, (_UserVariable, ast.AST))}
_embeddable_calls = { _embeddable_calls = {
units.Quantity,
core_language.delay, core_language.at, core_language.now, core_language.delay, core_language.at, core_language.now,
core_language.syscall, core_language.syscall,
range range,
Fraction, units.Quantity
} }
class _ReferenceReplacer(ast.NodeTransformer): class _ReferenceReplacer(ast.NodeTransformer):
@ -135,22 +112,30 @@ class _ReferenceReplacer(ast.NodeTransformer):
self.rm = rm self.rm = rm
self.obj = obj self.obj = obj
self.funcname = funcname self.funcname = funcname
self.module = inspect.getmodule(self.obj)
def visit_ref(self, node): def visit_ref(self, node):
return ast.copy_location( store = isinstance(node.ctx, ast.Store)
self.rm.get(self.obj, self.funcname, node), ival = self.rm.get(self.obj, self.funcname, node)
node) if isinstance(ival, _UserVariable):
newnode = ast.Name(ival.name, node.ctx)
elif isinstance(ival, ast.AST):
assert(not store)
newnode = ival
else:
if store:
raise NotImplementedError("Cannot turn object into user variable")
else:
newnode = value_to_ast(ival)
if newnode is None:
raise NotImplementedError("Cannot represent inlined value")
return ast.copy_location(newnode, node)
visit_Name = visit_ref visit_Name = visit_ref
visit_Attribute = visit_ref visit_Attribute = visit_ref
visit_Subscript = visit_ref visit_Subscript = visit_ref
def visit_Call(self, node): def visit_Call(self, node):
calldict = self.rm.get_constants(self.obj, self.funcname) func = self.rm.get(self.obj, self.funcname, node.func)
calldict.update(self.module.__dict__)
func = eval_ast(node.func, calldict)
new_args = [self.visit(arg) for arg in node.args] new_args = [self.visit(arg) for arg in node.args]
if func in _embeddable_calls: if func in _embeddable_calls: