Skip to content

Commit 02b1543

Browse files
author
whitequark
committedAug 9, 2015
Implement receiving exceptions from RPCs.
1 parent 8b7d38d commit 02b1543

File tree

6 files changed

+176
-73
lines changed

6 files changed

+176
-73
lines changed
 

Diff for: ‎artiq/compiler/transforms/llvm_ir_generator.py

+116-40
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ def llbuiltin(self, name):
146146
llty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.IntType(32)])
147147
elif name == "llvm.copysign.f64":
148148
llty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.DoubleType()])
149+
elif name == "llvm.stacksave":
150+
llty = ll.FunctionType(ll.IntType(8).as_pointer(), [])
151+
elif name == "llvm.stackrestore":
152+
llty = ll.FunctionType(ll.VoidType(), [ll.IntType(8).as_pointer()])
149153
elif name == self.target.print_function:
150154
llty = ll.FunctionType(ll.VoidType(), [ll.IntType(8).as_pointer()], var_arg=True)
151155
elif name == "__artiq_personality":
@@ -155,8 +159,10 @@ def llbuiltin(self, name):
155159
elif name == "__artiq_reraise":
156160
llty = ll.FunctionType(ll.VoidType(), [])
157161
elif name == "send_rpc":
158-
llty = ll.FunctionType(ll.IntType(32), [ll.IntType(32), ll.IntType(8).as_pointer()],
162+
llty = ll.FunctionType(ll.VoidType(), [ll.IntType(32), ll.IntType(8).as_pointer()],
159163
var_arg=True)
164+
elif name == "recv_rpc":
165+
llty = ll.FunctionType(ll.IntType(32), [ll.IntType(8).as_pointer().as_pointer()])
160166
else:
161167
assert False
162168

@@ -559,12 +565,18 @@ def process_Closure(self, insn):
559565
name=insn.name)
560566
return llvalue
561567

562-
# See session.c:send_rpc_value.
563-
def _rpc_tag(self, typ, root_type, root_loc):
568+
def _prepare_closure_call(self, insn):
569+
llclosure, llargs = self.map(insn.target_function()), map(self.map, insn.arguments())
570+
llenv = self.llbuilder.extract_value(llclosure, 0)
571+
llfun = self.llbuilder.extract_value(llclosure, 1)
572+
return llfun, [llenv] + list(llargs)
573+
574+
# See session.c:send_rpc_value and session.c:recv_rpc_value.
575+
def _rpc_tag(self, typ, error_handler):
564576
if types.is_tuple(typ):
565577
assert len(typ.elts) < 256
566578
return b"t" + bytes([len(typ.elts)]) + \
567-
b"".join([self._rpc_tag(elt_type, root_type, root_loc)
579+
b"".join([self._rpc_tag(elt_type, error_handler)
568580
for elt_type in typ.elts])
569581
elif builtins.is_none(typ):
570582
return b"n"
@@ -580,38 +592,53 @@ def _rpc_tag(self, typ, root_type, root_loc):
580592
return b"s"
581593
elif builtins.is_list(typ):
582594
return b"l" + self._rpc_tag(builtins.get_iterable_elt(typ),
583-
root_type, root_loc)
595+
error_handler)
584596
elif builtins.is_range(typ):
585597
return b"r" + self._rpc_tag(builtins.get_iterable_elt(typ),
586-
root_type, root_loc)
598+
error_handler)
587599
elif ir.is_option(typ):
588600
return b"o" + self._rpc_tag(typ.params["inner"],
589-
root_type, root_loc)
601+
error_handler)
590602
else:
603+
error_handler(typ)
604+
605+
def _build_rpc(self, fun_loc, fun_type, args, llnormalblock, llunwindblock):
606+
llservice = ll.Constant(ll.IntType(32), fun_type.service)
607+
608+
tag = b""
609+
610+
for arg in args:
611+
def arg_error_handler(typ):
612+
printer = types.TypePrinter()
613+
note = diagnostic.Diagnostic("note",
614+
"value of type {type}",
615+
{"type": printer.name(typ)},
616+
arg.loc)
617+
diag = diagnostic.Diagnostic("error",
618+
"type {type} is not supported in remote procedure calls",
619+
{"type": printer.name(arg.typ)},
620+
arg.loc)
621+
self.engine.process(diag)
622+
tag += self._rpc_tag(arg.type, arg_error_handler)
623+
tag += b":"
624+
625+
def ret_error_handler(typ):
591626
printer = types.TypePrinter()
592627
note = diagnostic.Diagnostic("note",
593628
"value of type {type}",
594-
{"type": printer.name(root_type)},
595-
root_loc)
596-
diag = diagnostic.Diagnostic("error",
597-
"type {type} is not supported in remote procedure calls",
598629
{"type": printer.name(typ)},
599-
root_loc)
630+
fun_loc)
631+
diag = diagnostic.Diagnostic("error",
632+
"return type {type} is not supported in remote procedure calls",
633+
{"type": printer.name(fun_type.ret)},
634+
fun_loc)
600635
self.engine.process(diag)
636+
tag += self._rpc_tag(fun_type.ret, ret_error_handler)
637+
tag += b"\x00"
601638

