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: 98b23eadf869
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: 2135e37dca0a
Choose a head ref
  • 9 commits
  • 9 files changed
  • 1 contributor

Commits on Aug 13, 2014

  1. Copy the full SHA
    a2691ab View commit details
  2. Copy the full SHA
    0145e52 View commit details
  3. Copy the full SHA
    fe5b3cc View commit details
  4. Copy the full SHA
    5481baa View commit details
  5. Copy the full SHA
    70fbc6a View commit details
  6. Copy the full SHA
    9cbf311 View commit details
  7. Copy the full SHA
    899a034 View commit details
  8. Copy the full SHA
    3446c1f View commit details
  9. Copy the full SHA
    2135e37 View commit details
32 changes: 5 additions & 27 deletions artiq/compiler/fold_constants.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,6 @@
import ast, operator

from artiq.language import units
from artiq.compiler.tools import value_to_ast, make_stmt_transformer

class _NotConstant(Exception):
pass

def _get_constant(node):
if isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.Str):
return node.s
elif isinstance(node, ast.Call) \
and isinstance(node.func, ast.Name) \
and node.func.id == "Quantity":
amount, unit = node.args
amount = _get_constant(amount)
try:
unit = getattr(units, unit.id)
except:
raise _NotConstant
return units.Quantity(amount, unit)
else:
raise _NotConstant
from artiq.compiler.tools import *

_ast_unops = {
ast.Invert: operator.inv,
@@ -50,8 +28,8 @@ class _ConstantFolder(ast.NodeTransformer):
def visit_UnaryOp(self, node):
self.generic_visit(node)
try:
operand = _get_constant(node.operand)
except _NotConstant:
operand = eval_constant(node.operand)
except NotConstant:
return node
try:
op = _ast_unops[type(node.op)]
@@ -66,8 +44,8 @@ def visit_UnaryOp(self, node):
def visit_BinOp(self, node):
self.generic_visit(node)
try:
left, right = _get_constant(node.left), _get_constant(node.right)
except _NotConstant:
left, right = eval_constant(node.left), eval_constant(node.right)
except NotConstant:
return node
try:
op = _ast_binops[type(node.op)]
91 changes: 38 additions & 53 deletions artiq/compiler/inline.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
from collections import namedtuple, defaultdict
from fractions import Fraction
import inspect, textwrap, ast

from artiq.compiler.tools import eval_ast, value_to_ast
from artiq.language import core as core_language
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")

def _is_in_attr_list(obj, attr, al):
@@ -30,12 +24,13 @@ def __init__(self):
self.kernel_attr_init = []

# 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:
self.use_count[kg] = 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):
if base_name[-1].isdigit():
@@ -54,26 +49,12 @@ def get(self, obj, funcname, ref):
if isinstance(ref, ast.Name):
key = (id(obj), funcname, ref.id)
try:
ival = self.to_inlined[key]
return self.to_inlined[key]
except KeyError:
if store:
iname = self.new_name(ref.id)
self.to_inlined[key] = _UserVariable(iname)
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)
ival = _UserVariable(self.new_name(ref.id))
self.to_inlined[key] = 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):
try:
@@ -82,35 +63,31 @@ def get(self, obj, funcname, ref):
pass
else:
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")
a = value_to_ast(getattr(value, ref.attr))
if a is None:
raise NotImplementedError("Cannot represent read-only kernel attribute")
return a
return getattr(value, ref.attr)
if _is_in_attr_list(value, ref.attr, "kernel_attr"):
key = (id(value), ref.attr, None)
try:
ival = self.to_inlined[key]
assert(isinstance(ival, _UserVariable))
iname = ival.name
except KeyError:
iname = self.new_name(ref.attr)
ival = _UserVariable(iname)
self.to_inlined[key] = _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 ast.Name(iname, ref.ctx)
return ival

if not store:
repl = _replace_global(obj, ref)
if repl is not None:
return repl

raise KeyError
evd = self.get_constants(obj, funcname)
evd.update(inspect.getmodule(obj).__dict__)
return eval_ast(ref, evd)
else:
raise KeyError

