From 38a99fde5273324138aa85a2a96c1206b242a888 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 2 Jan 2016 22:51:04 +0800 Subject: [PATCH] Implement selective attribute writeback using shadow memory. --- artiq/compiler/builtins.py | 16 ++- artiq/compiler/targets.py | 10 +- .../compiler/transforms/llvm_ir_generator.py | 104 ++++++++++++------ artiq/coredevice/comm_generic.py | 15 ++- artiq/runtime/ksupport.c | 62 ++++++++++- artiq/runtime/messages.h | 8 ++ artiq/runtime/session.c | 6 +- 7 files changed, 173 insertions(+), 48 deletions(-) diff --git a/artiq/compiler/builtins.py b/artiq/compiler/builtins.py index 8f5526a1c..2cdb09383 100644 --- a/artiq/compiler/builtins.py +++ b/artiq/compiler/builtins.py @@ -89,13 +89,13 @@ class TException(types.TMono): attributes = OrderedDict([ ("__name__", TStr()), ("__file__", TStr()), - ("__line__", TInt(types.TValue(32))), - ("__col__", TInt(types.TValue(32))), + ("__line__", TInt32()), + ("__col__", TInt32()), ("__func__", TStr()), ("__message__", TStr()), - ("__param0__", TInt(types.TValue(64))), - ("__param1__", TInt(types.TValue(64))), - ("__param2__", TInt(types.TValue(64))), + ("__param0__", TInt64()), + ("__param1__", TInt64()), + ("__param2__", TInt64()), ]) def __init__(self, name="Exception", id=0): @@ -191,6 +191,12 @@ def is_int(typ, width=None): else: return types.is_mono(typ, "int") +def is_int32(typ): + return is_int(typ, types.TValue(32)) + +def is_int64(typ): + return is_int(typ, types.TValue(64)) + def get_int_width(typ): if is_int(typ): return types.get_value(typ.find()["width"]) diff --git a/artiq/compiler/targets.py b/artiq/compiler/targets.py index 8889ee67c..0b9a382e3 100644 --- a/artiq/compiler/targets.py +++ b/artiq/compiler/targets.py @@ -81,8 +81,14 @@ class Target: print(function.as_entity(type_printer), file=sys.stderr) llmod = module.build_llvm_ir(self) - llparsedmod = llvm.parse_assembly(str(llmod)) - llparsedmod.verify() + + try: + llparsedmod = llvm.parse_assembly(str(llmod)) + llparsedmod.verify() + except RuntimeError: + print("====== LLVM IR DUMP (PARSE FAILED) ======", file=sys.stderr) + print(str(llmod), file=sys.stderr) + raise if os.getenv("ARTIQ_DUMP_LLVM"): print("====== LLVM IR DUMP ======", file=sys.stderr) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 96b019d10..fb1b357f0 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -3,10 +3,10 @@ into LLVM intermediate representation. """ -import os, re +import os, re, types as pytypes from collections import defaultdict from pythonparser import ast, diagnostic -from llvmlite_artiq import ir as ll +from llvmlite_artiq import ir as ll, binding as llvm from ...language import core as language_core from .. import types, builtins, ir @@ -418,11 +418,21 @@ class LLVMIRGenerator: return self.llmodule def emit_attribute_writeback(self): + llobjects = [] shadow_memory_dim = defaultdict(lambda: 0) for obj_id in self.object_map: + while len(llobjects) <= obj_id: + llobjects.append(ll.Constant(llptr, None)) + + llobject = self.llmodule.get_global("object.{}".format(obj_id)) + if llobject is not None: + llobjects[obj_id] = llobject.bitcast(llptr) + obj_ref = self.object_map.retrieve(obj_id) - if isinstance(obj_ref, type): + if isinstance(obj_ref, (pytypes.FunctionType, pytypes.MethodType)): + continue + elif isinstance(obj_ref, type): _, typ = self.type_map[obj_ref] else: typ, _ = self.type_map[type(obj_ref)] @@ -430,14 +440,18 @@ class LLVMIRGenerator: if shadow_memory_dim[typ] <= obj_id: shadow_memory_dim[typ] = obj_id + 1 + lldatalayout = llvm.create_target_data(self.llmodule.data_layout) + + llrpcattrty = self.llcontext.get_identified_type("shadow.attr") + llrpcattrty.elements = [lli32, llptr, llptr] + lldescty = self.llcontext.get_identified_type("shadow.desc") - lldescty.elements = [llptr.as_pointer(), llptr, lli32, llptr] + lldescty.elements = [llrpcattrty.as_pointer().as_pointer(), lli32, llptr] lldescs = [] for typ in shadow_memory_dim: if "__objectid__" not in typ.attributes: continue - assert list(typ.attributes.keys())[0] == "__objectid__" if types.is_constructor(typ): type_name = "class.{}".format(typ.name) @@ -455,44 +469,72 @@ class LLVMIRGenerator: llshadow.type = llshadowty.as_pointer() llshadow.initializer = ll.Constant(llshadowty, None) - def rpc_tag_error(typ): - print(typ) - assert False + def llrpcattr_of_attr(name, typ): + llty = self.llty_of_type(typ) + if isinstance(llty, ll.PointerType): + # Work around llvmlite bug where it is unable to get a C++ + # object for a type if it includes an identified type in a context + # other than the default. + size = llptr.get_abi_size(lldatalayout) + else: + size = llty.get_abi_size(lldatalayout) - rpcattrary = list(typ.attributes.keys())[1:] - llrpcattraryty = ll.ArrayType(llptr, len(rpcattrary) + 1) - llrpcattrary = list(map(lambda attr: self.llstr_of_str(attr), rpcattrary)) + def rpc_tag_error(typ): + print(typ) + assert False - llrpcattrs = ll.GlobalVariable(self.llmodule, llrpcattraryty, - name="shadow.attrs.{}".format(type_name)) - llrpcattrs.initializer = ll.Constant(llrpcattraryty, - llrpcattrary + [ll.Constant(llptr, None)]) - llrpcattrs.linkage = 'internal' + rpctag = b"Os" + if not (types.is_function(typ) or types.is_method(typ)): + rpctag += self._rpc_tag(typ, error_handler=rpc_tag_error) + else: + rpctag += b"" + rpctag += b":n\x00" + llrpctag = self.llstr_of_str(rpctag) - rpctag = b"" - for attr_type in list(typ.attributes.values())[1:]: - if types.is_function(attr_type) or types.is_method(attr_type): - continue - rpctag += self._rpc_tag(attr_type, error_handler=rpc_tag_error) - rpctag += b"\x00" - llrpctag = self.llstr_of_str(rpctag) + llrpcattr = ll.GlobalVariable(self.llmodule, llrpcattrty, + name="shadow.attr.{}.{}".format(type_name, name)) + llrpcattr.initializer = ll.Constant(llrpcattrty, [ + ll.Constant(lli32, size), + self.llstr_of_str(rpctag), + self.llstr_of_str(name) + ]) + llrpcattr.global_constant = True + llrpcattr.unnamed_addr = True + llrpcattr.linkage = 'internal' + + return llrpcattr + + llrpcattrs = [llrpcattr_of_attr(attr, typ.attributes[attr]) + for attr in typ.attributes] + + llrpcattraryty = ll.ArrayType(llrpcattrty.as_pointer(), len(llrpcattrs) + 1) + llrpcattrary = ll.GlobalVariable(self.llmodule, llrpcattraryty, + name="shadow.attrs.{}".format(type_name)) + llrpcattrary.initializer = ll.Constant(llrpcattraryty, + llrpcattrs + [ll.Constant(llrpcattrty.as_pointer(), None)]) + llrpcattrary.global_constant = True + llrpcattrary.unnamed_addr = True + llrpcattrary.linkage = 'internal' lldesc = ll.GlobalVariable(self.llmodule, lldescty, name="shadow.desc.{}".format(type_name)) lldesc.initializer = ll.Constant(lldescty, [ - llrpcattrs.bitcast(llptr.as_pointer()), - llrpctag, + llrpcattrary.bitcast(llrpcattrty.as_pointer().as_pointer()), ll.Constant(lli32, shadow_memory_dim[typ]), - llshadow.bitcast(llptr)]) + llshadow.bitcast(llptr) + ]) + lldesc.global_constant = True lldesc.linkage = 'internal' lldescs.append(lldesc) llglobaldescty = ll.ArrayType(lldescty.as_pointer(), len(lldescs) + 1) - llglobaldesc = ll.GlobalVariable(self.llmodule, llglobaldescty, - name="shadow.descs") + llglobaldesc = ll.GlobalVariable(self.llmodule, llglobaldescty, name="shadow") llglobaldesc.initializer = ll.Constant(llglobaldescty, lldescs + [ll.Constant(lldescty.as_pointer(), None)]) - # llglobaldesc.linkage = 'internal' + + llobjectaryty = ll.ArrayType(llptr, len(llobjects)) + llobjectary = ll.GlobalVariable(self.llmodule, llobjectaryty, name="objects") + llobjectary.initializer = ll.Constant(llobjectaryty, llobjects) def process_function(self, func): try: @@ -661,10 +703,10 @@ class LLVMIRGenerator: llidptr = self.llbuilder.gep(self.map(insn.object()), [self.llindex(0), self.llindex(0)]) llid = self.llbuilder.load(llidptr, name="shadow.id") - llattrcount = ll.Constant(lli32, len(object_type.attributes) - 1) + llattrcount = ll.Constant(lli32, len(object_type.attributes)) llshadowpos = self.llbuilder.add( self.llbuilder.mul(llid, llattrcount), - ll.Constant(lli32, self.attr_index(insn) - 1)) + ll.Constant(lli32, self.attr_index(insn))) if types.is_constructor(object_type): shadowname = "shadow.class.{}".format(object_type.name) diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index 8e72f9720..637f94d32 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -435,7 +435,11 @@ class CommGeneric: def _serve_rpc(self, object_map): service_id = self._read_int32() - service = object_map.retrieve(service_id) + if service_id == 0: + service = lambda obj, attr, value: setattr(obj, attr, value) + else: + service = object_map.retrieve(service_id) + arguments = self._receive_rpc_args(object_map, service.__defaults__) return_tags = self._read_bytes() logger.debug("rpc service: [%d]%r %r -> %s", service_id, service, arguments, return_tags) @@ -444,10 +448,11 @@ class CommGeneric: result = service(*arguments) logger.debug("rpc service: %d %r == %r", service_id, arguments, result) - self._write_header(_H2DMsgType.RPC_REPLY) - self._write_bytes(return_tags) - self._send_rpc_value(bytearray(return_tags), result, result, service) - self._write_flush() + if service_id != 0: + self._write_header(_H2DMsgType.RPC_REPLY) + self._write_bytes(return_tags) + self._send_rpc_value(bytearray(return_tags), result, result, service) + self._write_flush() except Exception as exn: logger.debug("rpc service: %d %r ! %r", service_id, arguments, exn) diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index fdf40fb32..20f6e75e8 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -20,6 +20,7 @@ double round(double x); void ksupport_abort(void); +static void attribute_writeback(void *, void *); int64_t now; @@ -249,14 +250,22 @@ int main(void) } if(request->run_kernel) { - void (*kernel_init)() = request->library_info->init; + void (*kernel_run)() = request->library_info->init; + void *__bss_start = dyld_lookup("__bss_start", request->library_info); + void *_end = dyld_lookup("_end", request->library_info); + void *shadow = dyld_lookup("shadow", request->library_info); + void *objects = dyld_lookup("objects", request->library_info); + + memset(__bss_start, 0, _end - __bss_start); mailbox_send_and_wait(&load_reply); now = now_init(); - kernel_init(); + kernel_run(); now_save(now); + attribute_writeback(shadow, objects); + struct msg_base finished_reply; finished_reply.type = MESSAGE_TYPE_FINISHED; mailbox_send_and_wait(&finished_reply); @@ -357,7 +366,10 @@ void send_rpc(int service, const char *tag, ...) { struct msg_rpc_send request; - request.type = MESSAGE_TYPE_RPC_SEND; + if(service != 0) + request.type = MESSAGE_TYPE_RPC_SEND; + else + request.type = MESSAGE_TYPE_RPC_BATCH; request.service = service; request.tag = tag; va_start(request.args, tag); @@ -393,6 +405,50 @@ int recv_rpc(void *slot) { } } +struct shadow_attr { + uint32_t size; + const char *tag; + const char *name; +}; + +struct shadow_desc { + struct shadow_attr **attributes; + uint32_t object_count; + uint8_t *shadow; +}; + +void attribute_writeback(void *udescs, void *uobjects) { + struct shadow_desc **descs = (struct shadow_desc **)udescs; + void **objects = (void **)uobjects; + + while(*descs) { + struct shadow_desc *desc = *descs++; + + size_t attr_count = 0; + for(struct shadow_attr **attr = desc->attributes; *attr; attr++) + attr_count++; + + for(int object_id = 0; object_id < desc->object_count; object_id++) { + uint8_t *shadow = &desc->shadow[object_id * attr_count]; + void *object = objects[object_id]; + + if(object == NULL) continue; + + size_t offset = 0; + for(int attr_index = 0; attr_index < attr_count; attr_index++) { + struct shadow_attr *attr = desc->attributes[attr_index]; + + if(shadow[attr_index]) { + uintptr_t value = (uintptr_t)object + offset; + send_rpc(0, attr->tag, &object, &attr->name, value); + } + + offset += attr->size; + } + } + } +} + void lognonl(const char *fmt, ...) { struct msg_log request; diff --git a/artiq/runtime/messages.h b/artiq/runtime/messages.h index 4c71d0ae1..2d055763e 100644 --- a/artiq/runtime/messages.h +++ b/artiq/runtime/messages.h @@ -17,6 +17,7 @@ enum { MESSAGE_TYPE_RPC_SEND, MESSAGE_TYPE_RPC_RECV_REQUEST, MESSAGE_TYPE_RPC_RECV_REPLY, + MESSAGE_TYPE_RPC_BATCH, MESSAGE_TYPE_LOG, MESSAGE_TYPE_BRG_READY, @@ -97,6 +98,13 @@ struct msg_rpc_recv_reply { struct artiq_exception *exception; }; +struct msg_rpc_batch { + int type; + int service; + const char *tag; + void *ptr; +}; + struct msg_log { int type; const char *fmt; diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c index 521f7d353..ef4bf5adf 100644 --- a/artiq/runtime/session.c +++ b/artiq/runtime/session.c @@ -962,7 +962,8 @@ static int process_kmsg(struct msg_base *umsg) break; } - case MESSAGE_TYPE_RPC_SEND: { + case MESSAGE_TYPE_RPC_SEND: + case MESSAGE_TYPE_RPC_BATCH: { struct msg_rpc_send *msg = (struct msg_rpc_send *)umsg; if(!send_rpc_request(msg->service, msg->tag, msg->args)) { @@ -971,7 +972,8 @@ static int process_kmsg(struct msg_base *umsg) return 0; // restart session } - user_kernel_state = USER_KERNEL_WAIT_RPC; + if(msg->type == MESSAGE_TYPE_RPC_SEND) + user_kernel_state = USER_KERNEL_WAIT_RPC; mailbox_acknowledge(); break; }