602-
def _build_rpc(self, service, args, return_type):
603-
llservice = ll.Constant(ll.IntType(32), service)
639+
lltag = self.llconst_of_const(ir.Constant(tag + b"\x00", builtins.TStr()))
604640

605-
tag = b""
606-
for arg in args:
607-
if isinstance(arg, ir.Constant):
608-
# Constants don't have locations, but conveniently
609-
# they also never fail to serialize.
610-
tag += self._rpc_tag(arg.type, arg.type, None)
611-
else:
612-
tag += self._rpc_tag(arg.type, arg.type, arg.loc)
613-
tag += b"\x00"
614-
lltag = self.llconst_of_const(ir.Constant(tag, builtins.TStr()))
641+
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [])
615642

616643
llargs = []
617644
for arg in args:
@@ -620,30 +647,79 @@ def _build_rpc(self, service, args, return_type):
620647
self.llbuilder.store(llarg, llargslot)
621648
llargs.append(llargslot)
622649

623-
return self.llbuiltin("send_rpc"), [llservice, lltag] + llargs
650+
self.llbuilder.call(self.llbuiltin("send_rpc"),
651+
[llservice, lltag] + llargs)
652+
653+
# Don't waste stack space on saved arguments.
654+
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
655+
656+
# T result = {
657+
# void *ptr = NULL;
658+
# loop: int size = rpc_recv("tag", ptr);
659+
# if(size) { ptr = alloca(size); goto loop; }
660+
# else *(T*)ptr
661+
# }
662+
llprehead = self.llbuilder.basic_block
663+
llhead = self.llbuilder.append_basic_block(name=llprehead.name + ".rpc.head")
664+
if llunwindblock:
665+
llheadu = self.llbuilder.append_basic_block(name=llprehead.name + ".rpc.head.unwind")
666+
llalloc = self.llbuilder.append_basic_block(name=llprehead.name + ".rpc.alloc")
667+
lltail = self.llbuilder.append_basic_block(name=llprehead.name + ".rpc.tail")
668+
669+
llslot = self.llbuilder.alloca(ll.IntType(8).as_pointer())
670+
self.llbuilder.store(ll.Constant(ll.IntType(8).as_pointer(), None), llslot)
671+
self.llbuilder.branch(llhead)
672+
673+
self.llbuilder.position_at_end(llhead)
674+
if llunwindblock:
675+
llsize = self.llbuilder.invoke(self.llbuiltin("recv_rpc"), [llslot],
676+
llheadu, llunwindblock)
677+
self.llbuilder.position_at_end(llheadu)
678+
else:
679+
llsize = self.llbuilder.call(self.llbuiltin("recv_rpc"), [llslot])
680+
lldone = self.llbuilder.icmp_unsigned('==', llsize, ll.Constant(llsize.type, 0))
681+
self.llbuilder.cbranch(lldone, lltail, llalloc)
682+
683+
self.llbuilder.position_at_end(llalloc)
684+
llalloca = self.llbuilder.alloca(ll.IntType(8), llsize)
685+
self.llbuilder.store(llalloca, llslot)
686+
self.llbuilder.branch(llhead)
687+
688+
self.llbuilder.position_at_end(lltail)
689+
llretty = self.llty_of_type(fun_type.ret, for_return=True)
690+
llretptr = self.llbuilder.bitcast(llslot, llretty.as_pointer())
691+
llret = self.llbuilder.load(llretptr)
692+
if not builtins.is_allocated(fun_type.ret):
693+
# We didn't allocate anything except the slot for the value itself.
694+
# Don't waste stack space.
695+
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
696+
if llnormalblock:
697+
self.llbuilder.branch(llnormalblock)
698+
return llret
624699

