From de9d7eb2e4f8ae24622721054bf1db00a4815efe Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 17 Nov 2015 05:16:43 +0300 Subject: [PATCH] compiler: add `delay` IR instruction. --- artiq/compiler/asttyped.py | 4 +- artiq/compiler/embedding.py | 2 +- artiq/compiler/ir.py | 37 +++++++++++++++++++ .../compiler/transforms/artiq_ir_generator.py | 29 +++++++++++++-- .../compiler/transforms/asttyped_rewriter.py | 2 +- .../compiler/transforms/iodelay_estimator.py | 9 +++-- 6 files changed, 73 insertions(+), 10 deletions(-) diff --git a/artiq/compiler/asttyped.py b/artiq/compiler/asttyped.py index c9e543ae2..6d7908d8d 100644 --- a/artiq/compiler/asttyped.py +++ b/artiq/compiler/asttyped.py @@ -46,7 +46,9 @@ class BinOpT(ast.BinOp, commontyped): class BoolOpT(ast.BoolOp, commontyped): pass class CallT(ast.Call, commontyped): - pass + """ + :ivar iodelay: (:class:`iodelay.Expr`) + """ class CompareT(ast.Compare, commontyped): pass class DictT(ast.Dict, commontyped): diff --git a/artiq/compiler/embedding.py b/artiq/compiler/embedding.py index 2a3fba5f3..637153dc0 100644 --- a/artiq/compiler/embedding.py +++ b/artiq/compiler/embedding.py @@ -173,7 +173,7 @@ class ASTSynthesizer: for kw, value, (arg_loc, equals_loc) in zip(kwargs, kwarg_nodes, kwarg_locs)], starargs=None, kwargs=None, - type=types.TVar(), + type=types.TVar(), iodelay=None, begin_loc=begin_loc, end_loc=end_loc, star_loc=None, dstar_loc=None, loc=name_loc.join(end_loc)) diff --git a/artiq/compiler/ir.py b/artiq/compiler/ir.py index aa428f9b9..60126b534 100644 --- a/artiq/compiler/ir.py +++ b/artiq/compiler/ir.py @@ -1188,6 +1188,43 @@ class LandingPad(Terminator): return "cleanup {}, [{}]".format(self.cleanup().as_operand(type_printer), ", ".join(table)) +class Delay(Terminator): + """ + A delay operation. Ties an :class:`iodelay.Expr` to SSA values so that + inlining could lead to the expression folding to a constant. + + :ivar expr: (:class:`iodelay.Expr`) expression + :ivar var_names: (list of string) + iodelay expression names corresponding to operands + """ + + """ + :param expr: (:class:`iodelay.Expr`) expression + :param substs: (dict of str to :class:`Value`) + :param target: (:class:`BasicBlock`) branch target + """ + def __init__(self, expr, substs, target, name=""): + for var_name in substs: assert isinstance(var_name, str) + assert isinstance(target, BasicBlock) + super().__init__([target, *substs.values()], builtins.TNone(), name) + self.expr = expr + self.var_names = list(substs.keys()) + + def target(self): + return self.operands[0] + + def substs(self): + return zip(self.var_names, self.operands[1:]) + + def _operands_as_string(self, type_printer): + substs = [] + for var_name, operand in self.substs(): + substs.append("{}={}".format(var_name, operand)) + return "[{}], {}".format(", ".join(substs), self.target().as_operand(type_printer)) + + def opcode(self): + return "delay({})".format(self.expr) + class Parallel(Terminator): """ An instruction that schedules several threads of execution diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index f73295d20..4ae04a366 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -44,6 +44,9 @@ class ARTIQIRGenerator(algorithm.Visitor): can become upvalues :ivar current_private_env: (:class:`ir.Alloc` of type :class:`ir.TEnvironment`) the private function environment, containing internal state + :ivar current_args: (dict of string to :class:`ir.Argument`) + the map of Python names of formal arguments to + the current function to their SSA names :ivar current_assign: (:class:`ir.Value` or None) the right-hand side of current assignment statement, or a component of a composite right-hand side when visiting @@ -91,6 +94,7 @@ class ARTIQIRGenerator(algorithm.Visitor): self.current_block = None self.current_env = None self.current_private_env = None + self.current_args = None self.current_assign = None self.current_assert_env = None self.current_assert_subexprs = None @@ -228,13 +232,19 @@ class ARTIQIRGenerator(algorithm.Visitor): env_arg = ir.EnvironmentArgument(self.current_env.type, "outerenv") + old_args, self.current_args = self.current_args, {} + args = [] for arg_name in typ.args: - args.append(ir.Argument(typ.args[arg_name], "arg." + arg_name)) + arg = ir.Argument(typ.args[arg_name], "arg." + arg_name) + self.current_args[arg_name] = arg + args.append(arg) optargs = [] for arg_name in typ.optargs: - optargs.append(ir.Argument(ir.TOption(typ.optargs[arg_name]), "arg." + arg_name)) + arg = ir.Argument(ir.TOption(typ.optargs[arg_name]), "arg." + arg_name) + self.current_args[arg_name] = arg + optargs.append(arg) func = ir.Function(typ, ".".join(self.name), [env_arg] + args + optargs, loc=node.lambda_loc if is_lambda else node.keyword_loc) @@ -284,6 +294,7 @@ class ARTIQIRGenerator(algorithm.Visitor): self.current_block.append(ir.Unreachable()) finally: self.name = old_name + self.current_args = old_args self.current_function = old_func self.current_block = old_block self.current_globals = old_globals @@ -1544,7 +1555,7 @@ class ARTIQIRGenerator(algorithm.Visitor): typ = node.func.type.find() if types.is_builtin(typ): - return self.visit_builtin_call(node) + insn = self.visit_builtin_call(node) else: if types.is_function(typ): func = self.visit(node.func) @@ -1557,6 +1568,8 @@ class ARTIQIRGenerator(algorithm.Visitor): self_arg = self.append(ir.GetAttr(method, "__self__")) fn_typ = types.get_method_function(typ) offset = 1 + else: + assert False args = [None] * (len(fn_typ.args) + len(fn_typ.optargs)) @@ -1606,7 +1619,15 @@ class ARTIQIRGenerator(algorithm.Visitor): attr_node = node.func self.method_map[(attr_node.value.type, attr_node.attr)].append(insn) - return insn + if node.iodelay is not None: + after_delay = self.add_block() + self.append(ir.Delay(node.iodelay, + {var_name: self.current_args[var_name] + for var_name in node.iodelay.free_vars()}, + after_delay)) + self.current_block = after_delay + + return insn def visit_QuoteT(self, node): return self.append(ir.Quote(node.value, node.type)) diff --git a/artiq/compiler/transforms/asttyped_rewriter.py b/artiq/compiler/transforms/asttyped_rewriter.py index 084b8a7ca..e8f811423 100644 --- a/artiq/compiler/transforms/asttyped_rewriter.py +++ b/artiq/compiler/transforms/asttyped_rewriter.py @@ -426,7 +426,7 @@ class ASTTypedRewriter(algorithm.Transformer): def visit_Call(self, node): node = self.generic_visit(node) - node = asttyped.CallT(type=types.TVar(), + node = asttyped.CallT(type=types.TVar(), iodelay=None, func=node.func, args=node.args, keywords=node.keywords, starargs=node.starargs, kwargs=node.kwargs, star_loc=node.star_loc, dstar_loc=node.dstar_loc, diff --git a/artiq/compiler/transforms/iodelay_estimator.py b/artiq/compiler/transforms/iodelay_estimator.py index cc3da45a1..d13f9a36a 100644 --- a/artiq/compiler/transforms/iodelay_estimator.py +++ b/artiq/compiler/transforms/iodelay_estimator.py @@ -230,10 +230,10 @@ class IODelayEstimator(algorithm.Visitor): 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) + call_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 + call_delay = value elif not types.is_builtin(typ): if types.is_function(typ): offset = 0 @@ -262,7 +262,10 @@ class IODelayEstimator(algorithm.Visitor): args[arg_name] = arg_node free_vars = delay.duration.free_vars() - self.current_delay += delay.duration.fold( + call_delay = delay.duration.fold( { arg: self.evaluate(args[arg], abort=abort) for arg in free_vars }) else: assert False + + self.current_delay += call_delay + node.iodelay = call_delay