From c5c5708f4909b8218846ee9a4b5b6cd6f367af69 Mon Sep 17 00:00:00 2001 From: mwojcik Date: Tue, 25 Jun 2024 13:16:03 +0800 Subject: [PATCH] compiler: add builtinInvoke for subkernel raising functions --- artiq/compiler/ir.py | 36 ++++++++++++++++++ .../compiler/transforms/artiq_ir_generator.py | 27 +++++++++++-- .../compiler/transforms/llvm_ir_generator.py | 38 +++++++++++++++++++ artiq/firmware/runtime/kernel.rs | 11 +++++- 4 files changed, 106 insertions(+), 6 deletions(-) diff --git a/artiq/compiler/ir.py b/artiq/compiler/ir.py index f630f914a..9d2dfec25 100644 --- a/artiq/compiler/ir.py +++ b/artiq/compiler/ir.py @@ -1047,6 +1047,42 @@ class Builtin(Instruction): def opcode(self): return "builtin({})".format(self.op) +class BuiltinInvoke(Terminator): + """ + A builtin operation which can raise exceptions. + + :ivar op: (string) operation name + """ + + """ + :param op: (string) operation name + :param normal: (:class:`BasicBlock`) normal target + :param exn: (:class:`BasicBlock`) exceptional target + """ + def __init__(self, op, operands, typ, normal, exn, name=None): + assert isinstance(op, str) + for operand in operands: assert isinstance(operand, Value) + assert isinstance(normal, BasicBlock) + assert isinstance(exn, BasicBlock) + if name is None: + name = "BLTINV.{}".format(op) + super().__init__(operands + [normal, exn], typ, name) + self.op = op + + def copy(self, mapper): + self_copy = super().copy(mapper) + self_copy.op = self.op + return self_copy + + def normal_target(self): + return self.operands[-2] + + def exception_target(self): + return self.operands[-1] + + def opcode(self): + return "builtinInvokable({})".format(self.op) + class Closure(Instruction): """ A closure creation operation. diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 92345caae..a47088d0d 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -2544,10 +2544,22 @@ class ARTIQIRGenerator(algorithm.Visitor): fn = types.get_method_function(fn) sid = ir.Constant(fn.sid, builtins.TInt32()) if not builtins.is_none(fn.ret): - ret = self.append(ir.Builtin("subkernel_retrieve_return", [sid, timeout], fn.ret)) + if self.unwind_target is None: + ret = self.append(ir.Builtin("subkernel_retrieve_return", [sid, timeout], fn.ret)) + else: + after_invoke = self.add_block("invoke") + ret = self.append(ir.BuiltinInvoke("subkernel_retrieve_return", [sid, timeout], + fn.ret, after_invoke, self.unwind_target)) + self.current_block = after_invoke else: ret = ir.Constant(None, builtins.TNone()) - self.append(ir.Builtin("subkernel_await_finish", [sid, timeout], builtins.TNone())) + if self.unwind_target is None: + self.append(ir.Builtin("subkernel_await_finish", [sid, timeout], builtins.TNone())) + else: + after_invoke = self.add_block("invoke") + self.append(ir.BuiltinInvoke("subkernel_await_finish", [sid, timeout], + builtins.TNone(), after_invoke, self.unwind_target)) + self.current_block = after_invoke return ret elif types.is_builtin(typ, "subkernel_preload"): if len(node.args) == 1 and len(node.keywords) == 0: @@ -2594,7 +2606,14 @@ class ARTIQIRGenerator(algorithm.Visitor): {"name": name, "recv": vartype, "send": msg.value_type}, node.loc) self.engine.process(diag) - return self.append(ir.Builtin("subkernel_recv", [msg_id, timeout], vartype)) + if self.unwind_target is None: + ret = self.append(ir.Builtin("subkernel_recv", [msg_id, timeout], vartype)) + else: + after_invoke = self.add_block("invoke") + ret = self.append(ir.BuiltinInvoke("subkernel_recv", [msg_id, timeout], + vartype, after_invoke, self.unwind_target)) + self.current_block = after_invoke + return ret 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): @@ -2946,7 +2965,7 @@ class ARTIQIRGenerator(algorithm.Visitor): format_string += ")" elif builtins.is_exception(value.type): # message may not be an actual string... - # so we cannot really print it + # so we cannot really print itInvoke name = self.append(ir.GetAttr(value, "#__name__")) param1 = self.append(ir.GetAttr(value, "#__param0__")) param2 = self.append(ir.GetAttr(value, "#__param1__")) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 08b71021c..28d24dffe 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -1437,6 +1437,44 @@ class LLVMIRGenerator: else: assert False + def process_BuiltinInvoke(self, insn): + llnormalblock = self.map(insn.normal_target()) + llunwindblock = self.map(insn.exception_target()) + if insn.op == "subkernel_retrieve_return": + llsid = self.map(insn.operands[0]) + lltimeout = self.map(insn.operands[1]) + lltagptr = self._build_subkernel_tags([insn.type]) + llheadu = self.llbuilder.append_basic_block(name="subkernel.await.unwind") + self.llbuilder.invoke(self.llbuiltin("subkernel_await_message"), + [llsid, lltimeout, lltagptr, ll.Constant(lli8, 1), ll.Constant(lli8, 1)], + llheadu, llunwindblock, + name="subkernel.await.message") + self.llbuilder.position_at_end(llheadu) + llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [], + name="subkernel.arg.stack") + return self._build_rpc_recv(insn.type, llstackptr, llnormalblock, llunwindblock) + elif insn.op == "subkernel_await_finish": + llsid = self.map(insn.operands[0]) + lltimeout = self.map(insn.operands[1]) + return self.llbuilder.invoke(self.llbuiltin("subkernel_await_finish"), [llsid, lltimeout], + llnormalblock, llunwindblock, + name="subkernel.await.finish") + elif insn.op == "subkernel_recv": + llmsgid = self.map(insn.operands[0]) + lltimeout = self.map(insn.operands[1]) + lltagptr = self._build_subkernel_tags([insn.type]) + llheadu = self.llbuilder.append_basic_block(name="subkernel.await.unwind") + self.llbuilder.invoke(self.llbuiltin("subkernel_await_message"), + [llmsgid, lltimeout, lltagptr, ll.Constant(lli8, 1), ll.Constant(lli8, 1)], + llheadu, llunwindblock, + name="subkernel.await.message") + self.llbuilder.position_at_end(llheadu) + llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [], + name="subkernel.arg.stack") + return self._build_rpc_recv(insn.type, llstackptr, llnormalblock, llunwindblock) + else: + assert False + def process_SubkernelAwaitArgs(self, insn): llmin = self.map(insn.operands[0]) llmax = self.map(insn.operands[1]) diff --git a/artiq/firmware/runtime/kernel.rs b/artiq/firmware/runtime/kernel.rs index 9db3bba9e..545f75981 100644 --- a/artiq/firmware/runtime/kernel.rs +++ b/artiq/firmware/runtime/kernel.rs @@ -278,8 +278,15 @@ pub mod subkernel { { let mut reader = Cursor::new(buffer); - let _sync = reader.read_u32()?; - let _9 = reader.read_u8()?; + let mut byte = reader.read_u8()?; + // to sync + while byte != 0x5a { + byte = reader.read_u8()?; + } + // skip sync bytes, 0x09 indicates exception + while byte != 0x09 { + byte = reader.read_u8()?; + } let _len = reader.read_u32()?; // ignore the remaining exceptions, stack traces etc. - unwinding from another device would be unwise anyway Ok(Exception {