forked from M-Labs/artiq
transforms.iodelay_estimator: make diagnostics much more clear.
This commit is contained in:
parent
7f9a180946
commit
0acc86b3b3
|
@ -24,11 +24,11 @@ class IODelayEstimator(algorithm.Visitor):
|
||||||
self.current_goto = None
|
self.current_goto = None
|
||||||
self.current_return = None
|
self.current_return = None
|
||||||
|
|
||||||
def evaluate(self, node, abort):
|
def evaluate(self, node, abort, context):
|
||||||
if isinstance(node, asttyped.NumT):
|
if isinstance(node, asttyped.NumT):
|
||||||
return iodelay.Const(node.n)
|
return iodelay.Const(node.n)
|
||||||
elif isinstance(node, asttyped.CoerceT):
|
elif isinstance(node, asttyped.CoerceT):
|
||||||
return self.evaluate(node.value, abort)
|
return self.evaluate(node.value, abort, context)
|
||||||
elif isinstance(node, asttyped.NameT):
|
elif isinstance(node, asttyped.NameT):
|
||||||
if self.current_args is None:
|
if self.current_args is None:
|
||||||
note = diagnostic.Diagnostic("note",
|
note = diagnostic.Diagnostic("note",
|
||||||
|
@ -48,8 +48,8 @@ class IODelayEstimator(algorithm.Visitor):
|
||||||
]
|
]
|
||||||
abort(notes)
|
abort(notes)
|
||||||
elif isinstance(node, asttyped.BinOpT):
|
elif isinstance(node, asttyped.BinOpT):
|
||||||
lhs = self.evaluate(node.left, abort)
|
lhs = self.evaluate(node.left, abort, context)
|
||||||
rhs = self.evaluate(node.right, abort)
|
rhs = self.evaluate(node.right, abort, context)
|
||||||
if isinstance(node.op, ast.Add):
|
if isinstance(node.op, ast.Add):
|
||||||
return lhs + rhs
|
return lhs + rhs
|
||||||
elif isinstance(node.op, ast.Sub):
|
elif isinstance(node.op, ast.Sub):
|
||||||
|
@ -62,12 +62,14 @@ class IODelayEstimator(algorithm.Visitor):
|
||||||
return lhs // rhs
|
return lhs // rhs
|
||||||
else:
|
else:
|
||||||
note = diagnostic.Diagnostic("note",
|
note = diagnostic.Diagnostic("note",
|
||||||
"this operator is not supported", {},
|
"this operator is not supported {context}",
|
||||||
|
{"context": context},
|
||||||
node.op.loc)
|
node.op.loc)
|
||||||
abort([note])
|
abort([note])
|
||||||
else:
|
else:
|
||||||
note = diagnostic.Diagnostic("note",
|
note = diagnostic.Diagnostic("note",
|
||||||
"this expression is not supported", {},
|
"this expression is not supported {context}",
|
||||||
|
{"context": context},
|
||||||
node.loc)
|
node.loc)
|
||||||
abort([note])
|
abort([note])
|
||||||
|
|
||||||
|
@ -143,14 +145,14 @@ class IODelayEstimator(algorithm.Visitor):
|
||||||
def visit_LambdaT(self, node):
|
def visit_LambdaT(self, node):
|
||||||
self.visit_function(node.args, node.body, node.type.find(), node.loc)
|
self.visit_function(node.args, node.body, node.type.find(), node.loc)
|
||||||
|
|
||||||
def get_iterable_length(self, node):
|
def get_iterable_length(self, node, context):
|
||||||
def abort(notes):
|
def abort(notes):
|
||||||
self.abort("for statement cannot be interleaved because "
|
self.abort("for statement cannot be interleaved because "
|
||||||
"trip count is indeterminate",
|
"iteration count is indeterminate",
|
||||||
node.loc, notes)
|
node.loc, notes)
|
||||||
|
|
||||||
def evaluate(node):
|
def evaluate(node):
|
||||||
return self.evaluate(node, abort)
|
return self.evaluate(node, abort, context)
|
||||||
|
|
||||||
if isinstance(node, asttyped.CallT) and types.is_builtin(node.func.type, "range"):
|
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)
|
range_min, range_max, range_step = iodelay.Const(0), None, iodelay.Const(1)
|
||||||
|
@ -177,10 +179,11 @@ class IODelayEstimator(algorithm.Visitor):
|
||||||
self.current_delay = old_delay
|
self.current_delay = old_delay
|
||||||
else:
|
else:
|
||||||
if self.current_goto is not None:
|
if self.current_goto is not None:
|
||||||
self.abort("loop trip count is indeterminate because of control flow",
|
self.abort("loop iteration count is indeterminate because of control flow",
|
||||||
self.current_goto.loc)
|
self.current_goto.loc)
|
||||||
|
|
||||||
node.trip_count = self.get_iterable_length(node.iter).fold()
|
context = "in an iterable used in a for loop that is being interleaved"
|
||||||
|
node.trip_count = self.get_iterable_length(node.iter, context).fold()
|
||||||
node.trip_interval = self.current_delay.fold()
|
node.trip_interval = self.current_delay.fold()
|
||||||
self.current_delay = old_delay + node.trip_interval * node.trip_count
|
self.current_delay = old_delay + node.trip_interval * node.trip_count
|
||||||
self.current_goto = old_goto
|
self.current_goto = old_goto
|
||||||
|
@ -231,6 +234,10 @@ class IODelayEstimator(algorithm.Visitor):
|
||||||
# Interleave failures inside `with` statements are hard failures,
|
# Interleave failures inside `with` statements are hard failures,
|
||||||
# since there's no chance that the code will never actually execute
|
# since there's no chance that the code will never actually execute
|
||||||
# inside a `with` statement after all.
|
# inside a `with` statement after all.
|
||||||
|
note = diagnostic.Diagnostic("note",
|
||||||
|
"while interleaving this 'with parallel:' statement", {},
|
||||||
|
node.loc)
|
||||||
|
error.cause.notes += [note]
|
||||||
self.engine.process(error.cause)
|
self.engine.process(error.cause)
|
||||||
|
|
||||||
flow_stmt = None
|
flow_stmt = None
|
||||||
|
@ -258,15 +265,17 @@ class IODelayEstimator(algorithm.Visitor):
|
||||||
def visit_CallT(self, node):
|
def visit_CallT(self, node):
|
||||||
typ = node.func.type.find()
|
typ = node.func.type.find()
|
||||||
def abort(notes):
|
def abort(notes):
|
||||||
self.abort("this call cannot be interleaved because "
|
self.abort("call cannot be interleaved because "
|
||||||
"an argument cannot be statically evaluated",
|
"an argument cannot be statically evaluated",
|
||||||
node.loc, notes)
|
node.loc, notes)
|
||||||
|
|
||||||
if types.is_builtin(typ, "delay"):
|
if types.is_builtin(typ, "delay"):
|
||||||
value = self.evaluate(node.args[0], abort=abort)
|
value = self.evaluate(node.args[0], abort=abort,
|
||||||
|
context="as an argument for delay()")
|
||||||
call_delay = iodelay.SToMU(value, ref_period=self.ref_period)
|
call_delay = iodelay.SToMU(value, ref_period=self.ref_period)
|
||||||
elif types.is_builtin(typ, "delay_mu"):
|
elif types.is_builtin(typ, "delay_mu"):
|
||||||
value = self.evaluate(node.args[0], abort=abort)
|
value = self.evaluate(node.args[0], abort=abort,
|
||||||
|
context="as an argument for delay_mu()")
|
||||||
call_delay = value
|
call_delay = value
|
||||||
elif not types.is_builtin(typ):
|
elif not types.is_builtin(typ):
|
||||||
if types.is_function(typ):
|
if types.is_function(typ):
|
||||||
|
@ -297,7 +306,12 @@ class IODelayEstimator(algorithm.Visitor):
|
||||||
args[arg_name] = arg_node
|
args[arg_name] = arg_node
|
||||||
|
|
||||||
free_vars = delay.duration.free_vars()
|
free_vars = delay.duration.free_vars()
|
||||||
node.arg_exprs = { arg: self.evaluate(args[arg], abort=abort) for arg in free_vars }
|
node.arg_exprs = {
|
||||||
|
arg: self.evaluate(args[arg], abort=abort,
|
||||||
|
context="in the expression for argument '{}' "
|
||||||
|
"that affects I/O delay".format(arg))
|
||||||
|
for arg in free_vars
|
||||||
|
}
|
||||||
call_delay = delay.duration.fold(node.arg_exprs)
|
call_delay = delay.duration.fold(node.arg_exprs)
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature +diag +delay %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
def f(x):
|
||||||
|
delay_mu(x)
|
||||||
|
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
def g():
|
||||||
|
# CHECK-L: ${LINE:+2}: error: call cannot be interleaved because an argument cannot be statically evaluated
|
||||||
|
# CHECK-L: ${LINE:+1}: note: this expression is not supported in the expression for argument 'x' that affects I/O delay
|
||||||
|
f(x if True else x)
|
|
@ -3,19 +3,19 @@
|
||||||
|
|
||||||
def f(a):
|
def f(a):
|
||||||
b = 1.0
|
b = 1.0
|
||||||
# CHECK-L: ${LINE:+3}: error: this call cannot be interleaved
|
# CHECK-L: ${LINE:+3}: error: call cannot be interleaved
|
||||||
# CHECK-L: ${LINE:+2}: note: this variable is not an argument of the innermost function
|
# 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
|
# CHECK-L: ${LINE:-4}: note: only these arguments are in scope of analysis
|
||||||
delay(b)
|
delay(b)
|
||||||
|
|
||||||
def g():
|
def g():
|
||||||
# CHECK-L: ${LINE:+2}: error: this call cannot be interleaved
|
# CHECK-L: ${LINE:+2}: error: call cannot be interleaved
|
||||||
# CHECK-L: ${LINE:+1}: note: this operator is not supported
|
# CHECK-L: ${LINE:+1}: note: this operator is not supported as an argument for delay()
|
||||||
delay(2.0**2)
|
delay(2.0**2)
|
||||||
|
|
||||||
def h():
|
def h():
|
||||||
# CHECK-L: ${LINE:+2}: error: this call cannot be interleaved
|
# CHECK-L: ${LINE:+2}: error: call cannot be interleaved
|
||||||
# CHECK-L: ${LINE:+1}: note: this expression is not supported
|
# CHECK-L: ${LINE:+1}: note: this expression is not supported as an argument for delay_mu()
|
||||||
delay_mu(1 if False else 2)
|
delay_mu(1 if False else 2)
|
||||||
|
|
||||||
f(1)
|
f(1)
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
x = 1
|
x = 1
|
||||||
# CHECK-L: ${LINE:+1}: error: this call cannot be interleaved because an argument cannot be statically evaluated
|
# CHECK-L: ${LINE:+1}: error: call cannot be interleaved because an argument cannot be statically evaluated
|
||||||
delay_mu(x)
|
delay_mu(x)
|
||||||
|
|
||||||
def g():
|
def g():
|
||||||
x = 1.0
|
x = 1.0
|
||||||
# CHECK-L: ${LINE:+1}: error: this call cannot be interleaved
|
# CHECK-L: ${LINE:+1}: error: call cannot be interleaved
|
||||||
delay(x)
|
delay(x)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# CHECK-L: ${LINE:+1}: error: this call cannot be interleaved
|
# CHECK-L: ${LINE:+1}: error: call cannot be interleaved
|
||||||
delay(1.0**2)
|
delay(1.0**2)
|
||||||
|
|
||||||
def g():
|
def g():
|
||||||
|
|
|
@ -9,5 +9,5 @@ def pulse(len):
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
a = 100
|
a = 100
|
||||||
# CHECK-L: ${LINE:+1}: error: this call cannot be interleaved
|
# CHECK-L: ${LINE:+1}: error: call cannot be interleaved
|
||||||
pulse(a)
|
pulse(a)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
r = range(10)
|
r = range(10)
|
||||||
# CHECK-L: ${LINE:+2}: error: for statement cannot be interleaved because trip count is indeterminate
|
# CHECK-L: ${LINE:+2}: error: for statement cannot be interleaved because iteration count is indeterminate
|
||||||
# CHECK-L: ${LINE:+1}: note: this value is not a constant range literal
|
# CHECK-L: ${LINE:+1}: note: this value is not a constant range literal
|
||||||
for _ in r:
|
for _ in r:
|
||||||
delay_mu(1)
|
delay_mu(1)
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
def f():
|
def f():
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
delay_mu(10)
|
delay_mu(10)
|
||||||
# CHECK-L: ${LINE:+1}: error: loop trip count is indeterminate because of control flow
|
# CHECK-L: ${LINE:+1}: error: loop iteration count is indeterminate because of control flow
|
||||||
break
|
break
|
||||||
|
|
||||||
def g():
|
def g():
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
delay_mu(10)
|
delay_mu(10)
|
||||||
# CHECK-L: ${LINE:+1}: error: loop trip count is indeterminate because of control flow
|
# CHECK-L: ${LINE:+1}: error: loop iteration count is indeterminate because of control flow
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.signature +diag +delay %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# CHECK-L: ${LINE:+2}: error: for statement cannot be interleaved because iteration count is indeterminate
|
||||||
|
# CHECK-L: ${LINE:+1}: note: this expression is not supported in an iterable used in a for loop that is being interleaved
|
||||||
|
for _ in range(x if True else x):
|
||||||
|
delay_mu(10)
|
Loading…
Reference in New Issue