From 501ba912c201f6a40baca0e1da3ca2c71e24d186 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 31 Aug 2015 09:59:33 -0600 Subject: [PATCH] Implement {delay,now,at}{,_mu} and {mu,seconds}_to_{seconds,mu}. --- artiq/compiler/builtins.py | 24 ++++++ artiq/compiler/module.py | 5 +- artiq/compiler/prelude.py | 17 +++++ artiq/compiler/testbench/jit.py | 6 +- .../compiler/transforms/artiq_ir_generator.py | 36 ++++++++- artiq/compiler/transforms/inferencer.py | 35 +++++++++ .../compiler/transforms/llvm_ir_generator.py | 32 ++++++-- artiq/coredevice/core.py | 2 +- artiq/py2llvm_old/transforms/lower_time.py | 64 ---------------- artiq/py2llvm_old/transforms/quantize_time.py | 70 ------------------ .../Makefile | 2 +- .../__cxxabi_config.h | 0 .../artiq_terminate.c | 0 lit-test/libartiq_support/artiq_time.c | 3 + .../libartiq_support/libartiq_personality.so | Bin 0 -> 23696 bytes lit-test/libartiq_support/libartiq_support.so | Bin 0 -> 24096 bytes .../unwind.h | 0 lit-test/test/lit.cfg | 8 +- lit-test/test/time/advance.py | 9 +++ lit-test/test/time/advance_mu.py | 7 ++ lit-test/test/time/conversion.py | 4 + soc/runtime/ksupport.c | 8 +- 22 files changed, 177 insertions(+), 155 deletions(-) delete mode 100644 artiq/py2llvm_old/transforms/lower_time.py rename lit-test/{libartiq_personality => libartiq_support}/Makefile (50%) rename lit-test/{libartiq_personality => libartiq_support}/__cxxabi_config.h (100%) rename lit-test/{libartiq_personality => libartiq_support}/artiq_terminate.c (100%) create mode 100644 lit-test/libartiq_support/artiq_time.c create mode 100755 lit-test/libartiq_support/libartiq_personality.so create mode 100755 lit-test/libartiq_support/libartiq_support.so rename lit-test/{libartiq_personality => libartiq_support}/unwind.h (100%) create mode 100644 lit-test/test/time/advance.py create mode 100644 lit-test/test/time/advance_mu.py create mode 100644 lit-test/test/time/conversion.py diff --git a/artiq/compiler/builtins.py b/artiq/compiler/builtins.py index 2e26b8f79..4133015cc 100644 --- a/artiq/compiler/builtins.py +++ b/artiq/compiler/builtins.py @@ -138,6 +138,30 @@ def fn_print(): def fn_kernel(): return types.TBuiltinFunction("kernel") +def fn_now(): + return types.TBuiltinFunction("now") + +def fn_delay(): + return types.TBuiltinFunction("delay") + +def fn_at(): + return types.TBuiltinFunction("at") + +def fn_now_mu(): + return types.TBuiltinFunction("now_mu") + +def fn_delay_mu(): + return types.TBuiltinFunction("delay_mu") + +def fn_at_mu(): + return types.TBuiltinFunction("at_mu") + +def fn_mu_to_seconds(): + return types.TBuiltinFunction("mu_to_seconds") + +def fn_seconds_to_mu(): + return types.TBuiltinFunction("seconds_to_mu") + # Accessors def is_none(typ): diff --git a/artiq/compiler/module.py b/artiq/compiler/module.py index dbcaf2cd7..1e4232694 100644 --- a/artiq/compiler/module.py +++ b/artiq/compiler/module.py @@ -42,7 +42,7 @@ class Source: return cls(source.Buffer(f.read(), filename, 1), engine=engine) class Module: - def __init__(self, src): + def __init__(self, src, ref_period=1e-6): self.engine = src.engine self.object_map = src.object_map @@ -51,7 +51,8 @@ class Module: monomorphism_validator = validators.MonomorphismValidator(engine=self.engine) escape_validator = validators.EscapeValidator(engine=self.engine) artiq_ir_generator = transforms.ARTIQIRGenerator(engine=self.engine, - module_name=src.name) + module_name=src.name, + ref_period=ref_period) dead_code_eliminator = transforms.DeadCodeEliminator(engine=self.engine) local_access_validator = validators.LocalAccessValidator(engine=self.engine) diff --git a/artiq/compiler/prelude.py b/artiq/compiler/prelude.py index 579a474cf..2929c9c7a 100644 --- a/artiq/compiler/prelude.py +++ b/artiq/compiler/prelude.py @@ -7,17 +7,34 @@ from . import builtins def globals(): return { + # Value constructors "bool": builtins.fn_bool(), "int": builtins.fn_int(), "float": builtins.fn_float(), "list": builtins.fn_list(), "range": builtins.fn_range(), + + # Exception constructors "Exception": builtins.fn_Exception(), "IndexError": builtins.fn_IndexError(), "ValueError": builtins.fn_ValueError(), "ZeroDivisionError": builtins.fn_ZeroDivisionError(), + + # Built-in Python functions "len": builtins.fn_len(), "round": builtins.fn_round(), "print": builtins.fn_print(), + + # ARTIQ decorators "kernel": builtins.fn_kernel(), + + # ARTIQ time management functions + "now": builtins.fn_now(), + "delay": builtins.fn_delay(), + "at": builtins.fn_at(), + "now_mu": builtins.fn_now_mu(), + "delay_mu": builtins.fn_delay_mu(), + "at_mu": builtins.fn_at_mu(), + "mu_to_seconds": builtins.fn_mu_to_seconds(), + "seconds_to_mu": builtins.fn_seconds_to_mu(), } diff --git a/artiq/compiler/testbench/jit.py b/artiq/compiler/testbench/jit.py index 717bdc555..c1c90dd56 100644 --- a/artiq/compiler/testbench/jit.py +++ b/artiq/compiler/testbench/jit.py @@ -5,9 +5,9 @@ from .. import Module, Source from ..targets import NativeTarget def main(): - libartiq_personality = os.getenv('LIBARTIQ_PERSONALITY') - if libartiq_personality is not None: - llvm.load_library_permanently(libartiq_personality) + libartiq_support = os.getenv('LIBARTIQ_SUPPORT') + if libartiq_support is not None: + llvm.load_library_permanently(libartiq_support) def process_diagnostic(diag): print("\n".join(diag.render())) diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index bca40580b..84d3b07e8 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -67,10 +67,11 @@ class ARTIQIRGenerator(algorithm.Visitor): _size_type = builtins.TInt(types.TValue(32)) - def __init__(self, module_name, engine): + def __init__(self, module_name, engine, ref_period): self.engine = engine self.functions = [] self.name = [module_name] if module_name != "" else [] + self.ref_period = ir.Constant(ref_period, builtins.TFloat()) self.current_loc = None self.current_function = None self.current_class = None @@ -1409,6 +1410,39 @@ class ARTIQIRGenerator(algorithm.Visitor): self.polymorphic_print([self.visit(arg) for arg in node.args], separator=" ", suffix="\n") return ir.Constant(None, builtins.TNone()) + elif types.is_builtin(typ, "now"): + if len(node.args) == 0 and len(node.keywords) == 0: + now_mu = self.append(ir.Builtin("now_mu", [], builtins.TInt(types.TValue(64)))) + now_mu_float = self.append(ir.Coerce(now_mu, builtins.TFloat())) + return self.append(ir.Arith(ast.Mult(loc=None), now_mu_float, self.ref_period)) + else: + assert False + elif types.is_builtin(typ, "delay") or types.is_builtin(typ, "at"): + if len(node.args) == 1 and len(node.keywords) == 0: + arg = self.visit(node.args[0]) + arg_mu_float = self.append(ir.Arith(ast.Div(loc=None), arg, self.ref_period)) + arg_mu = self.append(ir.Coerce(arg_mu_float, builtins.TInt(types.TValue(64)))) + self.append(ir.Builtin(typ.name + "_mu", [arg_mu], builtins.TNone())) + else: + assert False + elif types.is_builtin(typ, "now_mu") or types.is_builtin(typ, "delay_mu") \ + or types.is_builtin(typ, "at_mu"): + return self.append(ir.Builtin(typ.name, + [self.visit(arg) for arg in node.args], node.type)) + elif types.is_builtin(typ, "mu_to_seconds"): + if len(node.args) == 1 and len(node.keywords) == 0: + arg = self.visit(node.args[0]) + arg_float = self.append(ir.Coerce(arg, builtins.TFloat())) + return self.append(ir.Arith(ast.Mult(loc=None), arg_float, self.ref_period)) + else: + assert False + elif types.is_builtin(typ, "seconds_to_mu"): + if len(node.args) == 1 and len(node.keywords) == 0: + arg = self.visit(node.args[0]) + arg_mu = self.append(ir.Arith(ast.Div(loc=None), arg, self.ref_period)) + return self.append(ir.Coerce(arg_mu, builtins.TInt(types.TValue(64)))) + else: + assert False elif types.is_exn_constructor(typ): return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args]) elif types.is_constructor(typ): diff --git a/artiq/compiler/transforms/inferencer.py b/artiq/compiler/transforms/inferencer.py index 852617c5e..d82c32cd0 100644 --- a/artiq/compiler/transforms/inferencer.py +++ b/artiq/compiler/transforms/inferencer.py @@ -505,6 +505,17 @@ class Inferencer(algorithm.Visitor): node.func.loc, notes=valid_forms) self.engine.process(diag) + def simple_form(info, arg_types=[], return_type=builtins.TNone()): + self._unify(node.type, return_type, + node.loc, None) + + if len(node.args) == len(arg_types) and len(node.keywords) == 0: + for index, arg_type in enumerate(arg_types): + self._unify(node.args[index].type, arg_type, + node.args[index].loc, None) + else: + diagnose([ valid_form(info) ]) + if types.is_exn_constructor(typ): valid_forms = lambda: [ valid_form("{exn}() -> {exn}".format(exn=typ.name)), @@ -730,6 +741,30 @@ class Inferencer(algorithm.Visitor): pass else: diagnose(valid_forms()) + elif types.is_builtin(typ, "now"): + simple_form("now() -> float", + [], builtins.TFloat()) + elif types.is_builtin(typ, "delay"): + simple_form("delay(time:float) -> None", + [builtins.TFloat()]) + elif types.is_builtin(typ, "at"): + simple_form("at(time:float) -> None", + [builtins.TFloat()]) + elif types.is_builtin(typ, "now_mu"): + simple_form("now_mu() -> int(width=64)", + [], builtins.TInt(types.TValue(64))) + elif types.is_builtin(typ, "delay_mu"): + simple_form("delay_mu(time_mu:int(width=64)) -> None", + [builtins.TInt(types.TValue(64))]) + elif types.is_builtin(typ, "at_mu"): + simple_form("at_mu(time_mu:int(width=64)) -> None", + [builtins.TInt(types.TValue(64))]) + elif types.is_builtin(typ, "mu_to_seconds"): + simple_form("mu_to_seconds(time_mu:int(width=64)) -> float", + [builtins.TInt(types.TValue(64))], builtins.TFloat()) + elif types.is_builtin(typ, "seconds_to_mu"): + simple_form("seconds_to_mu(time:float) -> int(width=64)", + [builtins.TFloat()], builtins.TInt(types.TValue(64))) elif types.is_constructor(typ): # An user-defined class. self._unify(node.type, typ.find().instance, diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index ef20056f0..08408c2d6 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -14,6 +14,7 @@ llvoid = ll.VoidType() lli1 = ll.IntType(1) lli8 = ll.IntType(8) lli32 = ll.IntType(32) +lli64 = ll.IntType(64) lldouble = ll.DoubleType() llptr = ll.IntType(8).as_pointer() llmetadata = ll.MetaData() @@ -331,9 +332,9 @@ class LLVMIRGenerator: assert False def llbuiltin(self, name): - llfun = self.llmodule.get_global(name) - if llfun is not None: - return llfun + llglobal = self.llmodule.get_global(name) + if llglobal is not None: + return llglobal if name in "llvm.donothing": llty = ll.FunctionType(llvoid, []) @@ -366,13 +367,19 @@ class LLVMIRGenerator: var_arg=True) elif name == "recv_rpc": llty = ll.FunctionType(lli32, [llptr]) + elif name == "now": + llty = lli64 else: assert False - llfun = ll.Function(self.llmodule, llty, name) - if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"): - llfun.attributes.add("noreturn") - return llfun + if isinstance(llty, ll.FunctionType): + llglobal = ll.Function(self.llmodule, llty, name) + if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"): + llglobal.attributes.add("noreturn") + else: + llglobal = ll.GlobalVariable(self.llmodule, llty, name) + + return llglobal def map(self, value): if isinstance(value, (ir.Argument, ir.Instruction, ir.BasicBlock)): @@ -774,6 +781,17 @@ class LLVMIRGenerator: elif insn.op == "exncast": # This is an identity cast at LLVM IR level. return self.map(insn.operands[0]) + elif insn.op == "now_mu": + return self.llbuilder.load(self.llbuiltin("now"), name=insn.name) + elif insn.op == "delay_mu": + interval, = insn.operands + llnowptr = self.llbuiltin("now") + llnow = self.llbuilder.load(llnowptr) + lladjusted = self.llbuilder.add(llnow, self.map(interval)) + return self.llbuilder.store(lladjusted, llnowptr) + elif insn.op == "at_mu": + time, = insn.operands + return self.llbuilder.store(self.map(time), self.llbuiltin("now")) else: assert False diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index 18cc981c3..afced23bf 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -39,7 +39,7 @@ class Core: stitcher.stitch_call(function, args, kwargs) stitcher.finalize() - module = Module(stitcher) + module = Module(stitcher, ref_period=self.ref_period) target = OR1KTarget() library = target.compile_and_link([module]) diff --git a/artiq/py2llvm_old/transforms/lower_time.py b/artiq/py2llvm_old/transforms/lower_time.py deleted file mode 100644 index 5b3df0245..000000000 --- a/artiq/py2llvm_old/transforms/lower_time.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -This transform implements time management functions (delay_mu/now_mu/at_mu) -using an accumulator 'now' and simple replacement rules: - - delay_mu(t) -> now += t - now_mu() -> now - at_mu(t) -> now = t - -The function delay(), that uses seconds, must be lowered to delay_mu() before -invoking this transform. -The accumulator is initialized to an int64 value at the beginning of the -output function. -""" - -import ast - - -class _TimeLowerer(ast.NodeTransformer): - def visit_Call(self, node): - if node.func.id == "now_mu": - return ast.copy_location(ast.Name("now", ast.Load()), node) - else: - self.generic_visit(node) - return node - - def visit_Expr(self, node): - r = node - if isinstance(node.value, ast.Call): - funcname = node.value.func.id - if funcname == "delay_mu": - r = ast.copy_location( - ast.AugAssign(target=ast.Name("now", ast.Store()), - op=ast.Add(), - value=node.value.args[0]), - node) - elif funcname == "at_mu": - r = ast.copy_location( - ast.Assign(targets=[ast.Name("now", ast.Store())], - value=node.value.args[0]), - node) - self.generic_visit(r) - return r - - -def lower_time(func_def): - _TimeLowerer().visit(func_def) - call_init = ast.Call( - func=ast.Name("syscall", ast.Load()), - args=[ast.Str("now_init")], - keywords=[], starargs=None, kwargs=None) - stmt_init = ast.Assign(targets=[ast.Name("now", ast.Store())], - value=call_init) - call_save = ast.Call( - func=ast.Name("syscall", ast.Load()), - args=[ast.Str("now_save"), ast.Name("now", ast.Load())], - keywords=[], starargs=None, kwargs=None) - stmt_save = ast.Expr(call_save) - func_def.body = [ - stmt_init, - ast.Try(body=func_def.body, - handlers=[], - orelse=[], - finalbody=[stmt_save]) - ] diff --git a/artiq/py2llvm_old/transforms/quantize_time.py b/artiq/py2llvm_old/transforms/quantize_time.py index aad75069a..42e04f564 100644 --- a/artiq/py2llvm_old/transforms/quantize_time.py +++ b/artiq/py2llvm_old/transforms/quantize_time.py @@ -1,69 +1,3 @@ -""" -This transform turns calls to delay() that use non-integer time -expressed in seconds into calls to delay_mu() that use int64 time -expressed in multiples of ref_period. - -It does so by inserting multiplication/division/rounding operations around -those calls. - -The seconds_to_mu and mu_to_seconds core language functions are also -implemented here, as well as watchdog to syscall conversion. -""" - -import ast - -from artiq.transforms.tools import value_to_ast - - -def _seconds_to_mu(ref_period, node): - divided = ast.copy_location( - ast.BinOp(left=node, - op=ast.Div(), - right=value_to_ast(ref_period)), - node) - return ast.copy_location( - ast.Call(func=ast.Name("round64", ast.Load()), - args=[divided], - keywords=[], starargs=[], kwargs=[]), - divided) - - -def _mu_to_seconds(ref_period, node): - return ast.copy_location( - ast.BinOp(left=node, - op=ast.Mult(), - right=value_to_ast(ref_period)), - node) - - -class _TimeQuantizer(ast.NodeTransformer): - def __init__(self, ref_period): - self.ref_period = ref_period - self.watchdog_id_counter = 0 - - def visit_Call(self, node): - funcname = node.func.id - if funcname == "delay": - node.func.id = "delay_mu" - if (isinstance(node.args[0], ast.Call) - and node.args[0].func.id == "mu_to_seconds"): - # optimize: - # delay(mu_to_seconds(x)) -> delay_mu(x) - node.args[0] = self.visit(node.args[0].args[0]) - else: - node.args[0] = _seconds_to_mu(self.ref_period, - self.visit(node.args[0])) - return node - elif funcname == "seconds_to_mu": - return _seconds_to_mu(self.ref_period, - self.visit(node.args[0])) - elif funcname == "mu_to_seconds": - return _mu_to_seconds(self.ref_period, - self.visit(node.args[0])) - else: - self.generic_visit(node) - return node - def visit_With(self, node): self.generic_visit(node) if (isinstance(node.items[0].context_expr, ast.Call) @@ -107,7 +41,3 @@ class _TimeQuantizer(ast.NodeTransformer): finalbody=[stmt_clear]) ] return node - - -def quantize_time(func_def, ref_period): - _TimeQuantizer(ref_period).visit(func_def) diff --git a/lit-test/libartiq_personality/Makefile b/lit-test/libartiq_support/Makefile similarity index 50% rename from lit-test/libartiq_personality/Makefile rename to lit-test/libartiq_support/Makefile index 2a72a7185..bac7a11c9 100644 --- a/lit-test/libartiq_personality/Makefile +++ b/lit-test/libartiq_support/Makefile @@ -1,4 +1,4 @@ CC ?= clang -libartiq_personality.so: ../../soc/runtime/artiq_personality.c artiq_terminate.c +libartiq_support.so: ../../soc/runtime/artiq_personality.c artiq_terminate.c artiq_time.c $(CC) -std=c99 -Wall -Werror -I. -I../../soc/runtime -g -fPIC -shared -o $@ $^ diff --git a/lit-test/libartiq_personality/__cxxabi_config.h b/lit-test/libartiq_support/__cxxabi_config.h similarity index 100% rename from lit-test/libartiq_personality/__cxxabi_config.h rename to lit-test/libartiq_support/__cxxabi_config.h diff --git a/lit-test/libartiq_personality/artiq_terminate.c b/lit-test/libartiq_support/artiq_terminate.c similarity index 100% rename from lit-test/libartiq_personality/artiq_terminate.c rename to lit-test/libartiq_support/artiq_terminate.c diff --git a/lit-test/libartiq_support/artiq_time.c b/lit-test/libartiq_support/artiq_time.c new file mode 100644 index 000000000..1afeadbc0 --- /dev/null +++ b/lit-test/libartiq_support/artiq_time.c @@ -0,0 +1,3 @@ +#include + +int64_t now = 0; diff --git a/lit-test/libartiq_support/libartiq_personality.so b/lit-test/libartiq_support/libartiq_personality.so new file mode 100755 index 0000000000000000000000000000000000000000..80ff44cb82344354ecb654dc6bee6b8ed50efdb0 GIT binary patch literal 23696 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 mailbox_send_and_wait(&load_reply); + + now = now_init(); kernel_init(); + now_save(now); struct msg_base finished_reply; finished_reply.type = MESSAGE_TYPE_FINISHED;