Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: b207a3cef591
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 71adcb74bf2c
Choose a head ref
  • 4 commits
  • 3 files changed
  • 1 contributor

Commits on Sep 13, 2014

  1. Copy the full SHA
    3d440d5 View commit details
  2. Copy the full SHA
    dc9515f View commit details
  3. Copy the full SHA
    123ddb2 View commit details
  4. Copy the full SHA
    71adcb7 View commit details
Showing with 106 additions and 99 deletions.
  1. +0 −13 artiq/language/core.py
  2. +3 −0 artiq/py2llvm/ast_body.py
  3. +103 −86 artiq/transforms/inline.py
13 changes: 0 additions & 13 deletions artiq/language/core.py
Original file line number Diff line number Diff line change
@@ -40,23 +40,14 @@ def array(element, count):
return [copy(element) for i in range(count)]


def _make_kernel_ro(value):
return isinstance(
value, (bool, int, int64, float, Fraction, units.Quantity))


class AutoContext:
parameters = ""
implicit_core = True

def __init__(self, mvs=None, **kwargs):
kernel_attr_ro = []

self.mvs = mvs
for k, v in kwargs.items():
setattr(self, k, v)
if _make_kernel_ro(v):
kernel_attr_ro.append(k)

parameters = self.parameters.split()
if self.implicit_core:
@@ -67,10 +58,6 @@ def __init__(self, mvs=None, **kwargs):
except AttributeError:
value = self.mvs.get_missing_value(parameter)
setattr(self, parameter, value)
if _make_kernel_ro(value):
kernel_attr_ro.append(parameter)

self.kernel_attr_ro = " ".join(kernel_attr_ro)

self.build()

3 changes: 3 additions & 0 deletions artiq/py2llvm/ast_body.py
Original file line number Diff line number Diff line change
@@ -271,3 +271,6 @@ def _visit_stmt_Return(self, node):
self.builder.ret_void()
else:
self.builder.ret(val.auto_load(self.builder))

def _visit_stmt_Pass(self, node):
pass
189 changes: 103 additions & 86 deletions artiq/transforms/inline.py
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
import inspect
import textwrap
import ast
import builtins

from artiq.transforms.tools import eval_ast, value_to_ast
from artiq.language import core as core_language
@@ -20,9 +21,8 @@ def _is_in_attr_list(obj, attr, al):

class _ReferenceManager:
def __init__(self):
# (id(obj), funcname, local)
# (id(obj), func_name, local_name) or (id(obj), kernel_attr_name)
# -> _UserVariable(name) / ast / constant_object
# local is None for kernel attributes
self.to_inlined = dict()
# inlined_name -> use_count
self.use_count = dict()
@@ -48,66 +48,44 @@ def new_name(self, base_name):
self.use_count[base_name] = 1
return base_name

def get(self, obj, funcname, ref):
store = isinstance(ref.ctx, ast.Store)

def get(self, obj, func_name, ref):
if isinstance(ref, ast.Name):
key = (id(obj), funcname, ref.id)
key = (id(obj), func_name, ref.id)
try:
return self.to_inlined[key]
except KeyError:
if store:
if isinstance(ref.ctx, ast.Store):
ival = _UserVariable(self.new_name(ref.id))
self.to_inlined[key] = ival
return ival

if isinstance(ref, ast.Attribute) and isinstance(ref.value, ast.Name):
try:
value = self.to_inlined[(id(obj), funcname, ref.value.id)]
except KeyError:
pass
else:
if _is_in_attr_list(value, ref.attr, "kernel_attr_ro"):
if store:
raise TypeError(
"Attempted to assign to read-only"
" kernel attribute")
return getattr(value, ref.attr)
if _is_in_attr_list(value, ref.attr, "kernel_attr"):
key = (id(value), ref.attr, None)
else:
try:
ival = self.to_inlined[key]
assert(isinstance(ival, _UserVariable))
return inspect.getmodule(obj).__dict__[ref.id]
except KeyError:
iname = self.new_name(ref.attr)
ival = _UserVariable(iname)
self.to_inlined[key] = ival
a = value_to_ast(getattr(value, ref.attr))
if a is None:
raise NotImplementedError(
"Cannot represent initial value"
" of kernel attribute")
self.kernel_attr_init.append(ast.Assign(
[ast.Name(iname, ast.Store())], a))
return ival