625-
def prepare_call(self, insn):
700+
def process_Call(self, insn):
626701
if types.is_rpc_function(insn.target_function().type):
627-
return self._build_rpc(insn.target_function().type.service,
702+
return self._build_rpc(insn.target_function().loc,
703+
insn.target_function().type,
628704
insn.arguments(),
629-
insn.target_function().type.ret)
705+
llnormalblock=None, llunwindblock=None)
630706
else:
631-
llclosure, llargs = self.map(insn.target_function()), map(self.map, insn.arguments())
632-
llenv = self.llbuilder.extract_value(llclosure, 0)
633-
llfun = self.llbuilder.extract_value(llclosure, 1)
634-
return llfun, [llenv] + list(llargs)
635-
636-
def process_Call(self, insn):
637-
llfun, llargs = self.prepare_call(insn)
638-
return self.llbuilder.call(llfun, llargs,
639-
name=insn.name)
707+
llfun, llargs = self._prepare_closure_call(insn)
708+
return self.llbuilder.call(llfun, llargs,
709+
name=insn.name)
640710

641711
def process_Invoke(self, insn):
642-
llfun, llargs = self.prepare_call(insn)
643712
llnormalblock = self.map(insn.normal_target())
644713
llunwindblock = self.map(insn.exception_target())
645-
return self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
646-
name=insn.name)
714+
if types.is_rpc_function(insn.target_function().type):
715+
return self._build_rpc(insn.target_function().loc,
716+
insn.target_function().type,
717+
insn.arguments(),
718+
llnormalblock, llunwindblock)
719+
else:
720+
llfun, llargs = self._prepare_closure_call(insn)
721+
return self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
722+
name=insn.name)
647723