def set(self, obj, funcname, name, value):
self.to_inlined[(id(obj), funcname, name)] = value
@@ -119,14 +96,14 @@ 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))}
and funcname == r_funcname
and not isinstance(v, (_UserVariable, ast.AST))}

_embeddable_calls = {
units.Quantity,
core_language.delay, core_language.at, core_language.now,
core_language.syscall,
range
range,
Fraction, units.Quantity
}

class _ReferenceReplacer(ast.NodeTransformer):
@@ -135,22 +112,30 @@ def __init__(self, core, rm, obj, funcname):
self.rm = rm
self.obj = obj
self.funcname = funcname
self.module = inspect.getmodule(self.obj)

def visit_ref(self, node):
return ast.copy_location(
self.rm.get(self.obj, self.funcname, node),
node)
store = isinstance(node.ctx, ast.Store)
ival = self.rm.get(self.obj, self.funcname, 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_Attribute = visit_ref
visit_Subscript = visit_ref

def visit_Call(self, node):
calldict = self.rm.get_constants(self.obj, self.funcname)
calldict.update(self.module.__dict__)
func = eval_ast(node.func, calldict)

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

if func in _embeddable_calls:
@@ -164,7 +149,7 @@ def visit_Call(self, node):
inlined, _ = inline(self.core, func.k_function_info.k_function, args, dict(), self.rm)
return inlined
else:
args = [ast.Str("rpc"), ast.Num(self.rm.rpc_map[func])]
args = [ast.Str("rpc"), value_to_ast(self.rm.rpc_map[func])]
args += new_args
return ast.copy_location(
ast.Call(func=ast.Name("syscall", ast.Load()),
21 changes: 9 additions & 12 deletions artiq/compiler/interleave.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import ast, types

from artiq.language import units
from artiq.compiler.tools import eval_ast
from artiq.compiler.tools import *

# -1 statement duration could not be pre-determined
# 0 statement has no effect on timeline
# >0 statement is a static delay that advances the timeline
# by the given amount (in base_s_unit)
# by the given amount (in seconds)
def _get_duration(stmt):
if isinstance(stmt, (ast.Expr, ast.Assign)):
return _get_duration(stmt.value)
@@ -18,16 +18,13 @@ def _get_duration(stmt):
elif isinstance(stmt, ast.Call) and isinstance(stmt.func, ast.Name):
name = stmt.func.id
if name == "delay":
da = stmt.args[0]
if isinstance(da, ast.Call) \
and isinstance(da.func, ast.Name) \
and da.func.id == "Quantity" \
and isinstance(da.args[0], ast.Num):
if not isinstance(da.args[1], ast.Name) or da.args[1].id != "base_s_unit":
raise units.DimensionError("Delay not expressed in seconds")
return da.args[0].n
else:
try:
da = eval_constant(stmt.args[0])
except NotConstant:
return -1
if da.unit != units.s_unit:
raise units.DimensionError("Delay not expressed in seconds")
return da.amount
else:
return 0
else:
@@ -59,7 +56,7 @@ def _interleave_timelines(timelines):
ref_stmt = stmt.stmt
da_expr = ast.copy_location(
ast.Call(func=ast.Name("Quantity", ast.Load()),
args=[ast.Num(dt), ast.Name("base_s_unit", ast.Load())],
args=[value_to_ast(dt), ast.Name("s_unit", ast.Load())],
keywords=[], starargs=[], kwargs=[]),
ref_stmt)
delay_stmt = ast.copy_location(
4 changes: 4 additions & 0 deletions artiq/compiler/ir.py
Original file line number Diff line number Diff line change
@@ -72,6 +72,10 @@ def _emit_expr(env, builder, ns, node):
[_emit_expr(env, builder, ns, expr) for expr in node.args[1:]])
elif node.func.id == "Quantity":
return _emit_expr(env, builder, ns, node.args[0])
elif node.func.id == "Fraction":
if not isinstance(node.args[1], ast.Num) or node.args[1].n != 1:
raise NotImplementedError
return _emit_expr(env, builder, ns, node.args[0])
else:
raise NotImplementedError
else:
7 changes: 4 additions & 3 deletions artiq/compiler/lower_time.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import ast
from artiq.compiler.tools import value_to_ast

class _TimeLowerer(ast.NodeTransformer):
def __init__(self, ref_period):
self.ref_period = ref_period

def visit_Call(self, node):
if isinstance(node.func, ast.Name) \
and node.func.id == "Quantity" and node.args[1].id == "base_s_unit":
and node.func.id == "Quantity" and node.args[1].id == "s_unit":
return ast.copy_location(
ast.BinOp(left=node.args[0], op=ast.FloorDiv(), right=ast.Num(self.ref_period.amount)),
ast.BinOp(left=node.args[0], op=ast.Div(), right=value_to_ast(self.ref_period.amount)),
node)
elif isinstance(node.func, ast.Name) and node.func.id == "now":
return ast.copy_location(ast.Name("now", ast.Load()), node)
@@ -37,6 +38,6 @@ def lower_time(stmts, ref_period, initial_time):
transformer = _TimeLowerer(ref_period)
new_stmts = [transformer.visit(stmt) for stmt in stmts]
new_stmts.insert(0, ast.copy_location(
ast.Assign(targets=[ast.Name("now", ast.Store())], value=ast.Num(initial_time)),
ast.Assign(targets=[ast.Name("now", ast.Store())], value=value_to_ast(initial_time)),
stmts[0]))
stmts[:] = new_stmts
35 changes: 32 additions & 3 deletions artiq/compiler/tools.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ast
from fractions import Fraction

from artiq.language import core as core_language
from artiq.language import units
@@ -13,6 +14,10 @@ def eval_ast(expr, symdict=dict()):
def value_to_ast(value):
if isinstance(value, int):
return ast.Num(value)
elif isinstance(value, Fraction):
return ast.Call(func=ast.Name("Fraction", ast.Load()),
args=[ast.Num(value.numerator), ast.Num(value.denominator)],
keywords=[], starargs=None, kwargs=None)
elif isinstance(value, str):
return ast.Str(value)
else:
@@ -21,11 +26,35 @@ def value_to_ast(value):
return ast.Name(kg, ast.Load())
if isinstance(value, units.Quantity):
return ast.Call(
func=ast.Name("Quantity", ast.Load()),
args=[ast.Num(value.amount), ast.Name("base_"+value.unit.name+"_unit", ast.Load())],
keywords=[], starargs=None, kwargs=None)
func=ast.Name("Quantity", ast.Load()),
args=[value_to_ast(value.amount), ast.Name(value.unit.name+"_unit", ast.Load())],
keywords=[], starargs=None, kwargs=None)
return None

class NotConstant(Exception):
pass

def eval_constant(node):
if isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.Str):
return node.s
elif isinstance(node, ast.Call):
funcname = node.func.id
if funcname == "Fraction":
numerator, denominator = eval_constant(node.args[0]), eval_constant(node.args[1])
return Fraction(numerator, denominator)
elif funcname == "Quantity":
amount, unit = node.args
amount = eval_constant(amount)
try:
unit = getattr(units, unit.id)
except:
raise NotConstant
return units.Quantity(amount, unit)
else:
raise NotConstant

def make_stmt_transformer(transformer_class):
def stmt_transformer(stmts, *args, **kwargs):
transformer = transformer_class(*args, **kwargs)
4 changes: 2 additions & 2 deletions artiq/compiler/unroll_loops.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ast

from artiq.compiler.tools import eval_ast, make_stmt_transformer
from artiq.compiler.tools import eval_ast, make_stmt_transformer, value_to_ast

def _count_stmts(node):
if isinstance(node, (ast.For, ast.While, ast.If)):
@@ -32,7 +32,7 @@ def visit_For(self, node):
replacement = None
break
replacement.append(ast.copy_location(
ast.Assign(targets=[node.target], value=ast.Num(n=i)), node))
ast.Assign(targets=[node.target], value=value_to_ast(i)), node))
replacement += node.body
if replacement is not None:
return replacement
1 change: 0 additions & 1 deletion artiq/language/core.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import itertools
from collections import namedtuple

from artiq.language import units
Loading