if not store:
evd = self.get_constants(obj, funcname)
evd.update(inspect.getmodule(obj).__dict__)
return eval_ast(ref, evd)
return getattr(builtins, ref.id)
elif isinstance(ref, ast.Attribute):
target = self.get(obj, func_name, ref.value)
if _is_in_attr_list(target, ref.attr, "kernel_attr"):
key = (id(target), ref.attr)
try:
ival = self.to_inlined[key]
assert(isinstance(ival, _UserVariable))
except KeyError:
iname = self.new_name(ref.attr)
ival = _UserVariable(iname)
self.to_inlined[key] = ival
a = value_to_ast(getattr(target, ref.attr))
if a is None:
raise NotImplementedError(
"Cannot represent initial value"
" of kernel attribute")
self.kernel_attr_init.append(ast.Assign(
[ast.Name(iname, ast.Store())], a))
return ival
else:
return getattr(target, ref.attr)
else:
raise KeyError(ast.dump(ref))

def set(self, obj, funcname, name, value):
self.to_inlined[(id(obj), funcname, name)] = value

def get_constants(self, r_obj, r_funcname):
return {
local: v for (objid, funcname, local), v
in self.to_inlined.items()
if objid == id(r_obj)
and funcname == r_funcname
and not isinstance(v, (_UserVariable, ast.AST))}
raise NotImplementedError


_embeddable_calls = {
@@ -119,16 +97,47 @@ def get_constants(self, r_obj, r_funcname):
}


class _ReferenceReplacer(ast.NodeTransformer):
def __init__(self, core, rm, obj, funcname):
class _ReferenceReplacer(ast.NodeVisitor):
def __init__(self, core, rm, obj, func_name, retval_name):
self.core = core
self.rm = rm
self.obj = obj
self.funcname = funcname
self.func_name = func_name
self.retval_name = retval_name
self._insertion_point = None

# This is ast.NodeTransformer.generic_visit from CPython, modified
# to update self._insertion_point.
def generic_visit(self, node):
for field, old_value in ast.iter_fields(node):
old_value = getattr(node, field, None)
if isinstance(old_value, list):
prev_insertion_point = self._insertion_point
new_values = []
if field in ("body", "orelse", "finalbody"):
self._insertion_point = new_values
for value in old_value:
if isinstance(value, ast.AST):
value = self.visit(value)
if value is None:
continue
elif not isinstance(value, ast.AST):
new_values.extend(value)
continue
new_values.append(value)
old_value[:] = new_values
self._insertion_point = prev_insertion_point
elif isinstance(old_value, ast.AST):
new_node = self.visit(old_value)
if new_node is None:
delattr(node, field)
else:
setattr(node, field, new_node)
return node

def visit_ref(self, node):
store = isinstance(node.ctx, ast.Store)
ival = self.rm.get(self.obj, self.funcname, node)
ival = self.rm.get(self.obj, self.func_name, node)
if isinstance(ival, _UserVariable):
newnode = ast.Name(ival.name, node.ctx)
elif isinstance(ival, ast.AST):
@@ -149,7 +158,7 @@ def visit_ref(self, node):
visit_Attribute = visit_ref

def visit_Call(self, node):
func = self.rm.get(self.obj, self.funcname, node.func)
func = self.rm.get(self.obj, self.func_name, node.func)
new_args = [self.visit(arg) for arg in node.args]

