transforms.iodelay_estimator: make diagnostics much more clear.

This commit is contained in:
whitequark 2016-01-27 02:10:15 +00:00
parent 7f9a180946
commit 0acc86b3b3
9 changed files with 63 additions and 27 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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():

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)