648724
def process_Select(self, insn):
649725
return self.llbuilder.select(self.map(insn.condition()),

Diff for: ‎artiq/coredevice/comm_generic.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99

1010
logger = logging.getLogger(__name__)
11+
logger.setLevel(logging.DEBUG)
1112

1213

1314
class _H2DMsgType(Enum):
@@ -325,13 +326,14 @@ def _receive_rpc_args(self, rpc_map):
325326
def _serve_rpc(self, rpc_map):
326327
service = self._read_int32()
327328
args = self._receive_rpc_args(rpc_map)
328-
logger.debug("rpc service: %d %r", service, args)
329+
return_tag = self._read_string()
330+
logger.debug("rpc service: %d %r -> %s", service, args, return_tag)
329331

330332
try:
331333
result = rpc_map[service](*args)
332334
if not isinstance(result, int) or not (-2**31 < result < 2**31-1):
333335
raise ValueError("An RPC must return an int(width=32)")
334-
except ARTIQException as exn:
336+
except core_language.ARTIQException as exn:
335337
logger.debug("rpc service: %d %r ! %r", service, args, exn)
336338

337339
self._write_header(_H2DMsgType.RPC_EXCEPTION)
@@ -355,7 +357,7 @@ def _serve_rpc(self, rpc_map):
355357
for index in range(3):
356358
self._write_int64(0)
357359

358-
((filename, line, function, _), ) = traceback.extract_tb(exn.__traceback__)
360+
(_, (filename, line, function, _), ) = traceback.extract_tb(exn.__traceback__, 2)
359361
self._write_string(filename)
360362
self._write_int32(line)
361363
self._write_int32(-1) # column not known

Diff for: ‎soc/runtime/ksupport.c

+28-17
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ static const struct symbol runtime_exports[] = {
9393
{"log", &log},
9494
{"lognonl", &lognonl},
9595
{"send_rpc", &send_rpc},
96+
{"recv_rpc", &recv_rpc},
9697

9798
/* direct syscalls */
9899
{"rtio_get_counter", &rtio_get_counter},
@@ -301,7 +302,7 @@ void watchdog_clear(int id)
301302
mailbox_send_and_wait(&request);
302303
}
303304

304-
int send_rpc(int service, const char *tag, ...)
305+
void send_rpc(int service, const char *tag, ...)
305306
{
306307
struct msg_rpc_send request;
307308

@@ -311,24 +312,34 @@ int send_rpc(int service, const char *tag, ...)
311312
va_start(request.args, tag);
312313
mailbox_send_and_wait(&request);
313314
va_end(request.args);
315+
}
316+
317+
int recv_rpc(void **slot) {
318+
struct msg_rpc_recv_request request;
319+
struct msg_rpc_recv_reply *reply;
314320

315-
// struct msg_base *reply;
316-
// reply = mailbox_wait_and_receive();
317-
// if(reply->type == MESSAGE_TYPE_RPC_REPLY) {
318-
// int result = ((struct msg_rpc_reply *)reply)->result;
319-
// mailbox_acknowledge();
320-
// return result;
321-
// } else if(reply->type == MESSAGE_TYPE_RPC_EXCEPTION) {
322-
// struct artiq_exception exception;
323-
// memcpy(&exception, ((struct msg_rpc_exception *)reply)->exception,
324-
// sizeof(struct artiq_exception));
325-
// mailbox_acknowledge();
326-
// __artiq_raise(&exception);
327-
// } else {
328-
// log("Malformed MESSAGE_TYPE_RPC_REQUEST reply type %d",
329-
// reply->type);
321+
request.type = MESSAGE_TYPE_RPC_RECV_REQUEST;
322+
request.slot = slot;
323+
mailbox_send_and_wait(&request);
324+
325+
reply = mailbox_wait_and_receive();
326+
if(reply->type != MESSAGE_TYPE_RPC_RECV_REPLY) {
327+
log("Malformed MESSAGE_TYPE_RPC_RECV_REQUEST reply type %d",
328+
reply->type);
330329
while(1);
331-
// }
330+
}
331+
332+
if(reply->exception) {
333+
struct artiq_exception exception;
334+
memcpy(&exception, reply->exception,
335+
sizeof(struct artiq_exception));
336+
mailbox_acknowledge();
337+
__artiq_raise(&exception);
338+
} else {
339+
int alloc_size = reply->alloc_size;
340+
mailbox_acknowledge();
341+
return alloc_size;
342+
}
332343
}
333344

334345
void lognonl(const char *fmt, ...)

Diff for: ‎soc/runtime/ksupport.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ long long int now_init(void);
55
void now_save(long long int now);
66
int watchdog_set(int ms);
77
void watchdog_clear(int id);
8-
int send_rpc(int service, const char *tag, ...);
8+
void send_rpc(int service, const char *tag, ...);
9+
int recv_rpc(void **slot);
910
void lognonl(const char *fmt, ...);
1011
void log(const char *fmt, ...);
1112

Diff for: ‎soc/runtime/messages.h

+2-7
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ enum {
1717
MESSAGE_TYPE_RPC_SEND,
1818
MESSAGE_TYPE_RPC_RECV_REQUEST,
1919
MESSAGE_TYPE_RPC_RECV_REPLY,
20-
MESSAGE_TYPE_RPC_EXCEPTION,
2120
MESSAGE_TYPE_LOG,
2221

2322
MESSAGE_TYPE_BRG_READY,
@@ -90,16 +89,12 @@ struct msg_rpc_send {
9089

9190
struct msg_rpc_recv_request {
9291
int type;
93-
// TODO ???
92+
void **slot;
9493
};
9594

9695
struct msg_rpc_recv_reply {
9796
int type;
98-
// TODO ???
99-
};
100-
101-
struct msg_rpc_exception {
102-
int type;
97+
int alloc_size;
10398
struct artiq_exception *exception;
10499
};
105100

Diff for: ‎soc/runtime/session.c

+23-5
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,8 @@ static int process_input(void)
476476
// }
477477

478478
case REMOTEMSG_TYPE_RPC_EXCEPTION: {
479-
struct msg_rpc_exception reply;
479+
struct msg_rpc_recv_request *request;
480+
struct msg_rpc_recv_reply reply;
480481

481482
struct artiq_exception exception;
482483
exception.name = in_packet_string();
@@ -494,7 +495,15 @@ static int process_input(void)
494495
return 0; // restart session
495496
}
496497

497-
reply.type = MESSAGE_TYPE_RPC_EXCEPTION;
498+
request = mailbox_wait_and_receive();
499+
if(request->type != MESSAGE_TYPE_RPC_RECV_REQUEST) {
500+
log("Expected MESSAGE_TYPE_RPC_RECV_REQUEST, got %d",
501+
request->type);
502+
return 0; // restart session
503+
}
504+
505+
reply.type = MESSAGE_TYPE_RPC_RECV_REPLY;
506+
reply.alloc_size = 0;
498507
reply.exception = &exception;
499508
mailbox_send_and_wait(&reply);
500509

@@ -650,15 +659,17 @@ static int send_rpc_request(int service, const char *tag, va_list args)
650659
out_packet_start(REMOTEMSG_TYPE_RPC_REQUEST);
651660
out_packet_int32(service);
652661

653-
while(*tag) {
662+
while(*tag != ':') {
654663
void *value = va_arg(args, void*);
655664
if(!kloader_validate_kpointer(value))
656665
return 0;
657666
if(!send_rpc_value(&tag, &value))
658667
return 0;
659668
}
660-
661669
out_packet_int8(0);
670+
671+
out_packet_string(tag + 1);
672+
662673
out_packet_finish();
663674
return 1;
664675
}
@@ -670,6 +681,12 @@ static int process_kmsg(struct msg_base *umsg)
670681
return 0;
671682
if(kloader_is_essential_kmsg(umsg->type))
672683
return 1; /* handled elsewhere */
684+
if(user_kernel_state == USER_KERNEL_WAIT_RPC &&
685+
umsg->type == MESSAGE_TYPE_RPC_RECV_REQUEST) {
686+
// Handled and acknowledged when we receive
687+
// REMOTEMSG_TYPE_RPC_{EXCEPTION,REPLY}.
688+
return 1;
689+
}
673690
if(user_kernel_state != USER_KERNEL_RUNNING) {
674691
log("Received unexpected message from kernel CPU while not in running state");
675692
return 0;
@@ -739,7 +756,8 @@ static int process_kmsg(struct msg_base *umsg)
739756
struct msg_rpc_send *msg = (struct msg_rpc_send *)umsg;
740757

741758
if(!send_rpc_request(msg->service, msg->tag, msg->args)) {
742-
log("Failed to send RPC request");
759+
log("Failed to send RPC request (service %d, tag %s)",
760+
msg->service, msg->tag);
743761
return 0; // restart session
744762
}
745763

0 commit comments

Comments
 (0)
Please sign in to comment.