if func in _embeddable_calls:
@@ -161,15 +170,17 @@ def visit_Call(self, node):
elif (hasattr(func, "k_function_info")
and getattr(func.__self__, func.k_function_info.core_name)
is self.core):
retval_name = self.rm.new_name(
func.k_function_info.k_function.__name__ + "_return")
args = [func.__self__] + new_args
inlined, _ = inline(self.core, func.k_function_info.k_function,
args, dict(), self.rm)
r = ast.With(
args, dict(), self.rm, retval_name)
self._insertion_point.append(ast.With(
items=[ast.withitem(context_expr=ast.Name(id="sequential",
ctx=ast.Load()),
optional_vars=None)],
body=inlined.body)
return ast.copy_location(r, node)
body=inlined.body))
return ast.copy_location(ast.Name(retval_name, ast.Load()), node)
else:
args = [ast.Str("rpc"), value_to_ast(self.rm.rpc_map[func])]
args += new_args
@@ -178,16 +189,22 @@ def visit_Call(self, node):
args=args, keywords=[], starargs=None, kwargs=None),
node)

def visit_Return(self, node):
self.generic_visit(node)
return ast.copy_location(
ast.Assign(targets=[ast.Name(self.retval_name, ast.Store())],
value=node.value),
node)

def visit_Expr(self, node):
if isinstance(node.value, ast.Call):
r = self.visit_Call(node.value)
if isinstance(r, ast.With):
return r
else:
node.value = r
return node
self.generic_visit(node)
if isinstance(node.value, ast.Name):
# Remove Expr nodes that contain only a name, likely due to
# function call inlining. Such nodes that were originally in the
# code are also removed, but this does not affect the semantics of
# the code as they are nops.
return None
else:
self.generic_visit(node)
return node

def visit_FunctionDef(self, node):
@@ -213,46 +230,46 @@ def visit_Name(self, node):
pass


def _list_read_only_params(funcdef):
def _list_read_only_params(func_def):
lrp = _ListReadOnlyParams()
lrp.visit(funcdef)
lrp.visit(func_def)
return lrp.read_only_params


def _initialize_function_params(funcdef, k_args, k_kwargs, rm):
def _initialize_function_params(func_def, k_args, k_kwargs, rm):
obj = k_args[0]
funcname = funcdef.name
func_name = func_def.name
param_init = []
rop = _list_read_only_params(funcdef)
for arg_ast, arg_value in zip(funcdef.args.args, k_args):
rop = _list_read_only_params(func_def)
for arg_ast, arg_value in zip(func_def.args.args, k_args):
arg_name = arg_ast.arg
if arg_name in rop:
rm.set(obj, funcname, arg_name, arg_value)
rm.to_inlined[(id(obj), func_name, arg_name)] = arg_value
else:
target = rm.get(obj, funcname, ast.Name(arg_name, ast.Store()))
target = rm.get(obj, func_name, ast.Name(arg_name, ast.Store()))
value = value_to_ast(arg_value)
param_init.append(ast.Assign(targets=[target], value=value))
return param_init


def inline(core, k_function, k_args, k_kwargs, rm=None):
def inline(core, k_function, k_args, k_kwargs, rm=None, retval_name=None):
init_kernel_attr = rm is None
if rm is None:
rm = _ReferenceManager()

funcdef = ast.parse(textwrap.dedent(inspect.getsource(k_function))).body[0]
func_def = ast.parse(textwrap.dedent(inspect.getsource(k_function))).body[0]

param_init = _initialize_function_params(funcdef, k_args, k_kwargs, rm)
param_init = _initialize_function_params(func_def, k_args, k_kwargs, rm)

obj = k_args[0]
funcname = funcdef.name
rr = _ReferenceReplacer(core, rm, obj, funcname)
rr.visit(funcdef)
func_name = func_def.name
rr = _ReferenceReplacer(core, rm, obj, func_name, retval_name)
rr.visit(func_def)

funcdef.body[0:0] = param_init
func_def.body[0:0] = param_init
if init_kernel_attr:
funcdef.body[0:0] = rm.kernel_attr_init
func_def.body[0:0] = rm.kernel_attr_init

r_rpc_map = dict((rpc_num, rpc_fun)
for rpc_fun, rpc_num in rm.rpc_map.items())
return funcdef, r_rpc_map
return func_def, r_rpc_map