mirror of https://github.com/m-labs/artiq.git
compiler.{iodelay,transforms.iodelay_estimator}: implement.
This commit is contained in:
parent
3af54f5ffc
commit
b971cc8cdf
|
@ -138,6 +138,12 @@ def fn_print():
|
||||||
def fn_kernel():
|
def fn_kernel():
|
||||||
return types.TBuiltinFunction("kernel")
|
return types.TBuiltinFunction("kernel")
|
||||||
|
|
||||||
|
def fn_parallel():
|
||||||
|
return types.TBuiltinFunction("parallel")
|
||||||
|
|
||||||
|
def fn_sequential():
|
||||||
|
return types.TBuiltinFunction("sequential")
|
||||||
|
|
||||||
def fn_now():
|
def fn_now():
|
||||||
return types.TBuiltinFunction("now")
|
return types.TBuiltinFunction("now")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,285 @@
|
||||||
|
"""
|
||||||
|
The :mod:`iodelay` module contains the classes describing
|
||||||
|
the statically inferred RTIO delay arising from executing
|
||||||
|
a function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pythonparser import diagnostic
|
||||||
|
|
||||||
|
|
||||||
|
class Expr:
|
||||||
|
def __add__(lhs, rhs):
|
||||||
|
assert isinstance(rhs, Expr)
|
||||||
|
return Add(lhs, rhs)
|
||||||
|
__iadd__ = __add__
|
||||||
|
|
||||||
|
def __sub__(lhs, rhs):
|
||||||
|
assert isinstance(rhs, Expr)
|
||||||
|
return Sub(lhs, rhs)
|
||||||
|
__isub__ = __sub__
|
||||||
|
|
||||||
|
def __mul__(lhs, rhs):
|
||||||
|
assert isinstance(rhs, Expr)
|
||||||
|
return Mul(lhs, rhs)
|
||||||
|
__imul__ = __mul__
|
||||||
|
|
||||||
|
def __truediv__(lhs, rhs):
|
||||||
|
assert isinstance(rhs, Expr)
|
||||||
|
return TrueDiv(lhs, rhs)
|
||||||
|
__itruediv__ = __truediv__
|
||||||
|
|
||||||
|
def __floordiv__(lhs, rhs):
|
||||||
|
assert isinstance(rhs, Expr)
|
||||||
|
return FloorDiv(lhs, rhs)
|
||||||
|
__ifloordiv__ = __floordiv__
|
||||||
|
|
||||||
|
def __ne__(lhs, rhs):
|
||||||
|
return not (lhs == rhs)
|
||||||
|
|
||||||
|
def free_vars(self):
|
||||||
|
return set()
|
||||||
|
|
||||||
|
def fold(self, vars=None):
|
||||||
|
return self
|
||||||
|
|
||||||
|
class Const(Expr):
|
||||||
|
_priority = 1
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
assert isinstance(value, (int, float))
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.value)
|
||||||
|
|
||||||
|
def __eq__(lhs, rhs):
|
||||||
|
return rhs.__class__ == lhs.__class__ and lhs.value == rhs.value
|
||||||
|
|
||||||
|
def eval(self, env):
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
class Var(Expr):
|
||||||
|
_priority = 1
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
assert isinstance(name, str)
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __eq__(lhs, rhs):
|
||||||
|
return rhs.__class__ == lhs.__class__ and lhs.value == rhs.value
|
||||||
|
|
||||||
|
def free_vars(self):
|
||||||
|
return {self.name}
|
||||||
|
|
||||||
|
def fold(self, vars=None):
|
||||||
|
if vars is not None and self.name in vars:
|
||||||
|
return vars[self.name]
|
||||||
|
else:
|
||||||
|
return self
|
||||||
|
|
||||||
|
class Conv(Expr):
|
||||||
|
_priority = 1
|
||||||
|
|
||||||
|
def __init__(self, operand, ref_period):
|
||||||
|
assert isinstance(operand, Expr)
|
||||||
|
assert isinstance(ref_period, float)
|
||||||
|
self.operand, self.ref_period = operand, ref_period
|
||||||
|
|
||||||
|
def free_vars(self):
|
||||||
|
return self.operand.free_vars()
|
||||||
|
|
||||||
|
def fold(self, vars=None):
|
||||||
|
return self.__class__(self.operand.fold(vars),
|
||||||
|
ref_period=self.ref_period)
|
||||||
|
|
||||||
|
class MUToS(Conv):
|
||||||
|
def __str__(self):
|
||||||
|
return "mu->s({})".format(self.operand)
|
||||||
|
|
||||||
|
def eval(self, env):
|
||||||
|
return self.operand.eval(env) * self.ref_period
|
||||||
|
|
||||||
|
class SToMU(Conv):
|
||||||
|
def __str__(self):
|
||||||
|
return "s->mu({})".format(self.operand)
|
||||||
|
|
||||||
|
def eval(self, env):
|
||||||
|
return self.operand.eval(env) / self.ref_period
|
||||||
|
|
||||||
|
class BinOp(Expr):
|
||||||
|
def __init__(self, lhs, rhs):
|
||||||
|
self.lhs, self.rhs = lhs, rhs
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
lhs = "({})".format(self.lhs) if self.lhs._priority > self._priority else str(self.lhs)
|
||||||
|
rhs = "({})".format(self.rhs) if self.rhs._priority > self._priority else str(self.rhs)
|
||||||
|
return "{} {} {}".format(lhs, self._symbol, rhs)
|
||||||
|
|
||||||
|
def __eq__(lhs, rhs):
|
||||||
|
return rhs.__class__ == lhs.__class__ and lhs.lhs == rhs.lhs and rhs.lhs == rhs.rhs
|
||||||
|
|
||||||
|
def eval(self, env):
|
||||||
|
return self.__class__._op(self.lhs.eval(env), self.rhs.eval(env))
|
||||||
|
|
||||||
|
def free_vars(self):
|
||||||
|
return self.lhs.free_vars() | self.rhs.free_vars()
|
||||||
|
|
||||||
|
def _fold_binop(self, lhs, rhs):
|
||||||
|
if isinstance(lhs, Const) and lhs.__class__ == rhs.__class__:
|
||||||
|
return Const(self.__class__._op(lhs.value, rhs.value))
|
||||||
|
elif isinstance(lhs, (MUToS, SToMU)) and lhs.__class__ == rhs.__class__:
|
||||||
|
return lhs.__class__(self.__class__(lhs.operand, rhs.operand),
|
||||||
|
ref_period=lhs.ref_period).fold()
|
||||||
|
else:
|
||||||
|
return self.__class__(lhs, rhs)
|
||||||
|
|
||||||
|
def fold(self, vars=None):
|
||||||
|
return self._fold_binop(self.lhs.fold(vars), self.rhs.fold(vars))
|
||||||
|
|
||||||
|
class BinOpFixpoint(BinOp):
|
||||||
|
def _fold_binop(self, lhs, rhs):
|
||||||
|
if isinstance(lhs, Const) and lhs.value == self._fixpoint:
|
||||||
|
return rhs
|
||||||
|
elif isinstance(rhs, Const) and rhs.value == self._fixpoint:
|
||||||
|
return lhs
|
||||||
|
else:
|
||||||
|
return super()._fold_binop(lhs, rhs)
|
||||||
|
|
||||||
|
class Add(BinOpFixpoint):
|
||||||
|
_priority = 2
|
||||||
|
_symbol = "+"
|
||||||
|
_op = lambda a, b: a + b
|
||||||
|
_fixpoint = 0
|
||||||
|
|
||||||
|
class Sub(BinOpFixpoint):
|
||||||
|
_priority = 2
|
||||||
|
_symbol = "-"
|
||||||
|
_op = lambda a, b: a - b
|
||||||
|
_fixpoint = 0
|
||||||
|
|
||||||
|
class Mul(BinOpFixpoint):
|
||||||
|
_priority = 1
|
||||||
|
_symbol = "*"
|
||||||
|
_op = lambda a, b: a * b
|
||||||
|
_fixpoint = 1
|
||||||
|
|
||||||
|
class Div(BinOp):
|
||||||
|
def _fold_binop(self, lhs, rhs):
|
||||||
|
if isinstance(rhs, Const) and rhs.value == 1:
|
||||||
|
return lhs
|
||||||
|
else:
|
||||||
|
return super()._fold_binop(lhs, rhs)
|
||||||
|
|
||||||
|
class TrueDiv(Div):
|
||||||
|
_priority = 1
|
||||||
|
_symbol = "/"
|
||||||
|
_op = lambda a, b: a / b if b != 0 else 0
|
||||||
|
|
||||||
|
class FloorDiv(Div):
|
||||||
|
_priority = 1
|
||||||
|
_symbol = "//"
|
||||||
|
_op = lambda a, b: a // b if b != 0 else 0
|
||||||
|
|
||||||
|
class Max(Expr):
|
||||||
|
_priority = 1
|
||||||
|
|
||||||
|
def __init__(self, operands):
|
||||||
|
assert isinstance(operands, list)
|
||||||
|
assert all([isinstance(operand, Expr) for operand in operands])
|
||||||
|
self.operands = operands
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "max({})".format(", ".join([str(operand) for operand in self.operands]))
|
||||||
|
|
||||||
|
def __eq__(lhs, rhs):
|
||||||
|
return rhs.__class__ == lhs.__class__ and lhs.operands == rhs.operands
|
||||||
|
|
||||||
|
def free_vars(self):
|
||||||
|
return reduce(lambda a, b: a | b, [operand.free_vars() for operand in self.operands])
|
||||||
|
|
||||||
|
def eval(self, env):
|
||||||
|
return max([operand.eval() for operand in self.operands])
|
||||||
|
|
||||||
|
def fold(self, vars=None):
|
||||||
|
consts, exprs = [], []
|
||||||
|
for operand in self.operands:
|
||||||
|
operand = operand.fold(vars)
|
||||||
|
if isinstance(operand, Const):
|
||||||
|
consts.append(operand.value)
|
||||||
|
else:
|
||||||
|
exprs.append(operand)
|
||||||
|
if any(consts):
|
||||||
|
exprs.append(Const(max(consts)))
|
||||||
|
if len(exprs) == 1:
|
||||||
|
return exprs[0]
|
||||||
|
else:
|
||||||
|
return Max(exprs)
|
||||||
|
|
||||||
|
def is_const(expr, value=None):
|
||||||
|
expr = expr.fold()
|
||||||
|
if value is None:
|
||||||
|
return isinstance(expr, Const)
|
||||||
|
else:
|
||||||
|
return isinstance(expr, Const) and expr.value == value
|
||||||
|
|
||||||
|
def is_zero(expr):
|
||||||
|
return is_const(expr, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class Delay:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Unknown(Delay):
|
||||||
|
"""
|
||||||
|
Unknown delay, that is, IO delay that we have not
|
||||||
|
tried to compute yet.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "{}.Unknown()".format(__name__)
|
||||||
|
|
||||||
|
class Indeterminate(Delay):
|
||||||
|
"""
|
||||||
|
Indeterminate delay, that is, IO delay that can vary from
|
||||||
|
invocation to invocation.
|
||||||
|
|
||||||
|
:ivar cause: (:class:`pythonparser.diagnostic.Diagnostic`)
|
||||||
|
reason for the delay not being inferred
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, cause):
|
||||||
|
assert isinstance(cause, diagnostic.Diagnostic)
|
||||||
|
self.cause = cause
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<{}.Indeterminate>".format(__name__)
|
||||||
|
|
||||||
|
class Fixed(Delay):
|
||||||
|
"""
|
||||||
|
Fixed delay, that is, IO delay that is always the same
|
||||||
|
for every invocation.
|
||||||
|
|
||||||
|
:ivar length: (int) delay in machine units
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, length):
|
||||||
|
assert isinstance(length, Expr)
|
||||||
|
self.length = length
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "{}.Fixed({})".format(__name__, self.length)
|
||||||
|
|
||||||
|
def is_unknown(delay):
|
||||||
|
return isinstance(delay, Unknown)
|
||||||
|
|
||||||
|
def is_indeterminate(delay):
|
||||||
|
return isinstance(delay, Indeterminate)
|
||||||
|
|
||||||
|
def is_fixed(delay, length=None):
|
||||||
|
if length is None:
|
||||||
|
return isinstance(delay, Fixed)
|
||||||
|
else:
|
||||||
|
return isinstance(delay, Fixed) and delay.length == length
|
|
@ -50,6 +50,7 @@ class Module:
|
||||||
inferencer = transforms.Inferencer(engine=self.engine)
|
inferencer = transforms.Inferencer(engine=self.engine)
|
||||||
monomorphism_validator = validators.MonomorphismValidator(engine=self.engine)
|
monomorphism_validator = validators.MonomorphismValidator(engine=self.engine)
|
||||||
escape_validator = validators.EscapeValidator(engine=self.engine)
|
escape_validator = validators.EscapeValidator(engine=self.engine)
|
||||||
|
iodelay_estimator = transforms.IODelayEstimator(ref_period=ref_period)
|
||||||
artiq_ir_generator = transforms.ARTIQIRGenerator(engine=self.engine,
|
artiq_ir_generator = transforms.ARTIQIRGenerator(engine=self.engine,
|
||||||
module_name=src.name,
|
module_name=src.name,
|
||||||
ref_period=ref_period)
|
ref_period=ref_period)
|
||||||
|
@ -62,6 +63,7 @@ class Module:
|
||||||
inferencer.visit(src.typedtree)
|
inferencer.visit(src.typedtree)
|
||||||
monomorphism_validator.visit(src.typedtree)
|
monomorphism_validator.visit(src.typedtree)
|
||||||
escape_validator.visit(src.typedtree)
|
escape_validator.visit(src.typedtree)
|
||||||
|
iodelay_estimator.visit_fixpoint(src.typedtree)
|
||||||
self.artiq_ir = artiq_ir_generator.visit(src.typedtree)
|
self.artiq_ir = artiq_ir_generator.visit(src.typedtree)
|
||||||
dead_code_eliminator.process(self.artiq_ir)
|
dead_code_eliminator.process(self.artiq_ir)
|
||||||
local_access_validator.process(self.artiq_ir)
|
local_access_validator.process(self.artiq_ir)
|
||||||
|
|
|
@ -28,6 +28,10 @@ def globals():
|
||||||
# ARTIQ decorators
|
# ARTIQ decorators
|
||||||
"kernel": builtins.fn_kernel(),
|
"kernel": builtins.fn_kernel(),
|
||||||
|
|
||||||
|
# ARTIQ context managers
|
||||||
|
"parallel": builtins.fn_parallel(),
|
||||||
|
"sequential": builtins.fn_sequential(),
|
||||||
|
|
||||||
# ARTIQ time management functions
|
# ARTIQ time management functions
|
||||||
"now": builtins.fn_now(),
|
"now": builtins.fn_now(),
|
||||||
"delay": builtins.fn_delay(),
|
"delay": builtins.fn_delay(),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import sys, fileinput
|
import sys, fileinput
|
||||||
from pythonparser import diagnostic
|
from pythonparser import diagnostic
|
||||||
from .. import Module, Source
|
from .. import types, iodelay, Module, Source
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == "+diag":
|
if len(sys.argv) > 1 and sys.argv[1] == "+diag":
|
||||||
|
@ -13,15 +13,28 @@ def main():
|
||||||
else:
|
else:
|
||||||
diag = False
|
diag = False
|
||||||
def process_diagnostic(diag):
|
def process_diagnostic(diag):
|
||||||
print("\n".join(diag.render()))
|
print("\n".join(diag.render(colored=True)))
|
||||||
if diag.level in ("fatal", "error"):
|
if diag.level in ("fatal", "error"):
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] == "+delay":
|
||||||
|
del sys.argv[1]
|
||||||
|
force_delays = True
|
||||||
|
else:
|
||||||
|
force_delays = False
|
||||||
|
|
||||||
engine = diagnostic.Engine()
|
engine = diagnostic.Engine()
|
||||||
engine.process = process_diagnostic
|
engine.process = process_diagnostic
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mod = Module(Source.from_string("".join(fileinput.input()).expandtabs(), engine=engine))
|
mod = Module(Source.from_string("".join(fileinput.input()).expandtabs(), engine=engine))
|
||||||
|
|
||||||
|
if force_delays:
|
||||||
|
for var in mod.globals:
|
||||||
|
typ = mod.globals[var].find()
|
||||||
|
if types.is_function(typ) and iodelay.is_indeterminate(typ.delay):
|
||||||
|
process_diagnostic(typ.delay.cause)
|
||||||
|
|
||||||
print(repr(mod))
|
print(repr(mod))
|
||||||
except:
|
except:
|
||||||
if not diag: raise
|
if not diag: raise
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from .asttyped_rewriter import ASTTypedRewriter
|
from .asttyped_rewriter import ASTTypedRewriter
|
||||||
from .inferencer import Inferencer
|
from .inferencer import Inferencer
|
||||||
from .int_monomorphizer import IntMonomorphizer
|
from .int_monomorphizer import IntMonomorphizer
|
||||||
|
from .iodelay_estimator import IODelayEstimator
|
||||||
from .artiq_ir_generator import ARTIQIRGenerator
|
from .artiq_ir_generator import ARTIQIRGenerator
|
||||||
from .dead_code_eliminator import DeadCodeEliminator
|
from .dead_code_eliminator import DeadCodeEliminator
|
||||||
from .llvm_ir_generator import LLVMIRGenerator
|
from .llvm_ir_generator import LLVMIRGenerator
|
||||||
|
|
|
@ -945,13 +945,19 @@ class Inferencer(algorithm.Visitor):
|
||||||
|
|
||||||
def visit_withitem(self, node):
|
def visit_withitem(self, node):
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
if True: # none are supported yet
|
|
||||||
|
typ = node.context_expr.type
|
||||||
|
if not types.is_builtin(typ, "parallel") or types.is_builtin(typ, "sequential"):
|
||||||
diag = diagnostic.Diagnostic("error",
|
diag = diagnostic.Diagnostic("error",
|
||||||
"value of type {type} cannot act as a context manager",
|
"value of type {type} cannot act as a context manager",
|
||||||
{"type": types.TypePrinter().name(node.context_expr.type)},
|
{"type": types.TypePrinter().name(typ)},
|
||||||
node.context_expr.loc)
|
node.context_expr.loc)
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
|
|
||||||
|
if node.optional_vars is not None:
|
||||||
|
self._unify(node.optional_vars.type, node.context_expr.type,
|
||||||
|
node.optional_vars.loc, node.context_expr.loc)
|
||||||
|
|
||||||
def visit_ExceptHandlerT(self, node):
|
def visit_ExceptHandlerT(self, node):
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
"""
|
||||||
|
:class:`IODelayEstimator` calculates the amount of time
|
||||||
|
elapsed from the point of view of the RTIO core for
|
||||||
|
every function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pythonparser import ast, algorithm, diagnostic
|
||||||
|
from .. import types, iodelay, builtins, asttyped
|
||||||
|
|
||||||
|
class _UnknownDelay(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class IODelayEstimator(algorithm.Visitor):
|
||||||
|
def __init__(self, ref_period):
|
||||||
|
self.ref_period = ref_period
|
||||||
|
self.changed = False
|
||||||
|
self.current_delay = iodelay.Const(0)
|
||||||
|
self.current_args = None
|
||||||
|
self.current_goto = None
|
||||||
|
self.current_return = None
|
||||||
|
|
||||||
|
def evaluate(self, node, abort):
|
||||||
|
if isinstance(node, asttyped.NumT):
|
||||||
|
return iodelay.Const(node.n)
|
||||||
|
elif isinstance(node, asttyped.CoerceT):
|
||||||
|
return self.evaluate(node.value, abort)
|
||||||
|
elif isinstance(node, asttyped.NameT):
|
||||||
|
if self.current_args is None:
|
||||||
|
note = diagnostic.Diagnostic("note",
|
||||||
|
"this variable is not an argument", {},
|
||||||
|
node.loc)
|
||||||
|
abort([note])
|
||||||
|
elif node.id in [arg.arg for arg in self.current_args.args]:
|
||||||
|
return iodelay.Var(node.id)
|
||||||
|
else:
|
||||||
|
notes = [
|
||||||
|
diagnostic.Diagnostic("note",
|
||||||
|
"this variable is not an argument of the innermost function", {},
|
||||||
|
node.loc),
|
||||||
|
diagnostic.Diagnostic("note",
|
||||||
|
"only these arguments are in scope of analysis", {},
|
||||||
|
self.current_args.loc)
|
||||||
|
]
|
||||||
|
abort(notes)
|
||||||
|
elif isinstance(node, asttyped.BinOpT):
|
||||||
|
lhs = self.evaluate(node.left, abort)
|
||||||
|
rhs = self.evaluate(node.right, abort)
|
||||||
|
if isinstance(node.op, ast.Add):
|
||||||
|
return lhs + rhs
|
||||||
|
elif isinstance(node.op, ast.Sub):
|
||||||
|
return lhs - rhs
|
||||||
|
elif isinstance(node.op, ast.Mult):
|
||||||
|
return lhs * rhs
|
||||||
|
elif isinstance(node.op, ast.Div):
|
||||||
|
return lhs / rhs
|
||||||
|
elif isinstance(node.op, ast.FloorDiv):
|
||||||
|
return lhs // rhs
|
||||||
|
else:
|
||||||
|
note = diagnostic.Diagnostic("note",
|
||||||
|
"this operator is not supported", {},
|
||||||
|
node.op.loc)
|
||||||
|
abort([note])
|
||||||
|
else:
|
||||||
|
note = diagnostic.Diagnostic("note",
|
||||||
|
"this expression is not supported", {},
|
||||||
|
node.loc)
|
||||||
|
abort([note])
|
||||||
|
|
||||||
|
def abort(self, message, loc, notes=[]):
|
||||||
|
diag = diagnostic.Diagnostic("error", message, {}, loc, notes=notes)
|
||||||
|
raise diagnostic.Error(diag)
|
||||||
|
|
||||||
|
def visit_fixpoint(self, node):
|
||||||
|
while True:
|
||||||
|
self.changed = False
|
||||||
|
self.visit(node)
|
||||||
|
if not self.changed:
|
||||||
|
return
|
||||||
|
|
||||||
|
def visit_ModuleT(self, node):
|
||||||
|
try:
|
||||||
|
self.visit(node.body)
|
||||||
|
except (diagnostic.Error, _UnknownDelay):
|
||||||
|
pass # we don't care; module-level code is never interleaved
|
||||||
|
|
||||||
|
def visit_function(self, args, body, typ):
|
||||||
|
old_args, self.current_args = self.current_args, args
|
||||||
|
old_return, self.current_return = self.current_return, None
|
||||||
|
old_delay, self.current_delay = self.current_delay, iodelay.Const(0)
|
||||||
|
try:
|
||||||
|
self.visit(body)
|
||||||
|
if not iodelay.is_zero(self.current_delay) and self.current_return is not None:
|
||||||
|
self.abort("only return statement at the end of the function "
|
||||||
|
"can be interleaved", self.current_return.loc)
|
||||||
|
|
||||||
|
delay = iodelay.Fixed(self.current_delay.fold())
|
||||||
|
except diagnostic.Error as error:
|
||||||
|
delay = iodelay.Indeterminate(error.diagnostic)
|
||||||
|
self.current_delay = old_delay
|
||||||
|
self.current_return = old_return
|
||||||
|
self.current_args = old_args
|
||||||
|
|
||||||
|
if iodelay.is_unknown(typ.delay) or iodelay.is_indeterminate(typ.delay):
|
||||||
|
typ.delay = delay
|
||||||
|
elif iodelay.is_fixed(typ.delay):
|
||||||
|
assert typ.delay.value == delay.value
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def visit_FunctionDefT(self, node):
|
||||||
|
self.visit(node.args.defaults)
|
||||||
|
self.visit(node.args.kw_defaults)
|
||||||
|
|
||||||
|
# We can only handle return in tail position.
|
||||||
|
if isinstance(node.body[-1], ast.Return):
|
||||||
|
body = node.body[:-1]
|
||||||
|
else:
|
||||||
|
body = node.body
|
||||||
|
self.visit_function(node.args, body, node.signature_type.find())
|
||||||
|
|
||||||
|
def visit_LambdaT(self, node):
|
||||||
|
self.visit_function(node.args, node.body, node.type.find())
|
||||||
|
|
||||||
|
def get_iterable_length(self, node):
|
||||||
|
def abort(notes):
|
||||||
|
self.abort("for statement cannot be interleaved because "
|
||||||
|
"trip count is indeterminate",
|
||||||
|
node.loc, notes)
|
||||||
|
|
||||||
|
def evaluate(node):
|
||||||
|
return self.evaluate(node, abort)
|
||||||
|
|
||||||
|
if isinstance(node, asttyped.CallT) and types.is_builtin(node.func.type, "range"):
|
||||||
|
range_min, range_max, range_step = iodelay.Const(0), None, iodelay.Const(1)
|
||||||
|
if len(node.args) == 3:
|
||||||
|
range_min, range_max, range_step = map(evaluate, node.args)
|
||||||
|
elif len(node.args) == 2:
|
||||||
|
range_min, range_max = map(evaluate, node.args)
|
||||||
|
elif len(node.args) == 1:
|
||||||
|
range_max, = map(evaluate, node.args)
|
||||||
|
return (range_max - range_min) // range_step
|
||||||
|
else:
|
||||||
|
note = diagnostic.Diagnostic("note",
|
||||||
|
"this value is not a constant range literal", {},
|
||||||
|
node.loc)
|
||||||
|
abort([note])
|
||||||
|
|
||||||
|
def visit_For(self, node):
|
||||||
|
self.visit(node.iter)
|
||||||
|
|
||||||
|
old_goto, self.current_goto = self.current_goto, None
|
||||||
|
old_delay, self.current_delay = self.current_delay, iodelay.Const(0)
|
||||||
|
self.visit(node.body)
|
||||||
|
if iodelay.is_zero(self.current_delay):
|
||||||
|
self.current_delay = old_delay
|
||||||
|
else:
|
||||||
|
if self.current_goto is not None:
|
||||||
|
self.abort("loop trip count is indeterminate because of control flow",
|
||||||
|
self.current_goto.loc)
|
||||||
|
|
||||||
|
trip_count = self.get_iterable_length(node.iter)
|
||||||
|
self.current_delay = old_delay + self.current_delay * trip_count
|
||||||
|
self.current_goto = old_goto
|
||||||
|
|
||||||
|
self.visit(node.orelse)
|
||||||
|
|
||||||
|
def visit_goto(self, node):
|
||||||
|
self.current_goto = node
|
||||||
|
|
||||||
|
visit_Break = visit_goto
|
||||||
|
visit_Continue = visit_goto
|
||||||
|
|
||||||
|
def visit_control_flow(self, kind, node):
|
||||||
|
old_delay, self.current_delay = self.current_delay, iodelay.Const(0)
|
||||||
|
self.generic_visit(node)
|
||||||
|
if not iodelay.is_zero(self.current_delay):
|
||||||
|
self.abort("{} cannot be interleaved".format(kind), node.loc)
|
||||||
|
self.current_delay = old_delay
|
||||||
|
|
||||||
|
visit_While = lambda self, node: self.visit_control_flow("while statement", node)
|
||||||
|
visit_If = lambda self, node: self.visit_control_flow("if statement", node)
|
||||||
|
visit_IfExpT = lambda self, node: self.visit_control_flow("if expression", node)
|
||||||
|
visit_Try = lambda self, node: self.visit_control_flow("try statement", node)
|
||||||
|
|
||||||
|
def visit_Return(self, node):
|
||||||
|
self.current_return = node
|
||||||
|
|
||||||
|
def visit_With(self, node):
|
||||||
|
self.visit(node.items)
|
||||||
|
|
||||||
|
context_expr = node.items[0].context_expr
|
||||||
|
if len(node.items) == 1 and types.is_builtin(context_expr.type, "parallel"):
|
||||||
|
delays = []
|
||||||
|
for stmt in node.body:
|
||||||
|
old_delay, self.current_delay = self.current_delay, iodelay.Const(0)
|
||||||
|
self.visit(stmt)
|
||||||
|
delays.append(self.current_delay)
|
||||||
|
self.current_delay = old_delay
|
||||||
|
|
||||||
|
self.current_delay += iodelay.Max(delays)
|
||||||
|
elif len(node.items) == 1 and types.is_builtin(context_expr.type, "serial"):
|
||||||
|
self.visit(node.body)
|
||||||
|
else:
|
||||||
|
self.abort("with statement cannot be interleaved", node.loc)
|
||||||
|
|
||||||
|
def visit_CallT(self, node):
|
||||||
|
typ = node.func.type.find()
|
||||||
|
def abort(notes):
|
||||||
|
self.abort("this call cannot be interleaved because "
|
||||||
|
"an argument cannot be statically evaluated",
|
||||||
|
node.loc, notes)
|
||||||
|
|
||||||
|
if types.is_builtin(typ, "delay"):
|
||||||
|
value = self.evaluate(node.args[0], abort=abort)
|
||||||
|
self.current_delay += iodelay.SToMU(value, ref_period=self.ref_period)
|
||||||
|
elif types.is_builtin(typ, "delay_mu"):
|
||||||
|
value = self.evaluate(node.args[0], abort=abort)
|
||||||
|
self.current_delay += value
|
||||||
|
elif not types.is_builtin(typ):
|
||||||
|
if types.is_function(typ):
|
||||||
|
offset = 0
|
||||||
|
elif types.is_method(typ):
|
||||||
|
offset = 1
|
||||||
|
typ = types.get_method_function(typ)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
delay = typ.find().delay
|
||||||
|
if iodelay.is_unknown(delay):
|
||||||
|
raise _UnknownDelay()
|
||||||
|
elif iodelay.is_indeterminate(delay):
|
||||||
|
cause = delay.cause
|
||||||
|
note = diagnostic.Diagnostic("note",
|
||||||
|
"function called here", {},
|
||||||
|
node.loc)
|
||||||
|
diag = diagnostic.Diagnostic(cause.level, cause.reason, cause.arguments,
|
||||||
|
cause.location, cause.highlights, cause.notes + [note])
|
||||||
|
raise diagnostic.Error(diag)
|
||||||
|
elif iodelay.is_fixed(delay):
|
||||||
|
args = {}
|
||||||
|
for kw_node in node.keywords:
|
||||||
|
args[kw_node.arg] = kw_node.value
|
||||||
|
for arg_name, arg_node in zip(typ.args, node.args[offset:]):
|
||||||
|
args[arg_name] = arg_node
|
||||||
|
|
||||||
|
print(args)
|
||||||
|
|
||||||
|
free_vars = delay.length.free_vars()
|
||||||
|
self.current_delay += delay.length.fold(
|
||||||
|
{ arg: self.evaluate(args[arg], abort=abort) for arg in free_vars })
|
||||||
|
else:
|
||||||
|
assert False
|
|
@ -5,6 +5,7 @@ in :mod:`asttyped`.
|
||||||
|
|
||||||
import string
|
import string
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from . import iodelay
|
||||||
|
|
||||||
|
|
||||||
class UnificationError(Exception):
|
class UnificationError(Exception):
|
||||||
|
@ -191,6 +192,8 @@ class TFunction(Type):
|
||||||
optional arguments
|
optional arguments
|
||||||
:ivar ret: (:class:`Type`)
|
:ivar ret: (:class:`Type`)
|
||||||
return type
|
return type
|
||||||
|
:ivar delay: (:class:`iodelay.Delay`)
|
||||||
|
RTIO delay expression
|
||||||
"""
|
"""
|
||||||
|
|
||||||
attributes = OrderedDict([
|
attributes = OrderedDict([
|
||||||
|
@ -198,11 +201,12 @@ class TFunction(Type):
|
||||||
('__closure__', _TPointer()),
|
('__closure__', _TPointer()),
|
||||||
])
|
])
|
||||||
|
|
||||||
def __init__(self, args, optargs, ret):
|
def __init__(self, args, optargs, ret, delay=iodelay.Unknown()):
|
||||||
assert isinstance(args, OrderedDict)
|
assert isinstance(args, OrderedDict)
|
||||||
assert isinstance(optargs, OrderedDict)
|
assert isinstance(optargs, OrderedDict)
|
||||||
assert isinstance(ret, Type)
|
assert isinstance(ret, Type)
|
||||||
self.args, self.optargs, self.ret = args, optargs, ret
|
assert isinstance(delay, iodelay.Delay)
|
||||||
|
self.args, self.optargs, self.ret, self.delay = args, optargs, ret, delay
|
||||||
|
|
||||||
def arity(self):
|
def arity(self):
|
||||||
return len(self.args) + len(self.optargs)
|
return len(self.args) + len(self.optargs)
|
||||||
|
@ -256,7 +260,8 @@ class TRPCFunction(TFunction):
|
||||||
attributes = OrderedDict()
|
attributes = OrderedDict()
|
||||||
|
|
||||||
def __init__(self, args, optargs, ret, service):
|
def __init__(self, args, optargs, ret, service):
|
||||||
super().__init__(args, optargs, ret)
|
super().__init__(args, optargs, ret,
|
||||||
|
delay=iodelay.Fixed(iodelay.Constant(0)))
|
||||||
self.service = service
|
self.service = service
|
||||||
|
|
||||||
def unify(self, other):
|
def unify(self, other):
|
||||||
|
@ -278,7 +283,8 @@ class TCFunction(TFunction):
|
||||||
attributes = OrderedDict()
|
attributes = OrderedDict()
|
||||||
|
|
||||||
def __init__(self, args, ret, name):
|
def __init__(self, args, ret, name):
|
||||||
super().__init__(args, OrderedDict(), ret)
|
super().__init__(args, OrderedDict(), ret,
|
||||||
|
delay=iodelay.Fixed(iodelay.Constant(0)))
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def unify(self, other):
|
def unify(self, other):
|
||||||
|
@ -547,6 +553,15 @@ class TypePrinter(object):
|
||||||
args += ["?%s:%s" % (arg, self.name(typ.optargs[arg])) for arg in typ.optargs]
|
args += ["?%s:%s" % (arg, self.name(typ.optargs[arg])) for arg in typ.optargs]
|
||||||
signature = "(%s)->%s" % (", ".join(args), self.name(typ.ret))
|
signature = "(%s)->%s" % (", ".join(args), self.name(typ.ret))
|
||||||
|
|
||||||
|
if iodelay.is_unknown(typ.delay) or iodelay.is_fixed(typ.delay, 0):
|
||||||
|
pass
|
||||||
|
elif iodelay.is_fixed(typ.delay):
|
||||||
|
signature += " delay({} mu)".format(typ.delay.length)
|
||||||
|
elif iodelay.is_indeterminate(typ.delay):
|
||||||
|
signature += " delay(?)"
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
if isinstance(typ, TRPCFunction):
|
if isinstance(typ, TRPCFunction):
|
||||||
return "rpc({}) {}".format(typ.service, signature)
|
return "rpc({}) {}".format(typ.service, signature)
|
||||||
if isinstance(typ, TCFunction):
|
if isinstance(typ, TCFunction):
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: as x:<function parallel>
|
||||||
|
with parallel as x: pass
|
|
@ -0,0 +1,7 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: f: (a:float, b:int(width=64))->NoneType delay(s->mu(a) + b mu)
|
||||||
|
def f(a, b):
|
||||||
|
delay(a)
|
||||||
|
delay_mu(b)
|
|
@ -0,0 +1,8 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: f: (a:int(width=32), b:int(width=32), c:int(width=32), d:int(width=32), e:int(width=32))->NoneType delay(s->mu(a * b // c + d - 10 / e) mu)
|
||||||
|
def f(a, b, c, d, e):
|
||||||
|
delay(a * b // c + d - 10 / e)
|
||||||
|
|
||||||
|
f(1,2,3,4,5)
|
|
@ -0,0 +1,10 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def f():
|
||||||
|
delay(1.0)
|
||||||
|
|
||||||
|
# CHECK-L: g: ()->NoneType delay(s->mu(2.0) mu)
|
||||||
|
def g():
|
||||||
|
f()
|
||||||
|
f()
|
|
@ -0,0 +1,13 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def pulse(len):
|
||||||
|
# "on"
|
||||||
|
delay_mu(len)
|
||||||
|
# "off"
|
||||||
|
delay_mu(len)
|
||||||
|
|
||||||
|
# CHECK-L: f: ()->NoneType delay(600 mu)
|
||||||
|
def f():
|
||||||
|
pulse(100)
|
||||||
|
pulse(200)
|
|
@ -0,0 +1,21 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature +diag +delay %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def f(a):
|
||||||
|
b = 1.0
|
||||||
|
# CHECK-L: ${LINE:+3}: error: this call cannot be interleaved
|
||||||
|
# CHECK-L: ${LINE:+2}: note: this variable is not an argument of the innermost function
|
||||||
|
# CHECK-L: ${LINE:-4}: note: only these arguments are in scope of analysis
|
||||||
|
delay(b)
|
||||||
|
|
||||||
|
def g():
|
||||||
|
# CHECK-L: ${LINE:+2}: error: this call cannot be interleaved
|
||||||
|
# CHECK-L: ${LINE:+1}: note: this operator is not supported
|
||||||
|
delay(2.0**2)
|
||||||
|
|
||||||
|
def h():
|
||||||
|
# CHECK-L: ${LINE:+2}: error: this call cannot be interleaved
|
||||||
|
# CHECK-L: ${LINE:+1}: note: this expression is not supported
|
||||||
|
delay_mu(1 if False else 2)
|
||||||
|
|
||||||
|
f(1)
|
|
@ -0,0 +1,14 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature +diag +delay %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def f():
|
||||||
|
x = 1
|
||||||
|
# CHECK-L: ${LINE:+1}: error: this call cannot be interleaved because an argument cannot be statically evaluated
|
||||||
|
delay_mu(x)
|
||||||
|
|
||||||
|
def g():
|
||||||
|
x = 1.0
|
||||||
|
# CHECK-L: ${LINE:+1}: error: this call cannot be interleaved
|
||||||
|
delay(x)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature +diag +delay %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# CHECK-L: ${LINE:+1}: error: this call cannot be interleaved
|
||||||
|
delay(1.0**2)
|
||||||
|
|
||||||
|
def g():
|
||||||
|
# CHECK-L: ${LINE:+1}: note: function called here
|
||||||
|
f()
|
||||||
|
f()
|
|
@ -0,0 +1,13 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature +diag +delay %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def pulse(len):
|
||||||
|
# "on"
|
||||||
|
delay_mu(len)
|
||||||
|
# "off"
|
||||||
|
delay_mu(len)
|
||||||
|
|
||||||
|
def f():
|
||||||
|
a = 100
|
||||||
|
# CHECK-L: ${LINE:+1}: error: this call cannot be interleaved
|
||||||
|
pulse(a)
|
|
@ -0,0 +1,23 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature +diag +delay %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# CHECK-L: ${LINE:+1}: error: while statement cannot be interleaved
|
||||||
|
while True:
|
||||||
|
delay_mu(1)
|
||||||
|
|
||||||
|
def g():
|
||||||
|
# CHECK-L: ${LINE:+1}: error: if statement cannot be interleaved
|
||||||
|
if True:
|
||||||
|
delay_mu(1)
|
||||||
|
|
||||||
|
def h():
|
||||||
|
# CHECK-L: ${LINE:+1}: error: if expression cannot be interleaved
|
||||||
|
delay_mu(1) if True else delay_mu(2)
|
||||||
|
|
||||||
|
def i():
|
||||||
|
# CHECK-L: ${LINE:+1}: error: try statement cannot be interleaved
|
||||||
|
try:
|
||||||
|
delay_mu(1)
|
||||||
|
finally:
|
||||||
|
pass
|
|
@ -0,0 +1,9 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature +diag +delay %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def f():
|
||||||
|
r = range(10)
|
||||||
|
# CHECK-L: ${LINE:+2}: error: for statement cannot be interleaved because trip count is indeterminate
|
||||||
|
# CHECK-L: ${LINE:+1}: note: this value is not a constant range literal
|
||||||
|
for _ in r:
|
||||||
|
delay_mu(1)
|
|
@ -0,0 +1,14 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature +diag +delay %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def f():
|
||||||
|
for _ in range(10):
|
||||||
|
delay_mu(10)
|
||||||
|
# CHECK-L: ${LINE:+1}: error: loop trip count is indeterminate because of control flow
|
||||||
|
break
|
||||||
|
|
||||||
|
def g():
|
||||||
|
for _ in range(10):
|
||||||
|
delay_mu(10)
|
||||||
|
# CHECK-L: ${LINE:+1}: error: loop trip count is indeterminate because of control flow
|
||||||
|
continue
|
|
@ -0,0 +1,8 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature +diag +delay %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def f():
|
||||||
|
if True:
|
||||||
|
# CHECK-L: ${LINE:+1}: error: only return statement at the end of the function can be interleaved
|
||||||
|
return 1
|
||||||
|
delay_mu(1)
|
|
@ -0,0 +1,14 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: f: ()->NoneType delay(10 mu)
|
||||||
|
def f():
|
||||||
|
delay_mu(10)
|
||||||
|
for _ in range(10):
|
||||||
|
break
|
||||||
|
|
||||||
|
# CHECK-L: g: ()->NoneType delay(10 mu)
|
||||||
|
def g():
|
||||||
|
delay_mu(10)
|
||||||
|
for _ in range(10):
|
||||||
|
continue
|
|
@ -0,0 +1,12 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: f: ()->NoneType delay(s->mu(1.0) + 1000 mu)
|
||||||
|
def f():
|
||||||
|
delay(1.0)
|
||||||
|
delay_mu(1000)
|
||||||
|
|
||||||
|
# CHECK-L: g: ()->NoneType delay(s->mu(5.0) mu)
|
||||||
|
def g():
|
||||||
|
delay(1.0)
|
||||||
|
delay(2.0 * 2)
|
|
@ -0,0 +1,13 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: f: ()->NoneType delay(s->mu(1.5) * 10 mu)
|
||||||
|
def f():
|
||||||
|
for _ in range(10):
|
||||||
|
delay(1.5)
|
||||||
|
|
||||||
|
# CHECK-L: g: ()->NoneType delay(s->mu(1.5) * 2 * 10 mu)
|
||||||
|
def g():
|
||||||
|
for _ in range(10):
|
||||||
|
for _ in range(2):
|
||||||
|
delay(1.5)
|
|
@ -0,0 +1,15 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: f: (a:int(width=64), b:int(width=64))->NoneType delay(max(a, b) mu)
|
||||||
|
def f(a, b):
|
||||||
|
with parallel:
|
||||||
|
delay_mu(a)
|
||||||
|
delay_mu(b)
|
||||||
|
|
||||||
|
# CHECK-L: g: (a:int(width=64))->NoneType delay(max(a, 200) mu)
|
||||||
|
def g(a):
|
||||||
|
with parallel:
|
||||||
|
delay_mu(100)
|
||||||
|
delay_mu(200)
|
||||||
|
delay_mu(a)
|
|
@ -0,0 +1,21 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: f: (a:int(width=32))->NoneType delay(s->mu(1.5) * a mu)
|
||||||
|
def f(a):
|
||||||
|
for _ in range(a):
|
||||||
|
delay(1.5)
|
||||||
|
|
||||||
|
# CHECK-L: g: (a:int(width=32), b:int(width=32))->NoneType delay(s->mu(1.5) * (b - a) mu)
|
||||||
|
def g(a, b):
|
||||||
|
for _ in range(a, b):
|
||||||
|
delay(1.5)
|
||||||
|
|
||||||
|
# CHECK-L: h: (a:int(width=32), b:int(width=32), c:int(width=32))->NoneType delay(s->mu(1.5) * (b - a) // c mu)
|
||||||
|
def h(a, b, c):
|
||||||
|
for _ in range(a, b, c):
|
||||||
|
delay(1.5)
|
||||||
|
|
||||||
|
f(1)
|
||||||
|
g(1,2)
|
||||||
|
h(1,2,3)
|
|
@ -0,0 +1,16 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
# CHECK-L: f: ()->int(width=32) delay(s->mu(1.5) * 10 mu)
|
||||||
|
def f():
|
||||||
|
for _ in range(10):
|
||||||
|
delay(1.5)
|
||||||
|
return 10
|
||||||
|
|
||||||
|
# CHECK-L: g: (x:float)->int(width=32) delay(0 mu)
|
||||||
|
def g(x):
|
||||||
|
if x > 1.0:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
g(1.0)
|
Loading…
Reference in New Issue